Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions docs/concepts/cancellation/cancellation.md
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;
});
```
122 changes: 122 additions & 0 deletions docs/concepts/capabilities/capabilities.md
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.

### 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.
122 changes: 122 additions & 0 deletions docs/concepts/completions/completions.md
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" },
_ => 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}");
}
```
74 changes: 74 additions & 0 deletions docs/concepts/elicitation/elicitation.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,80 @@ For enum types, the SDK supports several schema formats:
- **TitledMultiSelectEnumSchema**: A multi-select enum with display titles for each option.
- **LegacyTitledEnumSchema** (deprecated): The legacy enum schema using `enumNames` for backward compatibility.

#### Default values

Each schema type supports a `Default` property that specifies a pre-populated value for the form field.
Clients should use defaults to pre-fill form fields, making it easier for users to accept common values or see expected input formats.

```csharp
var result = await server.ElicitAsync(new ElicitRequestParams
{
Message = "Configure your preferences",
RequestedSchema = new ElicitRequestParams.RequestSchema
{
Properties = new Dictionary<string, ElicitRequestParams.PrimitiveSchemaDefinition>
{
["name"] = new ElicitRequestParams.StringSchema
{
Description = "Your display name",
Default = "User"
},
["maxResults"] = new ElicitRequestParams.NumberSchema
{
Description = "Maximum number of results",
Default = 25
},
["enableNotifications"] = new ElicitRequestParams.BooleanSchema
{
Description = "Enable push notifications",
Default = true
},
["theme"] = new ElicitRequestParams.UntitledSingleSelectEnumSchema
{
Description = "UI theme",
Enum = ["light", "dark", "system"],
Default = "system"
}
}
}
}, cancellationToken);
```

#### Enum schema formats

Enum schemas allow the server to present a set of choices to the user.

- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.UntitledSingleSelectEnumSchema>: Simple single-select where enum values serve as both the value and display text.
- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.TitledSingleSelectEnumSchema>: Single-select with separate display titles for each option using JSON Schema `oneOf` with `const` and `title`.
- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.UntitledMultiSelectEnumSchema>: Multi-select allowing multiple values.
- <xref:ModelContextProtocol.Protocol.ElicitRequestParams.TitledMultiSelectEnumSchema>: Multi-select with display titles.

```csharp
// Titled single-select: display titles differ from values
["priority"] = new ElicitRequestParams.TitledSingleSelectEnumSchema
{
Description = "Task priority",
OneOf =
[
new() { Const = "p0", Title = "Critical (P0)" },
new() { Const = "p1", Title = "High (P1)" },
new() { Const = "p2", Title = "Normal (P2)" },
],
Default = "p2"
},

// Multi-select: user can select multiple values
["tags"] = new ElicitRequestParams.UntitledMultiSelectEnumSchema
{
Description = "Tags to apply",
Items = new()
{
Enum = ["bug", "feature", "docs", "test"]
},
Default = ["bug"]
}
```

The server can request a single input or multiple inputs at once.
To help distinguish multiple inputs, each input has a unique name.

Expand Down
Loading
Loading