Tanpin について
Tanpinは店舗向けの在庫管理サービスで、SKUごとの需要予測、発注計算、仕入先への発注書、納品予定を扱います。REST API、ダッシュボード、デーモン、MCPサーバーをNode.jsアプリにまとめています。
主な機能
- 01SKUごとの需要予測
- 02発注量と安全在庫の計算
- 03ABC分類と廃止候補の表示
- 04仕入先への発注書とメール
- 05納品予定の算出
- 06REST API、ダッシュボード、デーモン
活用シーン
- 01商品ごとの需要予測
- 02発注が必要な商品の特定
- 03仕入先への注文準備と追跡
- 04低回転または需要のない在庫の特定
MCP 対応
stdioのMCPサーバーがTanpinのHTTP APIに接続し、在庫操作をMCPクライアント向けのツールとして提供します。APIのURLと、必要に応じてAPIキーを設定します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
docker run -i --rm ghcr.io/willykeenan/tanpin:0.1.0ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ghcr.io/willykeenan/tanpin:0.1.0stdioツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Tanpin
品目ごとに自動で並べ替える在庫管理。 SKU ごとの需要予測、自動発注、仕入先へのメール送信、納品予定時刻に対応します。REST API、MCP サーバー、デーモン、ダッシュボードを備えています。純粋な Node 22 製で、実行時の依存関係はゼロです(node:* の組み込みモジュールのみ)。
ライブデモ:huggingface.co/spaces/willykeenan/tanpin(読み取り専用、サンプルデータを登録済み)

デモの実際のスクリーンショットです。表示されているデータは架空のサンプルデータです。
![]() 曜日とトレンドの要因、安全在庫、発注点、店長の仮説を含む SKU ごとの予測。 | ![]() サイクルで作成された、仕入先ごとにまとめられた発注。 |
単品管理とは
単品管理(単品管理、「単品ごとの管理」)では、すべての SKU をそれぞれ独立したビジネスとして扱います。店長は天候、地域のイベント、曜日などをもとに、その品目の翌日の需要を仮説として立て、それに基づいて発注し、実際の販売数と照らし合わせて結果を検証します。回転率の高い品目は厳密に管理し、頻繁に補充します。ロングテールの商品にはよりシンプルなルールを適用し、最初に販売終了の対象とします。
Tanpin は、この一連の流れをソフトウェアで実現します。
- SKU ごとに予測する — 直近の販売実績、曜日ごとの季節性、短期トレンド、店長が入力する仮説(イベント、天候、プロモーション)をもとに需要を予測します。
- 発注数を算出する — 選択したサービスレベルに応じた安全在庫、発注点、EOQ、入数単位への丸め、最小発注数量、在庫日数の JIT 上限を計算します。
- 発注して追跡する — 仕入先ごとに発注をまとめて自動でメール送信し、リードタイム、締め時刻、納品可能時間帯をもとに納品予定時刻を算出します。
- 売れない商品は在庫しない — 動きのない SKU と販売の遅い SKU に販売終了のフラグを付けます。
ABC 分類も同じ考え方に基づいています。少数の A 商品が価値の大半を生み出すため、より新しいデータに基づいて頻繁に発注を見直します。C 商品は販売終了の候補です。
機能
- SKU ごとの予測 — 直近のデータに重みを付けた移動平均、曜日係数、トレンド(±40%)、仮説の累積反映
- 自動発注 — 安全在庫、発注点、EOQ、入数、最小発注数量、在庫日数の上限
- ABC 分類と販売終了 — 売上構成比による A/B/C 分類。動きのない商品(需要なし)と販売の遅い商品(在庫日数が多すぎる)にフラグを付けます
- デーモン — N 分ごと(デフォルトは 15 分)に再予測、再分類、発注書の下書き作成を実行し、オプションでメール送信と自動入荷処理も行います
- 仕入先へのメール送信 —
SMTP_HOSTが設定されている場合は暗黙的 TLS(ポート 465)で SMTP を使用し、未設定の場合は.emlファイルを outbox に保存します - 納品予定時刻 — 締め時刻(現在は UTC)を考慮し、リードタイムを次の納品可能時間帯に合わせます
- REST API — 商品は SKU で指定します。
/openapi.jsonに OpenAPI 3.1 を、/llms.txtにエージェント向けガイドを用意しています - MCP サーバー — Claude Code、Codex、Cursor、および任意の MCP クライアント向けの、標準入出力経由の改行区切り JSON-RPC
- ダッシュボード —
/で利用できるバニラ JS の SPA(商品、発注、仕入先、予測、アクティビティ、API、設定) - Webhook — HMAC-SHA256 で署名した JSON POST(
X-Inventory-Signature) - CSV — 商品のインポート/エクスポート、在庫変動の監査記録のエクスポート
- API キー — SHA-256 ハッシュで保存され、キー全体は一度だけ表示されます
- Idempotency-Key — 販売記録と発注書の書き込みに対する 24 時間の再実行ウィンドウ
- プラグイン —
TANPIN_PLUGINで追加の HTTP ルートと利用上限を読み込みます - ストレージ — SQLite(
node:sqlite、WAL、行単位の増分書き込み、data/inventory.sqlite)をサーバーとスタンドアロンのデーモン間で安全に共有します。TANPIN_STORE=jsonを指定すると JSON にフォールバックします
60秒で始める
Node.js 22 以降が必要です。
npx github:willykeenan/tanpin serve
http://localhost:4173 を開き、デモデータを読み込むをクリックします。コンビニエンスストアの商品カタログ、約35日分の売上、リアルタイムの予測、再発注の推奨が読み込まれます。
このリポジトリをクローンした場合:
node bin/tanpin serve
Docker
公開デモイメージ(Hugging Face Space):
docker build -f space/Dockerfile -t tanpin-demo .
docker run --rm -p 7860:7860 tanpin-demo
http://localhost:7860 を開きます。このイメージは DEMO_MODE=1 PORT=7860 HOST=0.0.0.0 node bin/tanpin serve を実行します。読み取りは自由に行えます。書き込みはデモカタログ(売上、調整、発注書、仮説、シード、サイクル)に制限され、キーは不要です。Webhook と SMTP は無効で、カタログは30分ごとに再シードされます。
セルフホスト用イメージ(ルートの Dockerfile / compose.yml):リクエストは Docker のポートマッピングを経由してコンテナに届くため、「このマシン」からのリクエストではなく、すべての API 呼び出しにキーが必要です。マスターキーを指定して起動し、そのキーをダッシュボードのプロンプトに貼り付けます:
docker build -t tanpin .
docker run --rm -p 4173:4173 -e TANPIN_ADMIN_KEY="$(openssl rand -hex 24)" -v tanpin-data:/app/data tanpin
# or: echo "TANPIN_ADMIN_KEY=$(openssl rand -hex 24)" > .env && docker compose up
最初の売上と最初の発注書を含む詳細は、docs/QUICKSTART.md をご覧ください。
API
Authorization: Bearer <key> または X-API-Key: <key> を送信します。サーバーと同じマシン上から localhost に直接送信するリクエストには、デフォルトでキーは不要です。リバースプロキシ、コンテナのポートマッピング、または別のブラウザーオリジンを経由するリクエストにはキーが必要です(セキュリティモデルを参照)。
店舗を管理する5つの呼び出し:
# Whole world in one response: KPIs, products with live math, open POs, suppliers
curl http://localhost:4173/api/state
# Record a sale (SKU everywhere; Idempotency-Key for safe retries)
curl -X POST http://localhost:4173/api/sales \
-H 'Content-Type: application/json' \
-d '{"sku":"COFFEE-HOT","qty":2}'
# Dry run of the ordering engine, grouped by supplier
curl http://localhost:4173/api/recommendations
# Turn a recommendation into a real PO (and email it)
curl -X POST http://localhost:4173/api/purchase-orders \
-H 'Content-Type: application/json' \
-d '{"supplierId":"FreshFoods Distribution","fromRecommendations":true,"autoSend":true}'
# Forward-looking demand bump — forecasts and auto-orders adjust immediately
curl -X POST http://localhost:4173/api/hypotheses \
-H 'Content-Type: application/json' \
-d '{"note":"heatwave next week","multiplier":1.4,"category":"beverage"}'
エラーは {"error":"<message>","code":"<machine_code>"} 形式です。/api/... と /api/v1/... は同等です。
完全なリファレンス:docs/API.md。ライブコントラクト:GET /openapi.json。エージェント向けガイド:GET /llms.txt。
MCP
在庫サーバーを起動し、MCP クライアントの接続先を tanpin mcp に設定します。プロセスは stdio 上で JSON-RPC を使用し、HTTP API を呼び出します。
環境変数:INVENTORY_URL(デフォルトは http://localhost:4173)、サーバーがキーを要求する場合は INVENTORY_API_KEY。
Claude Code
claude mcp add tanpin --env INVENTORY_URL=http://localhost:4173 -- npx -y github:willykeenan/tanpin mcp
または、プロジェクトの .mcp.json:
{
"mcpServers": {
"tanpin": {
"command": "npx",
"args": ["-y", "github:willykeenan/tanpin", "mcp"],
"env": {
"INVENTORY_URL": "http://localhost:4173"
}
}
}
}
ローカルのチェックアウトから実行する場合は、npx の代わりに "command": "node" と "args": ["src/mcp.js"](または ["bin/tanpin", "mcp"])を使用します。
Codex
~/.codex/config.toml(またはプロジェクトの Codex 設定):
[mcp_servers.tanpin]
command = "npx"
args = ["-y", "github:willykeenan/tanpin", "mcp"]
[mcp_servers.tanpin.env]
INVENTORY_URL = "http://localhost:4173"
Cursor
.cursor/mcp.json:
{
"mcpServers": {
"tanpin": {
"command": "npx",
"args": ["-y", "github:willykeenan/tanpin", "mcp"],
"env": {
"INVENTORY_URL": "http://localhost:4173"
}
}
}
}
最初に get_overview を呼び出します。一般的なフロー:get_overview → get_reorder_recommendations → from_recommendations=true を指定して create_purchase_order。
ツール一覧とスキーマ:docs/MCP.md。
セキュリティモデル
- 直接のローカルリクエストのみ、キーが不要です。 TCP ピアがループバックであり、
Hostヘッダーがループバック名(localhost、127.x、::1)で、転送ヘッダー(Forwarded、X-Forwarded-*、X-Real-IP、Viaなど)がない場合に限り、リクエストにキーは不要です。そのため、同じホスト上のリバースプロキシ(nginx、Caddy、cloudflared)を使っても、インターネットが「localhost」になることはありません。すべてのリクエストでキーを必須にするには、TANPIN_REQUIRE_API_KEY=1を設定してください。キーが必要な場合、ダッシュボードで入力を求められます。 - クロスオリジンのブラウザーリクエスト(読み取りと書き込み。
TANPIN_CORS_ORIGINSで指定されたオリジンを含む)には、常にキーが必要です。 - シークレットは書き込み専用です。
settings.integrationsのWebhook署名シークレットとプロバイダーの認証情報は、読み取り時に返されません(代わりにhasWebhookSecret: trueが返されます)。Webhookのシークレットは、作成時に一度だけ表示されます。 - APIキーは
POST /api/keysで作成します。平文のキーはこのレスポンスで一度だけ表示され、ストアにはSHA-256ハッシュと表示用のプレフィックスが保存されます。キーはAuthorization: Bearer <key>またはX-API-Key: <key>として送信します。DELETE /api/keys/{id}で取り消せます。 - マスターキー。
TANPIN_ADMIN_KEYは、タイミング攻撃に安全なハッシュとの比較を行う、常に有効なキーです。初期設定に使用し、その後はハッシュ化されたキーを発行してください。 - Webhookは、
X-Inventory-Signature: sha256=<HMAC-SHA256(secret, raw_body)>で署名されたJSON POSTです。署名は生のバイト列に対して検証してください。配信はファイア・アンド・フォーゲット方式(タイムアウト10秒)のため、受信先が停止していても販売やデーモンのサイクルに影響しません。プライベート、ループバック、リンクローカル、CGNAT、予約済みのアドレスには配信されません(DNSリバインディング対策として、実際の接続先アドレスを確認します)。リダイレクトには追従せず、DEMO_MODEではサーバーとスタンドアロンデーモンの両方で無効になります。 - 冪等性。
POST /api/sales、/api/sales/bulk、/api/purchase-ordersでIdempotency-Keyを指定すると、24時間、元のレスポンスが再送されます。 - 入力の安全対策。 JSONボディから
__proto__/constructor/prototypeキーが除去されます。静的ファイルはpublic/内に限定されます。 - メール。 SMTPはポート465での暗黙的TLSを使用します(
SMTP_HOST、SMTP_PORT、SMTP_USER、SMTP_PASS)。SMTP設定がない場合、メールはデータディレクトリに.emlファイルとして書き込まれ、GET /api/outboxに記録されます。 - CORSは許可リスト(
TANPIN_CORS_ORIGINS)で制御され、*が送信されることはありません。HOSTで別の値が指定されていない限り、サーバーは127.0.0.1にバインドします。すべてのレスポンスにインラインスクリプトを許可しないContent-Security-Policyが付与されます。
ポートを外部に公開する前に、TANPIN_ADMIN_KEYを設定するか、localhostからキーを作成してください。詳細:docs/SECURITY-MODEL.md。
CLI
tanpin serve API + dashboard (PORT, HOST, TANPIN_DATA_DIR / TANPIN_DB)
tanpin daemon auto-management loop against the same data file (same TANPIN_* env as serve)
tanpin mcp MCP server on stdio (INVENTORY_URL, INVENTORY_API_KEY)
tanpin seed replace the data file with the demo store
TANPIN_NO_DAEMON=1が設定されていない限り、tanpin serveはデーモンをプロセス内で起動します。
設定
| 変数 | 使用元 | デフォルト |
|---|---|---|
PORT | serve | 4173 |
HOST | serve | 127.0.0.1 |
TANPIN_DATA_DIR | serve, daemon, seed | ./data |
TANPIN_DB | serve, daemon, seed | $TANPIN_DATA_DIR/inventory.json(inventory.sqlite として保存) |
TANPIN_OUTBOX | serve, daemon | <dir of TANPIN_DB>/outbox |
TANPIN_REQUIRE_API_KEY | serve | 未設定(ローカルからの直接リクエストは許可) |
TANPIN_ADMIN_KEY | serve | 未設定 |
TANPIN_NO_DAEMON | serve | 未設定(デーモンは有効) |
TANPIN_PLUGIN | serve | 未設定 |
TANPIN_CORS_ORIGINS | serve | 未設定(同一オリジンのみ) |
DEMO_MODE | serve, daemon | 未設定。Space イメージでは 1 を設定 |
INVENTORY_URL | mcp | http://localhost:4173 |
INVENTORY_API_KEY | mcp | 未設定 |
INVENTORY_DB | TANPIN_DB の旧エイリアス | 未設定 |
STRIPE_API_KEY / SQUARE_ACCESS_TOKEN | 販売連携 | 未設定(docs/INTEGRATIONS.md を参照) |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS | メール | SMTP_HOST が未設定の場合は outbox ファイル |
ランタイム状態は data/ と outbox/(gitignore 対象)に保存されます。autoManage、autoSend、autoReceive、serviceLevel、timezone、daemonIntervalMinutes などの設定はデータとともに保存され、PUT /api/settings または「設定」タブから編集できます。
ロードマップ
0.1.0 で提供
- 需要予測、再発注計算、デーモン、REST、MCP、ダッシュボード、Webhook、CSV、API キー、SMTP/outbox、プラグインフック
- セキュリティ — デフォルトで
127.0.0.1にバインド。CORS 許可リスト(TANPIN_CORS_ORIGINS)。Origin/Host チェック。プロキシを考慮したローカルバイパス。シークレットのマスキング。DNS ピンニングによる Webhook SSRF 対策。CSP。公開 Space 用のDEMO_MODE=1 - SQLite ストレージ — デフォルトの
node:sqliteバックエンド、WAL、増分書き込み、プロセス間の在庫差分。TANPIN_STORE=jsonによる JSON フォールバック - 連携 — Stripe Checkout/Shopify/Square の受信注文 Webhook(Webhook に明細項目が含まれない場合は、プロバイダー API から取得)、CSV 販売データ、追加のメールトランスポート
- バックテスト — ローリングオリジン方式による予測評価(MAPE、sMAPE、バイアス)の
GET /api/backtest。ワーカースレッドで実行 - タイムゾーン — カレンダー日、予測、配送時間帯、締め切り、CSV の日付に使用する IANA 店舗タイムゾーン(
settings.timezone)
開発
npm test # node --test --test-concurrency=1 test/
CONTRIBUTING.md を参照してください。MIT ライセンス — LICENSE。
Docker で実行
イメージは Apple シリコンおよび Intel 向けに ghcr.io/willykeenan/tanpin で公開されています。
docker run -d --name tanpin -p 4173:4173 -e TANPIN_ADMIN_KEY="REPLACE_WITH_A_LONG_RANDOM_KEY" -v tanpin-data:/app/data ghcr.io/willykeenan/tanpin
claude mcp add tanpin -- docker exec -i tanpin node bin/tanpin mcp
tanpin mcp は実行中のサーバーを HTTP 経由で呼び出すため、docker exec を使ってそのコンテナー内で実行します。この場合、リクエストはローカルから送信されるため、キーは不要です。別の docker run ... tanpin mcp コンテナーからは、localhost:4173 のサーバーに接続できません。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。

