|
| 1 | +# Field Trial 4 — snippets: SQLite共有MCP・ApiKey認証・CORSのDX検証 |
| 2 | + |
| 3 | +## Date |
| 4 | + |
| 5 | +2026-05-19 |
| 6 | + |
| 7 | +## Baseline |
| 8 | + |
| 9 | +- nene2-python v0.1.0 (`uv add git+https://github.com/hideyukiMORI/nene2-python.git`) |
| 10 | +- Python 3.14.5(uv managed) |
| 11 | +- プロジェクト: **snippets** — コードスニペット管理 JSON API |
| 12 | +- エンティティ: `Snippet`(title, language, code)— 5 エンドポイント(CRUD) |
| 13 | +- **`SqlAlchemySnippetRepository`(SQLite ファイル)** — HTTP API と MCP が共有 |
| 14 | +- **`ApiKeyAuthMiddleware`** ← FT1〜FT3 との差分① |
| 15 | +- **`CORSMiddleware`** ← FT1〜FT3 との差分② |
| 16 | +- **MCP + SQLite 共有** ← FT3-F2 の修正実証 |
| 17 | + |
| 18 | +## Goal |
| 19 | + |
| 20 | +1. FT3-F2(MCP と HTTP API の状態共有)を SQLite で実証する |
| 21 | +2. `ApiKeyAuthMiddleware`(X-Api-Key ヘッダー)の設定体験を確認する |
| 22 | +3. `CORSMiddleware` の設定体験を確認する(FT3-F1 修正後の JSON 配列形式) |
| 23 | +4. 3 つを同時に組み合わせたときの摩擦を洗い出す |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +## Steps Taken |
| 28 | + |
| 29 | +### 1. プロジェクト初期化・インストール |
| 30 | + |
| 31 | +問題なし。FT1〜FT3 で確立されたパターン通り。 |
| 32 | + |
| 33 | +### 2. `.env` 設定 |
| 34 | + |
| 35 | +FT3-F1 修正後のドキュメントを参照。JSON 配列形式を迷わず使えた: |
| 36 | + |
| 37 | +```dotenv |
| 38 | +API_KEYS=["ft4-api-key-1"] |
| 39 | +CORS_ORIGINS=["http://localhost:3000","http://localhost:5173"] |
| 40 | +``` |
| 41 | + |
| 42 | +→ FT3-F1 修正が効いていることを確認 ✓ |
| 43 | + |
| 44 | +### 3. ApiKeyAuthMiddleware |
| 45 | + |
| 46 | +X-Api-Key なし → 401(`"A valid X-Api-Key header is required."`)✓ |
| 47 | +X-Api-Key あり → 200 ✓ |
| 48 | + |
| 49 | +### 4. CORSMiddleware + ApiKeyAuthMiddleware の同時利用 |
| 50 | + |
| 51 | +最初の `add_middleware` 登録順: |
| 52 | + |
| 53 | +```python |
| 54 | +# 誤った順序(最初に書いたコード) |
| 55 | +app.add_middleware(CORSMiddleware, ...) # 先に登録 = 内側 |
| 56 | +app.add_middleware(ApiKeyAuthMiddleware, ...) # 後に登録 = 外側 ← preflight を遮断 |
| 57 | +``` |
| 58 | + |
| 59 | +OPTIONS preflight → 401 になり CORS が機能しなかった(**F-1**)。 |
| 60 | + |
| 61 | +修正後(CORS を後に登録して外側にする): |
| 62 | + |
| 63 | +```python |
| 64 | +app.add_middleware(ApiKeyAuthMiddleware, ...) # 先に登録 = 内側 |
| 65 | +app.add_middleware(CORSMiddleware, ...) # 後に登録 = 外側 ← preflight が通る |
| 66 | +``` |
| 67 | + |
| 68 | +→ OPTIONS preflight → 200 + `access-control-allow-origin` 付き ✓ |
| 69 | + |
| 70 | +### 5. MCP + SQLite 状態共有(FT3-F2 実証) |
| 71 | + |
| 72 | +HTTP API と MCP サーバーに同一の `DB_NAME=/tmp/ft4-snippets.db` を設定: |
| 73 | + |
| 74 | +- HTTP API でスニペット作成 → MCP `list_snippets` で確認 → **見える** ✓ |
| 75 | +- MCP `create_snippet` で作成 → HTTP API `GET /snippets` で確認 → **見える** ✓ |
| 76 | + |
| 77 | +FT3-F2 の修正(ドキュメントでの説明)が正しかったことを実動で証明。 |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## Friction Points |
| 82 | + |
| 83 | +### F-1 CORS + Auth 同時利用時のミドルウェア登録順が非自明 |
| 84 | + |
| 85 | +**severity**: 高 |
| 86 | +**type**: ドキュメント不足 |
| 87 | + |
| 88 | +`CORSMiddleware` と `ApiKeyAuthMiddleware`(または `BearerTokenMiddleware`)を |
| 89 | +同時に使う場合、**CORS を後に登録して外側に置かないと OPTIONS preflight が 401 になる**。 |
| 90 | + |
| 91 | +Starlette の逆順ルール(後に登録したものが外側)は既にドキュメントにあるが、 |
| 92 | +「CORS は Auth より外側にしなければならない」という組み合わせ固有のルールがない。 |
| 93 | + |
| 94 | +```python |
| 95 | +# 正しい登録順(コメントは実行時の順序) |
| 96 | +app.add_middleware(ErrorHandlerMiddleware, ...) # ① 最初に実行(最内側) |
| 97 | +app.add_middleware(SecurityHeadersMiddleware) # ② |
| 98 | +app.add_middleware(RequestIdMiddleware) # ③ |
| 99 | +app.add_middleware(RequestLoggingMiddleware) # ④ |
| 100 | +app.add_middleware(RequestSizeLimitMiddleware, ...) # ⑤ |
| 101 | +app.add_middleware(ThrottleMiddleware, ...) # ⑥(任意) |
| 102 | +app.add_middleware(ApiKeyAuthMiddleware, ...) # ⑦ Auth |
| 103 | +app.add_middleware(CORSMiddleware, ...) # ⑧ 最後に実行(最外側)← CORS は必ず最後 |
| 104 | +``` |
| 105 | + |
| 106 | +**Follow-up**: ミドルウェアの組み合わせパターンを `docs/reference/framework-modules.md` と |
| 107 | +`docs/how-to/new-project.md` に追記する。 |
| 108 | + |
| 109 | +--- |
| 110 | + |
| 111 | +## Summary |
| 112 | + |
| 113 | +| ID | 摩擦 | 深刻度 | 種別 | Follow-up Issue | |
| 114 | +|-----|-------------------------------------------------|--------|------------------|-----------------| |
| 115 | +| F-1 | CORS + Auth 同時利用時の登録順が非自明(preflight 401)| 高 | ドキュメント不足 | #90 | |
| 116 | + |
| 117 | +FT2/FT3 で修正した以下の項目はすべてスムーズに動作: |
| 118 | +- SQLite 永続化リポジトリパターン(#73) |
| 119 | +- `write()` 返り値(#74) |
| 120 | +- `db_url` 生成(#75) |
| 121 | +- `list[str]` の JSON 配列形式(#81) |
| 122 | +- MCP + SQLite 共有(#82)— 実動で証明 |
| 123 | + |
| 124 | +次回 FT5 は WebSocket または `SqlAlchemyTransactionManager.transactional()` の検証を推奨。 |
| 125 | +または F-1 修正後に PyPI 公開フローに移行するタイミング。 |
0 commit comments