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
51 changes: 22 additions & 29 deletions .github/workflows/ci-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,64 +34,57 @@ jobs:
runs-on: ${{ matrix.os }}

steps:
- name: Clone the repo
- name: 📥 Clone the repo
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis

- name: Set up .NET
- name: 🔧 Set up .NET
uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0
with:
dotnet-version: |
10.0.x
9.0.x

# NetFX testing on non-Windows requires mono
- name: Setup Mono
- name: 🔧 Setup Mono
if: runner.os == 'Linux'
run: sudo apt-get install -y mono-devel

- name: Setup Mono on macOS
- name: 🔧 Setup Mono on macOS
if: runner.os == 'macOS'
run: brew install mono

- name: Set up Node.js
- name: 🔧 Set up Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version: '20'

- name: Install dependencies for tests
- name: 📦 Install dependencies for tests
run: npm install @modelcontextprotocol/server-everything

- name: Install dependencies for tests
- name: 📦 Install dependencies for tests
run: npm install @modelcontextprotocol/server-memory

- name: Build
run: dotnet build --configuration ${{ matrix.configuration }}

- name: Pack
run: dotnet pack --configuration ${{ matrix.configuration }}

- name: Test
run: >-
dotnet test
--filter '(Execution!=Manual)'
--no-build
--configuration ${{ matrix.configuration }}
--logger "trx"
--logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true"
--blame
--blame-hang-timeout 7m
--blame-crash
--results-directory testresults
--collect "XPlat Code Coverage" -- RunConfiguration.CollectSourceInformation=true

- name: Upload test results artifact
- name: 🏗️ Build
run: make build CONFIGURATION=${{ matrix.configuration }}

- name: 🧪 Test
run: make test CONFIGURATION=${{ matrix.configuration }}

- name: 📦 Pack
if: matrix.configuration == 'Release'
run: make pack CONFIGURATION=${{ matrix.configuration }}

- name: 📚 Generate docs
run: make generate-docs CONFIGURATION=${{ matrix.configuration }}

- name: 📤 Upload test results artifact
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: testresults-${{ matrix.os }}-${{ matrix.configuration }}
path: testresults/**
path: artifacts/testresults/**

publish-coverage:
if: github.actor != 'dependabot[bot]'
Expand Down
15 changes: 10 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,21 @@ test: build
--configuration $(CONFIGURATION) \
--filter '(Execution!=Manual)' \
--blame \
--blame-crash \
--blame-hang-timeout 7m \
--diag "$(ARTIFACT_PATH)/diag.txt" \
--logger "trx" \
--collect "Code Coverage;Format=cobertura" \
--results-directory $(ARTIFACT_PATH)/test-results \
--logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true" \
--collect "XPlat Code Coverage" \
--results-directory $(ARTIFACT_PATH)/testresults \
-- \
RunConfiguration.CollectSourceInformation=true

generate-docs: clean restore
dotnet build --no-restore --configuration Release
dotnet docfx $(DOCS_PATH)/docfx.json
pack: restore
dotnet pack --no-restore --configuration $(CONFIGURATION)

generate-docs: build
dotnet docfx $(DOCS_PATH)/docfx.json --warningsAsErrors true

serve-docs: generate-docs
dotnet docfx serve $(ARTIFACT_PATH)/_site --port 8080
Expand Down
20 changes: 6 additions & 14 deletions docs/concepts/elicitation/elicitation.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@ The **elicitation** feature allows servers to request additional information fro

### Server Support for Elicitation

Servers request structured data from users with the [ElicitAsync] extension method on [IMcpServer].
The C# SDK registers an instance of [IMcpServer] with the dependency injection container,
so tools can simply add a parameter of type [IMcpServer] to their method signature to access it.

[ElicitAsync]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.McpServerExtensions.html#ModelContextProtocol_Server_McpServerExtensions_ElicitAsync_ModelContextProtocol_Server_IMcpServer_ModelContextProtocol_Protocol_ElicitRequestParams_System_Threading_CancellationToken_
[IMcpServer]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.IMcpServer.html
Servers request structured data from users with the <xref:ModelContextProtocol.Server.McpServer.ElicitAsync*> extension method on <xref:ModelContextProtocol.Server.McpServer>.
The C# SDK registers an instance of <xref:ModelContextProtocol.Server.McpServer> with the dependency injection container,
so tools can simply add a parameter of type <xref:ModelContextProtocol.Server.McpServer> to their method signature to access it.

The MCP Server must specify the schema of each input value it is requesting from the user.
Only primitive types (string, number, boolean) are supported for elicitation requests.
Expand All @@ -31,21 +28,16 @@ The following example demonstrates how a server could request a boolean response

### Client Support for Elicitation

Elicitation is an optional feature so clients declare their support for it in their capabilities as part of the `initialize` request. In the MCP C# SDK, this is done by configuring an [ElicitationHandler] in the [McpClientOptions]:

[ElicitationHandler]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Protocol.ElicitationCapability.html#ModelContextProtocol_Protocol_ElicitationCapability_ElicitationHandler
[McpClientOptions]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Client.McpClientOptions.html
Elicitation is an optional feature so clients declare their support for it in their capabilities as part of the `initialize` request. In the MCP C# SDK, this is done by configuring an <xref:ModelContextProtocol.Client.McpClientHandlers.ElicitationHandler> in the <xref:ModelContextProtocol.Client.McpClientOptions>:

[!code-csharp[](samples/client/Program.cs?name=snippet_McpInitialize)]

The ElicitationHandler is an asynchronous method that will be called when the server requests additional information.
The ElicitationHandler must request input from the user and return the data in a format that matches the requested schema.
This will be highly dependent on the client application and how it interacts with the user.

If the user provides the requested information, the ElicitationHandler should return an [ElicitResult] with the action set to "accept" and the content containing the user's input.
If the user does not provide the requested information, the ElicitationHandler should return an [ElicitResult] with the action set to "reject" and no content.

[ElicitResult]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Protocol.ElicitResult.html
If the user provides the requested information, the ElicitationHandler should return an <xref:ModelContextProtocol.Protocol.ElicitResult> with the action set to "accept" and the content containing the user's input.
If the user does not provide the requested information, the ElicitationHandler should return an [<xref:ModelContextProtocol.Protocol.ElicitResult> with the action set to "reject" and no content.

Below is an example of how a console application might handle elicitation requests.
Here's an example implementation:
Expand Down
31 changes: 7 additions & 24 deletions docs/concepts/logging/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,53 +49,36 @@ Servers built with the C# SDK always declare the logging capability. Doing so do
to send log messages -- only allows it. Note that stateless MCP servers may not be capable of sending log
messages as there may not be an open connection to the client on which the log messages could be sent.

The C# SDK provides an extension method [WithSetLoggingLevelHandler] on [IMcpServerBuilder] to allow the
The C# SDK provides an extension method <xref:Microsoft.Extensions.DependencyInjection.McpServerBuilderExtensions.WithSetLoggingLevelHandler*> on <xref:Microsoft.Extensions.DependencyInjection.IMcpServerBuilder> to allow the
server to perform any special logic it wants to perform when a client sets the logging level. However, the
SDK already takes care of setting the [LoggingLevel] in the [IMcpServer], so most servers will not need to
SDK already takes care of setting the <xref:ModelContextProtocol.Server.McpServer.LoggingLevel> in the <xref:ModelContextProtocol.Server.McpServer>, so most servers will not need to
implement this.

[IMcpServer]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.IMcpServer.html
[IMcpServerBuilder]: https://modelcontextprotocol.github.io/csharp-sdk/api/Microsoft.Extensions.DependencyInjection.IMcpServerBuilder.html
[WithSetLoggingLevelHandler]: https://modelcontextprotocol.github.io/csharp-sdk/api/Microsoft.Extensions.DependencyInjection.McpServerBuilderExtensions.html#Microsoft_Extensions_DependencyInjection_McpServerBuilderExtensions_WithSetLoggingLevelHandler_Microsoft_Extensions_DependencyInjection_IMcpServerBuilder_System_Func_ModelContextProtocol_Server_RequestContext_ModelContextProtocol_Protocol_SetLevelRequestParams__System_Threading_CancellationToken_System_Threading_Tasks_ValueTask_ModelContextProtocol_Protocol_EmptyResult___
[LoggingLevel]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.IMcpServer.html#ModelContextProtocol_Server_IMcpServer_LoggingLevel

MCP Servers using the MCP C# SDK can obtain an [ILoggerProvider] from the IMcpServer [AsClientLoggerProvider] extension method,
and from that can create an [ILogger] instance for logging messages that should be sent to the MCP client.
MCP Servers using the MCP C# SDK can obtain an [ILoggerProvider](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerprovider) from the IMcpServer <xref:ModelContextProtocol.Server.McpServer.AsClientLoggerProvider> extension method,
and from that can create an [ILogger](https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger) instance for logging messages that should be sent to the MCP client.

[!code-csharp[](samples/server/Tools/LoggingTools.cs?name=snippet_LoggingConfiguration)]

[ILoggerProvider]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.iloggerprovider
[AsClientLoggerProvider]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.McpServerExtensions.html#ModelContextProtocol_Server_McpServerExtensions_AsClientLoggerProvider_ModelContextProtocol_Server_IMcpServer_
[ILogger]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.logging.ilogger

### Client support for logging

When the server indicates that it supports logging, clients should configure
the logging level to specify which messages the server should send to the client.

Clients should check if the server supports logging by checking the [Logging] property of the [ServerCapabilities] field of [IMcpClient].

[IMcpClient]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Client.IMcpClient.html
[ServerCapabilities]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Client.IMcpClient.html#ModelContextProtocol_Client_IMcpClient_ServerCapabilities
[Logging]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Protocol.ServerCapabilities.html#ModelContextProtocol_Protocol_ServerCapabilities_Logging
Clients should check if the server supports logging by checking the <xref:ModelContextProtocol.Protocol.ServerCapabilities.Logging> property of the <xref:ModelContextProtocol.Client.McpClient.ServerCapabilities> field of <xref:ModelContextProtocol.Client.McpClient>.

[!code-csharp[](samples/client/Program.cs?name=snippet_LoggingCapabilities)]

If the server supports logging, the client should set the level of log messages it wishes to receive with
the [SetLoggingLevel] method on [IMcpClient]. If the client does not set a logging level, the server might choose
the <xref:ModelContextProtocol.Client.McpClient.SetLoggingLevel*> method on <xref:ModelContextProtocol.Client.McpClient>. If the client does not set a logging level, the server might choose
to send all log messages or none -- this is not specified in the protocol -- so it is important that the client
sets a logging level to ensure it receives the desired log messages and only those messages.

The `loggingLevel` set by the client is an MCP logging level.
See the [Logging Levels](#logging-levels) section above for the mapping between MCP and .NET logging levels.

[SetLoggingLevel]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Client.McpClientExtensions.html#ModelContextProtocol_Client_McpClientExtensions_SetLoggingLevel_ModelContextProtocol_Client_IMcpClient_Microsoft_Extensions_Logging_LogLevel_System_Threading_CancellationToken_

[!code-csharp[](samples/client/Program.cs?name=snippet_LoggingLevel)]

Lastly, the client must configure a notification handler for [NotificationMethods.LoggingMessageNotification] notifications.
Lastly, the client must configure a notification handler for <xref:ModelContextProtocol.Protocol.NotificationMethods.LoggingMessageNotification> notifications.
The following example simply writes the log messages to the console.

[NotificationMethods.LoggingMessageNotification]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Protocol.NotificationMethods.html#ModelContextProtocol_Protocol_NotificationMethods_LoggingMessageNotification

[!code-csharp[](samples/client/Program.cs?name=snippet_LoggingHandler)]
28 changes: 9 additions & 19 deletions docs/concepts/progress/progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,13 @@ This project illustrates the common case of a server tool that performs a long-r

### Server Implementation

When processing a request, the server can use the [sendNotificationAsync] extension method of [IMcpServer] to send progress updates,
When processing a request, the server can use the <xref:ModelContextProtocol.McpSession.SendNotificationAsync*> extension method of <xref:ModelContextProtocol.Server.McpServer> to send progress updates,
specifying `"notifications/progress"` as the notification method name.
The C# SDK registers an instance of [IMcpServer] with the dependency injection container,
so tools can simply add a parameter of type [IMcpServer] to their method signature to access it.
The parameters passed to [sendNotificationAsync] should be an instance of [ProgressNotificationParams], which includes the current progress, total steps, and an optional message.
The C# SDK registers an instance of <xref:ModelContextProtocol.Server.McpServer> with the dependency injection container,
so tools can simply add a parameter of type <xref:ModelContextProtocol.Server.McpServer> to their method signature to access it.
The parameters passed to <xref:ModelContextProtocol.McpSession.SendNotificationAsync*> should be an instance of <xref:ModelContextProtocol.Protocol.ProgressNotificationParams>, which includes the current progress, total steps, and an optional message.

[sendNotificationAsync]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.McpEndpointExtensions.html#ModelContextProtocol_McpEndpointExtensions_SendNotificationAsync_ModelContextProtocol_IMcpEndpoint_System_String_System_Threading_CancellationToken_
[IMcpServer]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Server.IMcpServer.html
[ProgressNotificationParams]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Protocol.ProgressNotificationParams.html

The server must verify that the caller provided a `progressToken` in the request and include it in the call to [sendNotificationAsync]. The following example demonstrates how a server can send a progress notification:
The server must verify that the caller provided a `progressToken` in the request and include it in the call to <xref:ModelContextProtocol.McpSession.SendNotificationAsync*>. The following example demonstrates how a server can send a progress notification:

[!code-csharp[](samples/server/Tools/LongRunningTools.cs?name=snippet_SendProgress)]

Expand All @@ -38,10 +34,7 @@ Note that servers are not required to support progress tracking, so clients shou

In the MCP C# SDK, clients can specify a `progressToken` in the request parameters when calling a tool method.
The client should also provide a notification handler to process "notifications/progress" notifications.
There are two way to do this. The first is to register a notification handler using the [RegisterNotificationHandler] method on the [IMcpClient] instance. A handler registered this way will receive all progress notifications sent by the server.

[IMcpClient]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.Client.IMcpClient.html
[RegisterNotificationHandler]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.IMcpEndpoint.html#ModelContextProtocol_IMcpEndpoint_RegisterNotificationHandler_System_String_System_Func_ModelContextProtocol_Protocol_JsonRpcNotification_System_Threading_CancellationToken_System_Threading_Tasks_ValueTask__
There are two way to do this. The first is to register a notification handler using the <xref:ModelContextProtocol.McpSession.RegisterNotificationHandler*> method on the <xref:ModelContextProtocol.Client.McpClient> instance. A handler registered this way will receive all progress notifications sent by the server.

```csharp
mcpClient.RegisterNotificationHandler(NotificationMethods.ProgressNotification,
Expand All @@ -57,13 +50,10 @@ mcpClient.RegisterNotificationHandler(NotificationMethods.ProgressNotification,
}).ConfigureAwait(false);
```

The second way is to pass a [Progress`<T>`] instance to the tool method. [Progress`<T>`] is a standard .NET type that provides a way to receive progress updates.
For the purposes of MCP progress notifications, `T` should be [ProgressNotificationValue].
The MCP C# SDK will automatically handle progress notifications and report them through the [Progress`<T>`] instance.
The second way is to pass a [`Progress<T>`](https://learn.microsoft.com/dotnet/api/system.progress-1) instance to the tool method. `Progress<T>` is a standard .NET type that provides a way to receive progress updates.
For the purposes of MCP progress notifications, `T` should be <xref:ModelContextProtocol.ProgressNotificationValue>.
The MCP C# SDK will automatically handle progress notifications and report them through the `Progress<T>` instance.
This notification handler will only receive progress updates for the specific request that was made,
rather than all progress notifications from the server.

[Progress`<T>`]: https://learn.microsoft.com/en-us/dotnet/api/system.progress-1
[ProgressNotificationValue]: https://modelcontextprotocol.github.io/csharp-sdk/api/ModelContextProtocol.ProgressNotificationValue.html

[!code-csharp[](samples/client/Program.cs?name=snippet_ProgressHandler)]
Loading