Skip to content

Commit 067766b

Browse files
committed
fix(fetch): use OpenAPI 3.0 compatible schema keywords for Gemini support
The Fetch model's JSON schema used keywords incompatible with LLM providers that only support OpenAPI 3.0 (e.g. Google Gemini 2.5 Pro), causing 400 INVALID_ARGUMENT errors: 1. max_length field used gt=0/lt=1000000 (Pydantic Field constraints), which generated exclusiveMinimum/exclusiveMaximum — not recognized by Gemini. Changed to ge=1/le=999999 (identical semantics for integers), which emits the supported minimum/maximum keywords. 2. url field used AnyUrl, which generated format: "uri" and minLength: 1 — Gemini only supports "enum" and "date-time" for string format. Added WithJsonSchema override to emit a plain string schema while preserving AnyUrl runtime validation. Fixes #1624 Made-with: Cursor
1 parent f424458 commit 067766b

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

src/fetch/src/mcp_server_fetch/server.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
INTERNAL_ERROR,
1919
)
2020
from protego import Protego
21-
from pydantic import BaseModel, Field, AnyUrl
21+
from pydantic import AnyUrl, BaseModel, Field, WithJsonSchema
2222

2323
DEFAULT_USER_AGENT_AUTONOMOUS = "ModelContextProtocol/1.0 (Autonomous; +https://github.com/modelcontextprotocol/servers)"
2424
DEFAULT_USER_AGENT_MANUAL = "ModelContextProtocol/1.0 (User-Specified; +https://github.com/modelcontextprotocol/servers)"
@@ -151,14 +151,18 @@ async def fetch_url(
151151
class Fetch(BaseModel):
152152
"""Parameters for fetching a URL."""
153153

154-
url: Annotated[AnyUrl, Field(description="URL to fetch")]
154+
url: Annotated[
155+
AnyUrl,
156+
Field(description="URL to fetch"),
157+
WithJsonSchema({"type": "string", "description": "URL to fetch"}),
158+
]
155159
max_length: Annotated[
156160
int,
157161
Field(
158162
default=5000,
159163
description="Maximum number of characters to return.",
160-
gt=0,
161-
lt=1000000,
164+
ge=1,
165+
le=999999,
162166
),
163167
]
164168
start_index: Annotated[

src/fetch/tests/test_server.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from mcp.shared.exceptions import McpError
66

77
from mcp_server_fetch.server import (
8+
Fetch,
89
extract_content_from_html,
910
get_robots_txt_url,
1011
check_may_autonomously_fetch_url,
@@ -13,6 +14,38 @@
1314
)
1415

1516

17+
class TestFetchToolSchema:
18+
"""Tests for the Fetch model JSON schema compatibility."""
19+
20+
def test_schema_uses_inclusive_bounds(self):
21+
"""Ensure the schema uses minimum/maximum instead of exclusiveMinimum/exclusiveMaximum.
22+
23+
Some LLM providers (e.g. Google Gemini) only support OpenAPI 3.0
24+
schema keywords and reject exclusiveMinimum/exclusiveMaximum.
25+
"""
26+
schema = Fetch.model_json_schema()
27+
max_length_schema = schema["properties"]["max_length"]
28+
29+
assert "exclusiveMinimum" not in max_length_schema
30+
assert "exclusiveMaximum" not in max_length_schema
31+
assert max_length_schema["minimum"] == 1
32+
assert max_length_schema["maximum"] == 999999
33+
34+
def test_url_schema_omits_unsupported_keywords(self):
35+
"""Ensure the url field schema avoids format/minLength keywords.
36+
37+
Some LLM providers (e.g. Google Gemini) only support a limited set
38+
of keywords for string types and reject unsupported ones like
39+
format: "uri" or minLength.
40+
"""
41+
schema = Fetch.model_json_schema()
42+
url_schema = schema["properties"]["url"]
43+
44+
assert "format" not in url_schema
45+
assert "minLength" not in url_schema
46+
assert url_schema["type"] == "string"
47+
48+
1649
class TestGetRobotsTxtUrl:
1750
"""Tests for get_robots_txt_url function."""
1851

0 commit comments

Comments
 (0)