Skip to content

Commit 46668f1

Browse files
jeffhandleyCopilotCopilotmikekistlerstephentoub
authored
Add and augment conceptual docs (#1375)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Mike Kistler <mikekistler@microsoft.com> Co-authored-by: Stephen Toub <stoub@microsoft.com> Co-authored-by: Stephen Halter <halter73@gmail.com>
1 parent 84cd691 commit 46668f1

14 files changed

Lines changed: 1765 additions & 0 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
title: Cancellation
3+
author: jeffhandley
4+
description: How to cancel in-flight MCP requests using cancellation tokens and notifications.
5+
uid: cancellation
6+
---
7+
8+
## Cancellation
9+
10+
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.
11+
12+
[cancellation]: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation
13+
[task cancellation]: https://learn.microsoft.com/dotnet/standard/parallel-programming/task-cancellation
14+
15+
### How cancellation maps to MCP notifications
16+
17+
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.
18+
19+
### Server-side cancellation handling
20+
21+
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:
22+
23+
```csharp
24+
[McpServerTool, Description("A long-running computation")]
25+
public static async Task<string> LongComputation(
26+
[Description("Number of iterations")] int iterations,
27+
CancellationToken cancellationToken)
28+
{
29+
for (int i = 0; i < iterations; i++)
30+
{
31+
await Task.Delay(1000, cancellationToken);
32+
}
33+
34+
return $"Completed {iterations} iterations.";
35+
}
36+
```
37+
38+
When the client sends a cancellation notification, the `OperationCanceledException` propagates back to the client as a cancellation response.
39+
40+
### Cancellation notification details
41+
42+
The cancellation notification includes:
43+
44+
- **RequestId**: The ID of the request to cancel, allowing the receiver to correlate the cancellation with the correct in-flight request.
45+
- **Reason**: An optional human-readable reason for the cancellation.
46+
47+
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>.
48+
49+
```csharp
50+
mcpClient.RegisterNotificationHandler(
51+
NotificationMethods.CancelledNotification,
52+
(notification, ct) =>
53+
{
54+
var cancelled = notification.Params?.Deserialize<CancelledNotificationParams>(
55+
McpJsonUtilities.DefaultOptions);
56+
if (cancelled is not null)
57+
{
58+
Console.WriteLine($"Request {cancelled.RequestId} cancelled: {cancelled.Reason}");
59+
}
60+
return default;
61+
});
62+
```
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
---
2+
title: Capabilities
3+
author: jeffhandley
4+
description: How capability and protocol version negotiation works in MCP.
5+
uid: capabilities
6+
---
7+
8+
## Capabilities
9+
10+
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.
11+
12+
[capability negotiation]: https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle#initialization
13+
14+
### Client capabilities
15+
16+
<xref:ModelContextProtocol.Protocol.ClientCapabilities> declares what features the client supports:
17+
18+
| Capability | Type | Description |
19+
|-----------|------|-------------|
20+
| `Roots` | <xref:ModelContextProtocol.Protocol.RootsCapability> | Client can provide filesystem root URIs |
21+
| `Sampling` | <xref:ModelContextProtocol.Protocol.SamplingCapability> | Client can handle LLM sampling requests |
22+
| `Elicitation` | <xref:ModelContextProtocol.Protocol.ElicitationCapability> | Client can present forms or URLs to the user |
23+
| `Experimental` | `IDictionary<string, object>` | Experimental capabilities |
24+
25+
Configure client capabilities when creating an MCP client:
26+
27+
```csharp
28+
var options = new McpClientOptions
29+
{
30+
Capabilities = new ClientCapabilities
31+
{
32+
Roots = new RootsCapability { ListChanged = true },
33+
Sampling = new SamplingCapability(),
34+
Elicitation = new ElicitationCapability
35+
{
36+
Form = new FormElicitationCapability(),
37+
Url = new UrlElicitationCapability()
38+
}
39+
}
40+
};
41+
42+
await using var client = await McpClient.CreateAsync(transport, options);
43+
```
44+
45+
Handlers for each capability (roots, sampling, elicitation) are covered in their respective documentation pages.
46+
47+
### Server capabilities
48+
49+
<xref:ModelContextProtocol.Protocol.ServerCapabilities> declares what features the server supports:
50+
51+
| Capability | Type | Description |
52+
|-----------|------|-------------|
53+
| `Tools` | <xref:ModelContextProtocol.Protocol.ToolsCapability> | Server exposes callable tools |
54+
| `Prompts` | <xref:ModelContextProtocol.Protocol.PromptsCapability> | Server exposes prompt templates |
55+
| `Resources` | <xref:ModelContextProtocol.Protocol.ResourcesCapability> | Server exposes readable resources |
56+
| `Logging` | <xref:ModelContextProtocol.Protocol.LoggingCapability> | Server can send log messages |
57+
| `Completions` | <xref:ModelContextProtocol.Protocol.CompletionsCapability> | Server supports argument completions |
58+
| `Experimental` | `IDictionary<string, object>` | Experimental capabilities |
59+
60+
Server capabilities are automatically inferred from the configured features. For example, registering tools with `.WithTools<T>()` automatically declares the tools capability.
61+
62+
### Checking capabilities
63+
64+
Before using an optional feature, check whether the other side declared the corresponding capability.
65+
66+
#### Checking server capabilities from the client
67+
68+
```csharp
69+
await using var client = await McpClient.CreateAsync(transport);
70+
71+
// Check if the server supports tools
72+
if (client.ServerCapabilities.Tools is not null)
73+
{
74+
var tools = await client.ListToolsAsync();
75+
}
76+
77+
// Check if the server supports resources with subscriptions
78+
if (client.ServerCapabilities.Resources is { Subscribe: true })
79+
{
80+
await client.SubscribeToResourceAsync("config://app/settings");
81+
}
82+
83+
// Check if the server supports prompts with list-changed notifications
84+
if (client.ServerCapabilities.Prompts is { ListChanged: true })
85+
{
86+
mcpClient.RegisterNotificationHandler(
87+
NotificationMethods.PromptListChangedNotification,
88+
async (notification, ct) =>
89+
{
90+
var prompts = await mcpClient.ListPromptsAsync(cancellationToken: ct);
91+
});
92+
}
93+
94+
// Check if the server supports logging
95+
if (client.ServerCapabilities.Logging is not null)
96+
{
97+
await client.SetLoggingLevelAsync(LoggingLevel.Info);
98+
}
99+
100+
// Check if the server supports completions
101+
if (client.ServerCapabilities.Completions is not null)
102+
{
103+
var completions = await client.CompleteAsync(
104+
new PromptReference { Name = "my_prompt" },
105+
argumentName: "language",
106+
argumentValue: "py");
107+
}
108+
```
109+
110+
### Protocol version negotiation
111+
112+
During connection setup, the client and server negotiate a mutually supported MCP protocol version. After initialization, the negotiated version is available on both sides:
113+
114+
```csharp
115+
// On the client
116+
string? version = client.NegotiatedProtocolVersion;
117+
118+
// On the server (within a tool or handler)
119+
string? version = server.NegotiatedProtocolVersion;
120+
```
121+
122+
Version negotiation is handled automatically. If the client and server cannot agree on a compatible protocol version, the initialization fails with an error.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
---
2+
title: Completions
3+
author: jeffhandley
4+
description: How to implement and use argument auto-completion for prompts and resources.
5+
uid: completions
6+
---
7+
8+
## Completions
9+
10+
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.
11+
12+
[completions]: https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion
13+
14+
### Overview
15+
16+
Completions work with two types of references:
17+
18+
- **Prompt argument completions**: Suggest values for prompt parameters (e.g., language names, style options)
19+
- **Resource template argument completions**: Suggest values for URI template parameters (e.g., file paths, resource IDs)
20+
21+
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.
22+
23+
### Implementing completions on the server
24+
25+
Register a completion handler when building the server. The handler receives a reference (prompt or resource template) and the current argument value:
26+
27+
```csharp
28+
builder.Services.AddMcpServer()
29+
.WithHttpTransport()
30+
.WithPrompts<MyPrompts>()
31+
.WithResources<MyResources>()
32+
.WithCompleteHandler(async (ctx, ct) =>
33+
{
34+
if (ctx.Params is not { } @params)
35+
throw new McpProtocolException("Params are required.", McpErrorCode.InvalidParams);
36+
37+
var argument = @params.Argument;
38+
39+
// Handle prompt argument completions
40+
if (@params.Ref is PromptReference promptRef)
41+
{
42+
var suggestions = argument.Name switch
43+
{
44+
"language" => new[] { "csharp", "python", "javascript", "typescript", "go", "rust" },
45+
"style" => new[] { "casual", "formal", "technical", "friendly" },
46+
_ => Array.Empty<string>()
47+
};
48+
49+
// Filter suggestions based on what the user has typed so far
50+
var filtered = suggestions.Where(s => s.StartsWith(argument.Value, StringComparison.OrdinalIgnoreCase)).ToList();
51+
52+
return new CompleteResult
53+
{
54+
Completion = new Completion
55+
{
56+
Values = filtered,
57+
Total = filtered.Count,
58+
HasMore = false
59+
}
60+
};
61+
}
62+
63+
// Handle resource template argument completions
64+
if (@params.Ref is ResourceTemplateReference resourceRef)
65+
{
66+
var availableIds = new[] { "1", "2", "3", "4", "5" };
67+
var filtered = availableIds.Where(id => id.StartsWith(argument.Value)).ToList();
68+
69+
return new CompleteResult
70+
{
71+
Completion = new Completion
72+
{
73+
Values = filtered,
74+
Total = filtered.Count,
75+
HasMore = false
76+
}
77+
};
78+
}
79+
80+
return new CompleteResult();
81+
});
82+
```
83+
84+
### Requesting completions on the client
85+
86+
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:
87+
88+
#### Prompt argument completions
89+
90+
```csharp
91+
// Get completions for a prompt argument
92+
CompleteResult result = await client.CompleteAsync(
93+
new PromptReference { Name = "code_review" },
94+
argumentName: "language",
95+
argumentValue: "type");
96+
97+
// result.Completion.Values might contain: ["typescript"]
98+
foreach (var suggestion in result.Completion.Values)
99+
{
100+
Console.WriteLine($" {suggestion}");
101+
}
102+
103+
if (result.Completion.HasMore == true)
104+
{
105+
Console.WriteLine($" ... and more ({result.Completion.Total} total)");
106+
}
107+
```
108+
109+
#### Resource template argument completions
110+
111+
```csharp
112+
// Get completions for a resource template argument
113+
CompleteResult result = await client.CompleteAsync(
114+
new ResourceTemplateReference { Uri = "file:///{path}" },
115+
argumentName: "path",
116+
argumentValue: "src/");
117+
118+
foreach (var suggestion in result.Completion.Values)
119+
{
120+
Console.WriteLine($" {suggestion}");
121+
}
122+
```

docs/concepts/elicitation/elicitation.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,80 @@ For enum types, the SDK supports several schema formats:
3434
- **TitledMultiSelectEnumSchema**: A multi-select enum with display titles for each option.
3535
- **LegacyTitledEnumSchema** (deprecated): The legacy enum schema using `enumNames` for backward compatibility.
3636

37+
#### Default values
38+
39+
Each schema type supports a `Default` property that specifies a pre-populated value for the form field.
40+
Clients should use defaults to pre-fill form fields, making it easier for users to accept common values or see expected input formats.
41+
42+
```csharp
43+
var result = await server.ElicitAsync(new ElicitRequestParams
44+
{
45+
Message = "Configure your preferences",
46+
RequestedSchema = new ElicitRequestParams.RequestSchema
47+
{
48+
Properties = new Dictionary<string, ElicitRequestParams.PrimitiveSchemaDefinition>
49+
{
50+
["name"] = new ElicitRequestParams.StringSchema
51+
{
52+
Description = "Your display name",
53+
Default = "User"
54+
},
55+
["maxResults"] = new ElicitRequestParams.NumberSchema
56+
{
57+
Description = "Maximum number of results",
58+
Default = 25
59+
},
60+
["enableNotifications"] = new ElicitRequestParams.BooleanSchema
61+
{
62+
Description = "Enable push notifications",
63+
Default = true
64+
},
65+
["theme"] = new ElicitRequestParams.UntitledSingleSelectEnumSchema
66+
{
67+
Description = "UI theme",
68+
Enum = ["light", "dark", "system"],
69+
Default = "system"
70+
}
71+
}
72+
}
73+
}, cancellationToken);
74+
```
75+
76+
#### Enum schema formats
77+
78+
Enum schemas allow the server to present a set of choices to the user.
79+
80+
- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.UntitledSingleSelectEnumSchema>: Simple single-select where enum values serve as both the value and display text.
81+
- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.TitledSingleSelectEnumSchema>: Single-select with separate display titles for each option using JSON Schema `oneOf` with `const` and `title`.
82+
- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.UntitledMultiSelectEnumSchema>: Multi-select allowing multiple values.
83+
- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.TitledMultiSelectEnumSchema>: Multi-select with display titles.
84+
85+
```csharp
86+
// Titled single-select: display titles differ from values
87+
["priority"] = new ElicitRequestParams.TitledSingleSelectEnumSchema
88+
{
89+
Description = "Task priority",
90+
OneOf =
91+
[
92+
new() { Const = "p0", Title = "Critical (P0)" },
93+
new() { Const = "p1", Title = "High (P1)" },
94+
new() { Const = "p2", Title = "Normal (P2)" },
95+
],
96+
Default = "p2"
97+
},
98+
99+
// Multi-select: user can select multiple values
100+
["tags"] = new ElicitRequestParams.UntitledMultiSelectEnumSchema
101+
{
102+
Description = "Tags to apply",
103+
Items = new()
104+
{
105+
Enum = ["bug", "feature", "docs", "test"]
106+
},
107+
Default = ["bug"]
108+
}
109+
```
110+
37111
The server can request a single input or multiple inputs at once.
38112
To help distinguish multiple inputs, each input has a unique name.
39113

0 commit comments

Comments
 (0)