Almaatla commited on
Commit
7ecfcd4
Β·
verified Β·
1 Parent(s): 2fb998d

Upload README_MCP.md

Browse files
Files changed (1) hide show
  1. docs/README_MCP.md +127 -0
docs/README_MCP.md ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MCP server support
2
+
3
+ SERPent now speaks two protocols from the same deployment:
4
+
5
+ | Audience | Endpoint |
6
+ |---|---|
7
+ | REST clients / Swagger UI | `https://<host>/` (docs), `POST /serp/...`, `/scrap/...`, `/ops/...` |
8
+ | MCP clients (Claude, Cursor, agents) | `https://<host>/mcp` β€” streamable HTTP transport |
9
+
10
+ ## What changed
11
+
12
+ | File | Change |
13
+ |---|---|
14
+ | `mcp_server.py` | **New.** Builds the MCP server from the FastAPI app and mounts it. |
15
+ | `app.py` | Two lines: `from mcp_server import mount_mcp_server` and `mcp = mount_mcp_server(app)` after the `include_router` calls. |
16
+ | `requirements.txt` | Added `fastmcp>=3.0,<4`. |
17
+ | `Dockerfile` | Unchanged β€” same image, same port 7860, same `CMD`. |
18
+
19
+ ## How it works
20
+
21
+ `FastMCP.from_fastapi()` reads the app's OpenAPI schema and turns every route
22
+ into an MCP tool. When a tool is called, the request is dispatched **back into
23
+ the same FastAPI app in-process** over an ASGI transport β€” no network hop, no
24
+ second server, no duplicated business logic. Add an endpoint, get a tool.
25
+
26
+ The 12 tools generated today:
27
+
28
+ ```
29
+ search search_arxiv search_google_scholar
30
+ search_patents search_brave search_bing
31
+ search_duck scrap_patent scrap_patents
32
+ ops_keyword_search ops_get_patent ops_get_patents_bulk
33
+ ```
34
+
35
+ Tool names come from the Python handler names rather than FastAPI's generated
36
+ operation ids, so agents see `search_arxiv` instead of
37
+ `search_arxiv_serp_search_arxiv_post`. Descriptions and JSON schemas come from
38
+ your docstrings and Pydantic models β€” improving a docstring improves the tool.
39
+ To pin a specific tool name, set `operation_id="..."` on the route decorator.
40
+
41
+ ## Connecting a client
42
+
43
+ **Claude Code / any streamable-HTTP client:**
44
+
45
+ ```bash
46
+ claude mcp add --transport http serpent https://<your-space>.hf.space/mcp
47
+ ```
48
+
49
+ **`.mcp.json` / `claude_desktop_config.json`:**
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "serpent": {
55
+ "type": "http",
56
+ "url": "https://<your-space>.hf.space/mcp"
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ **Private HF Space** β€” pass your token:
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "serpent": {
68
+ "type": "http",
69
+ "url": "https://<your-space>.hf.space/mcp",
70
+ "headers": { "Authorization": "Bearer hf_..." }
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ **Quick check without a client:**
77
+
78
+ ```bash
79
+ curl -X POST http://localhost:7860/mcp \
80
+ -H 'Content-Type: application/json' \
81
+ -H 'Accept: application/json, text/event-stream' \
82
+ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
83
+ "protocolVersion":"2025-06-18","capabilities":{},
84
+ "clientInfo":{"name":"curl","version":"1"}}}'
85
+ ```
86
+
87
+ Or with the FastMCP CLI: `fastmcp inspect http://localhost:7860/mcp`
88
+
89
+ ## Configuration
90
+
91
+ All optional, all environment variables:
92
+
93
+ | Variable | Default | Purpose |
94
+ |---|---|---|
95
+ | `MCP_ENABLED` | `1` | Set to `0` to run REST-only. |
96
+ | `MCP_PATH` | `/mcp` | Where the MCP endpoint lives. |
97
+ | `MCP_STATELESS` | `1` | Stateless HTTP β€” no session affinity needed behind the HF Spaces proxy. Set `0` for resumable SSE streams. |
98
+ | `MCP_ALLOWED_HOSTS` | `*` | Comma-separated Host allow-list (DNS-rebinding protection). |
99
+ | `MCP_ALLOWED_ORIGINS` | `*` | Comma-separated Origin allow-list (browser clients). |
100
+
101
+ ## Notes and caveats
102
+
103
+ - **Ordering matters.** `mount_mcp_server(app)` must come after every
104
+ `include_router()` call. Routes registered after it are not exposed as tools,
105
+ and are not reachable at all β€” the MCP app is mounted at the root so that a
106
+ bare `POST /mcp` answers directly instead of 307-redirecting to `/mcp/`
107
+ (a redirect some MCP clients mishandle).
108
+ - **Lifespans are combined**, so Playwright still starts up and shuts down
109
+ exactly as before, alongside the MCP session manager.
110
+ - **The `ops_*` tools** are exposed but return 503 without
111
+ `OPS_CONSUMER_KEY` / `OPS_CONSUMER_SECRET`. The server instructions tell
112
+ agents to prefer the regular tools, which already fall back to OPS.
113
+ - **Auth.** The MCP endpoint inherits whatever protects the Space. If you later
114
+ want per-token auth on MCP only, FastMCP takes an `auth=` provider in
115
+ `build_mcp_server()`.
116
+ - **Tool count.** 12 tools with six overlapping search backends is a lot of
117
+ surface for a model to choose from. If tool selection gets noisy, pass
118
+ `route_maps=[...]` to `FastMCP.from_fastapi()` in `mcp_server.py` to exclude
119
+ the per-backend tools and keep `search`, `search_arxiv`, `search_patents`,
120
+ `search_google_scholar` and the scrapers.
121
+
122
+ ## Verified
123
+
124
+ Tested against `fastmcp 3.4.7` / `fastapi 0.141.1`: server boots, all 12 tools
125
+ list over HTTP with correct schemas, tool calls round-trip through the real
126
+ endpoints, and the existing REST routes (`/`, `/openapi.json`, `/redoc`,
127
+ `/serp/*`) are unaffected.