python-sdk icon indicating copy to clipboard operation
python-sdk copied to clipboard

FastMCP read_resource() returns incorrect error code when resource not found

Open fennb opened this issue 1 month ago • 4 comments

Initial Checks

  • [x] I confirm that I'm using the latest version of MCP Python SDK
  • [x] I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue

Description

It appears to be unclear as to how an MCP server should respond to a read resource request if the resource is not found, however, FastMCP returns error code 0, which seems likely incorrect regardless.

In the Error Handling part of the protocol specification it states:

Resource not found: -32002

However, the python SDK's FastMCP attempts to raise a ResourceError, except it appears that code would never get executed because a ValueError is raised in ResourceManager first.

The net result of this is that servers implemented using FastMCP return the following for resource-not-found:

{"jsonrpc":"2.0","id":7,"error":{"code":0,"message":"Unknown resource: https://example.com/does-not-exist"}}

For the purposes of comparison, I compared this to the (presumably reference) typescript implementation, which raises an MCP error with InvalidParams: code -32602 - see here

-32602 is notably similar to -32002, but also seemingly incorrect (though in a different way ☹).

As things stand today, it is rather difficult to reliably write clients that behave predictably due to this. I understand this may be more of a protocol specification problem than a python-sdk problem, but error code 0 seems incorrect either way, so thought I'd report it.

Example Code

Basic server (anything will do):

from mcp.server.fastmcp import FastMCP

mcp = FastMCP('Pydantic AI MCP Server')
log_level = 'unset'


@mcp.resource('resource://user_name.txt', mime_type='text/plain')
async def user_name_resource() -> str:
    return 'Alice'


if __name__ == '__main__':
    mcp.run(transport='streamable-http')

Then try to fetch any resource via whatever MCP client you like, eg (manual curl):

MCP_URL="http://localhost:8000/mcp"
SESSION_ID=""

# Step 1: Initialize and get session ID
echo "1. Initializing MCP server..."
INIT_RESPONSE=$(curl -s -D - -X POST "$MCP_URL" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {
        "name": "test-mcp-script",
        "version": "1.0.0"
      }
    },
    "id": 1
  }')

# Extract session ID from headers
SESSION_ID=$(echo "$INIT_RESPONSE" | grep -i 'mcp-session-id:' | cut -d' ' -f2 | tr -d '\r')

# Try to read resource:
curl -s -X POST "$MCP_URL" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: $SESSION_ID" \
  -d '{
    "jsonrpc": "2.0",
    "method": "resources/read",
    "params": {
      "uri": "https://example.com/does-not-exist"
    },
    "id": 7
  }'

# > event: message
# > data: {"jsonrpc":"2.0","id":7,"error":{"code":0,"message":"Unknown resource: https://example.com/does-not-exist"}}

Python & MCP Python SDK

`main`

fennb avatar Nov 05 '25 09:11 fennb

Thank you for the report! It would be great if you could provide some example code for the server/client with which shows the errors and what you'd expect, or hope to be consistent.

maxisbey avatar Nov 05 '25 10:11 maxisbey

Thank you for the report! It would be great if you could provide some example code for the server/client with which shows the errors and what you'd expect, or hope to be consistent.

Done!

fennb avatar Nov 05 '25 10:11 fennb

Wait, before we go further on this, I just found this: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1545

It looks like the spec itself is wrong ☹️. I haven't read the whole thread but this may change the answer as to what the right solution is.

fennb avatar Nov 06 '25 13:11 fennb

I am getting the same error after updating FastMCP from v2.12.5 to v2.13.0.2. The error originates in mcp/shared/session.py → BaseSession → send_reques()

jsonrpc='2.0' id=8 error=ErrorData(code=0, message="Unknown resource: 'canvasapi://courses/DATA101-F25/syllabus?sis_user_id=123456789'", data=None)

Here is our resource template declaration:

@courses_mcp.resource(
            f"canvasapi://courses/{{sis_course_id}}/syllabus?sis_user_id={{sis_user_id}}",
            name="get_syllabus",
            description="Fetch the syllabus_body field for a course. Returns the syllabus text for the course.",
            tags={ToolPermission.COURSE, ToolPermission.SYLLABUS, ContentTag.SYLLABUS},
        )
        async def get_syllabus(
            sis_course_id: str,
            sis_user_id: str,
        ) -> str:
            """
            Fetch the syllabus_body for a course from Canvas.
            """
            try:
                logging.info("Fetching syllabus for course.")
                # Use get_course_for_user to get course details
                course = self._canvas.courses.get_course_for_user(
                    sis_course_id=sis_course_id,
                    sis_user_id=sis_user_id,
                    include=["syllabus_body", "term"],
                )
                # Use Pythonic fallback to empty string if syllabus_body is falsy
                syllabus_body = course.syllabus_body or ""
                return syllabus_body
            except Exception as e:
                logging.error(f"Error in get_syllabus: {e}")
                return {"error": str(e), "sis_course_id": sis_course_id}

tpamsler avatar Nov 16 '25 02:11 tpamsler