For Tool authors

Bring your Tool. Kura handles the rest.

This guide is for people who build a Tool and submit its folder to Kura. You do not need to manage Render, the directory server, counters, SEO, or related Tool cards.

You build one self-contained Tool folder.

Kura is the hosting shell. The Kura owner manages deployment, the public domain, runtime supervision, the directory page, and shared infrastructure. As a Tool author, your job is to deliver one folder that starts correctly and works under its assigned Kura URL.

You provide

Application files, dependencies, tool.json, and a working root page.

Kura provides

Public routing, internal ports, process startup, restart handling, listing cards, related Tools, SEO, and counters.

You do not edit

server.js, runtime-manager.js, Render settings, or another Tool's folder.

The normal submission is only tools/your-tool/. You should not need to change anything outside that folder.

Everything your Tool needs must live together.

Choose a short, URL-safe folder name using lowercase letters, numbers, and hyphens. The folder name becomes the public slug.

tools/
└── token-check/
    ├── tool.json
    ├── app.py
    ├── requirements.txt
    ├── templates/
    │   └── index.html
    └── static/
        ├── style.css
        └── app.js

The example above is published at /tools/token-check/. Folder names must be unique. Avoid spaces, uppercase letters, Japanese characters, query symbols, and a trailing dot.

Minimum required files

Tool typeMinimum files
Staticindex.html and tool.json
Pythonapp.py or main.py, requirements.txt, and tool.json
Node.jspackage.json with a start script, application entry file, and tool.json
PHPindex.php and tool.json
JavaA generated JAR or Maven/Gradle source project, plus tool.json
Starter folders are available in tools/_templates/. Copy one, rename the folder, then replace the sample code.

Do not rebuild features Kura already supplies.

Once the folder is accepted, Kura automatically adds the Tool to the directory and links the card to its folder page. It also handles:

  • English/Japanese directory metadata from tool.json.
  • Search, categories, tags, favorites, view counts, and click counts.
  • Related Tool cards based on shared categories and tags.
  • Page title, meta description, canonical URL, Open Graph tags, JSON-LD, sitemap, and robots metadata.
  • Private internal port assignment and proxying for server-based Tools.
  • Automatic restart after an unexpected process exit unless restart: false.

Your Tool should focus on its own function. Do not add a duplicate Kura header, directory navigation, favorite counter, related Tool section, or SEO generator.

Write a complete tool.json.

{
  "name": "Token Check",
  "runtime": "python",
  "entry": "app.py",
  "description": "Checks whether a token is valid.",
  "descriptionJa": "Tokenが有効か確認します。",
  "category": "Utility",
  "categoryJa": "ユーティリティ",
  "icon": "TC",
  "tags": ["Python", "Token"],
  "tagsJa": ["Python", "トークン"],
  "published": true,
  "healthPath": "/",
  "startupTimeoutMs": 30000,
  "restart": true
}
FieldAuthor guidance
nameHuman-readable Tool name. It must be unique after case, spaces, punctuation, and Unicode-width differences are normalized.
description / descriptionJaOne direct sentence describing what the Tool does. Do not use marketing filler.
category / categoryJaUse one broad category. Related Tools use this field.
tags / tagsJaUse a few precise tags. Related Tool matching and search use them.
iconShort text shown when no image icon is configured. Two characters work best.
runtimestatic, python, node, php, java, or custom. Explicitly setting it is recommended.
entryMain Python file, Node file when no package start script is used, or JAR path.
healthPathA fast GET endpoint returning a status below 500. Default is /.
startupTimeoutMsIncrease this only for slow startup, such as a large Java application. Default is 30000.
restartLeave true or omit it. Use false only when an automatic restart would be incorrect.
publishedUse false while the folder is unfinished. Omit it or set true for publication.
allowDuplicateNameLeave false or omit it. Set true on every matching Tool only when duplicate display names are intentional.
build / startAdvanced overrides. Use only when the standard runtime behavior cannot start the Tool.
envNon-secret defaults only. Never commit API keys or passwords here.
tool.json must be valid JSON. Tool names are checked during the Render build; an accidental duplicate stops the build before deployment.

Your Tool is not hosted at the domain root.

A folder named token-check appears at /tools/token-check/. Kura strips that prefix before proxying a dynamic request to your app, but browser-side URLs must still remain inside the Tool path.

UseAvoidReason
<link href="./style.css"><link href="/style.css">A leading slash points to the Kura site root.
fetch("./check")fetch("/check")JavaScript URL strings are not automatically rewritten.
url("./image.png")url("/image.png")CSS url() values are not rewritten.
<form action="./check"><form action="/check">The absolute action exits the Tool prefix.
<a href="./history"><a href="/history">Relative navigation stays within the Tool.

Kura rewrites common root-relative HTML attributes in server-rendered HTML, redirects beginning with /, and cookie paths. That is a compatibility fallback, not a substitute for prefix-safe code.

Useful environment and proxy values

PORT=12000                 # Assigned internal port; value changes
HOST=127.0.0.1             # Recommended bind address
KURA_TOOL_SLUG=token-check
KURA_BASE_PATH=/tools/token-check
X-Forwarded-Prefix: /tools/token-check

For WebSocket clients, build the URL from location and the current Tool path rather than hardcoding a domain.

Give Kura a safe place to append related Tools.

Kura appends its related Tool section to the first available element in this order: main, #app, [role="main"], then body. A normal Tool page should therefore wrap the application in <main id="app">.

<body>
  <main id="app">
    <section class="tool">
      <h1>Token Check</h1>
      <!-- Tool UI -->
    </section>
  </main>
</body>

Layout rules that prevent the related section from appearing beside your app

  • Do not place all application elements directly inside a flex or grid body.
  • If body uses flex, make it a column: flex-direction: column.
  • Use min-height: 100vh instead of a fixed height: 100vh.
  • Do not set overflow: hidden on the entire page unless scrolling is handled inside main.
  • Avoid full-page fixed positioning. It can cover content injected below the app.
html, body { margin: 0; }
body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
main {
  width: 100%;
  flex: 1 0 auto;
}

A server-based Tool must start an HTTP server.

Python, Node.js, PHP, Java, and custom Tools run as child processes. Kura assigns a private port and proxies public requests to it.

Read the provided PORT.

Never hardcode 3000, 5000, 8000, or another fixed port.

Bind to 127.0.0.1 or HOST.

The Tool does not need its own public listener.

Serve a fast health path.

Kura waits for healthPath and accepts any response below 500.

Keep the process alive.

A script that performs work and exits is not a web Tool.

Do not launch a second background server and let the configured process exit. Kura supervises the process it starts; that process should remain the actual server process.

HTML / CSS / JavaScript

No server required

Use the static runtime when all work can happen in the browser. Kura serves files directly from the Tool folder.

tools/color-picker/
├── index.html
├── style.css
├── app.js
└── tool.json
{
  "name": "Color Picker",
  "runtime": "static",
  "description": "Pick and convert colors in the browser.",
  "descriptionJa": "ブラウザ上で色を選択・変換します。",
  "category": "Design",
  "categoryJa": "デザイン",
  "icon": "CP",
  "tags": ["Color", "Browser"],
  "tagsJa": ["カラー", "ブラウザ"]
}
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Color Picker</title>
  <link rel="stylesheet" href="./style.css">
  <script src="./app.js" defer></script>
</head>
<body>
  <main id="app">...</main>
</body>
</html>

Static Tools cannot execute Python, Node, PHP, or Java on demand. Use a dynamic runtime whenever the Tool needs server-side code, private credentials, filesystem processing, or a database.

Python

Flask · FastAPI · Django

Each Python Tool with requirements.txt receives its own virtual environment during the build. The default entry is app.py, then main.py.

Flask: recommended simple structure

tools/token-check/
├── app.py
├── requirements.txt
├── templates/
│   └── index.html
├── static/
│   └── style.css
└── tool.json
# requirements.txt
Flask==3.1.1
# app.py
import os
from flask import Flask, jsonify, render_template, request

app = Flask(__name__)

@app.get("/")
def index():
    return render_template("index.html")

@app.get("/health")
def health():
    return {"ok": True}

@app.post("/check")
def check():
    value = request.form.get("value", "").strip()
    return jsonify({"valid": bool(value)})

if __name__ == "__main__":
    app.run(
        host=os.environ.get("HOST", "127.0.0.1"),
        port=int(os.environ["PORT"]),
    )
{
  "name": "Token Check",
  "runtime": "python",
  "entry": "app.py",
  "healthPath": "/health",
  "tags": ["Python", "Token"]
}

FastAPI

# requirements.txt
fastapi==0.116.1
uvicorn==0.35.0
# main.py
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def index():
    return "<main id='app'><h1>FastAPI Tool</h1></main>"

@app.get("/health")
def health():
    return {"ok": True}
{
  "name": "FastAPI Tool",
  "runtime": "python",
  "start": ".venv/bin/uvicorn main:app --host 127.0.0.1 --port $PORT",
  "healthPath": "/health"
}

Django

{
  "name": "Django Tool",
  "runtime": "python",
  "start": ".venv/bin/python manage.py runserver 127.0.0.1:$PORT",
  "healthPath": "/health/",
  "startupTimeoutMs": 60000
}

Include 127.0.0.1 and localhost in ALLOWED_HOSTS. For generated links, static files, and forms, configure the application for the KURA_BASE_PATH prefix or use relative URLs.

Python-specific mistakes

  • Every imported third-party package must appear in requirements.txt.
  • Do not call an external API secret directly from browser JavaScript; keep that call in Python.
  • Do not use a development reloader that starts a second process. Disable reload mode.
  • Long setup work should happen before the health endpoint reports ready.

Node.js

Express · Fastify · Native HTTP

When package.json exists, Kura installs dependencies and starts the start script. Commit package-lock.json for reproducible installs.

tools/text-tool/
├── package.json
├── package-lock.json
├── server.js
├── public/
│   └── index.html
└── tool.json
{
  "private": true,
  "scripts": { "start": "node server.js" },
  "dependencies": { "express": "^5.1.0" }
}
const express = require("express");
const path = require("path");

const app = express();
app.set("trust proxy", 1);
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(express.static(path.join(__dirname, "public")));

app.get("/health", (_req, res) => res.json({ ok: true }));
app.post("/convert", (req, res) => res.json({ result: req.body.value ?? "" }));

app.listen(Number(process.env.PORT), process.env.HOST || "127.0.0.1");
{
  "name": "Text Tool",
  "runtime": "node",
  "healthPath": "/health",
  "tags": ["Node.js", "Text"]
}

Set runNpmBuild: true when the repository needs npm run build. After the build, dev dependencies are pruned, so runtime packages must be in dependencies, not only devDependencies.

PHP

Built-in PHP server

Place index.php at the Tool root. Kura starts php -S 127.0.0.1:$PORT -t . automatically.

tools/hash-tool/
├── index.php
├── style.css
└── tool.json
<?php
$result = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $value = $_POST["value"] ?? "";
    $result = hash("sha256", $value);
}
?>
<!doctype html>
<html>
<head><link rel="stylesheet" href="./style.css"></head>
<body>
  <main id="app">
    <form method="post" action="./">
      <input name="value">
      <button>Hash</button>
    </form>
    <pre><?= htmlspecialchars($result, ENT_QUOTES, "UTF-8") ?></pre>
  </main>
</body>
</html>
{
  "name": "Hash Tool",
  "runtime": "php",
  "healthPath": "/",
  "tags": ["PHP", "Hash"]
}

The default image does not install Composer packages automatically. A Composer-based Tool requires coordination with the Kura owner or a custom build setup.

Java

JAR · Maven · Gradle · Spring Boot

Kura launches the configured JAR with java -jar. Maven and Gradle source projects are built automatically, but your entry must point to the actual generated JAR.

Prebuilt JAR

tools/java-tool/
├── app.jar
└── tool.json
{
  "name": "Java Tool",
  "runtime": "java",
  "entry": "app.jar",
  "healthPath": "/health"
}

Spring Boot with Maven

tools/spring-tool/
├── pom.xml
├── src/
└── tool.json
# src/main/resources/application.properties
server.address=127.0.0.1
server.port=${PORT}
server.forward-headers-strategy=framework
{
  "name": "Spring Tool",
  "runtime": "java",
  "entry": "target/spring-tool.jar",
  "healthPath": "/actuator/health",
  "startupTimeoutMs": 90000
}

For Gradle, set entry to the resulting file under build/libs/. Avoid version-changing JAR names when possible, or update the metadata whenever the version changes.

Custom runtime

Advanced

Use a custom runtime only when the required executable already exists in the Kura Docker image, or when the Kura owner has agreed to add it. A Tool folder cannot install a new system language by itself at runtime.

{
  "name": "Custom Server",
  "runtime": "custom",
  "build": "your-build-command",
  "start": "your-server --host 127.0.0.1 --port $PORT",
  "healthPath": "/health",
  "startupTimeoutMs": 60000,
  "restart": true
}

A string start command replaces $PORT. An array command does not replace it, so the program must read the environment variable itself.

Before submitting a custom runtime, tell the Kura owner exactly which system package, binary, version, build command, and start command are required.

Do not treat the Tool folder as permanent storage.

Files written into the deployed application directory may disappear after a redeploy or service replacement. Use an approved database or an agreed persistent directory for data that must survive.

Secrets

  • Never put API keys, passwords, private tokens, or credentials in tool.json, source code, frontend JavaScript, or committed .env files.
  • Send the required environment variable names to the Kura owner.
  • Prefix generic names to prevent collisions, for example TOKEN_CHECK_API_KEY rather than API_KEY.
  • Read them at runtime with your language's environment-variable API.
# Python
secret = os.environ["TOKEN_CHECK_API_KEY"]

// Node.js
const secret = process.env.TOKEN_CHECK_API_KEY;

// Java
String secret = System.getenv("TOKEN_CHECK_API_KEY");

Test both the app and the Kura-mounted URL.

1. Test the Tool by itself

Run a dynamic Tool with an arbitrary local port and verify the health route, main page, forms, API calls, static files, errors, and mobile layout.

# macOS / Linux example
PORT=12001 HOST=127.0.0.1 python app.py

# PowerShell example
$env:PORT="12001"
$env:HOST="127.0.0.1"
python app.py

2. Test inside Kura

Place the folder under tools/.

Do not put it inside _templates.

Install/build dependencies.

From the Kura project root, run npm run build.

Start Kura.

Run npm start and open the Tool through /tools/your-slug/.

Check the actual subpath.

Test forms, JavaScript fetch calls, redirects, cookies, assets, related Tools, and responsive layout.

Opening only http://127.0.0.1:12001/ is not enough. A Tool can work at the root and still break under /tools/your-slug/.

Fix the symptom at the Tool level.

SymptomLikely causeAuthor fix
Tool card does not appearInvalid JSON, published: false, ignored folder name, or missing static index.html.Validate tool.json, use a normal slug, and include the required entry file.
503 Tool failed to startThe process crashed, did not read PORT, or failed the health check.Run the same start command locally, inspect imports/dependencies, and verify healthPath.
CSS, image, or JavaScript is 404An asset URL begins with /.Use ./asset or framework base-path configuration.
Browser API call opens the Kura rootfetch("/route") is absolute.Use fetch("./route") or construct a URL from the current pathname.
Related Tools appear to the rightThe page has no main wrapper and body is a row flex/grid container.Wrap the Tool in <main id="app"> and use a column page layout.
Python module missingThe package is not in requirements.txt.Add an exact compatible dependency and rebuild.
Node module missing in productionA runtime package is only in devDependencies.Move it to dependencies.
Java never becomes readyWrong JAR path or slow boot.Correct entry, expose a health route, and increase startupTimeoutMs.
Saved data disappearsThe Tool writes to ephemeral application files.Use the approved persistent store or database.

Send the folder only after every item passes.

Folder slug is lowercase, unique, and URL-safe.
tool.json is valid and descriptions are complete.
All required dependencies are declared.
Dynamic server reads PORT and stays running.
Health endpoint responds quickly with a status below 500.
HTML, CSS, forms, and browser API calls use prefix-safe URLs.
The page contains <main id="app"> or an equivalent main root.
No secret is committed inside the Tool folder.
Tool works through /tools/your-slug/, not only at localhost root.
Mobile layout and the injected related Tool section are both usable.

制作者が用意するのは、自己完結したToolフォルダー1つです。

KuraはToolを公開するための土台です。Kuraの運営者が、デプロイ、公開ドメイン、プロセス管理、一覧ページ、共通機能を管理します。Tool制作者は、正しく起動し、割り当てられたKura上のURLで動作するフォルダーを提出します。

制作者が用意するもの

アプリ本体、依存関係、tool.json、正常に表示できるトップページ。

Kuraが用意するもの

公開ルーティング、内部ポート、起動・再起動、一覧カード、関連Tool、SEO、閲覧・クリック集計。

制作者が変更しないもの

server.jsruntime-manager.js、Render設定、他のToolフォルダー。

通常、提出するのは tools/あなたのTool/ だけです。自分のToolフォルダー外を変更する必要はありません。

Toolに必要なファイルは、すべて1つのフォルダーにまとめます。

フォルダー名には、小文字の英数字とハイフンを使います。このフォルダー名が公開URLのslugになります。

tools/
└── token-check/
    ├── tool.json
    ├── app.py
    ├── requirements.txt
    ├── templates/
    │   └── index.html
    └── static/
        ├── style.css
        └── app.js

この例は /tools/token-check/ で公開されます。フォルダー名は重複不可です。空白、大文字、日本語、URL記号、末尾のドットは避けてください。

ランタイム別の最低限必要なファイル

種類最低限必要なもの
静的Toolindex.htmltool.json
Pythonapp.py または main.pyrequirements.txttool.json
Node.jsstart スクリプト付きの package.json、アプリ本体、tool.json
PHPindex.phptool.json
Java生成済みJAR、またはMaven/Gradleプロジェクトと tool.json
tools/_templates/ にひな形があります。コピーしてフォルダー名を変更し、サンプルコードを置き換えてください。

Kura側にある機能を、Tool内で作り直す必要はありません。

フォルダーが承認されると、Kuraが自動で一覧へ追加し、そのカードからToolページへ移動できるようにします。さらに以下を自動処理します。

  • tool.json を使った日本語・英語の一覧情報。
  • 検索、カテゴリ、タグ、お気に入り、閲覧数、クリック数。
  • カテゴリとタグに基づく関連Tool。
  • ページタイトル、説明、canonical、OGP、JSON-LD、sitemap、robots。
  • サーバー型Toolの内部ポート割り当てと公開URLへのプロキシ。
  • restart: false でない限り、予期しない終了後の自動再起動。

Toolは本来の機能だけに集中してください。Kura共通ヘッダー、一覧ナビゲーション、お気に入り集計、関連Tool欄、SEO生成をTool側で重複実装しないでください。

tool.jsonを丁寧に記述します。

{
  "name": "Token Check",
  "runtime": "python",
  "entry": "app.py",
  "description": "Checks whether a token is valid.",
  "descriptionJa": "Tokenが有効か確認します。",
  "category": "Utility",
  "categoryJa": "ユーティリティ",
  "icon": "TC",
  "tags": ["Python", "Token"],
  "tagsJa": ["Python", "トークン"],
  "published": true,
  "healthPath": "/",
  "startupTimeoutMs": 30000,
  "restart": true
}
項目制作者向け説明
name一覧カードに表示されるTool名。大文字小文字、空白、記号、全角半角の差を正規化した後も一意である必要があります。
description / descriptionJa何ができるToolなのかを、誇張なしの1文で書きます。
category / categoryJa大分類を1つ指定します。関連Toolの判定にも使われます。
tags / tagsJa少数の正確なタグを指定します。検索と関連Toolに使われます。
icon画像アイコンがない場合に表示される短い文字。2文字程度を推奨します。
runtimestaticpythonnodephpjavacustom。明示指定を推奨します。
entryPythonの起動ファイル、package startを使わないNodeのファイル、またはJARのパス。
healthPath素早く応答し、500未満を返すGETルート。既定値は /
startupTimeoutMsJavaなど起動が遅い場合だけ増やします。既定値は30000。
restarttrueまたは省略を推奨。自動再起動が不適切な特殊ケースのみfalse。
published未完成時はfalse。公開時はtrueまたは省略。
allowDuplicateName通常は省略またはfalse。意図的に同名を許可する場合のみ、該当する全Toolでtrueにします。
build / start標準の起動方法で動かない場合だけ使う上級設定。
env秘密ではない初期値専用。APIキーやパスワードを入れないでください。
tool.json は正しいJSONである必要があります。Tool名はRenderのビルド時に検査され、意図しない重複があるとデプロイ前にビルドが停止します。

Toolはドメイン直下ではなく、KuraのURL配下で公開されます。

token-check フォルダーは /tools/token-check/ で表示されます。動的Toolへの転送時にはKuraがこのプレフィックスを取り除きますが、ブラウザ側のURLはToolの配下に残るように書く必要があります。

推奨避ける理由
<link href="./style.css"><link href="/style.css">先頭のスラッシュはKura本体のルートを指します。
fetch("./check")fetch("/check")JavaScript内のURL文字列は自動書き換えされません。
url("./image.png")url("/image.png")CSSの url() は自動書き換えされません。
<form action="./check"><form action="/check">絶対パスはToolの外へ送信されます。
<a href="./history"><a href="/history">相対リンクならTool内に留まります。

Kuraは、サーバーが返したHTML内の一般的なルート相対属性、/ から始まるリダイレクト、Cookie Pathを互換処理として書き換えます。ただし、最初から相対URLで作るのが最も確実です。

利用できる環境変数・プロキシ情報

PORT=12000                 # Kuraが割り当てる内部ポート。固定値ではありません
HOST=127.0.0.1             # 推奨待受アドレス
KURA_TOOL_SLUG=token-check
KURA_BASE_PATH=/tools/token-check
X-Forwarded-Prefix: /tools/token-check

WebSocketクライアントもドメインを固定せず、location と現在のToolパスからURLを組み立ててください。

関連Toolを下に追加できる、正常なページ構造にします。

Kuraは、main#app[role="main"]、最後に body の順で関連Toolの追加先を探します。そのため、Tool本体を <main id="app"> で囲むことを推奨します。

<body>
  <main id="app">
    <section class="tool">
      <h1>Token Check</h1>
      <!-- ToolのUI -->
    </section>
  </main>
</body>

関連Toolが右側へ押し込まれないためのルール

  • Toolの全要素を、横向きflex/gridの body 直下へ直接置かない。
  • body をflexにする場合は flex-direction: column にする。
  • 固定の height: 100vh ではなく min-height: 100vh を使う。
  • ページ全体へ overflow: hidden を設定しない。設定する場合はmain内でスクロールを管理する。
  • ページ全体を覆うfixed配置を避ける。下に追加された内容を隠す原因になります。
html, body { margin: 0; }
body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
main {
  width: 100%;
  flex: 1 0 auto;
}

サーバー型Toolは、HTTPサーバーを起動し続ける必要があります。

Python、Node.js、PHP、Java、カスタムToolは子プロセスとして起動します。Kuraが内部ポートを割り当て、公開URLからそのポートへ転送します。

渡されたPORTを読む。

3000、5000、8000などを固定しないでください。

127.0.0.1またはHOSTで待ち受ける。

Tool自身が外部公開ポートを持つ必要はありません。

軽いhealthPathを用意する。

KuraはそのルートへGETし、500未満なら起動完了と判断します。

プロセスを終了させない。

処理してすぐ終了するスクリプトはWeb Toolとして動きません。

別のバックグラウンドサーバーだけを起動して、Kuraが開始したプロセスを終了させないでください。Kuraが監視するプロセス自体がサーバーとして動き続ける必要があります。

HTML / CSS / JavaScript

サーバー不要

すべての処理がブラウザ内で完結する場合はstaticを使います。KuraがToolフォルダー内のファイルをそのまま配信します。

tools/color-picker/
├── index.html
├── style.css
├── app.js
└── tool.json
{
  "name": "Color Picker",
  "runtime": "static",
  "description": "Pick and convert colors in the browser.",
  "descriptionJa": "ブラウザ上で色を選択・変換します。",
  "category": "Design",
  "categoryJa": "デザイン",
  "icon": "CP",
  "tags": ["Color", "Browser"],
  "tagsJa": ["カラー", "ブラウザ"]
}
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Color Picker</title>
  <link rel="stylesheet" href="./style.css">
  <script src="./app.js" defer></script>
</head>
<body>
  <main id="app">...</main>
</body>
</html>

static ToolではPython、Node、PHP、Javaをオンデマンド実行できません。秘密情報、サーバー側処理、ファイル処理、データベースが必要なら動的ランタイムを使います。

Python

Flask · FastAPI · Django

requirements.txt があるPython Toolには、ビルド時にTool専用の仮想環境が作られます。標準の起動ファイルは app.py、次に main.py です。

Flaskの推奨構成

tools/token-check/
├── app.py
├── requirements.txt
├── templates/
│   └── index.html
├── static/
│   └── style.css
└── tool.json
# requirements.txt
Flask==3.1.1
# app.py
import os
from flask import Flask, jsonify, render_template, request

app = Flask(__name__)

@app.get("/")
def index():
    return render_template("index.html")

@app.get("/health")
def health():
    return {"ok": True}

@app.post("/check")
def check():
    value = request.form.get("value", "").strip()
    return jsonify({"valid": bool(value)})

if __name__ == "__main__":
    app.run(
        host=os.environ.get("HOST", "127.0.0.1"),
        port=int(os.environ["PORT"]),
    )
{
  "name": "Token Check",
  "runtime": "python",
  "entry": "app.py",
  "healthPath": "/health",
  "tags": ["Python", "Token"]
}

FastAPI

# requirements.txt
fastapi==0.116.1
uvicorn==0.35.0
# main.py
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def index():
    return "<main id='app'><h1>FastAPI Tool</h1></main>"

@app.get("/health")
def health():
    return {"ok": True}
{
  "name": "FastAPI Tool",
  "runtime": "python",
  "start": ".venv/bin/uvicorn main:app --host 127.0.0.1 --port $PORT",
  "healthPath": "/health"
}

Django

{
  "name": "Django Tool",
  "runtime": "python",
  "start": ".venv/bin/python manage.py runserver 127.0.0.1:$PORT",
  "healthPath": "/health/",
  "startupTimeoutMs": 60000
}

ALLOWED_HOSTS127.0.0.1localhost を含めます。生成リンク、static、フォームは KURA_BASE_PATH に対応させるか、相対URLを使ってください。

Pythonで起きやすいミス

  • importする外部パッケージをすべて requirements.txt に書く。
  • 秘密のAPIキーをブラウザJavaScriptから直接使わず、Python側で呼び出す。
  • 2つ目のプロセスを起動する開発用リローダーを使わない。reloadを無効にする。
  • 初期化処理が終わる前にhealthがreadyを返さないようにする。

Node.js

Express · Fastify · Native HTTP

package.json がある場合、依存関係をインストールして start スクリプトを実行します。再現性のため package-lock.json を含めてください。

tools/text-tool/
├── package.json
├── package-lock.json
├── server.js
├── public/
│   └── index.html
└── tool.json
{
  "private": true,
  "scripts": { "start": "node server.js" },
  "dependencies": { "express": "^5.1.0" }
}
const express = require("express");
const path = require("path");

const app = express();
app.set("trust proxy", 1);
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(express.static(path.join(__dirname, "public")));

app.get("/health", (_req, res) => res.json({ ok: true }));
app.post("/convert", (req, res) => res.json({ result: req.body.value ?? "" }));

app.listen(Number(process.env.PORT), process.env.HOST || "127.0.0.1");
{
  "name": "Text Tool",
  "runtime": "node",
  "healthPath": "/health",
  "tags": ["Node.js", "Text"]
}

本番起動前に npm run build が必要なら runNpmBuild: true を指定します。ビルド後にdev依存は削除されるため、実行時に必要なパッケージは dependencies に入れてください。

PHP

PHP内蔵サーバー

Tool直下へ index.php を置きます。Kuraが php -S 127.0.0.1:$PORT -t . を自動実行します。

tools/hash-tool/
├── index.php
├── style.css
└── tool.json
<?php
$result = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $value = $_POST["value"] ?? "";
    $result = hash("sha256", $value);
}
?>
<!doctype html>
<html>
<head><link rel="stylesheet" href="./style.css"></head>
<body>
  <main id="app">
    <form method="post" action="./">
      <input name="value">
      <button>Hash</button>
    </form>
    <pre><?= htmlspecialchars($result, ENT_QUOTES, "UTF-8") ?></pre>
  </main>
</body>
</html>
{
  "name": "Hash Tool",
  "runtime": "php",
  "healthPath": "/",
  "tags": ["PHP", "Hash"]
}

標準イメージではComposer依存を自動導入しません。Composerを使うToolは、Kura運営者と相談してカスタムビルドを用意する必要があります。

Java

JAR · Maven · Gradle · Spring Boot

Kuraは指定されたJARを java -jar で起動します。Maven・Gradleのソースプロジェクトは自動ビルドされますが、entry は実際に生成されるJARを指す必要があります。

生成済みJAR

tools/java-tool/
├── app.jar
└── tool.json
{
  "name": "Java Tool",
  "runtime": "java",
  "entry": "app.jar",
  "healthPath": "/health"
}

Maven + Spring Boot

tools/spring-tool/
├── pom.xml
├── src/
└── tool.json
# src/main/resources/application.properties
server.address=127.0.0.1
server.port=${PORT}
server.forward-headers-strategy=framework
{
  "name": "Spring Tool",
  "runtime": "java",
  "entry": "target/spring-tool.jar",
  "healthPath": "/actuator/health",
  "startupTimeoutMs": 90000
}

Gradleでは build/libs/ に生成される実ファイルを entry に指定します。可能なら、バージョンごとにJAR名が変わらない設定にしてください。

カスタムランタイム

上級者向け

必要な実行ファイルがKuraのDockerイメージに存在する場合、または運営者が追加に同意した場合のみ使います。Toolフォルダー単体で、実行時に新しいシステム言語を導入することはできません。

{
  "name": "Custom Server",
  "runtime": "custom",
  "build": "your-build-command",
  "start": "your-server --host 127.0.0.1 --port $PORT",
  "healthPath": "/health",
  "startupTimeoutMs": 60000,
  "restart": true
}

文字列形式の start では $PORT が置換されます。配列形式では置換されないため、プログラム側で環境変数を読んでください。

カスタムランタイムを提出する前に、必要なシステムパッケージ、バイナリ、バージョン、ビルドコマンド、起動コマンドをKura運営者へ伝えてください。

Toolフォルダーを永続ストレージとして使わないでください。

デプロイ済みアプリのフォルダーへ書き込んだファイルは、再デプロイやサービス置換で消える可能性があります。残す必要があるデータは、承認されたデータベースまたは運営者と合意した永続領域へ保存します。

秘密情報

  • APIキー、パスワード、非公開Token、認証情報を tool.json、ソースコード、フロントエンドJavaScript、コミット対象の .env に入れない。
  • 必要な環境変数名をKura運営者へ伝える。
  • 他Toolとの名前衝突を防ぐため、API_KEY ではなく TOKEN_CHECK_API_KEY のように接頭辞を付ける。
  • 各言語の環境変数APIから実行時に読み込む。
# Python
secret = os.environ["TOKEN_CHECK_API_KEY"]

// Node.js
const secret = process.env.TOKEN_CHECK_API_KEY;

// Java
String secret = System.getenv("TOKEN_CHECK_API_KEY");

単体動作と、Kura配下での動作を両方確認します。

1. Tool単体で確認

動的Toolには任意のローカルポートを設定し、health、トップページ、フォーム、API、静的ファイル、エラー処理、スマホ表示を確認します。

# macOS / Linux
PORT=12001 HOST=127.0.0.1 python app.py

# PowerShell
$env:PORT="12001"
$env:HOST="127.0.0.1"
python app.py

2. Kura内で確認

tools/直下へフォルダーを置く。

_templates の中には置かないでください。

依存関係を導入・ビルドする。

Kuraプロジェクト直下で npm run build を実行します。

Kuraを起動する。

npm start を実行し、/tools/your-slug/ からToolを開きます。

実際のサブパスで確認する。

フォーム、fetch、リダイレクト、Cookie、アセット、関連Tool、レスポンシブ表示を確認します。

http://127.0.0.1:12001/ だけで動くことを確認しても不十分です。ルートでは動いても、/tools/your-slug/ 配下では壊れることがあります。

症状に応じてTool側を修正します。

症状主な原因制作者が直す内容
一覧にToolが出ないJSON不正、published: false、無視対象のフォルダー名、staticのindex不足。tool.json を検証し、通常のslugと必要な起動ファイルを用意します。
503 Tool failed to startクラッシュ、PORT未使用、health失敗。同じ起動コマンドをローカルで試し、依存関係と healthPath を確認します。
CSS・画像・JavaScriptが404アセットURLが / から始まっている。./asset またはフレームワークのbase path設定を使います。
ブラウザAPIがKura本体へ飛ぶfetch("/route") が絶対パス。fetch("./route") または現在のpathnameからURLを作ります。
関連Toolが右側へ表示されるmainがなく、bodyが横向きflex/grid。<main id="app"> で囲み、ページを縦方向レイアウトにします。
Pythonモジュールがないrequirements.txt に未記載。互換性のある依存関係を追加して再ビルドします。
本番でNodeモジュールがない実行時パッケージが devDependencies のみ。dependencies へ移動します。
JavaがreadyにならないJARパス違い、起動が遅い。entry、health、startupTimeoutMs を修正します。
保存データが消える一時的なアプリファイルへ保存している。承認済みの永続領域またはデータベースを使います。

すべて確認してからフォルダーを提出します。

フォルダーslugが小文字、重複なし、URL安全。
tool.json が正しく、説明が埋まっている。
必要な依存関係をすべて宣言している。
動的サーバーが PORT を読み、終了せず動作する。
healthが高速に500未満を返す。
HTML、CSS、フォーム、fetchがサブパス対応。
<main id="app"> または同等のmain領域がある。
Toolフォルダー内に秘密情報がない。
localhost直下だけでなく /tools/your-slug/ で動く。
スマホ表示と自動追加される関連Tool欄が使いやすい。