Skip to content

In the StreamableHttp mode MCP server, Scoped type dependencies are not properly injected into classes marked with [McpServerToolType]. #544

Open
@wesley-wws

Description

@wesley-wws

Describe the bug

I'm trying to access RouteValues from HttpContext by adding a Middleware that stores them in an Argument class, registered as Scoped in the DI container. However, when invoking the MCP tool method, I can't retrieve the correct Argument instance in the TestTools class. The logs indicate that the Argument instance differs between the Middleware and the TestTools.

// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add McpServer services
builder.Services.AddMcpServer()
    .WithHttpTransport()
    .WithToolsFromAssembly();

builder.Services.AddScoped<Arguements>();

var app = builder.Build();
app.UseRouting();

app.Use((context, next) =>
{
    // The Middleware to capture route values and set them in Arguements
    var arguements = context.RequestServices.GetRequiredService<Arguements>();
    arguements.SetRouteValue(context.Request.RouteValues["value"]?.ToString());
    return next();
});

app.Map("/", (Arguements arguements) => arguements.Guid.ToString());
app.MapMcp("mcp/{value}");
app.Run();


[McpServerToolType]
public class TestTools
{
    private readonly Arguements arguements;

    public TestTools(Arguements arguements, ILogger<TestTools> logger)
    {
        this.arguements = arguements;
        logger.LogInformation($"TestTools created.");
    }

    [McpServerTool]
    [Description("Test")]
    public Task<string> TestAsync(CancellationToken cancellationToken = default)
    {
        return Task.FromResult($"RouteValue: {arguements.RouteValue}");
    }
}


public class Arguements
{
    private readonly ILogger<Arguements> _logger;

    public Guid Guid { get; private set; }

    public string? RouteValue { get; private set; }

    public Arguements(ILogger<Arguements> logger)
    {
        this._logger = logger;
        Guid = Guid.NewGuid();
        logger.LogInformation($"Arguements created ID: {Guid}");
    }

    public void SetRouteValue(string? value)
    {
        RouteValue = value;
        _logger.LogInformation($"RouteValue set to: {RouteValue}");
    }
}

To Reproduce
Steps to reproduce the behavior:

  1. Start the ASP.NET Core project
  2. Use the MCP debug tool - MCP Inspector, connect to http://localhost:xxxx/mcp/abc and call the Test tool
  3. The return value is RouteValue:

Expected behavior
The return value should be RouteValue: abc

Logs

info: CS.API.Arguements[0]
      Arguements created ID: f7616e71-1756-43c1-aed6-5f085f817e99
info: CS.API.Arguements[0]
      RouteValue set to: abc
info: ModelContextProtocol.Server.McpServer[570385771]
      Server (CS.API 1.0.0.0), Client (mcp-inspector 0.14.3) method 'tools/call' request handler called.
info: CS.API.Arguements[0]
      Arguements created ID: 6c51f94c-51e0-4ad9-ad44-cdbe722b1e64
info: CS.API.TestTools[0]
      TestTools created.
info: ModelContextProtocol.Server.McpServer[1867955179]
      Server (CS.API 1.0.0.0), Client (mcp-inspector 0.14.3) method 'tools/call' request handler completed.

Additional context

The log details show that the Arguments are created and RouteValues are correctly assigned in the Middleware. However, when resolving TestTools, a new Argument instance is created. It seems TestTool is resolved in a different scope from the HTTP request pipeline, which is confusing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions