Skip to content

Conversation

@matthew-gries
Copy link

@matthew-gries matthew-gries commented Nov 25, 2025

  • Fix template detection logic to exclude context when determining if a resource is a template or not
  • Add support for FunctionResources to be read with a context param, allowing resources registered with a context param to read from ccontext objects
  • Use func_metadata.call_fn_with_arg_validation() to validate arguments in resources while ignoring injected context param, to avoid Pydantic model validation issues
  • Extracted is_async_callable() as a public utility

Motivation and Context

This solves the issue of context not being properly injected into regular resources. Per #1635, when registering a resource with @mcp.resource(), the logic that determines whether to create a standard resource or a resource template was incorrectly counting the Context parameter as a function parameter. This caused functions with only a Context parameter (no URI parameters) to be incorrectly treated as templates.

How Has This Been Tested?

I added unit tests plus used the following test application to ensure the fix worked as expected and didn't affect existing functionality around resources and resource templates. I tested reading resources and resource templates with and without context params

from contextlib import asynccontextmanager
from datetime import datetime

from mcp import ServerSession
from mcp.server import FastMCP
from mcp.server.fastmcp import Context


class AppContext:
    """Application context with some shared state."""

    def __init__(self):
        self.server_start_time = datetime.now()
        self.request_count = 0

    def increment_requests(self):
        self.request_count += 1


@asynccontextmanager
async def lifespan(server: FastMCP):
    """Initialize application context."""
    print("Server starting up...")
    ctx = AppContext()
    yield ctx
    print(f"Server shutting down. Total requests: {ctx.request_count}")


mcp = FastMCP("Context Test Demo", lifespan=lifespan)


@mcp.resource("time://current")
async def current_time(ctx: Context[ServerSession, AppContext]) -> str:
    return f"Current time: {datetime.now().isoformat()}, request {ctx.request_id}"


@mcp.resource("time://format")
async def time_format() -> str:
    return "Supported formats: ISO8601, RFC3339, Unix timestamp"


@mcp.resource("greeting://{name}")
async def personalized_greeting(name: str, ctx: Context[ServerSession, AppContext]) -> str:
    uptime = (datetime.now() - ctx.request_context.lifespan_context.server_start_time).total_seconds()
    return f"Hello {name}! Server uptime: {uptime:.1f}s"


@mcp.resource("math://{operation}/{a}/{b}")
async def math_operation(operation: str, a: int, b: int) -> str:
    operations = {
        "add": a + b,
        "subtract": a - b,
        "multiply": a * b,
        "divide": a / b if b != 0 else "Error: Division by zero"
    }
    result = operations.get(operation, "Error: Unknown operation")
    return f"{operation}({a}, {b}) = {result}"


if __name__ == "__main__":
    import asyncio

    mcp.run()

Breaking Changes

Technically yes, because the signature of the read() function on the Resource type now includes an additional optional context param, but this shouldn't break at runtime since the arg is optional

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

@matthew-gries matthew-gries changed the title fix: Fix logic that determines standard resource vs. resource template to account for context param fix: Fix logic that determines standard resource vs. resource template to account for context param (#1635) Nov 25, 2025
@pepijndevos
Copy link

I can confirm that this fixes the problem for me

@felixweinberger felixweinberger added bug Something isn't working breaking change Will break existing deployments when updated without changes labels Dec 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Will break existing deployments when updated without changes bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants