-
Notifications
You must be signed in to change notification settings - Fork 677
Add and augment conceptual docs #1375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jeffhandley
merged 15 commits into
modelcontextprotocol:main
from
jeffhandley:jeffhandley/docs
Feb 25, 2026
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
a807ba8
Add conceptual docs for tools, resources, prompts, roots, completions…
jeffhandley 137f3fe
Add sampling docs, tools error handling, and SSE server example
jeffhandley db099e0
Apply suggestions from code review
jeffhandley a079c0f
Address PR feedback on conceptual docs
jeffhandley edd348a
Remove xref for OperationCanceledException
jeffhandley d79aa0f
Address PR feedback on conceptual docs
jeffhandley 08a9f5f
Apply suggestions from code review
jeffhandley 958c783
Use dnx NuGet.Mcp.Server in stdio client example
jeffhandley 952ba6c
Fix xref namespace for McpClientHandlers.RootsHandler
jeffhandley 89c744e
Fix compilation errors in new docs
jeffhandley 84c331d
Address feedback with simpler syntax
jeffhandley a72baf9
Remove duplicated sample code
jeffhandley 4cdb9a2
Illustrate using ResumeSessionAsync
jeffhandley aa0be80
Add content and example for http/sse endpoints and routes
jeffhandley 0b6f1bd
Fix subscription removal and broken xref.
jeffhandley File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| --- | ||
| title: Cancellation | ||
| author: jeffhandley | ||
| description: How to cancel in-flight MCP requests using cancellation tokens and notifications. | ||
| uid: cancellation | ||
| --- | ||
|
|
||
| ## Cancellation | ||
|
|
||
| MCP supports [cancellation] of in-flight requests. Either side can cancel a previously issued request, and `CancellationToken` parameters on MCP methods are wired to send and receive `notifications/cancelled` notifications over the protocol. | ||
|
|
||
| [cancellation]: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation | ||
| [task cancellation]: https://learn.microsoft.com/dotnet/standard/parallel-programming/task-cancellation | ||
|
|
||
| ### How cancellation maps to MCP notifications | ||
|
|
||
| When a `CancellationToken` passed to a client method (such as <xref:ModelContextProtocol.Client.McpClient.CallToolAsync*>) is cancelled, a `notifications/cancelled` notification is sent to the server with the request ID. On the server side, the `CancellationToken` provided to the tool method is then triggered, allowing the handler to stop work gracefully. This same mechanism works in reverse for server-to-client requests. | ||
|
|
||
| ### Server-side cancellation handling | ||
|
|
||
| Server tool methods receive a `CancellationToken` that is triggered when the client sends a cancellation notification. Pass this token through to any async operations so they stop promptly: | ||
|
|
||
| ```csharp | ||
| [McpServerTool, Description("A long-running computation")] | ||
| public static async Task<string> LongComputation( | ||
| [Description("Number of iterations")] int iterations, | ||
| CancellationToken cancellationToken) | ||
| { | ||
| for (int i = 0; i < iterations; i++) | ||
| { | ||
| await Task.Delay(1000, cancellationToken); | ||
| } | ||
|
|
||
| return $"Completed {iterations} iterations."; | ||
| } | ||
| ``` | ||
|
|
||
| When the client sends a cancellation notification, the `OperationCanceledException` propagates back to the client as a cancellation response. | ||
|
|
||
| ### Cancellation notification details | ||
|
|
||
| The cancellation notification includes: | ||
|
|
||
| - **RequestId**: The ID of the request to cancel, allowing the receiver to correlate the cancellation with the correct in-flight request. | ||
| - **Reason**: An optional human-readable reason for the cancellation. | ||
|
|
||
| Cancellation notifications can be observed by registering a handler. For broader interception of notifications and other messages, <xref:ModelContextProtocol.Server.McpMessageFilter> delegates can be added to the <xref:ModelContextProtocol.Server.McpMessageFilters.IncomingFilters> collection in <xref:ModelContextProtocol.Server.McpServerOptions.Filters>. | ||
|
|
||
| ```csharp | ||
| mcpClient.RegisterNotificationHandler( | ||
| NotificationMethods.CancelledNotification, | ||
| (notification, ct) => | ||
| { | ||
| var cancelled = notification.Params?.Deserialize<CancelledNotificationParams>( | ||
| McpJsonUtilities.DefaultOptions); | ||
| if (cancelled is not null) | ||
| { | ||
| Console.WriteLine($"Request {cancelled.RequestId} cancelled: {cancelled.Reason}"); | ||
| } | ||
| return default; | ||
| }); | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| --- | ||
| title: Capabilities | ||
| author: jeffhandley | ||
| description: How capability and protocol version negotiation works in MCP. | ||
| uid: capabilities | ||
| --- | ||
|
|
||
| ## Capabilities | ||
|
|
||
| MCP uses a [capability negotiation] mechanism during connection setup. Clients and servers exchange their supported capabilities so each side can adapt its behavior accordingly. Both sides should check the other's capabilities before using optional features. | ||
|
|
||
| [capability negotiation]: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle#initialization | ||
|
|
||
| ### Client capabilities | ||
|
|
||
| <xref:ModelContextProtocol.Protocol.ClientCapabilities> declares what features the client supports: | ||
|
|
||
| | Capability | Type | Description | | ||
| |-----------|------|-------------| | ||
| | `Roots` | <xref:ModelContextProtocol.Protocol.RootsCapability> | Client can provide filesystem root URIs | | ||
| | `Sampling` | <xref:ModelContextProtocol.Protocol.SamplingCapability> | Client can handle LLM sampling requests | | ||
| | `Elicitation` | <xref:ModelContextProtocol.Protocol.ElicitationCapability> | Client can present forms or URLs to the user | | ||
| | `Experimental` | `IDictionary<string, object>` | Experimental capabilities | | ||
|
|
||
| Configure client capabilities when creating an MCP client: | ||
|
|
||
| ```csharp | ||
| var options = new McpClientOptions | ||
| { | ||
| Capabilities = new ClientCapabilities | ||
| { | ||
| Roots = new RootsCapability { ListChanged = true }, | ||
| Sampling = new SamplingCapability(), | ||
| Elicitation = new ElicitationCapability | ||
| { | ||
| Form = new FormElicitationCapability(), | ||
| Url = new UrlElicitationCapability() | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| await using var client = await McpClient.CreateAsync(transport, options); | ||
| ``` | ||
|
|
||
| Handlers for each capability (roots, sampling, elicitation) are covered in their respective documentation pages. | ||
|
|
||
| ### Server capabilities | ||
|
|
||
| <xref:ModelContextProtocol.Protocol.ServerCapabilities> declares what features the server supports: | ||
|
|
||
| | Capability | Type | Description | | ||
| |-----------|------|-------------| | ||
| | `Tools` | <xref:ModelContextProtocol.Protocol.ToolsCapability> | Server exposes callable tools | | ||
| | `Prompts` | <xref:ModelContextProtocol.Protocol.PromptsCapability> | Server exposes prompt templates | | ||
| | `Resources` | <xref:ModelContextProtocol.Protocol.ResourcesCapability> | Server exposes readable resources | | ||
| | `Logging` | <xref:ModelContextProtocol.Protocol.LoggingCapability> | Server can send log messages | | ||
| | `Completions` | <xref:ModelContextProtocol.Protocol.CompletionsCapability> | Server supports argument completions | | ||
| | `Experimental` | `IDictionary<string, object>` | Experimental capabilities | | ||
|
|
||
| Server capabilities are automatically inferred from the configured features. For example, registering tools with `.WithTools<T>()` automatically declares the tools capability. | ||
jeffhandley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ### Checking capabilities | ||
|
|
||
| Before using an optional feature, check whether the other side declared the corresponding capability. | ||
|
|
||
| #### Checking server capabilities from the client | ||
|
|
||
| ```csharp | ||
| await using var client = await McpClient.CreateAsync(transport); | ||
|
|
||
| // Check if the server supports tools | ||
| if (client.ServerCapabilities.Tools is not null) | ||
| { | ||
| var tools = await client.ListToolsAsync(); | ||
| } | ||
|
|
||
| // Check if the server supports resources with subscriptions | ||
| if (client.ServerCapabilities.Resources is { Subscribe: true }) | ||
| { | ||
| await client.SubscribeToResourceAsync("config://app/settings"); | ||
| } | ||
|
|
||
| // Check if the server supports prompts with list-changed notifications | ||
| if (client.ServerCapabilities.Prompts is { ListChanged: true }) | ||
| { | ||
| mcpClient.RegisterNotificationHandler( | ||
| NotificationMethods.PromptListChangedNotification, | ||
| async (notification, ct) => | ||
| { | ||
| var prompts = await mcpClient.ListPromptsAsync(cancellationToken: ct); | ||
| }); | ||
| } | ||
|
|
||
| // Check if the server supports logging | ||
| if (client.ServerCapabilities.Logging is not null) | ||
| { | ||
| await client.SetLoggingLevelAsync(LoggingLevel.Info); | ||
| } | ||
|
|
||
| // Check if the server supports completions | ||
| if (client.ServerCapabilities.Completions is not null) | ||
| { | ||
| var completions = await client.CompleteAsync( | ||
| new PromptReference { Name = "my_prompt" }, | ||
| argumentName: "language", | ||
| argumentValue: "py"); | ||
| } | ||
| ``` | ||
|
|
||
| ### Protocol version negotiation | ||
|
|
||
| During connection setup, the client and server negotiate a mutually supported MCP protocol version. After initialization, the negotiated version is available on both sides: | ||
|
|
||
| ```csharp | ||
| // On the client | ||
| string? version = client.NegotiatedProtocolVersion; | ||
|
|
||
| // On the server (within a tool or handler) | ||
| string? version = server.NegotiatedProtocolVersion; | ||
| ``` | ||
|
|
||
| Version negotiation is handled automatically. If the client and server cannot agree on a compatible protocol version, the initialization fails with an error. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| --- | ||
| title: Completions | ||
| author: jeffhandley | ||
| description: How to implement and use argument auto-completion for prompts and resources. | ||
| uid: completions | ||
| --- | ||
|
|
||
| ## Completions | ||
|
|
||
| MCP [completions] allow servers to provide argument auto-completion suggestions for prompt and resource template parameters. This helps clients offer a better user experience by suggesting valid values as the user types. | ||
|
|
||
| [completions]: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion | ||
|
|
||
| ### Overview | ||
|
|
||
| Completions work with two types of references: | ||
|
|
||
| - **Prompt argument completions**: Suggest values for prompt parameters (e.g., language names, style options) | ||
| - **Resource template argument completions**: Suggest values for URI template parameters (e.g., file paths, resource IDs) | ||
|
|
||
| The server returns a <xref:ModelContextProtocol.Protocol.Completion> object containing a list of suggested values, an optional total count, and a flag indicating if more values are available. | ||
|
|
||
| ### Implementing completions on the server | ||
|
|
||
| Register a completion handler when building the server. The handler receives a reference (prompt or resource template) and the current argument value: | ||
|
|
||
| ```csharp | ||
| builder.Services.AddMcpServer() | ||
| .WithHttpTransport() | ||
| .WithPrompts<MyPrompts>() | ||
| .WithResources<MyResources>() | ||
| .WithCompleteHandler(async (ctx, ct) => | ||
| { | ||
| if (ctx.Params is not { } @params) | ||
| throw new McpProtocolException("Params are required.", McpErrorCode.InvalidParams); | ||
|
|
||
| var argument = @params.Argument; | ||
|
|
||
| // Handle prompt argument completions | ||
| if (@params.Ref is PromptReference promptRef) | ||
| { | ||
| var suggestions = argument.Name switch | ||
| { | ||
| "language" => new[] { "csharp", "python", "javascript", "typescript", "go", "rust" }, | ||
| "style" => new[] { "casual", "formal", "technical", "friendly" }, | ||
jeffhandley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| _ => Array.Empty<string>() | ||
| }; | ||
|
|
||
| // Filter suggestions based on what the user has typed so far | ||
| var filtered = suggestions.Where(s => s.StartsWith(argument.Value, StringComparison.OrdinalIgnoreCase)).ToList(); | ||
|
|
||
| return new CompleteResult | ||
| { | ||
| Completion = new Completion | ||
| { | ||
| Values = filtered, | ||
| Total = filtered.Count, | ||
| HasMore = false | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| // Handle resource template argument completions | ||
| if (@params.Ref is ResourceTemplateReference resourceRef) | ||
| { | ||
| var availableIds = new[] { "1", "2", "3", "4", "5" }; | ||
| var filtered = availableIds.Where(id => id.StartsWith(argument.Value)).ToList(); | ||
|
|
||
| return new CompleteResult | ||
| { | ||
| Completion = new Completion | ||
| { | ||
| Values = filtered, | ||
| Total = filtered.Count, | ||
| HasMore = false | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| return new CompleteResult(); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Requesting completions on the client | ||
|
|
||
| Clients request completions using <xref:ModelContextProtocol.Client.McpClient.CompleteAsync*>. Provide a reference to the prompt or resource template, the argument name, and the current partial value: | ||
|
|
||
| #### Prompt argument completions | ||
|
|
||
| ```csharp | ||
| // Get completions for a prompt argument | ||
| CompleteResult result = await client.CompleteAsync( | ||
| new PromptReference { Name = "code_review" }, | ||
| argumentName: "language", | ||
| argumentValue: "type"); | ||
|
|
||
| // result.Completion.Values might contain: ["typescript"] | ||
| foreach (var suggestion in result.Completion.Values) | ||
| { | ||
| Console.WriteLine($" {suggestion}"); | ||
| } | ||
|
|
||
| if (result.Completion.HasMore == true) | ||
| { | ||
| Console.WriteLine($" ... and more ({result.Completion.Total} total)"); | ||
| } | ||
| ``` | ||
|
|
||
| #### Resource template argument completions | ||
|
|
||
| ```csharp | ||
| // Get completions for a resource template argument | ||
| CompleteResult result = await client.CompleteAsync( | ||
| new ResourceTemplateReference { Uri = "file:///{path}" }, | ||
| argumentName: "path", | ||
| argumentValue: "src/"); | ||
|
|
||
| foreach (var suggestion in result.Completion.Values) | ||
| { | ||
| Console.WriteLine($" {suggestion}"); | ||
| } | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.