aws-samples / aws-samples/sample-ai-persona
[chore] MotherDuck MCP 廃止と DuckDB 構造化分析 tool 化
- Dominant language
- Python
- Stars
- 24
- Forks
- 6
- Avg merge
- 8h 10m
- Merged PRs (30d)
- 6
Description
## 概要
閉域対応(#128)の実装に着手する**前に**、公開環境で DuckDB 分析まわりの実行時インターネット依存を除去する。具体的には MotherDuck MCP(`uvx mcp-server-motherduck`)を廃止し、アプリケーション所有の構造化分析ツール(DuckDB backend)へ置換したうえで、DuckDB `httpfs` を container image へ事前組み込みする。
閉域化と dataset 分析方式の変更を同時に行わず、公開環境で機能回帰を確認してから同じ application artifact を閉域環境へ展開する。
調査根拠: `docs/note/closed-network-cognito-architecture-research.md`(8.2 / 8.3 / 10章)
## なぜ事前対応が必要か
現行の `src/services/mcp_server_manager.py` は `uvx mcp-server-motherduck` を `--read-write` で起動し、LLM へ任意 SQL を受け付ける `execute_query` を公開している。これは閉域対応の問題と密結合しており、先に切り離しておく:
- `uvx` による実行時 package download
- MCP subprocess と stdio session の起動・停止
- MCP 用 DuckDB と application 用 DuckDB の version 二重管理
- LLM による任意 SQL・任意 S3 path・local file 参照
- 公開用と閉域用で異なる dataset 分析実装
`INSTALL httpfs` は初回実行時に DuckDB extension repository へアクセスするため、閉域では動作しない。build 時に組み込み、runtime は `LOAD httpfs` のみとする。
> 対象は Fargate 内で起動する `mcp-server-motherduck`。AgentCore Gateway 用の `McpGatewayStack` は別機能であり、本対応の削除対象に含めない。
## 作業内容
### 1. 共通 tool contract と DuckDB backend
LLM には SQL ではなく分析目的を表す構造化引数だけを公開する。
```python
analyze_dataset(
dataset_id="...",
filters={"customer_id": {"eq": "123"}},
group_by=["category"],
metrics=["count", "sum:amount"],
order_by=["-sum:amount"],
limit=100,
)
```
`DatasetAnalysisService` で以下を強制:
- [ ] `dataset_id` から S3 path / Glue table を server 側で解決
- [ ] persona へ binding された dataset だけを許可
- [ ] binding filter を server 側で追加し、LLM から解除・上書き不可にする
- [ ] metadata 登録済みの column / operator / 集計関数だけを許可
- [ ] 値は parameterized query として渡す
- [ ] `LIMIT` / timeout / 返却文字数 / 同時 query 数へ上限を設ける
- [ ] SQL / S3 path / AWS credential / local file path を LLM へ公開しない
- [ ] query 対象・所要時間・件数・成功/失敗だけを監査 log へ記録
- [ ] 初期 backend は `DuckDBQueryBackend`(既存 parameterized query + pre-install 済み `httpfs`)。任意 SQL を受け取る method は公開しない
- [ ] ファイルからのペルソナ生成では temporary CSV path を LLM へ渡さず、server 発行の短命な `source_id` で解決し、処理終了時に必ず削除
### 2. 削除・変更対象(10.3)
| 対象 | 事前対応 |
|---|---|
| `src/services/mcp_server_manager.py` | subprocess lifecycle ごと削除 |
| `src/services/agent_service.py` | `get_mcp_tools()` を削除し、共通分析 tool factory を追加 |
| `src/services/survey_batch_service.py` | 構造化 query を実行する DuckDB backend を分離/共通化 |
| `src/managers/persona_generation_manager.py` | `use_mcp` を `enable_dataset_analysis` へ変更し、`source_id` を tool へ渡す |
| `src/managers/shared/agent_integration.py` | MCP ではなく共通分析 tool を Agent へ登録 |
| `src/managers/settings_manager.py` | MCP start/stop/status 操作を削除 |
| `web/routers/settings.py` | `/mcp/toggle`・`/mcp/status`・`/api/mcp/status` を削除 |
| `web/templates/settings/partials/mcp_status.html` | MCP process の toggle UI を削除 |
| `src/prompts/discussion_interview_prompts.py` | `execute_query`・`CREATE SECRET`・S3 path・SQL 例を削除 |
| MCP 関連 test | 共通 tool の allowlist / binding 強制 / 上限 / timeout test へ置換 |
kill switch が必要な場合は process 状態ではなく application 設定 `ENABLE_DATASET_ANALYSIS` で tool 登録自体を停止する。
### 3. Container の事前対応(10.4)
公開・閉域で Dockerfile を分けず、同じ image へ適用する。
- [ ] `uv sync --frozen` で DuckDB と Polars を固定
- [ ] Docker build 時に DuckDB `httpfs` を install
- [ ] runtime の `INSTALL httpfs` を `LOAD httpfs` へ変更
- [ ] `uv run` ではなく作成済み virtual environment の `uvicorn` を直接起動
- [ ] DuckDB の runtime extension 自動 install を無効化
- [ ] `uvx` および `mcp-server-motherduck` を application runtime から除去
```dockerfile
RUN uv sync --frozen \
&& /app/.venv/bin/python -c "import duckdb; c = duckdb.connect(); c.execute('INSTALL httpfs'); c.execute('LOAD httpfs'); c.close()"
CMD ["/app/.venv/bin/uvicorn", "web.main:app", "--host", "0.0.0.0", "--port", "80"]
```
## Rollout(10.5)
1. 共通 tool contract と `DuckDBQueryBackend` を追加
2. 既存 MCP と新 tool の結果を test dataset で比較
3. 公開 staging 環境で dataset binding / interview / discussion / persona 生成を回帰確認
4. 公開環境の Agent を新 tool へ切り替え
5. MCP manager / toggle API・UI / prompt 内 SQL 説明を削除
6. 公開本番で監査 log / error 率 / query 時間を確認
7. 完了後に閉域 stack 実装(#128)へ着手
移行中だけ内部 feature flag で旧・新実装を切り替え、移行完了後に旧 MCP 分岐と flag を削除。問題発生時は直前の immutable image digest へ rollback。
## 完了条件(10.6)
- [ ] application 起動後に package / DuckDB extension の download が発生しない
- [ ] application code から `uvx` / `mcp-server-motherduck` / `MCPServerManager` がなくなっている
- [ ] LLM へ任意 SQL / S3 path / local path / AWS credential を渡していない
- [ ] dataset binding を tool 引数で解除・上書きできない
- [ ] column / operator / 集計関数 / limit / timeout が server 側で制限されている
- [ ] network 無効の container test で `LOAD httpfs` と local dataset 分析が成功する
- [ ] 公開環境の dataset 連携と CSV ペルソナ生成の回帰 test が成功する
- [ ] 公開・閉域で同じ application image を使用できる
## スコープ外
- Athena 導入(8.7章): 本事前対応では追加せず、`DatasetQueryBackend` の実装差し替えで将来対応可能な構造にするに留める
- 閉域 stack 本体(VPC / VPCE / ALB / Fargate / Cognito API 認証): #128
Contributor guide
Research direction
Start with docs/note/closed-network-cognito-architecture-research.md and trace the current MCP flow through src/services/mcp_server_manager.py, src/services/agent_service.py, src/services/survey_batch_service.py, and the listed managers, routers, prompts, and Dockerfile. Run the existing MCP-related tests and compare behavior on the test dataset; done means the structured backend passes binding, allowlist, limit, timeout, audit, and offline-container checks without runtime downloads or MotherDuck MCP.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, docker, python, sql
- Domain
- backend, cloud, data, databases
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100