Repository navigation
Description
Initial Checks
- I confirm that I'm using the newest release of my line (the latest 2.x, or the latest 1.x if I'm still on v1)
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Release line
2.x (current stable)
Description
A resource template handler can take a parameter with a non-str annotation, such as user_id: int. The "Type conversion" section of docs/servers/uri-templates.md teaches this with orders://{order_id}. When a URI carries a value that doesn't convert, the SDK treats it as a server crash. Reading users://abc/profile returns -32603 Error creating resource from template users://abc/profile, and the server logs Resource 'users://abc/profile' raised an unexpected exception at ERROR with the full pydantic ValidationError traceback. Any client can trigger this, and it happens on every such request.
Expected: that URI names no resource, so it should get the same answer as an unmatched URI or a path-safety rejection: -32602 Unknown resource: users://abc/profile, logged once at INFO. Tools already treat a schema rejection as anticipated (one INFO line, no traceback).
Cause: ResourceTemplate.from_function (src/mcp/server/mcpserver/resources/templates.py) wraps the handler in validate_call, so validating the URI parameters and running the body happen in one call. In ResourceTemplate.create_resource, the ValidationError from a bad parameter lands in except Exception and becomes UnexpectedResourceError. #3314 lists this under "Not in here": "A resource template parameter that fails its type annotation is still logged as a crash: templates run through validate_call, which fuses validation with the call. Not a regression. Follow-up." No issue tracks it yet.
Proposed fix: from_function already builds the handler's argument model with func_metadata. Keep it on the template as a private attribute and, inside the existing try of create_resource, validate the extracted parameters against it before calling the handler, raising ResourceNotFoundError(f"Unknown resource: {uri}") on failure. A ValidationError from the handler body, or a validator raising something other than ValueError/AssertionError, stays a crash. I have this on a local branch (about 20 lines, two tests and one docs sentence) and would like to send the PR if the direction works for you.
Two choices I'd like your view on:
- The parameters are validated twice (here and again by
validate_call), so a custom validator runs twice per read, and a stateful one that passes the first time and fails the second surfaces as a crash. Validating once means calling the undecorated function with the model's output, which either changes what the publicResourceTemplate.fnholds or keeps a second, private reference to the function. ReusingFuncMetadata.validate_arguments()asTool.rundoes would also add itspre_parse_jsonstep, which JSON-decodes string values for non-strparameters. - Like the path-safety check, a value that fails stops at the first matching template. With
items://{item_id}/x(item_id: int) registered beforeitems://{slug}/x,items://hello/xgets-32602and does not fall through to the second template. Should it fall through instead?
Disclosure: Claude Code found this, wrote the fix and tests, ran the example and drafted this issue; I reviewed the output on main and on the branch, and the duplicate search, before filing.
Example Code
import anyio
from mcp import Client, MCPError
from mcp.server import MCPServer
mcp = MCPServer("repro")
@mcp.resource("users://{user_id}/profile")
def profile(user_id: int) -> str:
return f"user {user_id}"
async def main() -> None:
async with Client(mcp) as client:
print((await client.read_resource("users://42/profile")).contents[0].text)
try:
await client.read_resource("users://abc/profile")
except MCPError as e:
print(e.error.code, e.error.message)
anyio.run(main)
# user 42
# -32603 Error creating resource from template users://abc/profile
# stderr: ERROR Resource 'users://abc/profile' raised an unexpected exception
# + the pydantic ValidationError traceback (int_parsing, input_value='abc')Python & MCP Python SDK
Python 3.11.14, pydantic 2.12.5, macOS
mcp main at 77001c9. src/mcp/server/mcpserver/resources/templates.py is unchanged since v2.3.0 (2118f14).
Activity
tharunsridhar commentedon Nov 8, 2026
Reproduced on mcp 2.3.0 with the example from the report:
users://42/profilereturnsuser 42, andusers://abc/profilelogs "Resource 'users://abc/profile' raised an unexpected exception" with the full pydantic traceback instead of a clean "unknown resource" answer.Since the reporter has first call on this, I'm happy to take the PR only if you don't plan to open it yourself. If a maintainer assigns it to me, I'd follow the proposed fix (validate the extracted parameters against the stored argument model inside
create_resourceand raiseResourceNotFoundError) and add tests for both the invalid and the valid int cases.Disclosure: I used Claude Code to run the reproduction; I read the output and wrote this comment.
rodrigogaraujo commentedon Nov 8, 2026
Thanks for confirming on 2.3.0. Yes, I plan to open the PR: the branch is ready, with the check inside the existing try of create_resource and tests for both the invalid and the valid values. I'm waiting on a maintainer's call on the two questions above (single validation and fall-through) before sending it.
Drafted with Claude Code.