fooddb-jp 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ # fooddb-jp 環境変数
2
+ # ⚠️ このファイルは .gitignore に追加してください
3
+
4
+ # Stripe (テストモード)
5
+ STRIPE_SECRET_KEY=sk_test_XXXXXXXX
6
+ STRIPE_WEBHOOK_SECRET=whsec_XXXXXXXX
7
+ STRIPE_PRICE_DEVELOPER=price_1TCy31BtmXXkKBDksjW5nlzY
8
+ STRIPE_PRICE_PRO=price_1TCy31BtmXXkKBDkwYFXiLgw
9
+
10
+ # fooddb-jp
11
+ FOODDB_ADMIN_KEY=fdb_admin_XXXXXXXX
12
+ FOODDB_BASE_URL=https://fooddb.navii.online
@@ -0,0 +1 @@
1
+ github: [takakix2]
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ .venv/
5
+ .uv/
6
+
7
+ # IDE
8
+ .vscode/
9
+ .idea/
10
+
11
+ # OS
12
+ .DS_Store
13
+ Thumbs.db
14
+
15
+ # Temporary
16
+ *.bak
17
+ *.tmp
18
+
19
+ # Large files (distributed via GitHub Releases)
20
+ fooddb.sqlite
21
+ data/raw/
22
+ data/output/
23
+
24
+ # Secrets
25
+ .env
@@ -0,0 +1,150 @@
1
+ # BILLING.md — fooddb-jp 課金アーキテクチャ
2
+
3
+ > 内部ドキュメント。Stripe 連携の構成、Webhook フロー、API キー管理の仕組み。
4
+
5
+ ---
6
+
7
+ ## アーキテクチャ概要
8
+
9
+ ```
10
+ ユーザー
11
+ ↓ ランディングページ「Subscribe →」
12
+ Stripe Checkout(Stripe ホスト)
13
+ ↓ 決済完了
14
+ Stripe Webhook → POST /billing/webhook
15
+ ↓ checkout.session.completed
16
+ API キー自動発行 → api_keys テーブルに INSERT
17
+
18
+ ユーザーが MCP config に API キーを設定
19
+
20
+ MCP / REST API リクエスト
21
+ ↓ Authorization: Bearer fdb_xxx
22
+ check_rate_limit() → api_keys.requests_today +1
23
+ ```
24
+
25
+ ---
26
+
27
+ ## 料金プラン
28
+
29
+ | プラン | 月額 | 日次上限 | 対象 |
30
+ |---|---|---|---|
31
+ | Free | ¥0 | 100 req/日 | 評価用。API キー不要 |
32
+ | Developer | $9.99 | 10,000 req/日 | 個人開発者・小規模アプリ |
33
+ | Pro | $29.99 | 100,000 req/日 | 商用アプリ・チーム |
34
+ | Admin | — | 無制限 | 内部用(MCP サーバー等) |
35
+
36
+ ---
37
+
38
+ ## 環境変数
39
+
40
+ ```bash
41
+ # .env に設定
42
+ STRIPE_SECRET_KEY=sk_test_xxxx # Stripe Secret Key
43
+ STRIPE_WEBHOOK_SECRET=whsec_xxxx # Webhook Signing Secret
44
+ STRIPE_PRICE_DEVELOPER=price_xxxx # Developer プランの Price ID
45
+ STRIPE_PRICE_PRO=price_xxxx # Pro プランの Price ID
46
+ FOODDB_ADMIN_KEY=fdb_admin_xxxx # 管理 API キー
47
+ FOODDB_BASE_URL=https://fooddb.navii.online
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Stripe 構成
53
+
54
+ ### Products & Prices(setup_stripe_products.py で作成済み)
55
+
56
+ | Product | Price ID | 金額 | Billing |
57
+ |---|---|---|---|
58
+ | fooddb-jp Developer | `price_xxx` | $9.99/月 | Recurring (monthly) |
59
+ | fooddb-jp Pro | `price_xxx` | $29.99/月 | Recurring (monthly) |
60
+
61
+ ### Webhook Events(/billing/webhook で処理)
62
+
63
+ | Event | 処理内容 |
64
+ |---|---|
65
+ | `checkout.session.completed` | API キーを自動発行、`api_keys` テーブルに INSERT |
66
+ | `customer.subscription.deleted` | `label=stripe:{subscription_id}` のキーを DELETE |
67
+ | `customer.subscription.updated` | ログ記録(プラン変更・未払い検知) |
68
+ | `invoice.payment_failed` | ログ記録(要通知実装) |
69
+
70
+ ---
71
+
72
+ ## API キー管理
73
+
74
+ ### テーブル構造(api_keys)
75
+
76
+ ```sql
77
+ CREATE TABLE api_keys (
78
+ key TEXT PRIMARY KEY, -- fdb_{hex32}
79
+ plan TEXT NOT NULL DEFAULT 'developer',
80
+ label TEXT DEFAULT '', -- "stripe:{subscription_id}" or manual label
81
+ email TEXT DEFAULT '',
82
+ created_at TEXT NOT NULL,
83
+ requests_today INTEGER DEFAULT 0,
84
+ last_reset TEXT NOT NULL -- "YYYY-MM-DD"
85
+ );
86
+ ```
87
+
88
+ ### レート制限の仕組み
89
+
90
+ 1. リクエスト受信 → `check_rate_limit()` が `Authorization: Bearer` ヘッダーを確認
91
+ 2. API キーあり → `api_keys` テーブルで plan と requests_today を照合
92
+ 3. `last_reset` が今日でなければ → `requests_today = 0` にリセット
93
+ 4. `requests_today >= limit` なら → **429 Too Many Requests**
94
+ 5. OK なら → `requests_today += 1`
95
+ 6. API キーなし → Free ティア(100 req/日、IP ベース制限は未実装)
96
+
97
+ ### 管理 API
98
+
99
+ ```bash
100
+ # キー発行
101
+ curl -X POST https://fooddb.navii.online/admin/keys \
102
+ -H "X-Admin-Key: $FOODDB_ADMIN_KEY" \
103
+ -G -d "plan=developer" -d "label=test-key" -d "email=user@example.com"
104
+
105
+ # キー一覧
106
+ curl https://fooddb.navii.online/admin/keys \
107
+ -H "X-Admin-Key: $FOODDB_ADMIN_KEY"
108
+
109
+ # キー削除
110
+ curl -X DELETE https://fooddb.navii.online/admin/keys/fdb_xxx \
111
+ -H "X-Admin-Key: $FOODDB_ADMIN_KEY"
112
+
113
+ # 使用量確認(ユーザー向け)
114
+ curl "https://fooddb.navii.online/billing/usage?api_key=fdb_xxx"
115
+ ```
116
+
117
+ ---
118
+
119
+ ## MCP からの課金フロー
120
+
121
+ ```
122
+ MCP Server (ユーザーのマシン)
123
+ ↓ FOODDB_API_KEY=fdb_xxx
124
+ ↓ httpx GET /foods/search/オートミール
125
+ ↓ Authorization: Bearer fdb_xxx
126
+ FastAPI (Mac Mini)
127
+ ↓ check_rate_limit()
128
+ ↓ api_keys.requests_today += 1
129
+ ↓ 応答
130
+ MCP Server
131
+ ↓ JSON → テキスト整形
132
+ AI エージェント
133
+ ```
134
+
135
+ **MCP 経由でも REST API 経由でも、同一の認証・課金パスを通る。**
136
+
137
+ ---
138
+
139
+ ## TODO
140
+
141
+ - [ ] Free ティアの IP ベースレート制限実装
142
+ - [ ] 決済成功ページで API キーを表示する HTML ページ
143
+ - [ ] メール通知(SendGrid 連携)で API キーを送信
144
+ - [ ] 使用量ダッシュボード(`/billing/dashboard` — HTML)
145
+ - [ ] 未払い時の自動キー停止(`invoice.payment_failed` → キー無効化)
146
+ - [ ] Stripe Customer Portal(キャンセル・プラン変更のセルフサービス)
147
+
148
+ ---
149
+
150
+ *作成: 2026-03-21*
@@ -0,0 +1,195 @@
1
+ # fooddb-jp 公開デプロイ実装計画書
2
+
3
+ > Cloudflare Tunnel で Mac Mini 上の FastAPI を公開し、無料/有料ティアで提供する。
4
+ > Marrow 課金基盤のリハーサルを兼ねる。
5
+
6
+ ---
7
+
8
+ ## 1. アーキテクチャ
9
+
10
+ ```
11
+ [ユーザー] → [Cloudflare CDN/WAF]
12
+
13
+ [Cloudflare Tunnel]
14
+
15
+ [Mac Mini (192.168.1.10)]
16
+
17
+ [FastAPI :8800] ← 既に動作中
18
+
19
+ [SQLite (43MB)]
20
+ ```
21
+
22
+ ### なぜ Tunnel か(D1 ではなく)
23
+
24
+ | 比較項目 | D1 方式 | Tunnel 方式 |
25
+ |----------|---------|-------------|
26
+ | コード書き直し | Python → TypeScript (Hono) | **不要**(FastAPI そのまま) |
27
+ | FTS5 互換性 | D1 で使えるか不明 | **問題なし**(ローカル SQLite) |
28
+ | Marrow との共通性 | Workers 側のみ | **アーキテクチャ全体が同じ** |
29
+ | レイテンシ | エッジ実行で最速 | Tunnel 経由(数十ms追加) |
30
+ | 可用性 | Cloudflare 管理 | Mac Mini に依存 |
31
+
32
+ **結論**: Marrow のリハーサルが目的なので、Tunnel 方式が最適。
33
+
34
+ ---
35
+
36
+ ## 2. 技術スタック
37
+
38
+ | レイヤー | 技術 | 役割 |
39
+ |----------|------|------|
40
+ | CDN/WAF | Cloudflare | DDoS防御、SSL終端、キャッシュ |
41
+ | Tunnel | cloudflared | Mac Mini への暗号化トンネル |
42
+ | API Server | FastAPI (api.py) | REST API(既存) |
43
+ | DB | SQLite (fooddb.sqlite) | データストア(既存) |
44
+ | レート制限 | Cloudflare WAF Rules | IP/APIキー単位 |
45
+ | 課金 | Stripe | Webhook → APIキー発行 |
46
+
47
+ ---
48
+
49
+ ## 3. 実装ステップ
50
+
51
+ ### Phase A: Tunnel セットアップ + 公開 ✅ 完了(2026-03-19)
52
+
53
+ ```
54
+ ✅ 1. cloudflared v2026.3.0 確認済み(/usr/local/bin/cloudflared)
55
+ ✅ 2. Cloudflare 認証完了(navii.online)
56
+ ✅ 3. Tunnel 作成: fooddb-jp (ID: 1addee5b-0685-4ea8-a7a6-ded0b7af7d2a)
57
+ ✅ 4. DNS 設定: fooddb.navii.online → CNAME
58
+ ✅ 5. config.yml: ~/.cloudflared/config.yml
59
+ ✅ 6. 動作確認: https://fooddb.navii.online/ — 4コネクション (nrt09/12/16)
60
+ □ 7. systemd で常駐化(Mac Mini 移行時に実施)
61
+ ```
62
+
63
+ **起動コマンド(手動):**
64
+ ```bash
65
+ # FastAPI
66
+ cd ~/Documents/MyKnowledge_vault/50_Projects/fooddb-jp
67
+ uv run uvicorn api:app --port 8800
68
+
69
+ # Tunnel
70
+ cloudflared tunnel run fooddb-jp
71
+ ```
72
+
73
+ ### Phase B: レート制限 + APIキー ✅ 完了(2026-03-19)
74
+
75
+ ```
76
+ ✅ 8. FastAPI に認証ミドルウェア実装
77
+ - Authorization: Bearer fdb_xxxxx
78
+ - キーの検証: ローカル SQLite テーブル
79
+ - キー無し = Free ティア(100 req/日)
80
+
81
+ ✅ 9. APIキー管理テーブル + 管理エンドポイント
82
+ - POST /admin/keys — キー発行
83
+ - GET /admin/keys — キー一覧
84
+ - DELETE /admin/keys/{key} — キー無効化
85
+ - X-Admin-Key ヘッダーでマスター認証
86
+
87
+ ✅ 10. 3ティア料金設定(Free / Developer / Pro)
88
+ - 日次リセット(UTC 00:00)
89
+ - 429 Too Many Requests 返却
90
+ ```
91
+
92
+ ### Phase C: Stripe 課金連携 ✅ 完了(2026-03-20)
93
+
94
+ ```
95
+ ✅ 11. Stripe Product / Price 作成
96
+ - Product: fooddb-jp API
97
+ - Developer: $9.99/mo (price_1TCy31BtmXXkKBDksjW5nlzY)
98
+ - Pro: $29.99/mo (price_1TCy31BtmXXkKBDkwYFXiLgw)
99
+
100
+ ✅ 12. Stripe Checkout Webhook
101
+ - checkout.session.completed → api_keys テーブルにキー発行
102
+ - customer.subscription.deleted → キー無効化
103
+
104
+ ✅ 13. 課金エンドポイント追加(FastAPI)
105
+ POST /billing/checkout → Stripe Checkout URL生成
106
+ GET /billing/success → 決済成功ページ
107
+ GET /billing/cancel → キャンセルページ
108
+ GET /billing/usage → 使用量確認
109
+
110
+ ✅ 14. Webhook 受信
111
+ POST /billing/webhook → Stripe Signature 検証 + キー管理
112
+ ```
113
+
114
+ ### Phase D: 公開準備(1セッション)
115
+
116
+ ```
117
+ □ 15. ドメイン設定
118
+ - DNS + SSL は Cloudflare 自動
119
+
120
+ □ 16. ドキュメント
121
+ - OpenAPI / Swagger(FastAPI 自動生成)
122
+ - 登録手順・コード例
123
+
124
+ □ 17. GitHub Public Repo
125
+ - SQLite + スクリプト = Public
126
+ - API キーは別管理
127
+
128
+ □ 18. 告知(Product Hunt / Hacker News / Zenn)
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 4. 料金プラン
134
+
135
+ | プラン | 月額 | レート制限 | 認証 |
136
+ |--------|------|-----------|------|
137
+ | **Free** | ¥0 | 100 req/日 | 不要 |
138
+ | **Developer** | $9.99/mo | 10,000 req/日 | APIキー |
139
+ | **Pro** | $29.99/mo | 100,000 req/日 | APIキー |
140
+
141
+ ---
142
+
143
+ ## 5. Marrow へのフィードバック項目
144
+
145
+ | 項目 | fooddb-jp で検証 | Marrow に適用 |
146
+ |------|-----------------|---------------|
147
+ | **Tunnel セットアップ** | 手順・信頼性・レイテンシ | 同じ構成を流用 |
148
+ | **常駐化** | systemd/launchd の設定 | Marrow も同様に常駐 |
149
+ | **Cloudflare WAF** | レート制限の粒度 | APIプラン制御 |
150
+ | **Stripe Webhook** | 決済 → キー発行フロー | 課金フロー全体 |
151
+ | **可用性** | Mac Mini ダウン時の挙動 | 運用ポリシーの判断材料 |
152
+ | **レイテンシ** | Tunnel 経由の実測値 | SLA 設計の根拠 |
153
+
154
+ ---
155
+
156
+ ## 6. 投資対効果
157
+
158
+ | コスト | 金額 |
159
+ |--------|------|
160
+ | Cloudflare Free プラン | ¥0 |
161
+ | Tunnel | ¥0(Free プランに含む) |
162
+ | ドメイン | $10/年 |
163
+ | Stripe 手数料 | 3.6% + ¥40/件 |
164
+ | Mac Mini 電気代 | 既存インフラ |
165
+ | **合計追加費用** | **≒ ¥100/月** |
166
+
167
+ **損益分岐点**: Developer プラン 1件で黒字
168
+
169
+ ---
170
+
171
+ ## 7. リスク・注意事項
172
+
173
+ 1. **Mac Mini の可用性**
174
+ - 停電・再起動時に API ダウン
175
+ - UPS + launchd 自動復帰で軽減
176
+ - Cloudflare が 502 を返すので、カスタムエラーページを設定
177
+
178
+ 2. **Tunnel のレイテンシ**
179
+ - ローカル実測: 3-5ms(同一筐体ループバック)
180
+ - 外部からの実測は未実施(日本国内: 10-30ms 追加の見込み)
181
+ - 読み取り専用 + 軽量クエリなので許容範囲
182
+
183
+ 3. **データ更新**
184
+ - 成分表は年1回更新(文科省)
185
+ - SQLite を差し替えて FastAPI 再起動のみ
186
+
187
+ 4. **著作権**
188
+ - 政府統計データ → 出典明記で自由利用可
189
+
190
+ ---
191
+
192
+ *作成: 2026-03-18*
193
+ *改訂: 2026-03-19(D1方式 → Tunnel方式に変更、Phase A 完了、Phase B 完了)*
194
+ *改訂: 2026-03-20(Phase C 完了、GitHub 公開完了)*
195
+ *Status: Phase A+B+C 完了 → Phase D(ドキュメント・告知)待ち*
@@ -0,0 +1,36 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 takakix2
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ ## Data License
26
+
27
+ The food composition data included in this project is sourced from the
28
+ "Standard Tables of Food Composition in Japan" (日本食品標準成分表)
29
+ published by the Ministry of Education, Culture, Sports, Science and
30
+ Technology (MEXT), Japan.
31
+
32
+ This data is available under the Government of Japan Standard Terms of Use
33
+ (Version 2.0), which is compatible with Creative Commons Attribution 4.0
34
+ International (CC BY 4.0).
35
+
36
+ Attribution: 文部科学省「日本食品標準成分表(八訂)増補2023年」
@@ -0,0 +1,244 @@
1
+ # MCP → REST API 統合計画(課金ゲートウェイ化)
2
+
3
+ > MCP Server が SQLite を直接読む構造を廃止し、REST API 経由に切り替える。
4
+ > これにより MCP 経由の利用にも認証・レート制限・課金が適用される。
5
+
6
+ ---
7
+
8
+ ## 1. 現状の問題
9
+
10
+ ```
11
+ REST API → FastAPI (api.py) → 認証 ✅ → レート制限 ✅ → 課金 ✅ → SQLite
12
+ MCP Server → mcp_server.py → 直接 SQLite 読み取り ❌ 課金されない
13
+ ```
14
+
15
+ ### 影響
16
+ - MCP 経由の利用が課金対象外
17
+ - 検索ロジックが api.py と mcp_server.py に二重実装
18
+ - エイリアス検索・FTS5 ロジックの同期コスト
19
+
20
+ ---
21
+
22
+ ## 2. 目標アーキテクチャ
23
+
24
+ ```
25
+ MCP Server (mcp_server.py)
26
+ ↓ HTTP (httpx)
27
+ REST API (FastAPI, api.py) — https://fooddb.navii.online/
28
+ ↓ 認証 + レート制限 + 課金
29
+ SQLite (fooddb.sqlite)
30
+ ```
31
+
32
+ ### 原則
33
+ - **MCP Server=薄いラッパー**(REST API の結果を LLM 向けに整形するだけ)
34
+ - **ビジネスロジック=REST API に一本化**
35
+ - **MCP Server は API キーを持つ**(環境変数 `FOODDB_API_KEY` で注入)
36
+
37
+ ---
38
+
39
+ ## 3. MCP ツール ↔ REST エンドポイント マッピング
40
+
41
+ | MCP ツール | 現在の実装 | 対応 REST エンドポイント |
42
+ |---|---|---|
43
+ | `search_food(query, limit)` | 直接 SQL(4段フォールバック) | `GET /foods/search/{query}?limit=N` |
44
+ | `get_food_nutrients(food_number, table)` | 直接 SQL(JOIN) | `GET /foods/{food_number}?table=T` |
45
+ | `calculate_nutrition(foods_and_amounts)` | 直接 SQL(JOIN + 集計) | `GET /calculate?foods=1004:100,12004:60` |
46
+ | `nutrient_ranking(tag, limit)` | 直接 SQL(ORDER BY) | `GET /ranking/{tag}?limit=N` |
47
+ | `list_food_groups()` | 直接 SQL(GROUP BY) | `GET /groups` |
48
+
49
+ **すべてのツールに対応するエンドポイントが既に存在する。** コードの書き換えのみで済む。
50
+
51
+ ---
52
+
53
+ ## 4. 実装ステップ
54
+
55
+ ### Step 1: MCP Server 用 API キーの発行
56
+
57
+ Mac Mini 上で実行:
58
+ ```bash
59
+ curl -X POST https://fooddb.navii.online/admin/keys \
60
+ -H "X-Admin-Key: $FOODDB_ADMIN_KEY" \
61
+ -G -d "plan=admin" -d "label=mcp-server-internal"
62
+ ```
63
+
64
+ → 発行されたキーを `.env` に追加:
65
+ ```
66
+ FOODDB_API_KEY=fdb_xxxxxxxxxxxxxxxx
67
+ ```
68
+
69
+ ### Step 2: mcp_server.py の書き換え
70
+
71
+ **Before(直接 DB アクセス):**
72
+ ```python
73
+ import sqlite3
74
+
75
+ DB_PATH = Path(__file__).parent / "fooddb.sqlite"
76
+
77
+ def get_db():
78
+ conn = sqlite3.connect(str(DB_PATH))
79
+ conn.row_factory = sqlite3.Row
80
+ return conn
81
+
82
+ @mcp.tool()
83
+ def search_food(query: str, limit: int = 10) -> str:
84
+ db = get_db()
85
+ rows = db.execute("SELECT ... WHERE food_name LIKE ?", ...)
86
+ # 100行以上の SQL ロジック
87
+ db.close()
88
+ return formatted_result
89
+ ```
90
+
91
+ **After(REST API 経由):**
92
+ ```python
93
+ import httpx
94
+ import os
95
+
96
+ API_BASE = os.environ.get("FOODDB_API_URL", "https://fooddb.navii.online")
97
+ API_KEY = os.environ.get("FOODDB_API_KEY", "")
98
+
99
+ def _client() -> httpx.Client:
100
+ headers = {}
101
+ if API_KEY:
102
+ headers["Authorization"] = f"Bearer {API_KEY}"
103
+ return httpx.Client(base_url=API_BASE, headers=headers, timeout=10.0)
104
+
105
+ @mcp.tool()
106
+ def search_food(query: str, limit: int = 10) -> str:
107
+ """食品名であいまい検索する。例: 'オートミール', '鶏むね', '豆腐'"""
108
+ with _client() as c:
109
+ res = c.get(f"/foods/search/{query}", params={"limit": limit})
110
+ res.raise_for_status()
111
+ data = res.json()
112
+
113
+ if not data:
114
+ return f"「{query}」に一致する食品は見つかりませんでした。"
115
+
116
+ lines = [f"検索結果 ({len(data)}件):"]
117
+ for food in data:
118
+ parts = [f"{food['food_number']}: {food['food_name']}"]
119
+ meta = []
120
+ if food.get("group_name"): meta.append(food["group_name"])
121
+ if food.get("category"): meta.append(f"<{food['category']}>")
122
+ if meta: parts.append(f"({', '.join(meta)})")
123
+ lines.append(" ".join(parts))
124
+ return "\n".join(lines)
125
+ ```
126
+
127
+ ### Step 3: 各ツールの書き換え
128
+
129
+ | ツール | API コール | 整形 |
130
+ |---|---|---|
131
+ | `search_food` | `GET /foods/search/{query}` | JSON → テキスト一覧 |
132
+ | `get_food_nutrients` | `GET /foods/{number}?table=T` | JSON → 成分表テキスト |
133
+ | `calculate_nutrition` | `GET /calculate?foods=...` | JSON → 栄養素合計テキスト |
134
+ | `nutrient_ranking` | `GET /ranking/{tag}?limit=N` | JSON → ランキングテキスト |
135
+ | `list_food_groups` | `GET /groups` | JSON → グループ一覧テキスト |
136
+
137
+ ### Step 4: REST API 側の微調整
138
+
139
+ 現在の REST API レスポンスを確認し、MCP から使いやすい形になっているか検証。
140
+ 不足があれば追加:
141
+
142
+ - [ ] `/foods/search/{query}` — カテゴリ・サブカテゴリ情報が含まれるか確認
143
+ - [ ] `/calculate` — 主要栄養素の優先表示順が API 側にもあるか確認
144
+ - [ ] エラーレスポンスの統一(404, 400 のフォーマット)
145
+
146
+ ### Step 5: 依存関係の更新
147
+
148
+ ```bash
149
+ uv add httpx # mcp_server.py の新しい依存
150
+ uv remove sqlite3 # 不要になる(標準ライブラリなので実際は不要)
151
+ ```
152
+
153
+ ### Step 6: テスト
154
+
155
+ 1. **ユニットテスト**: MCP ツールが正しい REST エンドポイントを呼ぶか
156
+ 2. **統合テスト**: MCP 経由で検索 → API キーのカウンタが増加するか
157
+ 3. **レート制限テスト**: Free キーで 100 回超えたら MCP も 429 になるか
158
+
159
+ ```bash
160
+ # 統合テスト例
161
+ uv run python -c "
162
+ from mcp_server import search_food
163
+ result = search_food('米', 3)
164
+ print(result)
165
+ "
166
+ # → API の requests_today が +1 されていることを確認
167
+ ```
168
+
169
+ ### Step 7: Mac Mini へのデプロイ
170
+
171
+ ```bash
172
+ # Mac Mini にSSH
173
+ ssh takaki@192.168.1.10
174
+
175
+ # コード更新
176
+ cd ~/projects/fooddb-jp
177
+ git pull
178
+
179
+ # .env に API キーを追加
180
+ echo 'FOODDB_API_KEY=fdb_xxx' >> .env
181
+
182
+ # サービス再起動(systemd)
183
+ sudo systemctl restart fooddb-api
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 5. 削除されるコード
189
+
190
+ mcp_server.py から削除される部分:
191
+ - `sqlite3` import と `DB_PATH` 定義
192
+ - `get_db()` 関数
193
+ - 各ツール内の SQL クエリ(合計 ~150行)
194
+ - エイリアス交差検索ロジック(api.py に一本化済み)
195
+
196
+ **mcp_server.py は ~260行 → ~100行 に縮小**
197
+
198
+ ---
199
+
200
+ ## 6. 副次効果
201
+
202
+ | Before | After |
203
+ |---|---|
204
+ | MCP は SQLite を直接読む(課金外) | **全アクセスが課金対象** |
205
+ | 検索ロジックが2箇所に存在 | **api.py に一本化** |
206
+ | mcp_server.py が DB ファイルに依存 | **URL だけ知っていれば動く** |
207
+ | Mac Mini 上でしか MCP が動かない | **どこからでも REST 経由で接続可能** |
208
+ | MCP の食品検索品質 ≠ API の検索品質 | **同一のロジックを共有** |
209
+
210
+ ---
211
+
212
+ ## 7. Marrow への水平展開
213
+
214
+ この「MCP = 薄いラッパー / REST API = 課金ゲートウェイ」パターンは、
215
+ Marrow の MCP Server にもそのまま適用できる:
216
+
217
+ ```
218
+ Marrow MCP Server
219
+ ↓ HTTP
220
+ Marrow REST API (Cloudflare Workers)
221
+ ↓ 認証 + 課金 (Stripe)
222
+ Marrow Gateway (Crawler Engine)
223
+ ```
224
+
225
+ fooddb-jp をこのパターンの **最初の実証例** として確立する。
226
+
227
+ ---
228
+
229
+ ## 8. 工数見積
230
+
231
+ | ステップ | 工数 |
232
+ |---|---|
233
+ | Step 1: API キー発行 | 1分 |
234
+ | Step 2-3: mcp_server.py 書き換え | 30分 |
235
+ | Step 4: REST API 微調整 | 15分 |
236
+ | Step 5: 依存関係更新 | 5分 |
237
+ | Step 6: テスト | 15分 |
238
+ | Step 7: デプロイ | 10分 |
239
+ | **合計** | **~1.5時間** |
240
+
241
+ ---
242
+
243
+ *作成: 2026-03-21*
244
+ *Status: 計画完了 → 実装待ち*