The official C# SDK for Model Context Protocol servers and clients. Maintained in collaboration with Microsoft.
by modelcontextprotocolLast 12 weeks ยท 183 commits
5 of 6 standards met
fire-and-forgets tasks but returns as soon as the transport channel completes โ without waiting for in-flight handlers. The caller () then disposes service scopes, causing in still-running tool handlers. This is easily triggered by OpenAI's stateless HTTP transport closing the connection while a long-running tool is executing. Changes : Track in-flight handlers with an counter (starts at 1 for the loop itself) and a . Increment before each dispatch, decrement in its . When the count hits zero, signal the TCS. The block of decrements its own count and awaits the TCS if handlers are still running. : Regression test that blocks a tool handler, disposes the transport, then verifies doesn't return until the handler completes. Uses instead of non-generic for .NET Standard 2.0 compatibility. Original prompt This section details on the original issue you should resolve* ObjectDisposedException - IServiceProvider disposed while tool handler execution. Describe the bug Sometimes I get from DI services like the itself or inside the tool call handler. It looks like OpenAI closes the connection for too-long-running tools, and instead of canceling the , the service scope gets disposed. Or the scope gets disposed of without waiting for the handler to cancel cleanly. To Reproduce Steps to reproduce the behavior: 1. Setup: Stateless, Http Transport 2. Call a long-running tool 3. Close the connection to simulate a read-timeout. [!NOTE] I use it with OpenAI OpenAI requires statelessness and closes the connection after some timeout. Expected behavior I expect the handler to either complete or cancel through before the service scope is disposed. Logs Additional context Add any other context about the problem here. Both https://github.com/modelcontextprotocol/csharp-sdk/pull/1401 and https://github.com/modelcontextprotocol/csharp-sdk/pull/1400 are trying to fix this issue. I'd like you to create a PR to fix it as well, in a similar manner, but you shouldn't need to actually track the task instances / maintain a list. It should be enough to just have a counter that's incremented each time and just before ProcessMessageAsync is called, and then ProcessMessageAsync decrements it at the end of its finally block. When the count drops to zero, ProcessMessagesCoreAsync can be allowed to continue / complete. The count can start at 1 and then be Interlocked.Decremented just before it waits on a TaskCompletionSource Task that whoever Interlocked.Decrements the count to 0 will set. Start by writing one or more tests that will fail before the fix and will pass after. ## Comments on the Issue (you are @copilot in this section) Fixes modelcontextprotocol/csharp-sdk#1269 ๐ GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.
Remove stale ignore rules for anthropic.com and hackerone.com Add ignore rule for DocFX links that mlc cannot check Add redirect suppression for learn.microsoft.com locale redirects Add redirect suppression for modelcontextprotocol.io homepage redirects Update RFC URLs from tools.ietf.org to datatracker.ietf.org Update GitHub Pages URLs to csharp.sdk.modelcontextprotocol.io custom domain Fix GitHub org projects URL to include query parameter Add `[Description]` as inline code in list-of-diagnostics.md Reformat args to multi-line with descriptive comments
Repository: modelcontextprotocol/csharp-sdk. Description: The official C# SDK for Model Context Protocol servers and clients. Maintained in collaboration with Microsoft. Stars: 3973, Forks: 644. Primary language: C#. Languages: C# (100%), Makefile (0%). Homepage: https://modelcontextprotocol.github.io/csharp-sdk/ Latest release: v1.0.0 (3d ago). Open PRs: 23, open issues: 105. Last activity: 6h ago. Community health: 87%. Top contributors: Copilot, stephentoub, halter73, dependabot[bot], eiriktsarpalis, jeffhandley, PederHP, mikekistler, MackinnonBuck, aaronpowell and others.
C#
Describe the bug Sometimes I get from DI services like the itself or inside the tool call handler. It looks like OpenAI closes the connection for too-long-running tools, and instead of canceling the , the service scope gets disposed. Or the scope gets disposed of without waiting for the handler to cancel cleanly. To Reproduce Steps to reproduce the behavior: 1. Setup: Stateless, Http Transport 2. Call a long-running tool 3. Close the connection to simulate a read-timeout. [!NOTE] I use it with OpenAI OpenAI requires statelessness and closes the connection after some timeout. Expected behavior I expect the handler to either complete or cancel through before the service scope is disposed. Logs Additional context Add any other context about the problem here.
Summary This PR addresses two issues from the Backlog milestone: Fix #648: OAuth authentication fails with MS Entra-ID The SDK sends a parameter (RFC 8707) in OAuth authorization, token exchange, and token refresh requests. MS Entra-ID v2.0 does not support this parameter and returns . Changes: Added property to (defaults to for backward compatibility) When set to , the parameter is omitted from all OAuth requests Added test verifying the authorization URL omits the parameter when the option is disabled Usage: Fix #147: Add client sample with DI guidance Created showing how to register as a singleton via and consume it from an Added DI wiring + Docker transport guidance section to Added new sample project to the solution file Testing All 49 OAuth tests pass across net8.0, net9.0, and net10.0 New test validates the feature DI sample compiles and builds successfully
Summary Fixes a race condition in stateless HTTP mode where is thrown when an HTTP connection is aborted while tool handlers are still executing. Closes #1269 Problem In stateless mode, fires and forgets message handler tasks (). When the HTTP connection is aborted (e.g., client timeout), the disposal chain runs: 1. fires 2. Session transport is disposed, completing the message channel 3. exits the loop 4. Session and server are disposed 5. ASP.NET Core disposes (the HTTP request scope) But in step 3, fire-and-forget handler tasks may still be running. When they try to access scoped services (like or ), is thrown because the request scope was disposed in step 5. Fix Track handler tasks in and await them in the block before allowing the method to return. Since is awaited indirectly by the HTTP request handler (via ), this keeps the request scope alive until all handlers complete: Handler tasks are captured in a instead of being discarded The block calls before failing pending requests Exceptions from handlers are already logged individually in , so they are caught and suppressed in This is a minimal change to the core message processing loop that fixes the issue without changing the service provider or scope configuration. Testing Added regression test that: Registers a tool handler that blocks, waits for the connection to abort, then accesses scoped services Verifies the scoped service is accessible without after connection abort Uses a side-channel () to verify handler outcome since the client call is aborted All existing continue to pass Full test suite: 303 passed, 11 skipped, 2 pre-existing conformance failures
Summary Fixes #1269 When using HTTP transport with , if the client closes the connection (e.g., OpenAI times out on a long-running tool), the ASP.NET Core request scope is disposed while tool handlers are still executing, causing . Root Cause In , message handlers were launched as fire-and-forget tasks (). These tasks were not tracked or awaited. When cancelled the message processing CTS and awaited , the channel reader loop exited immediately but the fire-and-forget handler tasks continued running. The session disposal then completed, ASP.NET Core disposed the request service scope, and the still-running handlers got when resolving scoped services. Fix Track all handler tasks in a instead of discarding them in the block of before the method returns Periodically prune completed tasks to avoid unbounded list growth Added a regression test verifying scoped services remain accessible throughout a delayed handler's lifetime Test Results Build: 0 warnings, 0 errors Core tests: 1838 passed (net10.0), 1838 passed (net9.0), 1654 passed (net472) ASP.NET Core tests**: 302 passed (2 pre-existing conformance failures unrelated to this change)