Skip to content

ProtectedMcpServer: enforce per-user concurrent tool-call limit with PartitionedRateLimiter in call-tool filter#1393

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/update-protected-mcp-server-sample
Draft

ProtectedMcpServer: enforce per-user concurrent tool-call limit with PartitionedRateLimiter in call-tool filter#1393
Copilot wants to merge 3 commits intomainfrom
copilot/update-protected-mcp-server-sample

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

This sample currently relies on auth middleware but does not control concurrent MCP tool executions, which can diverge from HTTP request concurrency (notably with SSE/stream reconnect behavior). This change adds tool-call-level throttling directly in the MCP pipeline using PartitionedRateLimiter.Create.

  • Tool-call concurrency limiting in MCP filter

    • Added a WithRequestFilters(...).AddCallToolFilter(...) filter in ProtectedMcpServer.
    • Acquires a limiter lease per tools/call request and releases it when the call completes.
    • Returns a CallToolResult error when the limit is exceeded (instead of using ASP.NET UseRateLimiter middleware).
  • Per-user in-memory partitioning

    • Registered a singleton PartitionedRateLimiter<RequestContext<CallToolRequestParams>>.
    • Partition key resolution: NameIdentifier claim → Identity.Name → transport SessionId"anonymous".
    • Concurrency policy:
      • PermitLimit = 10
      • QueueLimit = 0 (fail fast, no queueing)
  • Sample documentation update

    • Updated samples/ProtectedMcpServer/README.md overview to note per-user concurrent tool-call limiting.
builder.Services.AddSingleton<PartitionedRateLimiter<RequestContext<CallToolRequestParams>>>(_ =>
    PartitionedRateLimiter.Create<RequestContext<CallToolRequestParams>, string>(context =>
        RateLimitPartition.GetConcurrencyLimiter(
            context.User?.FindFirstValue(ClaimTypes.NameIdentifier) ??
            context.User?.Identity?.Name ??
            context.JsonRpcMessage.Context?.RelatedTransport?.SessionId ??
            "anonymous",
            _ => new ConcurrencyLimiterOptions
            {
                PermitLimit = 10,
                QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                QueueLimit = 0
            })));

builder.Services.AddMcpServer()
    .WithRequestFilters(filters => filters.AddCallToolFilter(next => async (request, ct) =>
    {
        var limiter = request.Services!
            .GetRequiredService<PartitionedRateLimiter<RequestContext<CallToolRequestParams>>>();

        using var lease = await limiter.AcquireAsync(request, 1, ct).ConfigureAwait(false);
        if (!lease.IsAcquired)
        {
            return new CallToolResult
            {
                IsError = true,
                Content = [new TextContentBlock { Text = "Maximum concurrent tool calls (10) exceeded. Try again after in-progress calls complete." }]
            };
        }

        return await next(request, ct).ConfigureAwait(false);
    }));

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits February 26, 2026 18:21
Co-authored-by: halter73 <54385+halter73@users.noreply.github.com>
Co-authored-by: halter73 <54385+halter73@users.noreply.github.com>
Copilot AI changed the title [WIP] Update ProtectedMcpServer sample for per-user concurrent rate limit ProtectedMcpServer: enforce per-user concurrent tool-call limit with PartitionedRateLimiter in call-tool filter Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants