diff --git a/.github/workflows/ci-build-test.yml b/.github/workflows/ci-build-test.yml index 3d9aaf4a9..dd648a6ca 100644 --- a/.github/workflows/ci-build-test.yml +++ b/.github/workflows/ci-build-test.yml @@ -34,12 +34,12 @@ 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: | @@ -47,51 +47,44 @@ jobs: 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]' diff --git a/Makefile b/Makefile index c2a0a012f..b9d0ffdd7 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/docs/concepts/elicitation/elicitation.md b/docs/concepts/elicitation/elicitation.md index ebda0979a..2d9d43c9f 100644 --- a/docs/concepts/elicitation/elicitation.md +++ b/docs/concepts/elicitation/elicitation.md @@ -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 extension method on . +The C# SDK registers an instance of with the dependency injection container, +so tools can simply add a parameter of type 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. @@ -31,10 +28,7 @@ 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 in the : [!code-csharp[](samples/client/Program.cs?name=snippet_McpInitialize)] @@ -42,10 +36,8 @@ The ElicitationHandler is an asynchronous method that will be called when the se 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 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 [ 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: diff --git a/docs/concepts/logging/logging.md b/docs/concepts/logging/logging.md index 411a61b1c..742aa9fc1 100644 --- a/docs/concepts/logging/logging.md +++ b/docs/concepts/logging/logging.md @@ -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 on 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 in the , 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 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 property of the field of . [!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 method on . 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 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)] diff --git a/docs/concepts/progress/progress.md b/docs/concepts/progress/progress.md index ccdf9f19c..f23f3e895 100644 --- a/docs/concepts/progress/progress.md +++ b/docs/concepts/progress/progress.md @@ -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 extension method of 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 with the dependency injection container, +so tools can simply add a parameter of type to their method signature to access it. +The parameters passed to should be an instance of , 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 . The following example demonstrates how a server can send a progress notification: [!code-csharp[](samples/server/Tools/LongRunningTools.cs?name=snippet_SendProgress)] @@ -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 method on the instance. A handler registered this way will receive all progress notifications sent by the server. ```csharp mcpClient.RegisterNotificationHandler(NotificationMethods.ProgressNotification, @@ -57,13 +50,10 @@ mcpClient.RegisterNotificationHandler(NotificationMethods.ProgressNotification, }).ConfigureAwait(false); ``` -The second way is to pass a [Progress``] instance to the tool method. [Progress``] 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``] instance. +The second way is to pass a [`Progress`](https://learn.microsoft.com/dotnet/api/system.progress-1) instance to the tool method. `Progress` is a standard .NET type that provides a way to receive progress updates. +For the purposes of MCP progress notifications, `T` should be . +The MCP C# SDK will automatically handle progress notifications and report them through the `Progress` 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``]: 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)]