Skip to content

fix(fetch): validate prompt URL and raise INVALID_PARAMS (#4914) - #4918

Closed
DawnofGenX wants to merge 2 commits into
modelcontextprotocol:mainfrom
DawnofGenX:fix-fetch-prompt-invalid-url-4914
Closed

DawnofGenX wants to merge 2 commits into
modelcontextprotocol:mainfrom
DawnofGenX:fix-fetch-prompt-invalid-url-4914

Conversation

@DawnofGenX

Copy link
Copy Markdown

Problem

Fixes #4914

The fetch prompt passes its url argument straight to fetch_url, which converts only httpx.HTTPError. httpx.InvalidURL is not an HTTPError, so it escapes the prompt's except McpError handler; with raise_exceptions=False the SDK answers with a generic JSON-RPC error, code 0, carrying the raw exception text. A client cannot distinguish a bad argument from a server fault.

The fetch tool already handles this correctly by validating through the Fetch model and raising INVALID_PARAMS.

Before

{"jsonrpc": "2.0", "id": 3, "error": {"code": 0, "message": "Invalid port: ':1'"}}

After

{"jsonrpc": "2.0", "id": 3, "error": {"code": -32602, "message": "1 validation error for Fetch\nurl\n  Input should be a valid URL, invalid IPv6 address ..."}}

Fix

Validate the prompt's url through the same Fetch model the tool uses, before fetching:

try:
    url = str(Fetch.model_validate({"url": arguments["url"]}).url)
except ValidationError as e:
    raise McpError(ErrorData(code=INVALID_PARAMS, message=str(e)))

model_validate is used rather than Fetch(url=...) because a direct constructor call makes pyright complain about the model's required-with-default fields; behaviour is identical.

Test plan

  • New test TestGetPrompt::test_invalid_url_raises_invalid_params — drives get_prompt through a real ClientSession over in-memory transports and asserts error.code == INVALID_PARAMS
  • New test TestGetPrompt::test_valid_url_returns_fetched_content — regression coverage for the happy path
  • Differential proof: reverting only the fix hunk makes the new test fail with ErrorData(code=0, message="Invalid port: ':1'"), i.e. the reported bug
  • uv run --no-sync pytest in src/fetch: 22 passed
  • uv run --no-sync pyright: 0 errors, 0 warnings
  • uv run --no-sync ruff check .: all checks passed

httpx.InvalidURL is not an httpx.HTTPError, so it escaped the prompt
handler's 'except McpError' block and the SDK answered with a generic
JSON-RPC code 0 instead of a proper error. Validate the url argument
through the Fetch model before fetching (the same way the fetch tool
already does), so invalid URLs raise McpError with
INVALID_PARAMS (-32602).

Fixes modelcontextprotocol#4914
@DawnofGenX

Copy link
Copy Markdown
Author

Closing due to inactivity — 4d idle, no review. Feel free to reopen if still wanted.

@DawnofGenX DawnofGenX closed this Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mcp-server-fetch: fetch prompt returns JSON-RPC error code 0 with the raw exception text for an invalid URL

1 participant