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

Upload mcp_server.py

Browse files

The tool surface goes from 12 to 6: search, search_arxiv, search_google_scholar, search_patents, scrap_patent, scrap_patents. The exclusions live in one EXCLUDED_ROUTES list at the top of the file with the reasoning next to each pattern, so it's easy to adjust. MCP_EXPOSE_ALL=1 puts all 12 back without a code change — I verified both paths (6 tools by default, 12 with the flag set).

I also rewrote the server instructions, since the old ones described ops_* tools the model can no longer see. The new text tells the agent that search and search_patents already exhaust every backend internally, so a "not found" is genuine and it should move to the next id rather than hunting for a lower-level tool — which is exactly the loop that produced your JP7904662B2 traceback.

All the /ops/* REST endpoints still work exactly as before; this only changes what an LLM is offered.

One caveat worth knowing: after you redeploy, Claude may keep serving a cached tool list for a few minutes. If you still see the old 12 tools, toggle the connector off and on.
Claude

Files changed (1) hide show
  1. mcp_server.py +39 -4
mcp_server.py CHANGED
@@ -24,6 +24,8 @@ Configuration (all optional, read from the environment):
24
  is what you want behind the HF Spaces proxy.
25
  MCP_ALLOWED_HOSTS Comma-separated Host allow-list. Default "*".
26
  MCP_ALLOWED_ORIGINS Comma-separated Origin allow-list. Default "*".
 
 
27
  """
28
 
29
  from __future__ import annotations
@@ -37,12 +39,33 @@ from fastapi.routing import APIRoute
37
  from fastapi.utils import generate_unique_id
38
 
39
  from fastmcp import FastMCP
 
40
  from fastmcp.utilities.lifespan import combine_lifespans
41
 
42
  logger = logging.getLogger(__name__)
43
 
44
  MCP_SERVER_NAME = "SERPent"
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  MCP_INSTRUCTIONS = """\
47
  SERPent gives you web, academic and patent search plus full-text patent retrieval.
48
 
@@ -59,9 +82,11 @@ Every search tool accepts a LIST of queries and runs them concurrently, so send
59
  all your query variations in one call rather than making several calls. Set
60
  `n_results` to control how many hits come back per query.
61
 
62
- The `ops_*` tools hit the official EPO OPS API directly and only work when the
63
- deployment has OPS credentials configured; the regular tools already fall back
64
- to OPS on their own, so prefer them unless you specifically need EPO data.
 
 
65
  """
66
 
67
 
@@ -117,11 +142,21 @@ def _tool_names(app: FastAPI) -> dict[str, str]:
117
 
118
 
119
  def build_mcp_server(app: FastAPI) -> FastMCP:
120
- """Build an MCP server exposing every route of `app` as a tool."""
 
 
 
 
 
 
 
 
 
121
  mcp = FastMCP.from_fastapi(
122
  app=app,
123
  name=MCP_SERVER_NAME,
124
  mcp_names=_tool_names(app),
 
125
  # Requests are dispatched straight back into this same app in-process.
126
  httpx_client_kwargs={"timeout": 300.0},
127
  )
 
24
  is what you want behind the HF Spaces proxy.
25
  MCP_ALLOWED_HOSTS Comma-separated Host allow-list. Default "*".
26
  MCP_ALLOWED_ORIGINS Comma-separated Origin allow-list. Default "*".
27
+ MCP_EXPOSE_ALL "1" to expose every endpoint as a tool instead of the
28
+ curated set (see EXCLUDED_ROUTES below).
29
  """
30
 
31
  from __future__ import annotations
 
39
  from fastapi.utils import generate_unique_id
40
 
41
  from fastmcp import FastMCP
42
+ from fastmcp.server.providers.openapi import MCPType, RouteMap
43
  from fastmcp.utilities.lifespan import combine_lifespans
44
 
45
  logger = logging.getLogger(__name__)
46
 
47
  MCP_SERVER_NAME = "SERPent"
48
 
49
+ # Endpoints kept out of the MCP tool surface. They stay fully available over
50
+ # REST — this only controls what an LLM sees.
51
+ #
52
+ # Fewer, well-differentiated tools measurably improve tool selection, and both
53
+ # groups below are redundant *for an agent*:
54
+ #
55
+ # /ops/* The regular `scrap_patent` / `search_patents` tools already
56
+ # fall back to EPO OPS on their own. Calling the OPS tools
57
+ # directly skips that fallback, so a patent missing from OPS
58
+ # becomes a hard error instead of a Google Patents hit.
59
+ # search_brave These are `search` with the fallback chain removed. `search`
60
+ # search_bing already tries DuckDuckGo, then Brave, then Bing, so exposing
61
+ # search_duck them separately only invites the model to pick a worse path.
62
+ #
63
+ # Set MCP_EXPOSE_ALL=1 to expose everything again, or edit this list.
64
+ EXCLUDED_ROUTES = [
65
+ r"^/ops/.*",
66
+ r"^/serp/search_(brave|bing|duck)$",
67
+ ]
68
+
69
  MCP_INSTRUCTIONS = """\
70
  SERPent gives you web, academic and patent search plus full-text patent retrieval.
71
 
 
82
  all your query variations in one call rather than making several calls. Set
83
  `n_results` to control how many hits come back per query.
84
 
85
+ `search` and `search_patents` already fall back across several backends
86
+ internally, including the official EPO OPS patent API, so a single call is the
87
+ most thorough option available there is no lower-level tool to reach for when
88
+ one comes back empty. A patent that returns "not found" is genuinely absent
89
+ from every backend; move on to the next id rather than retrying.
90
  """
91
 
92
 
 
142
 
143
 
144
  def build_mcp_server(app: FastAPI) -> FastMCP:
145
+ """Build an MCP server exposing the app's routes as tools."""
146
+ if _env_flag("MCP_EXPOSE_ALL", False):
147
+ route_maps = None
148
+ logger.info("MCP exposing all endpoints (MCP_EXPOSE_ALL=1).")
149
+ else:
150
+ route_maps = [
151
+ RouteMap(pattern=pattern, mcp_type=MCPType.EXCLUDE)
152
+ for pattern in EXCLUDED_ROUTES
153
+ ]
154
+
155
  mcp = FastMCP.from_fastapi(
156
  app=app,
157
  name=MCP_SERVER_NAME,
158
  mcp_names=_tool_names(app),
159
+ route_maps=route_maps,
160
  # Requests are dispatched straight back into this same app in-process.
161
  httpx_client_kwargs={"timeout": 300.0},
162
  )