Skip to content

A resource template value that fails the handler's type hint is logged as a crash and answered -32603 #3837

Open

Description

@rodrigogaraujo

Initial Checks

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:

  1. 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 public ResourceTemplate.fn holds or keeps a second, private reference to the function. Reusing FuncMetadata.validate_arguments() as Tool.run does would also add its pre_parse_json step, which JSON-decodes string values for non-str parameters.
  2. Like the path-safety check, a value that fails stops at the first matching template. With items://{item_id}/x (item_id: int) registered before items://{slug}/x, items://hello/x gets -32602 and 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

  1. added
    bugSomething isn't working
    v2Affects the v2 line (2.x on main)
    on Nov 8, 2026
  2. tharunsridhar commented on Nov 8, 2026

    @tharunsridhar

    Reproduced on mcp 2.3.0 with the example from the report: users://42/profile returns user 42, and users://abc/profile logs "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_resource and raise ResourceNotFoundError) 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.

  3. rodrigogaraujo commented on Nov 8, 2026

    @rodrigogaraujo
    Author

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv2Affects the v2 line (2.x on main)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions