modelcontextprotocol / modelcontextprotocol/csharp-sdk

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

Open
#544 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug P2
Dominant language
C#
Stars
4.5k
Forks
814
Avg merge
9d 19h
Merged PRs (30d)
4

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the Streamable HTTP setup using WithHttpTransport, WithToolsFromAssembly, MapMcp, and MCP Inspector. Trace how the scoped Arguements instance is resolved in the HTTP pipeline versus TestTools; done means both use the same instance and calling Test returns the route value, such as abc.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.