diff --git a/CLAUDE.md b/CLAUDE.md index dcb2b3f1..b33f5638 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -161,7 +161,7 @@ template's PLAN.md. │ ├── becks_pfr.py │ └── becks_products.py ├── rag/ # chunk + embed + Chroma + BM25 -├── docs_mcp/ # FastMCP server + lessons.md +├── docs_mcp/ # MCPServer (mcp 2.x) + lessons.md ├── eval/ # Golden-query harness └── scripts/ # registry_gc.py, usage_report.py ``` @@ -189,7 +189,8 @@ template's PLAN.md. - **Lexical store**: SQLite FTS5 - **Fusion**: RRF k=60 - **Transport**: streamable-HTTP in prod, stdio for local dev -- **MCP framework**: FastMCP with `stateless_http=True` +- **MCP framework**: `mcp` 2.x `MCPServer` (the 1.x `FastMCP`), with + `stateless_http=True` passed to `run()` ### Image name and package linking are repo-name-derived @@ -230,7 +231,9 @@ python -m eval.run_eval --queries eval/queries.jsonl --output eval/results/basel - **Reranker per-pair token limit**: jina-reranker GGUF rejects the ENTIRE batch if any doc exceeds `n_ctx_train=1024`. Truncate reranked docs to ~2000 chars. -- **FastMCP `stateless_http=True`**: critical for prod. +- **`stateless_http=True`**: critical for prod. Under mcp 2.x it is a + `run()` kwarg, NOT a constructor arg — every transport option moved off + the constructor. `mcp.settings` no longer exists. - **Runner shell is `/bin/sh` (dash)** in CI — no `${VAR::N}`. - **Cloudflare 100 MB body cap**: push via LAN endpoint `192.168.0.2:1234`, pull via `git.jpaul.io`. diff --git a/README.md b/README.md index 23619548..83c3dffe 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ PRODUCT_NAME=crop_seed python -m docs_mcp.server \ │ ├── index.py # Chroma + BM25 builder │ └── bm25.py # FTS5 lexical index w/ seed-domain facets ├── docs_mcp/ -│ ├── server.py # FastMCP — 6 tools, hybrid+rerank +│ ├── server.py # MCPServer — 6 tools, hybrid+rerank │ ├── lessons.md # Curated knowledge layer (Pioneer fallback) │ └── usage.py # TimedCall + JSONL telemetry ├── eval/ diff --git a/docs_mcp/server.py b/docs_mcp/server.py index e30db021..3abcc66d 100644 --- a/docs_mcp/server.py +++ b/docs_mcp/server.py @@ -25,7 +25,8 @@ import re from pathlib import Path from typing import Annotated, Any -from mcp.server.fastmcp import FastMCP +from mcp.server.mcpserver import MCPServer +from mcp.server.transport_security import TransportSecuritySettings from pydantic import Field from .usage import TimedCall @@ -57,9 +58,12 @@ RRF_K = int(os.environ.get("RRF_K", "60")) # --------------------------------------------------------------------------- -# FastMCP setup. +# MCPServer setup. +# +# mcp 2.x moved every transport option (stateless_http, host, port, +# transport_security, ...) off the constructor and onto run() — see main(). # --------------------------------------------------------------------------- -mcp = FastMCP(f"{PRODUCT_NAME}-docs", stateless_http=True) +mcp = MCPServer(f"{PRODUCT_NAME}-docs") # --------------------------------------------------------------------------- @@ -1196,12 +1200,20 @@ def main() -> None: if args.transport == "stdio": mcp.run() - else: - mcp.settings.host = args.host - mcp.settings.port = args.port - if os.environ.get("MCP_DISABLE_DNS_REBINDING_PROTECTION") in {"1", "true", "yes"}: - mcp.settings.transport_security.enable_dns_rebinding_protection = False - mcp.run(transport=args.transport) + return + + # mcp 2.x: transport options are run() kwargs, and `mcp.settings` is gone. + run_kwargs: dict[str, Any] = {"host": args.host, "port": args.port} + if args.transport == "streamable-http": + # Was a constructor arg under 1.x. Required in prod — see CLAUDE.md. + run_kwargs["stateless_http"] = True + if os.environ.get("MCP_DISABLE_DNS_REBINDING_PROTECTION") in {"1", "true", "yes"}: + # We're only reachable as `seed-mcp:8080` on Drawbar's internal docker + # network, never published to a host port. + run_kwargs["transport_security"] = TransportSecuritySettings( + enable_dns_rebinding_protection=False, + ) + mcp.run(transport=args.transport, **run_kwargs) if __name__ == "__main__": diff --git a/requirements.txt b/requirements.txt index 79bbb463..f02699da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # MCP server -mcp[fastmcp]>=1.0.0,<2 # 2.x removed mcp.server.fastmcp (FastMCP -> MCPServer); see docs_mcp/server.py +mcp>=2,<3 # 2.x: no [fastmcp] extra; FastMCP -> MCPServer pydantic>=2.0 httpx>=0.27