├── .config └── dotnet-tools.json ├── .editorconfig ├── .github ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GitVersion.yml ├── LICENSE ├── README.MD ├── SECURITY.md ├── TODO.md ├── build.cake ├── build.ps1 ├── build.sh ├── mcpdotnet.sln ├── samples ├── AspNetCoreSseServer │ ├── AspNetCoreSseServer.csproj │ ├── McpEndpointRouteBuilderExtensions.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── SseServerStreamTransport.cs │ ├── Tools │ │ ├── EchoTool.cs │ │ └── SampleLlmTool.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── README.md ├── TestServerWithHosting │ ├── Program.cs │ ├── TestServerWithHosting.csproj │ └── Tools │ │ ├── EchoTool.cs │ │ └── SampleLlmTool.cs ├── anthropic │ └── tools │ │ └── ToolsConsole │ │ ├── AnthropicToolsConsole.csproj │ │ ├── McpToolExtensions.cs │ │ ├── Program.cs │ │ └── Properties │ │ └── launchSettings.json └── microsoft.extensions.ai │ └── tools │ └── ToolsConsole │ ├── MEAIToolsConsole.csproj │ ├── Program.cs │ └── Properties │ └── launchSettings.json ├── src ├── Common │ └── Polyfills │ │ └── System │ │ ├── Collections │ │ └── Generic │ │ │ └── CollectionExtensions.cs │ │ ├── Diagnostics │ │ ├── CodeAnalysis │ │ │ ├── DynamicallyAccessedMemberTypes.cs │ │ │ ├── DynamicallyAccessedMembersAttribute.cs │ │ │ ├── NullableAttributes.cs │ │ │ ├── RequiresDynamicCodeAttribute.cs │ │ │ ├── RequiresUnreferencedCode.cs │ │ │ ├── SetsRequiredMembersAttribute.cs │ │ │ └── UnconditionalSuppressMessageAttribute.cs │ │ └── ProcessExtensions.cs │ │ ├── IO │ │ ├── TextReaderExtensions.cs │ │ └── TextWriterExtensions.cs │ │ ├── Net │ │ └── Http │ │ │ └── HttpClientExtensions.cs │ │ ├── Runtime │ │ └── CompilerServices │ │ │ ├── CallerArgumentExpressionAttribute.cs │ │ │ ├── CompilerFeatureRequiredAttribute.cs │ │ │ ├── IsExternalInit.cs │ │ │ └── RequiredMemberAttribute.cs │ │ └── Threading │ │ ├── CancellationTokenSourceExtensions.cs │ │ └── Tasks │ │ └── TaskExtensions.cs ├── McpDotNet.Extensions.AI │ ├── McpDotNet.Extensions.AI.csproj │ └── README.md └── mcpdotnet │ ├── Client │ ├── IMcpClient.cs │ ├── McpClient.cs │ ├── McpClientException.cs │ ├── McpClientExtensions.cs │ ├── McpClientFactory.cs │ └── McpClientOptions.cs │ ├── Configuration │ ├── DefaultMcpServerBuilder.cs │ ├── IMcpServerBuilder.cs │ ├── McpServerBuilderExtensions.Handler.cs │ ├── McpServerBuilderExtensions.Tools.cs │ ├── McpServerBuilderExtensions.Transports.cs │ ├── McpServerConfig.cs │ ├── McpServerOptionsSetup.cs │ └── McpServerServiceCollectionExtension.cs │ ├── Hosting │ └── McpServerHostedService.cs │ ├── Logging │ └── Log.cs │ ├── Protocol │ ├── Messages │ │ ├── ErrorCodes.cs │ │ ├── IJsonRpcMessage.cs │ │ ├── IJsonRpcMessageWithId.cs │ │ ├── JsonRpcError.cs │ │ ├── JsonRpcErrorDetail.cs │ │ ├── JsonRpcMessage.cs │ │ ├── JsonRpcNotification.cs │ │ ├── JsonRpcResponse.cs │ │ ├── NotificationMethods.cs │ │ ├── OperationNames.cs │ │ ├── PaginatedResult.cs │ │ ├── RequestId.cs │ │ └── RequestIdConverter.cs │ ├── Transport │ │ ├── HttpListenerServerProvider.cs │ │ ├── HttpListenerSseServerTransport.cs │ │ ├── IClientTransport.cs │ │ ├── IServerTransport.cs │ │ ├── ITransport.cs │ │ ├── McpTransportException.cs │ │ ├── SseClientTransport.cs │ │ ├── SseClientTransportOptions.cs │ │ ├── StdioClientTransport.cs │ │ ├── StdioClientTransportOptions.cs │ │ ├── StdioServerTransport.cs │ │ ├── TransportBase.cs │ │ └── TransportTypes.cs │ └── Types │ │ ├── Annotations.cs │ │ ├── Argument.cs │ │ ├── CallToolRequestParams.cs │ │ ├── CallToolResponse.cs │ │ ├── Capabilities.cs │ │ ├── CompleteRequestParams.cs │ │ ├── CompleteResult.cs │ │ ├── Completion.cs │ │ ├── Content.cs │ │ ├── ContextInclusion.cs │ │ ├── CreateMessageRequestParams.cs │ │ ├── CreateMessageResult.cs │ │ ├── EmptyResult.cs │ │ ├── GetPromptRequestParams.cs │ │ ├── GetPromptResult.cs │ │ ├── Implementation.cs │ │ ├── InitializeRequestParams.cs │ │ ├── InitializeResult.cs │ │ ├── JsonSchema.cs │ │ ├── JsonSchemaProperty.cs │ │ ├── ListPromptsRequestParams.cs │ │ ├── ListPromptsResult.cs │ │ ├── ListResourcesRequestParams.cs │ │ ├── ListResourcesResult.cs │ │ ├── ListRootsRequestParams.cs │ │ ├── ListRootsResult.cs │ │ ├── ListToolsRequestParams.cs │ │ ├── ListToolsResult.cs │ │ ├── ModelHint.cs │ │ ├── ModelPreferences.cs │ │ ├── PingResult.cs │ │ ├── Prompt.cs │ │ ├── PromptArgument.cs │ │ ├── PromptMessage.cs │ │ ├── ReadResourceRequestParams.cs │ │ ├── ReadResourceResult.cs │ │ ├── Reference.cs │ │ ├── ResourceContents.cs │ │ ├── ResourceUpdatedNotificationParams.cs │ │ ├── Resources.cs │ │ ├── Role.cs │ │ ├── Root.cs │ │ ├── SamplingMessage.cs │ │ ├── ServerCapabilities.cs │ │ ├── SubscribeRequestParams.cs │ │ ├── Tool.cs │ │ ├── UnsubscribeFromResourceRequestParams.cs │ │ └── UnsubscribeRequestParams.cs │ ├── Server │ ├── IMcpServer.cs │ ├── McpServer.cs │ ├── McpServerException.cs │ ├── McpServerExtensions.cs │ ├── McpServerFactory.cs │ ├── McpServerHandlers.cs │ ├── McpServerOptions.cs │ ├── McpToolAttribute.cs │ ├── McpToolTypeAttribute.cs │ └── RequestContext.cs │ ├── Shared │ └── McpJsonRpcEndpoint.cs │ ├── Utils │ ├── Json │ │ ├── JsonRpcMessageConverter.cs │ │ └── JsonSerializerOptionsExtensions.cs │ ├── ProcessHelper.cs │ └── Throw.cs │ └── mcpdotnet.csproj └── tests ├── mcpdotnet.TestServer ├── Program.cs └── mcpdotnet.TestServer.csproj ├── mcpdotnet.TestSseServer ├── Program.cs └── mcpdotnet.TestSseServer.csproj └── mcpdotnet.Tests ├── Client └── McpClientFactoryTests.cs ├── ClientIntegrationTestFixture.cs ├── ClientIntegrationTests.cs ├── Configuration ├── McpServerBuilderExtensionsHandlerTests.cs ├── McpServerBuilderExtensionsToolsTests.cs └── McpServerBuilderExtensionsTransportsTests.cs ├── EverythingSseServerFixture.cs ├── GlobalUsings.cs ├── Server ├── McpServerDelegatesTests.cs ├── McpServerFactoryTests.cs └── McpServerTests.cs ├── SseIntegrationTests.cs ├── SseServerIntegrationTestFixture.cs ├── SseServerIntegrationTests.cs ├── Transport ├── SseClientTransportTests.cs └── StdioServerTransportTests.cs ├── Utils ├── InMemoryTestSseServer.cs ├── MockHttpHandler.cs └── TestServerTransport.cs └── mcpdotnet.Tests.csproj /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "cake.tool": { 6 | "version": "5.0.0", 7 | "commands": [ 8 | "dotnet-cake" 9 | ], 10 | "rollForward": false 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | 7 | # Code Analysis 8 | dotnet_diagnostic.CA1002.severity = none # CA1002: Do not expose generic lists 9 | dotnet_diagnostic.CA1031.severity = none # CA1031: Do not catch general exception types 10 | dotnet_diagnostic.CA1054.severity = none # CA1054: URI-like parameters should not be strings 11 | dotnet_diagnostic.CA1056.severity = none # CA1056: URI-like properties should not be strings 12 | dotnet_diagnostic.CA1062.severity = suggestion # CA1062: Validate arguments of public methods 13 | dotnet_diagnostic.CA1510.severity = suggestion # CA1510: Use ArgumentNullException throw helper 14 | dotnet_diagnostic.CA1849.severity = suggestion # CA1849: Call async methods when in an async method 15 | dotnet_diagnostic.CA2000.severity = suggestion # CA2000: Dispose objects before losing scope 16 | dotnet_diagnostic.CA2227.severity = suggestion # CA2227: Collection properties should be read only 17 | dotnet_diagnostic.CA2249.severity = suggestion # CA2249: Use 'string.Contains' instead of 'string.IndexOf' 18 | dotnet_diagnostic.CA1819.severity = suggestion # CA1819: Properties should not return arrays -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull Request 2 | 3 | ## Description of Changes 4 | 5 | 6 | ## Related Issue(s) 7 | 8 | - Fixes # 9 | 10 | ## Testing Done 11 | 12 | - [ ] Unit tests added/updated 13 | - [ ] Integration tests added/updated 14 | - [ ] Manual testing performed 15 | 16 | ## Breaking Changes 17 | 18 | None -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: .NET Build, Test and publish 2 | 3 | on: 4 | push: 5 | branches: [ "main", "master" ] 6 | pull_request: 7 | branches: [ "main", "master" ] 8 | release: 9 | types: [published,edited] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 23 | 24 | - name: Setup .NET 25 | uses: actions/setup-dotnet@v2 26 | with: 27 | dotnet-version: | 28 | 8.0.x 29 | 9.0.x 30 | 31 | - name: Setup Mono 32 | run: sudo apt-get install -y mono-devel 33 | 34 | - name: Set up Node.js 35 | uses: actions/setup-node@v3 36 | with: 37 | node-version: '20' 38 | 39 | - name: Install dependencies for tests 40 | run: npm install @modelcontextprotocol/server-everything 41 | 42 | - name: Install dependencies for tests 43 | run: npm install @modelcontextprotocol/server-memory 44 | 45 | - name: Run Cake script 46 | uses: cake-build/cake-action@v1 47 | with: 48 | verbosity: Diagnostic 49 | env: 50 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY}} 51 | SONAR_LOGIN: ${{ secrets.SONAR_LOGIN }} 52 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cake tools 2 | /[Tt]ools/ 3 | 4 | # Build output 5 | [Bb]uildArtifacts/ 6 | # Build results 7 | [Dd]ebug/ 8 | [Rr]elease/ 9 | x64/ 10 | x86/ 11 | [Aa][Rr][Mm]/ 12 | [Aa][Rr][Mm]64/ 13 | bld/ 14 | [Bb]in/ 15 | [Oo]bj/ 16 | [Ll]og/ 17 | [Ll]ogs/ 18 | 19 | # Visual Studio files 20 | .vs/ 21 | *.user 22 | *.userosscache 23 | *.sln.docstates 24 | *.userprefs 25 | 26 | # ReSharper 27 | _ReSharper*/ 28 | *.[Rr]e[Ss]harper 29 | *.DotSettings.user 30 | 31 | # NuGet 32 | *.nupkg 33 | *.snupkg 34 | **/packages/* 35 | !**/packages/build/ 36 | 37 | # MSTest test Results 38 | [Tt]est[Rr]esult*/ 39 | [Bb]uild[Ll]og.* 40 | 41 | # .NET Core 42 | project.lock.json 43 | project.fragment.lock.json 44 | artifacts/ 45 | 46 | # VS Code files 47 | .vscode/* 48 | !.vscode/settings.json 49 | !.vscode/tasks.json 50 | !.vscode/launch.json 51 | !.vscode/extensions.json 52 | *.code-workspace 53 | 54 | # Local History for Visual Studio Code 55 | .history/ 56 | 57 | # Windows image file caches 58 | Thumbs.db 59 | ehthumbs.db 60 | 61 | # Folder config file 62 | Desktop.ini 63 | 64 | # Recycle Bin used on file shares 65 | $RECYCLE.BIN/ 66 | 67 | # DotCover is a Code Coverage Tool 68 | *.dotCover 69 | 70 | # NCrunch 71 | _NCrunch_* 72 | .*crunch*.local.xml 73 | nCrunchTemp_* 74 | /mcpdotnet.v3.ncrunchsolution 75 | /.NCrunch_mcpdotnet 76 | *.ncrunchproject 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to mcpdotnet 2 | 3 | Thank you for your interest in contributing to mcpdotnet! This project is in its early stages, and we welcome contributions from the community. 4 | 5 | ## Getting Started 6 | 7 | 1. Fork the repository 8 | 2. Clone your fork: `git clone https://github.com/yourusername/mcpdotnet.git` 9 | 3. Create a branch: `git checkout -b your-feature-name` 10 | 11 | ## Prerequisites 12 | 13 | - .NET 8.0 SDK or later 14 | - Your favorite IDE (Visual Studio 2022, VS Code, Rider, etc.) 15 | 16 | ## Development Process 17 | 18 | 1. Write your code 19 | 2. Add tests for your changes 20 | 3. Ensure all tests pass 21 | 4. Update documentation if needed 22 | 5. Submit a pull request 23 | 24 | ## Coding Guidelines 25 | 26 | - Follow standard C# coding conventions 27 | - Use meaningful variable and method names 28 | - Write XML documentation comments for public APIs 29 | - Keep methods focused and concise 30 | 31 | ## Pull Request Process 32 | 33 | 1. Update the README.md with details of changes if needed 34 | 2. Follow the PR template 35 | 3. Wait for review from maintainers 36 | 37 | ## Code of Conduct 38 | 39 | Please note that this project adheres to a Code of Conduct. By participating, you agree to uphold this code. 40 | 41 | ## Questions? 42 | 43 | Feel free to open an issue for any questions or concerns. -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDeployment 2 | commit-message-incrementing: Disabled -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Peder Holdgaard Pedersen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | The mcpdotnet team takes security vulnerabilities seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. 4 | 5 | To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/PederHP/mcpdotnet/security/advisories/new) tab. 6 | 7 | **Please do not report security vulnerabilities through public issues, discussions, or change requests.** 8 | 9 | ## Supported Versions 10 | 11 | Please note that mcpdotnet is currently in Early Development and versions are pre-release. We strongly advise against including mcpdotnet in production systems at this time. 12 | 13 | We will list supported versions here once the project is further along in development. -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODOs for mcpdotnet 2 | 3 | ## Integration Testing 4 | - Add comprehensive Roots feature testing once reference servers implement support 5 | - Consider using everything server adding `listRoots` tool) 6 | - Add integration tests similar to Sampling tests 7 | - Verify roots notification handling 8 | - Expand SSE test server to support all features or use a reference SSE server if one becomes available 9 | 10 | ## Future Improvements 11 | - [X] Add HTTPS/SSE transport support 12 | - [ ] Add more example applications showing different capabilities 13 | - [ ] Add comprehensive documentation for advanced scenarios 14 | - [ ] Profile and optimize performance 15 | - [ ] Linux support in stdio transport 16 | 17 | ## Code Improvements 18 | - [ ] Consolidate notification handling in McpClient to reduce duplication between SendNotificationAsync overloads -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop' 2 | 3 | Set-Location -LiteralPath $PSScriptRoot 4 | 5 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' 6 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' 7 | $env:DOTNET_NOLOGO = '1' 8 | 9 | dotnet tool restore 10 | if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } 11 | 12 | dotnet cake @args 13 | if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } 14 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euox pipefail 3 | 4 | cd "$(dirname "${BASH_SOURCE[0]}")" 5 | 6 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 7 | export DOTNET_CLI_TELEMETRY_OPTOUT=1 8 | export DOTNET_NOLOGO=1 9 | 10 | dotnet tool restore 11 | 12 | dotnet cake "$@" 13 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/AspNetCoreSseServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/McpEndpointRouteBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | using McpDotNet.Server; 3 | using McpDotNet.Utils.Json; 4 | using Microsoft.Extensions.Options; 5 | 6 | namespace AspNetCoreSseServer; 7 | 8 | public static class McpEndpointRouteBuilderExtensions 9 | { 10 | public static IEndpointConventionBuilder MapMcpSse(this IEndpointRouteBuilder endpoints) 11 | { 12 | IMcpServer? server = null; 13 | SseServerStreamTransport? transport = null; 14 | var loggerFactory = endpoints.ServiceProvider.GetRequiredService(); 15 | var mcpServerOptions = endpoints.ServiceProvider.GetRequiredService>(); 16 | 17 | var routeGroup = endpoints.MapGroup(""); 18 | 19 | routeGroup.MapGet("/sse", async (HttpResponse response, CancellationToken requestAborted) => 20 | { 21 | await using var localTransport = transport = new SseServerStreamTransport(response.Body); 22 | await using var localServer = server = McpServerFactory.Create(transport, mcpServerOptions.Value, loggerFactory, endpoints.ServiceProvider); 23 | 24 | await localServer.StartAsync(requestAborted); 25 | 26 | response.Headers.ContentType = "text/event-stream"; 27 | response.Headers.CacheControl = "no-cache"; 28 | 29 | try 30 | { 31 | await transport.RunAsync(requestAborted); 32 | } 33 | catch (OperationCanceledException) when (requestAborted.IsCancellationRequested) 34 | { 35 | // RequestAborted always triggers when the client disconnects before a complete response body is written, 36 | // but this is how SSE connections are typically closed. 37 | } 38 | }); 39 | 40 | routeGroup.MapPost("/message", async (HttpContext context) => 41 | { 42 | if (transport is null) 43 | { 44 | await Results.BadRequest("Connect to the /sse endpoint before sending messages.").ExecuteAsync(context); 45 | return; 46 | } 47 | 48 | var message = await context.Request.ReadFromJsonAsync(JsonSerializerOptionsExtensions.DefaultOptions, context.RequestAborted); 49 | if (message is null) 50 | { 51 | await Results.BadRequest("No message in request body.").ExecuteAsync(context); 52 | return; 53 | } 54 | 55 | await transport.OnMessageReceivedAsync(message, context.RequestAborted); 56 | context.Response.StatusCode = StatusCodes.Status202Accepted; 57 | await context.Response.WriteAsync("Accepted"); 58 | }); 59 | 60 | return routeGroup; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/Program.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet; 2 | using AspNetCoreSseServer; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | builder.Services.AddMcpServer().WithTools(); 6 | var app = builder.Build(); 7 | 8 | app.MapGet("/", () => "Hello World!"); 9 | app.MapMcpSse(); 10 | 11 | app.Run(); 12 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "http": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "applicationUrl": "http://localhost:3001", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development" 11 | } 12 | }, 13 | "https": { 14 | "commandName": "Project", 15 | "dotnetRunMessages": true, 16 | "launchBrowser": true, 17 | "applicationUrl": "https://localhost:7133;http://localhost:3001", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/SseServerStreamTransport.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers; 2 | using System.Net.ServerSentEvents; 3 | using System.Text.Json; 4 | using System.Threading.Channels; 5 | using McpDotNet.Protocol.Messages; 6 | using McpDotNet.Protocol.Transport; 7 | using McpDotNet.Utils.Json; 8 | 9 | namespace AspNetCoreSseServer; 10 | 11 | public class SseServerStreamTransport(Stream sseResponseStream) : ITransport 12 | { 13 | private readonly Channel _incomingChannel = CreateSingleItemChannel(); 14 | private readonly Channel> _outgoingSseChannel = CreateSingleItemChannel>(); 15 | 16 | private Task? _sseWriteTask; 17 | private Utf8JsonWriter? _jsonWriter; 18 | 19 | public bool IsConnected => _sseWriteTask?.IsCompleted == false; 20 | 21 | public Task RunAsync(CancellationToken cancellationToken) 22 | { 23 | void WriteJsonRpcMessageToBuffer(SseItem item, IBufferWriter writer) 24 | { 25 | if (item.EventType == "endpoint") 26 | { 27 | writer.Write("/message"u8); 28 | return; 29 | } 30 | 31 | JsonSerializer.Serialize(GetUtf8JsonWriter(writer), item.Data, JsonSerializerOptionsExtensions.DefaultOptions); 32 | } 33 | 34 | // The very first SSE event isn't really an IJsonRpcMessage, but there's no API to write a single item of a different type, 35 | // so we fib and special-case the "endpoint" event type in the formatter. 36 | _outgoingSseChannel.Writer.TryWrite(new SseItem(null, "endpoint")); 37 | 38 | var sseItems = _outgoingSseChannel.Reader.ReadAllAsync(cancellationToken); 39 | return _sseWriteTask = SseFormatter.WriteAsync(sseItems, sseResponseStream, WriteJsonRpcMessageToBuffer, cancellationToken); 40 | } 41 | 42 | public ChannelReader MessageReader => _incomingChannel.Reader; 43 | 44 | public ValueTask DisposeAsync() 45 | { 46 | _incomingChannel.Writer.TryComplete(); 47 | _outgoingSseChannel.Writer.TryComplete(); 48 | return new ValueTask(_sseWriteTask ?? Task.CompletedTask); 49 | } 50 | 51 | public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default) => 52 | _outgoingSseChannel.Writer.WriteAsync(new SseItem(message), cancellationToken).AsTask(); 53 | 54 | public Task OnMessageReceivedAsync(IJsonRpcMessage message, CancellationToken cancellationToken) 55 | { 56 | if (!IsConnected) 57 | { 58 | throw new McpTransportException("Transport is not connected"); 59 | } 60 | 61 | return _incomingChannel.Writer.WriteAsync(message, cancellationToken).AsTask(); 62 | } 63 | 64 | private static Channel CreateSingleItemChannel() => 65 | Channel.CreateBounded(new BoundedChannelOptions(1) 66 | { 67 | SingleReader = true, 68 | SingleWriter = false, 69 | }); 70 | 71 | private Utf8JsonWriter GetUtf8JsonWriter(IBufferWriter writer) 72 | { 73 | if (_jsonWriter is null) 74 | { 75 | _jsonWriter = new Utf8JsonWriter(writer); 76 | } 77 | else 78 | { 79 | _jsonWriter.Reset(writer); 80 | } 81 | 82 | return _jsonWriter; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/Tools/EchoTool.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Server; 2 | using System.ComponentModel; 3 | 4 | namespace TestServerWithHosting.Tools; 5 | 6 | [McpToolType] 7 | public static class EchoTool 8 | { 9 | [McpTool, Description("Echoes the input back to the client.")] 10 | public static string Echo(string message) 11 | { 12 | return "hello " + message; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/Tools/SampleLlmTool.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Types; 2 | using McpDotNet.Server; 3 | using System.ComponentModel; 4 | 5 | namespace TestServerWithHosting.Tools; 6 | 7 | /// 8 | /// This tool uses depenency injection and async method 9 | /// 10 | [McpToolType] 11 | public class SampleLlmTool 12 | { 13 | private readonly IMcpServer _server; 14 | 15 | public SampleLlmTool(IMcpServer server) 16 | { 17 | _server = server ?? throw new ArgumentNullException(nameof(server)); 18 | } 19 | 20 | [McpTool("sampleLLM"), Description("Samples from an LLM using MCP's sampling feature")] 21 | public async Task SampleLLM( 22 | [Description("The prompt to send to the LLM")] string prompt, 23 | [Description("Maximum number of tokens to generate")] int maxTokens, 24 | CancellationToken cancellationToken) 25 | { 26 | var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens); 27 | var sampleResult = await _server.RequestSamplingAsync(samplingParams, cancellationToken); 28 | 29 | return $"LLM sampling result: {sampleResult.Content.Text}"; 30 | } 31 | 32 | private static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100) 33 | { 34 | return new CreateMessageRequestParams() 35 | { 36 | Messages = [new SamplingMessage() 37 | { 38 | Role = Role.User, 39 | Content = new Content() 40 | { 41 | Type = "text", 42 | Text = $"Resource {uri} context: {context}" 43 | } 44 | }], 45 | SystemPrompt = "You are a helpful test server.", 46 | MaxTokens = maxTokens, 47 | Temperature = 0.7f, 48 | IncludeContext = ContextInclusion.ThisServer 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/AspNetCoreSseServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # Samples 2 | 3 | This directory contains example projects demonstrating how to use mcpdotnet with different LLM SDKs and platforms. 4 | 5 | ## Environment Setup 6 | 7 | Before running the samples, you'll need to set up the following environment variables: 8 | 9 | - `ANTHROPIC_API_KEY` - Required for the Anthropic sample 10 | - `OPENAI_API_KEY` - Required for the MEAI/OpenAI sample 11 | 12 | ## Sample Projects 13 | 14 | ### Anthropic Integration 15 | 16 | Located in `samples/anthropic`, this sample demonstrates integration with Anthropic's Claude API using mcpdotnet. The example shows how to: 17 | - Configure an MCP client for use with Anthropic 18 | - Map between MCP protocol types and Anthropic SDK types 19 | - Handle tool invocations and responses 20 | 21 | ### Microsoft.Extensions.AI Integration 22 | 23 | Located in `samples/microsoft.extensions.ai`, this sample shows how to use mcpdotnet with Microsoft's AI SDK. It demonstrates: 24 | - Setting up an MCP client with MEAI 25 | - Mapping MCP tools to MEAI function calls 26 | - Handling streaming responses and tool invocations 27 | 28 | ## Implementation Notes 29 | 30 | Each sample focuses on the mapping between MCP types and SDK-specific types, which is the primary integration point when working with different LLM SDKs. The samples provide reusable mapping code that can serve as a starting point for your own implementations. 31 | 32 | The complexity of this mapping varies depending on how closely the SDK's design aligns with typical LLM APIs. The provided samples demonstrate best practices for both straightforward and more complex mapping scenarios. 33 | 34 | ## Running the Samples 35 | 36 | 1. Clone the repository 37 | 2. Set up the required environment variables 38 | 3. Navigate to the desired sample directory 39 | 4. Run `dotnet run` to execute the sample 40 | 41 | For more detailed examples of mcpdotnet usage, you can also refer to the integration tests in the `tests/McpDotNet.IntegrationTests` project. -------------------------------------------------------------------------------- /samples/TestServerWithHosting/Program.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet; 2 | using Microsoft.Extensions.Hosting; 3 | using Serilog; 4 | 5 | Log.Logger = new LoggerConfiguration() 6 | .MinimumLevel.Verbose() // Capture all log levels 7 | .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "TestServer_.log"), 8 | rollingInterval: RollingInterval.Day, 9 | outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}") 10 | .WriteTo.Debug() 11 | .WriteTo.Console(standardErrorFromLevel: Serilog.Events.LogEventLevel.Verbose) 12 | .CreateLogger(); 13 | 14 | try 15 | { 16 | Log.Information("Starting server..."); 17 | 18 | var builder = Host.CreateApplicationBuilder(args); 19 | builder.Services.AddSerilog(); 20 | builder.Services.AddMcpServer() 21 | .WithStdioServerTransport() 22 | .WithTools(); 23 | 24 | var app = builder.Build(); 25 | 26 | await app.RunAsync(); 27 | return 0; 28 | } 29 | catch (Exception ex) 30 | { 31 | Log.Fatal(ex, "Host terminated unexpectedly"); 32 | return 1; 33 | } 34 | finally 35 | { 36 | await Log.CloseAndFlushAsync(); 37 | } -------------------------------------------------------------------------------- /samples/TestServerWithHosting/TestServerWithHosting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/TestServerWithHosting/Tools/EchoTool.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Server; 2 | using System.ComponentModel; 3 | 4 | namespace TestServerWithHosting.Tools; 5 | 6 | [McpToolType] 7 | public static class EchoTool 8 | { 9 | [McpTool, Description("Echoes the input back to the client.")] 10 | public static string Echo(string message) 11 | { 12 | return "hello " + message; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/TestServerWithHosting/Tools/SampleLlmTool.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Types; 2 | using McpDotNet.Server; 3 | using System.ComponentModel; 4 | 5 | namespace TestServerWithHosting.Tools; 6 | 7 | /// 8 | /// This tool uses depenency injection and async method 9 | /// 10 | [McpToolType] 11 | public class SampleLlmTool 12 | { 13 | private readonly IMcpServer _server; 14 | 15 | public SampleLlmTool(IMcpServer server) 16 | { 17 | _server = server ?? throw new ArgumentNullException(nameof(server)); 18 | } 19 | 20 | [McpTool("sampleLLM"), Description("Samples from an LLM using MCP's sampling feature")] 21 | public async Task SampleLLM( 22 | [Description("The prompt to send to the LLM")] string prompt, 23 | [Description("Maximum number of tokens to generate")] int maxTokens, 24 | CancellationToken cancellationToken) 25 | { 26 | var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens); 27 | var sampleResult = await _server.RequestSamplingAsync(samplingParams, cancellationToken); 28 | 29 | return $"LLM sampling result: {sampleResult.Content.Text}"; 30 | } 31 | 32 | private static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100) 33 | { 34 | return new CreateMessageRequestParams() 35 | { 36 | Messages = [new SamplingMessage() 37 | { 38 | Role = Role.User, 39 | Content = new Content() 40 | { 41 | Type = "text", 42 | Text = $"Resource {uri} context: {context}" 43 | } 44 | }], 45 | SystemPrompt = "You are a helpful test server.", 46 | MaxTokens = maxTokens, 47 | Temperature = 0.7f, 48 | IncludeContext = ContextInclusion.ThisServer 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /samples/anthropic/tools/ToolsConsole/AnthropicToolsConsole.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /samples/anthropic/tools/ToolsConsole/McpToolExtensions.cs: -------------------------------------------------------------------------------- 1 | using Anthropic.SDK.Common; 2 | using System.Text.Json; 3 | using System.Text.Json.Nodes; 4 | 5 | namespace McpDotNet; 6 | 7 | public static class McpToolExtensions 8 | { 9 | public static IList ToAnthropicTools(this IEnumerable tools) 10 | { 11 | if (tools is null) 12 | { 13 | throw new ArgumentNullException(nameof(tools)); 14 | } 15 | 16 | List result = []; 17 | foreach (var tool in tools) 18 | { 19 | var function = tool.InputSchema == null 20 | ? new Function(tool.Name, tool.Description) 21 | : new Function(tool.Name, tool.Description, JsonSerializer.Serialize(tool.InputSchema)); 22 | result.Add(function); 23 | } 24 | return result; 25 | } 26 | 27 | public static Dictionary? ToMCPArguments(this JsonNode jsonNode) 28 | { 29 | if (jsonNode == null) 30 | return null; 31 | 32 | // Convert JsonNode to Dictionary 33 | return jsonNode.AsObject() 34 | .ToDictionary( 35 | prop => prop.Key, 36 | prop => JsonSerializer.Deserialize(prop.Value)! 37 | ); 38 | } 39 | } -------------------------------------------------------------------------------- /samples/anthropic/tools/ToolsConsole/Program.cs: -------------------------------------------------------------------------------- 1 | using Anthropic.SDK; 2 | using Anthropic.SDK.Constants; 3 | using Anthropic.SDK.Messaging; 4 | using McpDotNet; 5 | using McpDotNet.Client; 6 | using McpDotNet.Configuration; 7 | using McpDotNet.Protocol.Transport; 8 | 9 | internal class Program 10 | { 11 | private static async Task GetMcpClientAsync() 12 | { 13 | McpClientOptions clientOptions = new() 14 | { 15 | ClientInfo = new() { Name = "SimpleToolsConsole", Version = "1.0.0" } 16 | }; 17 | 18 | McpServerConfig serverConfig = new() 19 | { 20 | Id = "everything", 21 | Name = "Everything", 22 | TransportType = TransportTypes.StdIo, 23 | TransportOptions = new Dictionary 24 | { 25 | ["command"] = "npx", 26 | ["arguments"] = "-y @modelcontextprotocol/server-everything", 27 | } 28 | }; 29 | 30 | return await McpClientFactory.CreateAsync(serverConfig, clientOptions); 31 | } 32 | 33 | private static async Task Main(string[] args) 34 | { 35 | try 36 | { 37 | Console.WriteLine("Initializing MCP 'everything' server"); 38 | await using var client = await GetMcpClientAsync(); 39 | Console.WriteLine("MCP 'everything' server initialized"); 40 | Console.WriteLine("Listing tools..."); 41 | var tools = await client.ListToolsAsync().ToListAsync(); 42 | var anthropicTools = tools.ToAnthropicTools(); 43 | Console.WriteLine("Tools available:"); 44 | foreach (var tool in anthropicTools) 45 | { 46 | Console.WriteLine(" " + tool.Function.Name); 47 | } 48 | 49 | Console.WriteLine("Starting chat with Claude Haiku 3.5..."); 50 | using var antClient = new AnthropicClient(Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")!); 51 | 52 | Console.WriteLine("Asking Claude to call the Echo Tool..."); 53 | 54 | List messages = 55 | [ 56 | new Message(RoleType.User, "Please call the echo tool with the string 'Hello MCP!' and show me the echoed response.") 57 | ]; 58 | 59 | var parameters = new MessageParameters() 60 | { 61 | Messages = messages, 62 | MaxTokens = 2048, 63 | Model = AnthropicModels.Claude35Haiku, 64 | Stream = false, 65 | Temperature = 0.5m, 66 | Tools = anthropicTools, 67 | System = [new SystemMessage("You will be helping the user test MCP server tool call functionality. Remember that the user cannot see your tool calls or tool results.")] 68 | }; 69 | 70 | // If the server provides instructions, add them as the system prompt 71 | if (!string.IsNullOrEmpty(client.ServerInstructions)) 72 | { 73 | parameters.System.Add(new SystemMessage(client.ServerInstructions)); 74 | } 75 | 76 | var res = await antClient.Messages.GetClaudeMessageAsync(parameters); 77 | 78 | messages.Add(res.Message); 79 | 80 | foreach (var toolCall in res.ToolCalls) 81 | { 82 | var response = await client.CallToolAsync(toolCall.Name, toolCall.Arguments?.ToMCPArguments() ?? []); 83 | 84 | messages.Add(new Message(toolCall, response.Content[0].Text)); 85 | } 86 | 87 | var finalResult = await antClient.Messages.GetClaudeMessageAsync(parameters); 88 | Console.WriteLine("Final result: " + finalResult.Message.ToString()); 89 | 90 | Console.WriteLine(); 91 | 92 | Console.WriteLine("Chat with Claude Haiku 3.5 complete"); 93 | } 94 | catch (Exception ex) 95 | { 96 | Console.WriteLine("Error occurred: " + ex.Message); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /samples/anthropic/tools/ToolsConsole/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SimpleToolsConsole": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /samples/microsoft.extensions.ai/tools/ToolsConsole/MEAIToolsConsole.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /samples/microsoft.extensions.ai/tools/ToolsConsole/Program.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Client; 2 | using McpDotNet.Configuration; 3 | using McpDotNet.Protocol.Transport; 4 | using Microsoft.Extensions.AI; 5 | using OpenAI; 6 | 7 | try 8 | { 9 | Console.WriteLine("Initializing MCP 'everything' server"); 10 | await using var client = await GetMcpClientAsync(); 11 | 12 | Console.WriteLine("MCP 'everything' server initialized"); 13 | Console.WriteLine("Listing tools..."); 14 | var mappedTools = await client.GetAIFunctionsAsync(); 15 | 16 | Console.WriteLine("Tools available:"); 17 | foreach (var tool in mappedTools) 18 | { 19 | Console.WriteLine($" {tool}"); 20 | } 21 | 22 | Console.WriteLine("Starting chat with GPT-4o-mini..."); 23 | 24 | // Note: We use then Microsoft.Extensions.AI.OpenAI client here, but it could be any other MEAI client. 25 | // Provide your own OPENAI_API_KEY via an environment variable, secret or file-based appsettings. Do not hardcode it. 26 | IChatClient chatClient = new OpenAIClient(Environment.GetEnvironmentVariable("OPENAI_API_KEY")) 27 | .AsChatClient("gpt-4o-mini") 28 | .AsBuilder() 29 | .UseFunctionInvocation() 30 | .Build(); 31 | 32 | // Create message list 33 | IList messages = 34 | [ 35 | // Add a system message 36 | new(ChatRole.System, "You are a helpful assistant, helping us test MCP server functionality."), 37 | ]; 38 | // If MCP server provides instructions, add them as an additional system message (you could also add it as a content part) 39 | if (!string.IsNullOrEmpty(client.ServerInstructions)) 40 | { 41 | messages.Add(new(ChatRole.System, client.ServerInstructions)); 42 | } 43 | // Add a user message 44 | messages.Add(new(ChatRole.User, "Please call the echo tool with the string 'Hello MCP!' and give me the response as-is.")); 45 | 46 | // Call the chat client 47 | Console.WriteLine("Asking GPT-4o-mini to call the Echo Tool..."); 48 | await foreach (var update in chatClient.GetStreamingResponseAsync(messages, new() { Tools = [.. mappedTools] })) 49 | { 50 | Console.Write(update); 51 | } 52 | Console.WriteLine(); 53 | } 54 | catch (Exception ex) 55 | { 56 | Console.WriteLine($"Error occurred: {ex.Message}"); 57 | } 58 | 59 | async Task GetMcpClientAsync() 60 | { 61 | McpClientOptions clientOptions = new() 62 | { 63 | ClientInfo = new() { Name = "SimpleToolsConsole", Version = "1.0.0" } 64 | }; 65 | 66 | McpServerConfig serverConfig = new() 67 | { 68 | Id = "everything", 69 | Name = "Everything", 70 | TransportType = TransportTypes.StdIo, 71 | TransportOptions = new Dictionary 72 | { 73 | ["command"] = "npx", 74 | ["arguments"] = "-y @modelcontextprotocol/server-everything", 75 | } 76 | }; 77 | 78 | return await McpClientFactory.CreateAsync(serverConfig, clientOptions); 79 | } -------------------------------------------------------------------------------- /samples/microsoft.extensions.ai/tools/ToolsConsole/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SimpleToolsConsole": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Collections/Generic/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Collections.Generic; 2 | 3 | internal static class CollectionExtensions 4 | { 5 | public static TValue? GetValueOrDefault(this IReadOnlyDictionary dictionary, TKey key) 6 | { 7 | return dictionary.GetValueOrDefault(key, default!); 8 | } 9 | 10 | public static TValue GetValueOrDefault(this IReadOnlyDictionary dictionary, TKey key, TValue defaultValue) 11 | { 12 | if (dictionary is null) 13 | { 14 | throw new ArgumentNullException(nameof(dictionary)); 15 | } 16 | 17 | return dictionary.TryGetValue(key, out TValue? value) ? value : defaultValue; 18 | } 19 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Diagnostics/CodeAnalysis/DynamicallyAccessedMemberTypes.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Diagnostics.CodeAnalysis; 5 | 6 | /// 7 | /// Specifies the types of members that are dynamically accessed. 8 | /// 9 | /// This enumeration has a attribute that allows a 10 | /// bitwise combination of its member values. 11 | /// 12 | [Flags] 13 | internal enum DynamicallyAccessedMemberTypes 14 | { 15 | /// 16 | /// Specifies no members. 17 | /// 18 | None = 0, 19 | 20 | /// 21 | /// Specifies the default, parameterless public constructor. 22 | /// 23 | PublicParameterlessConstructor = 0x0001, 24 | 25 | /// 26 | /// Specifies all public constructors. 27 | /// 28 | PublicConstructors = 0x0002 | PublicParameterlessConstructor, 29 | 30 | /// 31 | /// Specifies all non-public constructors. 32 | /// 33 | NonPublicConstructors = 0x0004, 34 | 35 | /// 36 | /// Specifies all public methods. 37 | /// 38 | PublicMethods = 0x0008, 39 | 40 | /// 41 | /// Specifies all non-public methods. 42 | /// 43 | NonPublicMethods = 0x0010, 44 | 45 | /// 46 | /// Specifies all public fields. 47 | /// 48 | PublicFields = 0x0020, 49 | 50 | /// 51 | /// Specifies all non-public fields. 52 | /// 53 | NonPublicFields = 0x0040, 54 | 55 | /// 56 | /// Specifies all public nested types. 57 | /// 58 | PublicNestedTypes = 0x0080, 59 | 60 | /// 61 | /// Specifies all non-public nested types. 62 | /// 63 | NonPublicNestedTypes = 0x0100, 64 | 65 | /// 66 | /// Specifies all public properties. 67 | /// 68 | PublicProperties = 0x0200, 69 | 70 | /// 71 | /// Specifies all non-public properties. 72 | /// 73 | NonPublicProperties = 0x0400, 74 | 75 | /// 76 | /// Specifies all public events. 77 | /// 78 | PublicEvents = 0x0800, 79 | 80 | /// 81 | /// Specifies all non-public events. 82 | /// 83 | NonPublicEvents = 0x1000, 84 | 85 | /// 86 | /// Specifies all interfaces implemented by the type. 87 | /// 88 | Interfaces = 0x2000, 89 | 90 | /// 91 | /// Specifies all non-public constructors, including those inherited from base classes. 92 | /// 93 | NonPublicConstructorsWithInherited = NonPublicConstructors | 0x4000, 94 | 95 | /// 96 | /// Specifies all non-public methods, including those inherited from base classes. 97 | /// 98 | NonPublicMethodsWithInherited = NonPublicMethods | 0x8000, 99 | 100 | /// 101 | /// Specifies all non-public fields, including those inherited from base classes. 102 | /// 103 | NonPublicFieldsWithInherited = NonPublicFields | 0x10000, 104 | 105 | /// 106 | /// Specifies all non-public nested types, including those inherited from base classes. 107 | /// 108 | NonPublicNestedTypesWithInherited = NonPublicNestedTypes | 0x20000, 109 | 110 | /// 111 | /// Specifies all non-public properties, including those inherited from base classes. 112 | /// 113 | NonPublicPropertiesWithInherited = NonPublicProperties | 0x40000, 114 | 115 | /// 116 | /// Specifies all non-public events, including those inherited from base classes. 117 | /// 118 | NonPublicEventsWithInherited = NonPublicEvents | 0x80000, 119 | 120 | /// 121 | /// Specifies all public constructors, including those inherited from base classes. 122 | /// 123 | PublicConstructorsWithInherited = PublicConstructors | 0x100000, 124 | 125 | /// 126 | /// Specifies all public nested types, including those inherited from base classes. 127 | /// 128 | PublicNestedTypesWithInherited = PublicNestedTypes | 0x200000, 129 | 130 | /// 131 | /// Specifies all constructors, including those inherited from base classes. 132 | /// 133 | AllConstructors = PublicConstructorsWithInherited | NonPublicConstructorsWithInherited, 134 | 135 | /// 136 | /// Specifies all methods, including those inherited from base classes. 137 | /// 138 | AllMethods = PublicMethods | NonPublicMethodsWithInherited, 139 | 140 | /// 141 | /// Specifies all fields, including those inherited from base classes. 142 | /// 143 | AllFields = PublicFields | NonPublicFieldsWithInherited, 144 | 145 | /// 146 | /// Specifies all nested types, including those inherited from base classes. 147 | /// 148 | AllNestedTypes = PublicNestedTypesWithInherited | NonPublicNestedTypesWithInherited, 149 | 150 | /// 151 | /// Specifies all properties, including those inherited from base classes. 152 | /// 153 | AllProperties = PublicProperties | NonPublicPropertiesWithInherited, 154 | 155 | /// 156 | /// Specifies all events, including those inherited from base classes. 157 | /// 158 | AllEvents = PublicEvents | NonPublicEventsWithInherited, 159 | 160 | /// 161 | /// Specifies all members. 162 | /// 163 | All = ~None 164 | } 165 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Diagnostics/CodeAnalysis/DynamicallyAccessedMembersAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Diagnostics.CodeAnalysis; 5 | 6 | /// 7 | /// Indicates that certain members on a specified are accessed dynamically, 8 | /// for example through . 9 | /// 10 | /// 11 | /// This allows tools to understand which members are being accessed during the execution 12 | /// of a program. 13 | /// 14 | /// This attribute is valid on members whose type is or . 15 | /// 16 | /// When this attribute is applied to a location of type , the assumption is 17 | /// that the string represents a fully qualified type name. 18 | /// 19 | /// When this attribute is applied to a class, interface, or struct, the members specified 20 | /// can be accessed dynamically on instances returned from calling 21 | /// on instances of that class, interface, or struct. 22 | /// 23 | /// If the attribute is applied to a method it's treated as a special case and it implies 24 | /// the attribute should be applied to the "this" parameter of the method. As such the attribute 25 | /// should only be used on instance methods of types assignable to System.Type (or string, but no methods 26 | /// will use it there). 27 | /// 28 | [AttributeUsage( 29 | AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | 30 | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | 31 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, 32 | Inherited = false)] 33 | internal sealed class DynamicallyAccessedMembersAttribute : Attribute 34 | { 35 | /// 36 | /// Initializes a new instance of the class 37 | /// with the specified member types. 38 | /// 39 | /// The types of members dynamically accessed. 40 | public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) 41 | { 42 | MemberTypes = memberTypes; 43 | } 44 | 45 | /// 46 | /// Gets the which specifies the type 47 | /// of members dynamically accessed. 48 | /// 49 | public DynamicallyAccessedMemberTypes MemberTypes { get; } 50 | } 51 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Diagnostics/CodeAnalysis/RequiresDynamicCodeAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Diagnostics.CodeAnalysis; 5 | 6 | /// 7 | /// Indicates that the specified method requires the ability to generate new code at runtime, 8 | /// for example through . 9 | /// 10 | /// 11 | /// This allows tools to understand which methods are unsafe to call when compiling ahead of time. 12 | /// 13 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] 14 | internal sealed class RequiresDynamicCodeAttribute : Attribute 15 | { 16 | /// 17 | /// Initializes a new instance of the class 18 | /// with the specified message. 19 | /// 20 | /// 21 | /// A message that contains information about the usage of dynamic code. 22 | /// 23 | public RequiresDynamicCodeAttribute(string message) 24 | { 25 | Message = message; 26 | } 27 | 28 | /// 29 | /// Gets a message that contains information about the usage of dynamic code. 30 | /// 31 | public string Message { get; } 32 | 33 | /// 34 | /// Gets or sets an optional URL that contains more information about the method, 35 | /// why it requires dynamic code, and what options a consumer has to deal with it. 36 | /// 37 | public string? Url { get; set; } 38 | } 39 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Diagnostics/CodeAnalysis/RequiresUnreferencedCode.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Diagnostics.CodeAnalysis; 5 | 6 | /// 7 | /// Indicates that the specified method requires dynamic access to code that is not referenced 8 | /// statically, for example through . 9 | /// 10 | /// 11 | /// This allows tools to understand which methods are unsafe to call when removing unreferenced 12 | /// code from an application. 13 | /// 14 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] 15 | internal sealed class RequiresUnreferencedCodeAttribute : Attribute 16 | { 17 | /// 18 | /// Initializes a new instance of the class 19 | /// with the specified message. 20 | /// 21 | /// 22 | /// A message that contains information about the usage of unreferenced code. 23 | /// 24 | public RequiresUnreferencedCodeAttribute(string message) 25 | { 26 | Message = message; 27 | } 28 | 29 | /// 30 | /// Gets a message that contains information about the usage of unreferenced code. 31 | /// 32 | public string Message { get; } 33 | 34 | /// 35 | /// Gets or sets an optional URL that contains more information about the method, 36 | /// why it requires unreferenced code, and what options a consumer has to deal with it. 37 | /// 38 | public string? Url { get; set; } 39 | } 40 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Diagnostics/CodeAnalysis/SetsRequiredMembersAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace System.Diagnostics.CodeAnalysis 2 | { 3 | [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] 4 | internal sealed class SetsRequiredMembersAttribute : Attribute; 5 | } 6 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Diagnostics/CodeAnalysis/UnconditionalSuppressMessageAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Diagnostics.CodeAnalysis; 5 | 6 | /// 7 | /// /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a 8 | /// single code artifact. 9 | /// 10 | /// 11 | /// is different than 12 | /// in that it doesn't have a 13 | /// . So it is always preserved in the compiled assembly. 14 | /// 15 | [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] 16 | internal sealed class UnconditionalSuppressMessageAttribute : Attribute 17 | { 18 | /// 19 | /// Initializes a new instance of the 20 | /// class, specifying the category of the tool and the identifier for an analysis rule. 21 | /// 22 | /// The category for the attribute. 23 | /// The identifier of the analysis rule the attribute applies to. 24 | public UnconditionalSuppressMessageAttribute(string category, string checkId) 25 | { 26 | Category = category; 27 | CheckId = checkId; 28 | } 29 | 30 | /// 31 | /// Gets the category identifying the classification of the attribute. 32 | /// 33 | /// 34 | /// The property describes the tool or tool analysis category 35 | /// for which a message suppression attribute applies. 36 | /// 37 | public string Category { get; } 38 | 39 | /// 40 | /// Gets the identifier of the analysis tool rule to be suppressed. 41 | /// 42 | /// 43 | /// Concatenated together, the and 44 | /// properties form a unique check identifier. 45 | /// 46 | public string CheckId { get; } 47 | 48 | /// 49 | /// Gets or sets the scope of the code that is relevant for the attribute. 50 | /// 51 | /// 52 | /// The Scope property is an optional argument that specifies the metadata scope for which 53 | /// the attribute is relevant. 54 | /// 55 | public string? Scope { get; set; } 56 | 57 | /// 58 | /// Gets or sets a fully qualified path that represents the target of the attribute. 59 | /// 60 | /// 61 | /// The property is an optional argument identifying the analysis target 62 | /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". 63 | /// Because it is fully qualified, it can be long, particularly for targets such as parameters. 64 | /// The analysis tool user interface should be capable of automatically formatting the parameter. 65 | /// 66 | public string? Target { get; set; } 67 | 68 | /// 69 | /// Gets or sets an optional argument expanding on exclusion criteria. 70 | /// 71 | /// 72 | /// The property is an optional argument that specifies additional 73 | /// exclusion where the literal metadata target is not sufficiently precise. For example, 74 | /// the cannot be applied within a method, 75 | /// and it may be desirable to suppress a violation against a statement in the method that will 76 | /// give a rule violation, but not against all statements in the method. 77 | /// 78 | public string? MessageId { get; set; } 79 | 80 | /// 81 | /// Gets or sets the justification for suppressing the code analysis message. 82 | /// 83 | public string? Justification { get; set; } 84 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Diagnostics/ProcessExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Diagnostics; 5 | 6 | internal static class ProcessExtensions 7 | { 8 | public static void Kill(this Process process, bool entireProcessTree) 9 | { 10 | _ = entireProcessTree; 11 | process.Kill(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/IO/TextReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.IO; 2 | 3 | internal static class TextReaderExtensions 4 | { 5 | public static Task ReadLineAsync(this TextReader reader, CancellationToken cancellationToken) 6 | { 7 | _ = cancellationToken; 8 | return reader.ReadLineAsync(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/IO/TextWriterExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace System.IO; 4 | 5 | internal static class TextWriterExtensions 6 | { 7 | public static async Task WriteLineAsync(this TextWriter writer, ReadOnlyMemory value, CancellationToken cancellationToken) 8 | { 9 | if (writer is null) 10 | { 11 | throw new ArgumentNullException(nameof(writer)); 12 | } 13 | 14 | if (value.IsEmpty) 15 | { 16 | return; 17 | } 18 | 19 | cancellationToken.ThrowIfCancellationRequested(); 20 | 21 | if (MemoryMarshal.TryGetString(value, out string str, out int start, out int length) && 22 | start == 0 && length == str.Length) 23 | { 24 | await writer.WriteLineAsync(str).ConfigureAwait(false); 25 | } 26 | else if (MemoryMarshal.TryGetArray(value, out ArraySegment array) && 27 | array.Array is not null && array.Offset == 0 && array.Count == array.Array.Length) 28 | { 29 | await writer.WriteLineAsync(array.Array).ConfigureAwait(false); 30 | } 31 | else 32 | { 33 | await writer.WriteLineAsync(value.ToArray()).ConfigureAwait(false); 34 | } 35 | } 36 | 37 | public static async Task FlushAsync(this TextWriter writer, CancellationToken cancellationToken) 38 | { 39 | cancellationToken.ThrowIfCancellationRequested(); 40 | await writer.FlushAsync(); 41 | } 42 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Net/Http/HttpClientExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Net.Http; 2 | 3 | internal static class HttpClientExtensions 4 | { 5 | public static async Task ReadAsStreamAsync(this HttpContent content, CancellationToken cancellationToken) 6 | { 7 | if (content is null) 8 | { 9 | throw new ArgumentNullException(nameof(content)); 10 | } 11 | 12 | cancellationToken.ThrowIfCancellationRequested(); 13 | return await content.ReadAsStreamAsync(); 14 | } 15 | 16 | public static async Task ReadAsStringAsync(this HttpContent content, CancellationToken cancellationToken) 17 | { 18 | if (content is null) 19 | { 20 | throw new ArgumentNullException(nameof(content)); 21 | } 22 | 23 | cancellationToken.ThrowIfCancellationRequested(); 24 | return await content.ReadAsStringAsync(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Runtime/CompilerServices/CallerArgumentExpressionAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Runtime.CompilerServices; 5 | 6 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] 7 | internal sealed class CallerArgumentExpressionAttribute : Attribute 8 | { 9 | public CallerArgumentExpressionAttribute(string parameterName) 10 | { 11 | ParameterName = parameterName; 12 | } 13 | 14 | public string ParameterName { get; } 15 | } 16 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Runtime/CompilerServices/CompilerFeatureRequiredAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | namespace System.Runtime.CompilerServices 5 | { 6 | /// 7 | /// Indicates that compiler support for a particular feature is required for the location where this attribute is applied. 8 | /// 9 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] 10 | internal sealed class CompilerFeatureRequiredAttribute : Attribute 11 | { 12 | public CompilerFeatureRequiredAttribute(string featureName) 13 | { 14 | FeatureName = featureName; 15 | } 16 | 17 | /// 18 | /// The name of the compiler feature. 19 | /// 20 | public string FeatureName { get; } 21 | 22 | /// 23 | /// If true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand . 24 | /// 25 | public bool IsOptional { get; init; } 26 | 27 | /// 28 | /// The used for the required members C# feature. 29 | /// 30 | public const string RequiredMembers = nameof(RequiredMembers); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Runtime/CompilerServices/IsExternalInit.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.ComponentModel; 5 | 6 | namespace System.Runtime.CompilerServices 7 | { 8 | /// 9 | /// Reserved to be used by the compiler for tracking metadata. 10 | /// This class should not be used by developers in source code. 11 | /// 12 | [EditorBrowsable(EditorBrowsableState.Never)] 13 | internal static class IsExternalInit; 14 | } 15 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Runtime/CompilerServices/RequiredMemberAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | 4 | using System.ComponentModel; 5 | 6 | namespace System.Runtime.CompilerServices 7 | { 8 | /// Specifies that a type has required members or that a member is required. 9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] 10 | [EditorBrowsable(EditorBrowsableState.Never)] 11 | internal sealed class RequiredMemberAttribute : Attribute; 12 | } 13 | -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Threading/CancellationTokenSourceExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Threading.Tasks; 2 | 3 | internal static class CancellationTokenSourceExtensions 4 | { 5 | public static Task CancelAsync(this CancellationTokenSource cancellationTokenSource) 6 | { 7 | if (cancellationTokenSource is null) 8 | { 9 | throw new ArgumentNullException(nameof(cancellationTokenSource)); 10 | } 11 | 12 | cancellationTokenSource.Cancel(); 13 | return Task.CompletedTask; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Common/Polyfills/System/Threading/Tasks/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Threading.Tasks; 2 | 3 | internal static class TaskExtensions 4 | { 5 | public static Task WaitAsync(this Task task, CancellationToken cancellationToken) 6 | { 7 | return WaitAsync(task, Timeout.InfiniteTimeSpan, cancellationToken); 8 | } 9 | 10 | public static async Task WaitAsync(this Task task, CancellationToken cancellationToken) 11 | { 12 | await WaitAsync(task, Timeout.InfiniteTimeSpan, cancellationToken); 13 | return task.Result; 14 | } 15 | 16 | public static async Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken cancellationToken) 17 | { 18 | if (task is null) 19 | { 20 | throw new ArgumentNullException(nameof(task)); 21 | } 22 | 23 | if (timeout < TimeSpan.Zero && timeout != Timeout.InfiniteTimeSpan) 24 | { 25 | throw new ArgumentOutOfRangeException(nameof(timeout)); 26 | } 27 | 28 | if (!task.IsCompleted) 29 | { 30 | cancellationToken.ThrowIfCancellationRequested(); 31 | 32 | using CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); 33 | cts.CancelAfter(timeout); 34 | 35 | var cancellationTask = new TaskCompletionSource(); 36 | using var _ = cts.Token.Register(tcs => ((TaskCompletionSource)tcs!).TrySetResult(true), cancellationTask); 37 | await Task.WhenAny(task, cancellationTask.Task).ConfigureAwait(false); 38 | 39 | if (!task.IsCompleted) 40 | { 41 | cancellationToken.ThrowIfCancellationRequested(); 42 | throw new TimeoutException(); 43 | } 44 | } 45 | 46 | await task.ConfigureAwait(false); 47 | } 48 | } -------------------------------------------------------------------------------- /src/McpDotNet.Extensions.AI/McpDotNet.Extensions.AI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0;netstandard2.0 5 | enable 6 | enable 7 | Latest 8 | true 9 | All 10 | 11 | 12 | 13 | true 14 | 15 | 16 | 17 | 18 | McpDotNet.Extensions.AI 19 | 1.1.0.1 20 | PederHP 21 | Microsoft.Extensions.AI integration for the Model Context Protocol (MCP). Enables seamless use of MCP tools as AI functions in any IChatClient implementation. 22 | https://github.com/PederHP/mcpdotnet/tree/main/src/McpDotNet.Extensions.AI 23 | https://github.com/PederHP/mcpdotnet 24 | git 25 | mcp;ai;microsoft-extensions-ai;chatclient;llm 26 | README.md 27 | MIT 28 | 29 | McpDotNet.Extensions.AI 30 | McpDotNet.Extensions.AI 31 | true 32 | true 33 | snupkg 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/McpDotNet.Extensions.AI/README.md: -------------------------------------------------------------------------------- 1 | # McpDotNet.Extensions.AI 2 | [![NuGet version](https://img.shields.io/nuget/v/McpDotNet.Extensions.AI.svg)](https://www.nuget.org/packages/McpDotNet.Extensions.AI/) 3 | 4 | This package is deprecated and no longer needed. The functionality it exposes is part of the core mcpdotnet library. Please remove references to it. -------------------------------------------------------------------------------- /src/mcpdotnet/Client/IMcpClient.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | using McpDotNet.Protocol.Types; 3 | 4 | namespace McpDotNet.Client; 5 | 6 | /// 7 | /// Represents an instance of an MCP client connecting to a specific server. 8 | /// 9 | public interface IMcpClient : IAsyncDisposable 10 | { 11 | /// 12 | /// Gets the capabilities supported by the server. 13 | /// 14 | ServerCapabilities? ServerCapabilities { get; } 15 | 16 | /// 17 | /// Gets the version and implementation information of the server. 18 | /// 19 | Implementation? ServerInfo { get; } 20 | 21 | /// 22 | /// Instructions describing how to use the server and its features. 23 | /// This can be used by clients to improve the LLM's understanding of available tools, resources, etc. 24 | /// It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt. 25 | /// 26 | string? ServerInstructions { get; } 27 | 28 | /// 29 | /// Adds a handler for server notifications of a specific method. 30 | /// 31 | /// The notification method to handle. 32 | /// The async handler function to process notifications. 33 | /// 34 | /// 35 | /// Each method may have multiple handlers. Adding a handler for a method that already has one 36 | /// will not replace the existing handler. 37 | /// 38 | /// 39 | /// provides constants for common notification methods. 40 | /// 41 | /// 42 | void AddNotificationHandler(string method, Func handler); 43 | 44 | /// 45 | /// Sends a generic JSON-RPC request to the server. 46 | /// 47 | /// The expected response type. 48 | /// The JSON-RPC request to send. 49 | /// A token to cancel the operation. 50 | /// A task containing the server's response. 51 | /// 52 | /// It is recommended to use the capability-specific methods that use this one in their implementation. 53 | /// Use this method for custom requests or those not yet covered explicitly. 54 | /// 55 | Task SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken = default) where TResult : class; 56 | 57 | /// 58 | /// Sends a message to the server. 59 | /// 60 | /// The message. 61 | /// A token to cancel the operation. 62 | Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default); 63 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Client/McpClientException.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Client; 2 | 3 | /// 4 | /// Represents errors that occur in the MCP client. 5 | /// 6 | public class McpClientException : Exception 7 | { 8 | /// 9 | /// Gets the error code if this exception was caused by a JSON-RPC error response. 10 | /// 11 | public int? ErrorCode { get; } 12 | 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public McpClientException() 17 | { 18 | } 19 | 20 | /// 21 | /// Initializes a new instance of the class with a specified error message. 22 | /// 23 | /// The message that describes the error. 24 | public McpClientException(string message) : base(message) 25 | { 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the class with a specified error message and error code. 30 | /// 31 | /// The message that describes the error. 32 | /// The error code associated with the JSON-RPC error response. 33 | public McpClientException(string message, int errorCode) : base(message) 34 | { 35 | ErrorCode = errorCode; 36 | } 37 | 38 | /// 39 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 40 | /// 41 | /// The message that describes the error. 42 | /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. 43 | public McpClientException(string message, Exception innerException) : base(message, innerException) 44 | { 45 | } 46 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Client/McpClientOptions.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Types; 2 | 3 | namespace McpDotNet.Client; 4 | 5 | /// 6 | /// Configuration options for the MCP client. This is passed to servers during the initialization sequence, letting them know about the client's capabilities and 7 | /// protocol version. 8 | /// See the protocol specification for details on capability negotiation 9 | /// 10 | public record McpClientOptions 11 | { 12 | /// 13 | /// Information about this client implementation. 14 | /// 15 | public required Implementation ClientInfo { get; init; } 16 | 17 | /// 18 | /// Client capabilities to advertise to the server. 19 | /// 20 | public ClientCapabilities? Capabilities { get; init; } 21 | 22 | /// 23 | /// Protocol version to request from the server. 24 | /// 25 | public string ProtocolVersion { get; init; } = "2024-11-05"; 26 | 27 | /// 28 | /// Timeout for initialization sequence. 29 | /// 30 | public TimeSpan InitializationTimeout { get; init; } = TimeSpan.FromSeconds(60); 31 | } 32 | -------------------------------------------------------------------------------- /src/mcpdotnet/Configuration/DefaultMcpServerBuilder.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Utils; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace McpDotNet.Configuration; 5 | 6 | /// 7 | /// Default implementation of . 8 | /// 9 | internal class DefaultMcpServerBuilder : IMcpServerBuilder 10 | { 11 | /// 12 | public IServiceCollection Services { get; } 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// 18 | /// 19 | public DefaultMcpServerBuilder(IServiceCollection services) 20 | { 21 | Throw.IfNull(services); 22 | 23 | Services = services; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/mcpdotnet/Configuration/IMcpServerBuilder.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Server; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace McpDotNet.Configuration; 5 | 6 | /// 7 | /// Builder for configuring instances. 8 | /// 9 | public interface IMcpServerBuilder 10 | { 11 | /// 12 | /// Gets the service collection. 13 | /// 14 | IServiceCollection Services { get; } 15 | } 16 | -------------------------------------------------------------------------------- /src/mcpdotnet/Configuration/McpServerBuilderExtensions.Handler.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Configuration; 2 | using McpDotNet.Protocol.Types; 3 | using McpDotNet.Server; 4 | using McpDotNet.Utils; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace McpDotNet; 8 | 9 | /// 10 | /// Extension to configure the MCP server with handlers 11 | /// 12 | public static partial class McpServerBuilderExtensions 13 | { 14 | /// 15 | /// Sets the handler for list tools requests. 16 | /// 17 | /// The builder instance. 18 | /// The handler. 19 | public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 20 | { 21 | Throw.IfNull(builder); 22 | 23 | builder.Services.Configure(s => s.ListToolsHandler = handler); 24 | return builder; 25 | } 26 | 27 | /// 28 | /// Sets the handler for call tool requests. 29 | /// 30 | /// The builder instance. 31 | /// The handler. 32 | public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 33 | { 34 | Throw.IfNull(builder); 35 | 36 | builder.Services.Configure(s => s.CallToolHandler = handler); 37 | return builder; 38 | } 39 | 40 | /// 41 | /// Sets the handler for list prompts requests. 42 | /// 43 | /// The builder instance. 44 | /// The handler. 45 | public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 46 | { 47 | Throw.IfNull(builder); 48 | 49 | builder.Services.Configure(s => s.ListPromptsHandler = handler); 50 | return builder; 51 | } 52 | 53 | /// 54 | /// Sets the handler for get prompt requests. 55 | /// 56 | /// The builder instance. 57 | /// The handler. 58 | public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 59 | { 60 | Throw.IfNull(builder); 61 | 62 | builder.Services.Configure(s => s.GetPromptHandler = handler); 63 | return builder; 64 | } 65 | 66 | /// 67 | /// Sets the handler for list resources requests. 68 | /// 69 | /// The builder instance. 70 | /// The handler. 71 | public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 72 | { 73 | Throw.IfNull(builder); 74 | 75 | builder.Services.Configure(s => s.ListResourcesHandler = handler); 76 | return builder; 77 | } 78 | 79 | /// 80 | /// Sets the handler for read resources requests. 81 | /// 82 | /// The builder instance. 83 | /// The handler. 84 | public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 85 | { 86 | Throw.IfNull(builder); 87 | 88 | builder.Services.Configure(s => s.ReadResourceHandler = handler); 89 | return builder; 90 | } 91 | 92 | /// 93 | /// Sets the handler for get completion requests. 94 | /// 95 | /// The builder instance. 96 | /// The handler. 97 | public static IMcpServerBuilder WithGetCompletionHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 98 | { 99 | Throw.IfNull(builder); 100 | 101 | builder.Services.Configure(s => s.GetCompletionHandler = handler); 102 | return builder; 103 | } 104 | 105 | /// 106 | /// Sets the handler for subscribe to resources messages. 107 | /// 108 | /// The builder instance. 109 | /// The handler. 110 | public static IMcpServerBuilder WithSubscribeToResourcesHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 111 | { 112 | Throw.IfNull(builder); 113 | 114 | builder.Services.Configure(s => s.SubscribeToResourcesHandler = handler); 115 | return builder; 116 | } 117 | 118 | /// 119 | /// Sets or sets the handler for subscribe to resources messages. 120 | /// 121 | /// The builder instance. 122 | /// The handler. 123 | public static IMcpServerBuilder WithUnsubscribeFromResourcesHandler(this IMcpServerBuilder builder, Func, CancellationToken, Task> handler) 124 | { 125 | Throw.IfNull(builder); 126 | 127 | builder.Services.Configure(s => s.UnsubscribeFromResourcesHandler = handler); 128 | return builder; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/mcpdotnet/Configuration/McpServerBuilderExtensions.Transports.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Configuration; 2 | using McpDotNet.Hosting; 3 | using McpDotNet.Protocol.Transport; 4 | using McpDotNet.Utils; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace McpDotNet; 8 | 9 | /// 10 | /// Extension to configure the MCP server with transports 11 | /// 12 | public static partial class McpServerBuilderExtensions 13 | { 14 | /// 15 | /// Adds a server transport that uses stdin/stdout for communication. 16 | /// 17 | /// The builder instance. 18 | public static IMcpServerBuilder WithStdioServerTransport(this IMcpServerBuilder builder) 19 | { 20 | Throw.IfNull(builder); 21 | 22 | builder.Services.AddSingleton(); 23 | builder.Services.AddHostedService(); 24 | return builder; 25 | } 26 | 27 | /// 28 | /// Adds a server transport that uses SSE via a HttpListener for communication. 29 | /// 30 | /// The builder instance. 31 | public static IMcpServerBuilder WithHttpListenerSseServerTransport(this IMcpServerBuilder builder) 32 | { 33 | if (builder is null) 34 | { 35 | throw new ArgumentNullException(nameof(builder)); 36 | } 37 | 38 | builder.Services.AddSingleton(); 39 | builder.Services.AddHostedService(); 40 | return builder; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/mcpdotnet/Configuration/McpServerConfig.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Configuration; 2 | 3 | /// 4 | /// Configuration for an MCP server connection. 5 | /// This is passed to the client factory to create a client for a specific server. 6 | /// 7 | public record McpServerConfig 8 | { 9 | /// 10 | /// Unique identifier for this server configuration. 11 | /// 12 | public required string Id { get; init; } 13 | 14 | /// 15 | /// Display name for the server. 16 | /// 17 | public required string Name { get; init; } 18 | 19 | /// 20 | /// The type of transport to use. 21 | /// 22 | public required string TransportType { get; init; } 23 | 24 | /// 25 | /// For stdio transport: path to the executable 26 | /// For HTTP transport: base URL of the server 27 | /// 28 | public string? Location { get; set; } 29 | 30 | /// 31 | /// Arguments (if any) to pass to the executable. 32 | /// 33 | public string[]? Arguments { get; init; } 34 | 35 | /// 36 | /// Additional transport-specific configuration. 37 | /// 38 | public Dictionary? TransportOptions { get; init; } 39 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Configuration/McpServerOptionsSetup.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using McpDotNet.Protocol.Types; 3 | using McpDotNet.Server; 4 | using Microsoft.Extensions.Options; 5 | 6 | namespace McpDotNet.Configuration; 7 | 8 | internal sealed class McpServerOptionsSetup(IOptions serverHandlers) : IConfigureOptions 9 | { 10 | public void Configure(McpServerOptions options) 11 | { 12 | if (options is null) 13 | { 14 | throw new ArgumentNullException(nameof(options)); 15 | } 16 | 17 | var assemblyName = Assembly.GetEntryAssembly()?.GetName(); 18 | options.ServerInfo = new Implementation 19 | { 20 | Name = assemblyName?.Name ?? "McpServer", 21 | Version = assemblyName?.Version?.ToString() ?? "1.0.0", 22 | }; 23 | 24 | serverHandlers.Value.OverwriteWithSetHandlers(options); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/mcpdotnet/Configuration/McpServerServiceCollectionExtension.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Configuration; 2 | using McpDotNet.Protocol.Transport; 3 | using McpDotNet.Server; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace McpDotNet; 9 | 10 | /// 11 | /// Extension to host the MCP server 12 | /// 13 | public static class McpServerServiceCollectionExtension 14 | { 15 | /// 16 | /// Adds the MCP server to the service collection with default options. 17 | /// 18 | /// 19 | /// 20 | /// 21 | public static IMcpServerBuilder AddMcpServer(this IServiceCollection services, Action? configureOptions = null) 22 | { 23 | services.AddSingleton(services => 24 | { 25 | IServerTransport serverTransport = services.GetRequiredService(); 26 | IOptions options = services.GetRequiredService>(); 27 | ILoggerFactory? loggerFactory = services.GetService(); 28 | 29 | return McpServerFactory.Create(serverTransport, options.Value, loggerFactory, services); 30 | }); 31 | 32 | services.AddOptions(); 33 | services.AddTransient, McpServerOptionsSetup>(); 34 | if (configureOptions is not null) 35 | { 36 | services.Configure(configureOptions); 37 | } 38 | 39 | return new DefaultMcpServerBuilder(services); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/mcpdotnet/Hosting/McpServerHostedService.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Server; 2 | using McpDotNet.Utils; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace McpDotNet.Hosting; 6 | 7 | /// 8 | /// Hosted service for the MCP server. 9 | /// 10 | public class McpServerHostedService : BackgroundService 11 | { 12 | private readonly IMcpServer _server; 13 | 14 | /// 15 | /// Creates a new instance of the McpServerHostedService. 16 | /// 17 | /// The MCP server instance 18 | /// 19 | public McpServerHostedService(IMcpServer server) 20 | { 21 | Throw.IfNull(server); 22 | 23 | _server = server; 24 | } 25 | 26 | /// 27 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 28 | { 29 | await _server.StartAsync(stoppingToken).ConfigureAwait(false); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/ErrorCodes.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Messages; 2 | 3 | /// 4 | /// Standard JSON-RPC error codes as defined in the MCP specification. 5 | /// 6 | internal static class ErrorCodes 7 | { 8 | /// 9 | /// Invalid JSON was received by the server. 10 | /// 11 | public const int ParseError = -32700; 12 | 13 | /// 14 | /// The JSON sent is not a valid Request object. 15 | /// 16 | public const int InvalidRequest = -32600; 17 | 18 | /// 19 | /// The method does not exist / is not available. 20 | /// 21 | public const int MethodNotFound = -32601; 22 | 23 | /// 24 | /// Invalid method parameter(s). 25 | /// 26 | public const int InvalidParams = -32602; 27 | 28 | /// 29 | /// Internal JSON-RPC error. 30 | /// 31 | public const int InternalError = -32603; 32 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/IJsonRpcMessage.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Utils.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace McpDotNet.Protocol.Messages; 5 | 6 | /// 7 | /// Base interface for all JSON-RPC messages in the MCP protocol. 8 | /// 9 | [JsonConverter(typeof(JsonRpcMessageConverter))] 10 | public interface IJsonRpcMessage 11 | { 12 | /// 13 | /// JSON-RPC protocol version. Must be "2.0". 14 | /// 15 | string JsonRpc { get; } 16 | } 17 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/IJsonRpcMessageWithId.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Messages; 2 | 3 | /// 4 | /// Base interface for JSON-RPC messages that include an ID. 5 | /// 6 | public interface IJsonRpcMessageWithId : IJsonRpcMessage 7 | { 8 | /// 9 | /// The message identifier. 10 | /// 11 | RequestId Id { get; } 12 | } 13 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/JsonRpcError.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Messages; 4 | 5 | /// 6 | /// An error response message in the JSON-RPC protocol. 7 | /// 8 | public record JsonRpcError : IJsonRpcMessageWithId 9 | { 10 | /// 11 | /// JSON-RPC protocol version. Always "2.0". 12 | /// 13 | [JsonPropertyName("jsonrpc")] 14 | public string JsonRpc { get; init; } = "2.0"; 15 | 16 | /// 17 | /// Request identifier matching the original request. 18 | /// 19 | [JsonPropertyName("id")] 20 | public required RequestId Id { get; init; } 21 | 22 | /// 23 | /// Error information. 24 | /// 25 | [JsonPropertyName("error")] 26 | public required JsonRpcErrorDetail Error { get; init; } 27 | } 28 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/JsonRpcErrorDetail.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Messages; 4 | 5 | /// 6 | /// Detailed error information for JSON-RPC error responses. 7 | /// 8 | public record JsonRpcErrorDetail 9 | { 10 | /// 11 | /// Integer error code. 12 | /// 13 | [JsonPropertyName("code")] 14 | public required int Code { get; init; } 15 | 16 | /// 17 | /// Short description of the error. 18 | /// 19 | [JsonPropertyName("message")] 20 | public required string Message { get; init; } 21 | 22 | /// 23 | /// Optional additional error data. 24 | /// 25 | [JsonPropertyName("data")] 26 | public object? Data { get; init; } 27 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/JsonRpcMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Messages; 4 | 5 | /// 6 | /// A request message in the JSON-RPC protocol. 7 | /// 8 | public record JsonRpcRequest : IJsonRpcMessageWithId 9 | { 10 | /// 11 | /// JSON-RPC protocol version. Always "2.0". 12 | /// 13 | [JsonPropertyName("jsonrpc")] 14 | public string JsonRpc { get; init; } = "2.0"; 15 | 16 | /// 17 | /// Request identifier. Must be a string or number and unique within the session. 18 | /// 19 | [JsonPropertyName("id")] 20 | public RequestId Id { get; set; } 21 | 22 | /// 23 | /// Name of the method to invoke. 24 | /// 25 | [JsonPropertyName("method")] 26 | public required string Method { get; init; } 27 | 28 | /// 29 | /// Optional parameters for the method. 30 | /// 31 | [JsonPropertyName("params")] 32 | public object? Params { get; init; } 33 | } 34 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/JsonRpcNotification.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Messages; 4 | 5 | /// 6 | /// A notification message in the JSON-RPC protocol (a request that doesn't expect a response). 7 | /// 8 | public record JsonRpcNotification : IJsonRpcMessage 9 | { 10 | /// 11 | /// JSON-RPC protocol version. Always "2.0". 12 | /// 13 | [JsonPropertyName("jsonrpc")] 14 | public string JsonRpc { get; init; } = "2.0"; 15 | 16 | /// 17 | /// Name of the notification method. 18 | /// 19 | [JsonPropertyName("method")] 20 | public required string Method { get; init; } 21 | 22 | /// 23 | /// Optional parameters for the notification. 24 | /// 25 | [JsonPropertyName("params")] 26 | public object? Params { get; init; } 27 | } 28 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/JsonRpcResponse.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Text.Json.Serialization; 3 | 4 | namespace McpDotNet.Protocol.Messages; 5 | /// 6 | /// A successful response message in the JSON-RPC protocol. 7 | /// 8 | public record JsonRpcResponse : IJsonRpcMessageWithId 9 | { 10 | /// 11 | /// JSON-RPC protocol version. Always "2.0". 12 | /// 13 | [JsonPropertyName("jsonrpc")] 14 | public string JsonRpc { get; init; } = "2.0"; 15 | 16 | /// 17 | /// Request identifier matching the original request. 18 | /// 19 | [JsonPropertyName("id")] 20 | public required RequestId Id { get; init; } 21 | 22 | /// 23 | /// The result of the method invocation. 24 | /// 25 | [JsonPropertyName("result")] 26 | public required object? Result { get; init; } 27 | } 28 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/NotificationMethods.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Messages; 2 | 3 | /// 4 | /// A class containing constants for notification methods. 5 | /// 6 | public static class NotificationMethods 7 | { 8 | /// 9 | /// Sent by the server when the list of tools changes. 10 | /// 11 | public const string ToolListChangedNotification = "notifications/tools/list_changed"; 12 | 13 | /// 14 | /// Sent by the server when the list of prompts changes. 15 | /// 16 | public const string PromptsListChanged = "notifications/prompts/list_changed"; 17 | 18 | /// 19 | /// Sent by the server when the list of resources changes. 20 | /// 21 | public const string ResourceListChangedNotification = "notifications/resources/list_changed"; 22 | 23 | /// 24 | /// Sent by the server when a resource is updated. 25 | /// 26 | public const string ResourceUpdatedNotification = "notifications/resources/updated"; 27 | 28 | /// 29 | /// Sent by the client when roots have been updated. 30 | /// 31 | public const string RootsUpdatedNotification = "notifications/roots/list_changed"; 32 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/OperationNames.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Messages; 2 | 3 | /// Provides names of standard operations for use with registering handlers. 4 | /// 5 | /// These values should not be inspected or relied on for their exact values. 6 | /// They serve only as opaque keys. They will be stable for the lifetime of a process 7 | /// but may change between versions of this library. 8 | /// 9 | public static class OperationNames 10 | { 11 | /// Gets the name of the sampling operation. 12 | public static string Sampling { get; } = "operation/sampling"; 13 | 14 | /// Gets the name of the roots operation. 15 | public static string Roots { get; } = "operation/roots"; 16 | 17 | /// Gets the name of the list tools operation. 18 | public static string ListTools { get; } = "operation/listTools"; 19 | 20 | /// Gets the name of the call tool operation. 21 | public static string CallTool { get; } = "operation/callTool"; 22 | 23 | /// Gets the name of the list prompts operation. 24 | public static string ListPrompts { get; } = "operation/listPrompts"; 25 | 26 | /// Gets the name of the get prompt operation. 27 | public static string GetPrompt { get; } = "operation/getPrompt"; 28 | 29 | /// Gets the name of the list resources operation. 30 | public static string ListResources { get; } = "operation/listResources"; 31 | 32 | /// Gets the name of the read resource operation. 33 | public static string ReadResource { get; } = "operation/readResource"; 34 | 35 | /// Gets the name of the get completion operation. 36 | public static string GetCompletion { get; } = "operation/getCompletion"; 37 | 38 | /// Gets the name of the subscribe to resources operation. 39 | public static string SubscribeToResources { get; } = "operation/subscribeToResources"; 40 | 41 | /// Gets the name of the subscribe to resources operation. 42 | public static string UnsubscribeFromResources { get; } = "operation/unsubscribeFromResources"; 43 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/PaginatedResult.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Messages; 2 | 3 | /// 4 | /// A paginated result payload. 5 | /// 6 | public class PaginatedResult 7 | { 8 | /// 9 | /// An opaque token representing the pagination position after the last returned result.\nIf present, there may be more results available. 10 | /// 11 | public string? NextCursor { get; set; } 12 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/RequestId.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Messages; 4 | 5 | /// 6 | /// Represents a JSON-RPC request identifier which can be either a string or a number. 7 | /// 8 | [JsonConverter(typeof(RequestIdConverter))] 9 | public readonly struct RequestId : IEquatable 10 | { 11 | private readonly object _value; 12 | 13 | private RequestId(object value) 14 | { 15 | _value = value; 16 | } 17 | 18 | /// 19 | /// Creates a new RequestId from a string. 20 | /// 21 | /// The Id 22 | /// Wrapped Id object 23 | public static RequestId FromString(string value) => new(value); 24 | 25 | /// 26 | /// Creates a new RequestId from a number. 27 | /// 28 | /// The Id 29 | /// Wrapped Id object 30 | public static RequestId FromNumber(long value) => new(value); 31 | 32 | /// 33 | /// Checks if the RequestId is a string. 34 | /// 35 | public bool IsString => _value is string; 36 | 37 | /// 38 | /// Checks if the RequestId is a number. 39 | /// 40 | public bool IsNumber => _value is long; 41 | 42 | /// 43 | /// Checks if the request id is valid (has a value) 44 | /// 45 | public bool IsValid => _value != null; 46 | 47 | /// 48 | /// Gets the RequestId as a string. 49 | /// 50 | /// Thrown if the RequestId is not a string" 51 | public string AsString => _value as string ?? throw new InvalidOperationException("RequestId is not a string"); 52 | 53 | /// 54 | /// Gets the RequestId as a number. 55 | /// 56 | /// Thrown if the RequestId is not a number"" 57 | public long AsNumber => _value is long number ? number : throw new InvalidOperationException("RequestId is not a number"); 58 | 59 | /// 60 | /// Returns the string representation of the RequestId. Will box the value if it is a number. 61 | /// 62 | public override string ToString() => _value.ToString() ?? ""; 63 | 64 | /// 65 | /// Compares this RequestId to another RequestId. 66 | /// 67 | public bool Equals(RequestId other) => _value.Equals(other._value); 68 | 69 | /// 70 | public override bool Equals(object? obj) => obj is RequestId other && Equals(other); 71 | 72 | /// 73 | public override int GetHashCode() => _value.GetHashCode(); 74 | 75 | /// 76 | /// Compares two RequestIds for equality. 77 | /// 78 | public static bool operator ==(RequestId left, RequestId right) => left.Equals(right); 79 | 80 | /// 81 | /// Compares two RequestIds for inequality. 82 | /// 83 | public static bool operator !=(RequestId left, RequestId right) => !left.Equals(right); 84 | } 85 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Messages/RequestIdConverter.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Utils; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace McpDotNet.Protocol.Messages; 6 | 7 | /// 8 | /// JSON converter for RequestId that handles both string and number values. 9 | /// 10 | public class RequestIdConverter : JsonConverter 11 | { 12 | /// 13 | public override RequestId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 14 | { 15 | return reader.TokenType switch 16 | { 17 | JsonTokenType.String => RequestId.FromString(reader.GetString()!), 18 | JsonTokenType.Number => RequestId.FromNumber(reader.GetInt64()), 19 | _ => throw new JsonException("RequestId must be either a string or a number"), 20 | }; 21 | } 22 | 23 | /// 24 | public override void Write(Utf8JsonWriter writer, RequestId value, JsonSerializerOptions options) 25 | { 26 | Throw.IfNull(writer); 27 | 28 | if (value.IsString) 29 | { 30 | writer.WriteStringValue(value.AsString); 31 | } 32 | else 33 | { 34 | writer.WriteNumberValue(value.AsNumber); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/IClientTransport.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Transport; 2 | 3 | /// 4 | /// Represents a transport mechanism for MCP communication (from the client). 5 | /// 6 | public interface IClientTransport : ITransport 7 | { 8 | /// 9 | /// Establishes the transport connection. 10 | /// 11 | /// Token to cancel the operation. 12 | Task ConnectAsync(CancellationToken cancellationToken = default); 13 | } 14 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/IServerTransport.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Transport; 2 | 3 | /// 4 | /// Represents a transport mechanism for MCP communication (from the server). 5 | /// 6 | public interface IServerTransport : ITransport 7 | { 8 | /// 9 | /// Starts listening for incoming messages. 10 | /// 11 | /// Token to cancel the operation. 12 | Task StartListeningAsync(CancellationToken cancellationToken = default); 13 | } 14 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/ITransport.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Channels; 2 | using McpDotNet.Protocol.Messages; 3 | 4 | namespace McpDotNet.Protocol.Transport; 5 | 6 | /// 7 | /// Represents a transport mechanism for MCP communication. 8 | /// 9 | public interface ITransport : IAsyncDisposable 10 | { 11 | /// 12 | /// Gets whether the transport is currently connected. 13 | /// 14 | bool IsConnected { get; } 15 | 16 | /// 17 | /// Channel for receiving messages from the transport. 18 | /// 19 | ChannelReader MessageReader { get; } 20 | 21 | /// 22 | /// Sends a message through the transport. 23 | /// 24 | /// The message to send. 25 | /// Token to cancel the operation. 26 | Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default); 27 | } 28 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/McpTransportException.cs: -------------------------------------------------------------------------------- 1 | // Protocol/Transport/IMcpTransport.cs 2 | namespace McpDotNet.Protocol.Transport; 3 | 4 | // Protocol/Transport/McpTransportException.cs 5 | 6 | /// 7 | /// Represents errors that occur in MCP transport operations. 8 | /// 9 | public class McpTransportException : Exception 10 | { 11 | /// 12 | /// Initializes a new instance of the McpTransportException class. 13 | /// 14 | public McpTransportException() 15 | { 16 | } 17 | 18 | /// 19 | /// Initializes a new instance of the McpTransportException class with a specified error message. 20 | /// 21 | /// The message that describes the error. 22 | public McpTransportException(string message) 23 | : base(message) 24 | { 25 | } 26 | 27 | /// 28 | /// Initializes a new instance of the McpTransportException class with a specified error message 29 | /// and a reference to the inner exception that is the cause of this exception. 30 | /// 31 | /// The message that describes the error. 32 | /// The exception that is the cause of the current exception. 33 | public McpTransportException(string message, Exception innerException) 34 | : base(message, innerException) 35 | { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/SseClientTransportOptions.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Transport; 2 | 3 | /// 4 | /// Options for configuring the SSE transport. 5 | /// 6 | public record SseClientTransportOptions 7 | { 8 | /// 9 | /// Timeout for initial connection and endpoint event. 10 | /// 11 | public TimeSpan ConnectionTimeout { get; init; } = TimeSpan.FromSeconds(30); 12 | 13 | /// 14 | /// Number of reconnection attempts for SSE connection. 15 | /// 16 | public int MaxReconnectAttempts { get; init; } = 3; 17 | 18 | /// 19 | /// Delay between reconnection attempts. 20 | /// 21 | public TimeSpan ReconnectDelay { get; init; } = TimeSpan.FromSeconds(5); 22 | 23 | /// 24 | /// Headers to include in HTTP requests. 25 | /// 26 | public Dictionary? AdditionalHeaders { get; init; } 27 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/StdioClientTransportOptions.cs: -------------------------------------------------------------------------------- 1 | // Protocol/Transport/StdioTransport.cs 2 | namespace McpDotNet.Protocol.Transport; 3 | 4 | /// 5 | /// Represents configuration options for the stdio transport. 6 | /// 7 | public record StdioClientTransportOptions 8 | { 9 | /// 10 | /// The default timeout to wait for the server to shut down gracefully. 11 | /// 12 | public static readonly TimeSpan DefaultShutdownTimeout = TimeSpan.FromSeconds(5); 13 | 14 | /// 15 | /// The command to execute to start the server process. 16 | /// 17 | public required string Command { get; set; } 18 | 19 | /// 20 | /// Arguments to pass to the server process. 21 | /// 22 | public string? Arguments { get; set; } 23 | 24 | /// 25 | /// The working directory for the server process. 26 | /// 27 | public string? WorkingDirectory { get; set; } 28 | 29 | /// 30 | /// Environment variables to set for the server process. 31 | /// 32 | public Dictionary? EnvironmentVariables { get; set; } 33 | 34 | /// 35 | /// The timeout to wait for the server to shut down gracefully. 36 | /// 37 | public TimeSpan ShutdownTimeout { get; init; } = DefaultShutdownTimeout; 38 | } 39 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/TransportBase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Channels; 2 | using Microsoft.Extensions.Logging; 3 | using McpDotNet.Logging; 4 | using McpDotNet.Protocol.Messages; 5 | using Microsoft.Extensions.Logging.Abstractions; 6 | 7 | namespace McpDotNet.Protocol.Transport; 8 | 9 | /// 10 | /// Base class for implementing MCP transports with common functionality. 11 | /// 12 | public abstract class TransportBase : ITransport 13 | { 14 | private readonly Channel _messageChannel; 15 | private readonly ILogger _logger; 16 | private bool _isConnected; 17 | 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | protected TransportBase(ILoggerFactory? loggerFactory) 22 | { 23 | // Unbounded channel to prevent blocking on writes 24 | _messageChannel = Channel.CreateUnbounded(new UnboundedChannelOptions 25 | { 26 | SingleReader = true, 27 | SingleWriter = true, 28 | }); 29 | _logger = (ILogger?)loggerFactory?.CreateLogger() ?? NullLogger.Instance; 30 | } 31 | 32 | /// 33 | public bool IsConnected => _isConnected; 34 | 35 | /// 36 | public ChannelReader MessageReader => _messageChannel.Reader; 37 | 38 | /// 39 | public abstract Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default); 40 | 41 | /// 42 | public abstract ValueTask DisposeAsync(); 43 | 44 | /// 45 | /// Writes a message to the message channel. 46 | /// 47 | /// The message to write. 48 | /// Token to cancel the operation. 49 | protected async Task WriteMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default) 50 | { 51 | if (!_isConnected) 52 | { 53 | throw new McpTransportException("Transport is not connected"); 54 | } 55 | 56 | _logger.TransportWritingMessageToChannel(message); 57 | await _messageChannel.Writer.WriteAsync(message, cancellationToken).ConfigureAwait(false); 58 | _logger.TransportMessageWrittenToChannel(); 59 | } 60 | 61 | /// 62 | /// Sets the connected state of the transport. 63 | /// 64 | /// Whether the transport is connected. 65 | protected void SetConnected(bool isConnected) 66 | { 67 | if (_isConnected == isConnected) 68 | { 69 | return; 70 | } 71 | 72 | _isConnected = isConnected; 73 | if (!isConnected) 74 | { 75 | _messageChannel.Writer.Complete(); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Transport/TransportTypes.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Transport; 2 | 3 | /// 4 | /// List all transport types 5 | /// 6 | public static class TransportTypes 7 | { 8 | /// 9 | /// The name of the Standard IO transport. 10 | /// 11 | public const string StdIo = "stdio"; 12 | 13 | /// 14 | /// The name of the ServerSideEvents transport. 15 | /// 16 | public const string Sse = "sse"; 17 | } 18 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Annotations.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents annotations that can be attached to content. 7 | /// See the schema for details 8 | /// 9 | public record Annotations 10 | { 11 | /// 12 | /// Describes who the intended customer of this object or data is. 13 | /// 14 | [JsonPropertyName("audience")] 15 | public Role[]? Audience { get; init; } 16 | 17 | /// 18 | /// Describes how important this data is for operating the server (0 to 1). 19 | /// 20 | [JsonPropertyName("priority")] 21 | public float? Priority { get; init; } 22 | } 23 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Argument.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Used for completion requests to provide additional context for the completion options. 7 | /// See the schema for details 8 | /// 9 | public class Argument 10 | { 11 | /// 12 | /// The name of the argument. 13 | /// 14 | [JsonPropertyName("name")] 15 | public string Name { get; set; } = string.Empty; 16 | 17 | /// 18 | /// The value of the argument to use for completion matching. 19 | /// 20 | [JsonPropertyName("value")] 21 | public string Value { get; set; } = string.Empty; 22 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/CallToolRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Used by the client to invoke a tool provided by the server. 5 | /// See the schema for details 6 | /// 7 | public class CallToolRequestParams 8 | { 9 | /// 10 | /// Tool name. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("name")] 13 | public required string Name { get; init; } 14 | 15 | /// 16 | /// Optional arguments to pass to the tool. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("arguments")] 19 | public Dictionary? Arguments { get; init; } 20 | } 21 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/CallToolResponse.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// The server's response to a tool call. 5 | /// 6 | /// Any errors that originate from the tool SHOULD be reported inside the result 7 | /// object, with `isError` set to true, _not_ as an MCP protocol-level error 8 | /// response. Otherwise, the LLM would not be able to see that an error occurred 9 | /// and self-correct. 10 | /// 11 | /// However, any errors in _finding_ the tool, an error indicating that the 12 | /// server does not support tool calls, or any other exceptional conditions, 13 | /// should be reported as an MCP error response. 14 | /// See the schema for details 15 | /// 16 | public class CallToolResponse 17 | { 18 | /// 19 | /// The server's response to a tools/call request from the client. 20 | /// 21 | [System.Text.Json.Serialization.JsonPropertyName("content")] 22 | public List Content { get; set; } = []; 23 | 24 | /// 25 | /// Whether the tool call was unsuccessful. If true, the call was unsuccessful. 26 | /// 27 | [System.Text.Json.Serialization.JsonPropertyName("isError")] 28 | public bool IsError { get; set; } 29 | } 30 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/CompleteRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// A request from the client to the server, to ask for completion options. 5 | /// See the schema for details 6 | /// 7 | public class CompleteRequestParams 8 | { 9 | /// 10 | /// The reference's information 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("ref")] 13 | public required Reference Ref { get; init; } 14 | 15 | /// 16 | /// The argument's information 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("argument")] 19 | public required Argument Argument { get; init; } 20 | } 21 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/CompleteResult.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// The server's response to a completion/complete request 7 | /// See the schema for details 8 | /// 9 | public class CompleteResult 10 | { 11 | /// 12 | /// The completion object containing the completion values. 13 | /// 14 | [JsonPropertyName("completion")] 15 | public Completion Completion { get; set; } = new Completion(); 16 | } 17 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Completion.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents a completion object in the server's response 7 | /// See the schema for details 8 | /// 9 | public class Completion 10 | { 11 | /// 12 | /// An array of completion values. Must not exceed 100 items. 13 | /// 14 | [JsonPropertyName("values")] 15 | public string[] Values { get; set; } = []; 16 | 17 | /// 18 | /// The total number of completion options available. This can exceed the number of values actually sent in the response. 19 | /// 20 | [JsonPropertyName("total")] 21 | public int? Total { get; set; } 22 | 23 | /// 24 | /// Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. 25 | /// 26 | [JsonPropertyName("hasMore")] 27 | public bool? HasMore { get; set; } 28 | } 29 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Content.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents the content of a tool response. 7 | /// See the schema for details 8 | /// There are multiple subtypes of content, depending on the "type" field, these are represented as separate classes. 9 | /// 10 | public class Content 11 | { 12 | /// 13 | /// The type of content. This determines the structure of the content object. Can be "image", "text", "resource". 14 | /// 15 | 16 | [JsonPropertyName("type")] 17 | public string Type { get; set; } = string.Empty; 18 | 19 | /// 20 | /// The text content of the message. 21 | /// 22 | [JsonPropertyName("text")] 23 | public string? Text { get; set; } 24 | 25 | /// 26 | /// The base64-encoded image data. 27 | /// 28 | [JsonPropertyName("data")] 29 | public string? Data { get; set; } 30 | 31 | /// 32 | /// The MIME type of the image. 33 | /// 34 | [JsonPropertyName("mimeType")] 35 | public string? MimeType { get; set; } 36 | 37 | /// 38 | /// The resource content of the message (if embedded). 39 | /// 40 | [JsonPropertyName("resource")] 41 | public ResourceContents? Resource { get; set; } 42 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ContextInclusion.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. 5 | /// See the schema for details 6 | /// 7 | public enum ContextInclusion 8 | { 9 | /// 10 | /// No context should be included. 11 | /// 12 | None, 13 | 14 | /// 15 | /// Include context from the server that sent the request. 16 | /// 17 | ThisServer, 18 | 19 | /// 20 | /// Include context from all servers that the client is connected to. 21 | /// 22 | AllServers 23 | } 24 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/CreateMessageRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// A request from the server to sample an LLM via the client. 5 | /// The client has full discretion over which model to select. 6 | /// The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. 7 | /// 8 | /// While these align with the protocol specification, 9 | /// clients have full discretion over model selection and should inform users before sampling. 10 | /// See the schema for details 11 | /// 12 | public class CreateMessageRequestParams 13 | { 14 | /// 15 | /// A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The client MAY ignore this request. 16 | /// 17 | [System.Text.Json.Serialization.JsonPropertyName("includeContext")] 18 | public ContextInclusion? IncludeContext { get; init; } 19 | 20 | /// 21 | /// The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested. 22 | /// 23 | [System.Text.Json.Serialization.JsonPropertyName("maxTokens")] 24 | public int? MaxTokens { get; init; } 25 | 26 | /// 27 | /// Messages requested by the server to be included in the prompt. 28 | /// 29 | [System.Text.Json.Serialization.JsonPropertyName("messages")] 30 | public required IReadOnlyList Messages { get; init; } 31 | 32 | /// 33 | /// Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific. 34 | /// 35 | [System.Text.Json.Serialization.JsonPropertyName("metadata")] 36 | public object? Metadata { get; init; } 37 | 38 | /// 39 | /// The server's preferences for which model to select. The client MAY ignore these preferences. 40 | /// 41 | [System.Text.Json.Serialization.JsonPropertyName("modelPreferences")] 42 | public ModelPreferences? ModelPreferences { get; init; } 43 | 44 | /// 45 | /// Optional stop sequences that the server wants to use for sampling. 46 | /// 47 | [System.Text.Json.Serialization.JsonPropertyName("stopSequences")] 48 | public IReadOnlyList? StopSequences { get; init; } 49 | 50 | /// 51 | /// An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt. 52 | /// 53 | [System.Text.Json.Serialization.JsonPropertyName("systemPrompt")] 54 | public string? SystemPrompt { get; init; } 55 | 56 | /// 57 | /// The temperature to use for sampling, as requested by the server. 58 | /// 59 | [System.Text.Json.Serialization.JsonPropertyName("temperature")] 60 | public float? Temperature { get; init; } 61 | } 62 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/CreateMessageResult.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// The client's response to a sampling/create_message request from the server. 7 | /// The client should inform the user before returning the sampled message, to allow them to inspect the response (human in the loop) 8 | /// and decide whether to allow the server to see it. 9 | /// See the schema for details 10 | /// 11 | public class CreateMessageResult 12 | { 13 | /// 14 | /// Text or image content of the message. 15 | /// 16 | [JsonPropertyName("content")] 17 | public required Content Content { get; init; } 18 | 19 | /// 20 | /// The name of the model that generated the message. 21 | /// 22 | [JsonPropertyName("model")] 23 | public required string Model { get; init; } 24 | 25 | /// 26 | /// The reason why sampling stopped, if known. 27 | /// 28 | [JsonPropertyName("stopReason")] 29 | public string? StopReason { get; init; } 30 | 31 | /// 32 | /// The role of the user who generated the message. 33 | /// 34 | [JsonPropertyName("role")] 35 | public required string Role { get; init; } 36 | } 37 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/EmptyResult.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// An empty result object. 5 | /// See the schema for details 6 | /// 7 | public class EmptyResult 8 | { 9 | 10 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/GetPromptRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Used by the client to get a prompt provided by the server. 5 | /// See the schema for details 6 | /// 7 | public class GetPromptRequestParams 8 | { 9 | /// 10 | /// he name of the prompt or prompt template. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("name")] 13 | public required string Name { get; init; } 14 | 15 | /// 16 | /// Arguments to use for templating the prompt. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("arguments")] 19 | public Dictionary? Arguments { get; init; } 20 | } 21 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/GetPromptResult.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// The server's response to a prompts/get request from the client. 5 | /// See the schema for details 6 | /// 7 | public class GetPromptResult 8 | { 9 | /// 10 | /// An optional description for the prompt. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("description")] 13 | public string? Description { get; set; } 14 | 15 | /// 16 | /// The prompt or prompt template that the server offers. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("messages")] 19 | public List Messages { get; set; } = []; 20 | } 21 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Implementation.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Describes the name and version of an MCP implementation. 7 | /// See the schema for details 8 | /// 9 | public record Implementation 10 | { 11 | /// 12 | /// Name of the implementation. 13 | /// 14 | [JsonPropertyName("name")] 15 | public required string Name { get; init; } 16 | 17 | /// 18 | /// Version of the implementation. 19 | /// 20 | [JsonPropertyName("version")] 21 | public required string Version { get; init; } 22 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/InitializeRequestParams.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Parameters for an initialization request sent to the server. 7 | /// See the schema for details 8 | /// 9 | public record InitializeRequestParams 10 | { 11 | /// 12 | /// The version of the Model Context Protocol that the client wants to use. 13 | /// 14 | [JsonPropertyName("protocolVersion")] 15 | 16 | public required string ProtocolVersion { get; init; } 17 | /// 18 | /// The client's capabilities. 19 | /// 20 | [JsonPropertyName("capabilities")] 21 | public ClientCapabilities? Capabilities { get; init; } 22 | 23 | /// 24 | /// Information about the client implementation. 25 | /// 26 | [JsonPropertyName("clientInfo")] 27 | public required Implementation ClientInfo { get; init; } 28 | } 29 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/InitializeResult.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Result of the initialization request sent to the server. 7 | /// See the schema for details 8 | /// 9 | public record InitializeResult 10 | { 11 | /// 12 | /// The version of the Model Context Protocol that the server wants to use. 13 | /// 14 | [JsonPropertyName("protocolVersion")] 15 | public required string ProtocolVersion { get; init; } 16 | 17 | /// 18 | /// The server's capabilities. 19 | /// 20 | [JsonPropertyName("capabilities")] 21 | public required ServerCapabilities Capabilities { get; init; } 22 | 23 | /// 24 | /// Information about the server implementation. 25 | /// 26 | [JsonPropertyName("serverInfo")] 27 | public required Implementation ServerInfo { get; init; } 28 | 29 | /// 30 | /// Optional instructions for using the server and its features. 31 | /// 32 | [JsonPropertyName("instructions")] 33 | public string? Instructions { get; init; } 34 | } 35 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/JsonSchema.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Represents a JSON schema for a tool's input arguments. 5 | /// See the schema for details 6 | /// 7 | public class JsonSchema 8 | { 9 | /// 10 | /// The type of the schema, should be "object". 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("type")] 13 | public string Type { get; set; } = "object"; 14 | 15 | /// 16 | /// Map of property names to property definitions. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("properties")] 19 | public Dictionary? Properties { get; set; } 20 | 21 | /// 22 | /// List of required property names. 23 | /// 24 | [System.Text.Json.Serialization.JsonPropertyName("required")] 25 | public List? Required { get; set; } 26 | } 27 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/JsonSchemaProperty.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Represents a property in a JSON schema. 5 | /// See the schema for details 6 | /// 7 | public class JsonSchemaProperty 8 | { 9 | /// 10 | /// The type of the property. Should be a JSON Schema type and is required. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("type")] 13 | public string Type { get; set; } = string.Empty; 14 | 15 | /// 16 | /// A human-readable description of the property. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("description")] 19 | public string? Description { get; set; } = string.Empty; 20 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListPromptsRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the client to request a list of prompts and prompt templates the server has. 5 | /// See the schema for details 6 | /// 7 | public class ListPromptsRequestParams 8 | { 9 | /// 10 | /// An opaque token representing the current pagination position. 11 | /// If provided, the server should return results starting after this cursor. 12 | /// 13 | [System.Text.Json.Serialization.JsonPropertyName("cursor")] 14 | public string? Cursor { get; init; } 15 | } 16 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListPromptsResult.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// The server's response to a prompts/list request from the client. 7 | /// See the schema for details 8 | /// 9 | public class ListPromptsResult : PaginatedResult 10 | { 11 | /// 12 | /// A list of prompts or prompt templates that the server offers. 13 | /// 14 | [System.Text.Json.Serialization.JsonPropertyName("prompts")] 15 | public List Prompts { get; set; } = []; 16 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListResourcesRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the client to request a list of resources the server has. 5 | /// See the schema for details 6 | /// 7 | public class ListResourcesRequestParams 8 | { 9 | /// 10 | /// An opaque token representing the current pagination position. 11 | /// If provided, the server should return results starting after this cursor. 12 | /// 13 | [System.Text.Json.Serialization.JsonPropertyName("cursor")] 14 | public string? Cursor { get; init; } 15 | } 16 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListResourcesResult.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// The server's response to a resources/list request from the client. 7 | /// See the schema for details 8 | /// 9 | public class ListResourcesResult : PaginatedResult 10 | { 11 | /// 12 | /// A list of resources that the server offers. 13 | /// 14 | [System.Text.Json.Serialization.JsonPropertyName("resources")] 15 | public List Resources { get; set; } = []; 16 | } 17 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListRootsRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// A request from the server to get a list of root URIs from the client. 5 | /// See the schema for details 6 | /// 7 | public class ListRootsRequestParams 8 | { 9 | /// 10 | /// Optional progress token for out-of-band progress notifications. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("progressToken")] 13 | public string? ProgressToken { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListRootsResult.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// The client's response to a roots/list request from the server. 5 | /// See the schema for details 6 | /// 7 | public class ListRootsResult 8 | { 9 | /// 10 | /// Additional metadata for the result. Reserved by the protocol for future use. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("meta")] 13 | public object? Meta { get; init; } 14 | 15 | /// 16 | /// The list of root URIs provided by the client. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("roots")] 19 | public required IReadOnlyList Roots { get; init; } 20 | } 21 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListToolsRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the client to request a list of tools the server has. 5 | /// See the schema for details 6 | /// 7 | public class ListToolsRequestParams 8 | { 9 | /// 10 | /// An opaque token representing the current pagination position. 11 | /// If provided, the server should return results starting after this cursor. 12 | /// 13 | [System.Text.Json.Serialization.JsonPropertyName("cursor")] 14 | public string? Cursor { get; init; } 15 | } 16 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ListToolsResult.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// A response to a request to list the tools available on the server. 7 | /// See the schema for details 8 | /// 9 | public class ListToolsResult : PaginatedResult 10 | { 11 | /// 12 | /// The server's response to a tools/list request from the client. 13 | /// 14 | [System.Text.Json.Serialization.JsonPropertyName("tools")] 15 | public List Tools { get; set; } = []; 16 | } 17 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ModelHint.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Hints to use for model selection. 5 | /// Keys not declared here are currently left unspecified by the spec and are up 6 | /// to the client to interpret. 7 | /// See the schema for details 8 | /// 9 | public class ModelHint 10 | { 11 | /// 12 | /// A hint for a model name. 13 | /// 14 | /// The client SHOULD treat this as a substring of a model name; for example: 15 | /// - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022` 16 | /// - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc. 17 | /// - `claude` should match any Claude model 18 | /// 19 | /// The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example: 20 | /// - `gemini-1.5-flash` could match `claude-3-haiku-20240307` 21 | /// 22 | [System.Text.Json.Serialization.JsonPropertyName("name")] 23 | public string? Name { get; init; } 24 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ModelPreferences.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// The server's preferences for model selection, requested of the client during sampling. 5 | /// Because LLMs can vary along multiple dimensions, choosing the \"best\" model is 6 | /// rarely straightforward. Different models excel in different areas—some are 7 | /// faster but less capable, others are more capable but more expensive, and so 8 | /// on. This interface allows servers to express their priorities across multiple 9 | /// dimensions to help clients make an appropriate selection for their use case. 10 | /// 11 | /// These preferences are always advisory. The client MAY ignore them. It is also 12 | /// up to the client to decide how to interpret these preferences and how to 13 | /// balance them against other considerations. 14 | /// See the schema for details 15 | /// 16 | public class ModelPreferences 17 | { 18 | /// 19 | /// How much to prioritize cost when selecting a model. A value of 0 means cost\nis not important, while a value of 1 means cost is the most important 20 | /// factor. 21 | /// 22 | [System.Text.Json.Serialization.JsonPropertyName("costPriority")] 23 | public float? CostPriority { get; init; } 24 | 25 | /// 26 | /// Optional hints to use for model selection. 27 | /// 28 | /// If multiple hints are specified, the client MUST evaluate them in order 29 | /// (such that the first match is taken). 30 | /// 31 | /// The client SHOULD prioritize these hints over the numeric priorities, but 32 | /// MAY still use the priorities to select from ambiguous matches. 33 | /// 34 | [System.Text.Json.Serialization.JsonPropertyName("hints")] 35 | public IReadOnlyList? Hints { get; init; } 36 | 37 | /// 38 | /// How much to prioritize sampling speed (latency) when selecting a model. A 39 | /// value of 0 means speed is not important, while a value of 1 means speed is 40 | /// the most important factor. 41 | /// 42 | [System.Text.Json.Serialization.JsonPropertyName("speedPriority")] 43 | public float? SpeedPriority { get; init; } 44 | 45 | /// 46 | /// How much to prioritize intelligence and capabilities when selecting a 47 | /// model. A value of 0 means intelligence is not important, while a value of 1 48 | /// means intelligence is the most important factor. 49 | /// 50 | [System.Text.Json.Serialization.JsonPropertyName("intelligencePriority")] 51 | public float? IntelligencePriority { get; init; } 52 | 53 | /// 54 | /// Validates the model preferences. 55 | /// 56 | /// Error message if object isn't valid 57 | /// True if valid, false if invalid 58 | public bool Validate(out string errorMessage) 59 | { 60 | bool valid = true; 61 | List errors = []; 62 | 63 | if (CostPriority is < 0 or > 1) 64 | { 65 | errors.Add("CostPriority must be between 0 and 1"); 66 | valid = false; 67 | } 68 | 69 | if (SpeedPriority is < 0 or > 1) 70 | { 71 | errors.Add("SpeedPriority must be between 0 and 1"); 72 | valid = false; 73 | } 74 | 75 | if (IntelligencePriority is < 0 or > 1) 76 | { 77 | errors.Add("IntelligencePriority must be between 0 and 1"); 78 | valid = false; 79 | } 80 | 81 | if (!valid) 82 | { 83 | errorMessage = string.Join(", ", errors); 84 | } 85 | else 86 | { 87 | errorMessage = ""; 88 | } 89 | 90 | return valid; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/PingResult.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Dummy result for the ping request. 5 | /// See the schema for details 6 | /// 7 | public record PingResult 8 | { 9 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Prompt.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// A prompt or prompt template that the server offers. 5 | /// See the schema for details 6 | /// 7 | public class Prompt 8 | { 9 | /// 10 | /// A list of arguments to use for templating the prompt. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("arguments")] 13 | public List? Arguments { get; set; } 14 | 15 | /// 16 | /// An optional description of what this prompt provides 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("description")] 19 | public string? Description { get; set; } 20 | 21 | /// 22 | /// The name of the prompt or prompt template. 23 | /// 24 | [System.Text.Json.Serialization.JsonPropertyName("name")] 25 | public string Name { get; set; } = string.Empty; 26 | } 27 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/PromptArgument.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Describes an argument that a prompt can accept. 5 | /// See the schema for details 6 | /// 7 | public class PromptArgument 8 | { 9 | /// 10 | /// The name of the argument. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("name")] 13 | public string Name { get; set; } = string.Empty; 14 | 15 | /// 16 | /// A human-readable description of the argument. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("description")] 19 | public string? Description { get; set; } = string.Empty; 20 | 21 | /// 22 | /// Whether this argument must be provided. 23 | /// 24 | [System.Text.Json.Serialization.JsonPropertyName("required")] 25 | public bool? Required { get; set; } 26 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/PromptMessage.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Describes a message returned as part of a prompt. 5 | /// 6 | /// This is similar to `SamplingMessage`, but also supports the embedding of 7 | /// resources from the MCP server. 8 | /// See the schema for details 9 | /// 10 | public class PromptMessage 11 | { 12 | /// 13 | /// The content of the message. Any of TextContent, ImageContent, EmbeddedResource. 14 | /// 15 | [System.Text.Json.Serialization.JsonPropertyName("content")] 16 | public Content Content { get; set; } = new(); 17 | 18 | /// 19 | /// The role of the message ("user" or "assistant"). 20 | /// 21 | [System.Text.Json.Serialization.JsonPropertyName("role")] 22 | public Role Role { get; set; } = new(); 23 | } 24 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ReadResourceRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the client to the server, to read a specific resource URI. 5 | /// See the schema for details 6 | /// 7 | public class ReadResourceRequestParams 8 | { 9 | /// 10 | /// The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("uri")] 13 | public string? Uri { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ReadResourceResult.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// The server's response to a resources/read request from the client. 5 | /// See the schema for details 6 | /// 7 | public class ReadResourceResult 8 | { 9 | /// 10 | /// A list of ResourceContents that this resource contains. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("contents")] 13 | public List Contents { get; set; } = []; 14 | } 15 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Reference.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace McpDotNet.Protocol.Types; 5 | 6 | /// 7 | /// Represents a reference to a resource or prompt. Umbrella type for both ResourceReference and PromptReference from the spec schema. 8 | /// See the schema for details 9 | /// 10 | public class Reference 11 | { 12 | /// 13 | /// The type of content. Can be ref/resource or ref/prompt. 14 | /// 15 | [JsonPropertyName("type")] 16 | public string Type { get; set; } = string.Empty; 17 | 18 | /// 19 | /// The URI or URI template of the resource. 20 | /// 21 | [JsonPropertyName("uri")] 22 | public string? Uri { get; set; } 23 | 24 | /// 25 | /// The name of the prompt or prompt template. 26 | /// 27 | [JsonPropertyName("name")] 28 | public string? Name { get; set; } 29 | 30 | /// 31 | /// Returns a string representation of the reference. 32 | /// 33 | public override string ToString() 34 | { 35 | return $"\"{Type}\": \"{Uri ?? Name}\""; 36 | } 37 | 38 | /// 39 | /// Validates the reference object. 40 | /// 41 | public bool Validate([NotNullWhen(false)] out string? validationMessage) 42 | { 43 | if (Type == "ref/resource") 44 | { 45 | if (string.IsNullOrEmpty(Uri)) 46 | { 47 | validationMessage = "Uri is required for ref/resource"; 48 | return false; 49 | } 50 | } 51 | else if (Type == "ref/prompt") 52 | { 53 | if (string.IsNullOrEmpty(Name)) 54 | { 55 | validationMessage = "Name is required for ref/prompt"; 56 | return false; 57 | } 58 | } 59 | else 60 | { 61 | validationMessage = $"Unknown reference type: {Type}"; 62 | return false; 63 | } 64 | 65 | validationMessage = null; 66 | return true; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ResourceContents.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents the content of a resource. 7 | /// See the schema for details 8 | /// 9 | public class ResourceContents 10 | { 11 | /// 12 | /// The URI of the resource. 13 | /// 14 | [JsonPropertyName("uri")] 15 | public string Uri { get; set; } = string.Empty; 16 | 17 | /// 18 | /// The type of content. 19 | /// 20 | [JsonPropertyName("mimeType")] 21 | public string? MimeType { get; set; } 22 | 23 | /// 24 | /// The text content of the resource. 25 | /// 26 | [JsonPropertyName("text")] 27 | public string? Text { get; set; } 28 | 29 | 30 | /// 31 | /// The base64-encoded binary content of the resource. 32 | /// 33 | [JsonPropertyName("blob")] 34 | public string? Blob { get; set; } 35 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ResourceUpdatedNotificationParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the server as the payload of "notifications/resources/updated" notifications whenever a subscribed resource changes. 5 | /// See the schema for details 6 | /// 7 | public class ResourceUpdatedNotificationParams 8 | { 9 | /// 10 | /// The URI of the resource to subscribe to. The URI can use any protocol; it is up to the server how to interpret it. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("uri")] 13 | public string? Uri { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Resources.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents a known resource that the server is capable of reading. 7 | /// See the schema for details 8 | /// 9 | public record Resource 10 | { 11 | /// 12 | /// The URI of this resource. 13 | /// 14 | [JsonPropertyName("uri")] 15 | public required string Uri { get; init; } 16 | 17 | /// 18 | /// A human-readable name for this resource. 19 | /// 20 | [JsonPropertyName("name")] 21 | public required string Name { get; init; } 22 | 23 | /// 24 | /// A description of what this resource represents. 25 | /// 26 | [JsonPropertyName("description")] 27 | public string? Description { get; init; } 28 | 29 | /// 30 | /// The MIME type of this resource, if known. 31 | /// 32 | [JsonPropertyName("mimeType")] 33 | public string? MimeType { get; init; } 34 | 35 | /// 36 | /// Optional annotations for the resource. 37 | /// 38 | [JsonPropertyName("annotations")] 39 | public Annotations? Annotations { get; init; } 40 | } 41 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Role.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents the type of role in the conversation. 7 | /// See the schema for details 8 | /// 9 | public enum Role 10 | { 11 | /// 12 | /// Corresponds to the user in the conversation. 13 | /// 14 | [JsonPropertyName("user")] 15 | User, 16 | 17 | /// 18 | /// Corresponds to the AI in the conversation. 19 | /// 20 | [JsonPropertyName("assistant")] 21 | Assistant 22 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Root.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Represents a root URI and its metadata. 5 | /// See the schema for details 6 | /// 7 | public class Root 8 | { 9 | /// 10 | /// The URI of the root. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("uri")] 13 | public required string Uri { get; init; } 14 | 15 | /// 16 | /// A human-readable name for the root. 17 | /// 18 | [System.Text.Json.Serialization.JsonPropertyName("name")] 19 | public string? Name { get; init; } 20 | 21 | /// 22 | /// Additional metadata for the root. Reserved by the protocol for future use. 23 | /// 24 | [System.Text.Json.Serialization.JsonPropertyName("meta")] 25 | public object? Meta { get; init; } 26 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/SamplingMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Describes a message issued to or received from an LLM API. 7 | /// See the schema for details 8 | /// 9 | public class SamplingMessage 10 | { 11 | /// 12 | /// Text or image content of the message. 13 | /// 14 | [JsonPropertyName("content")] 15 | public required Content Content { get; init; } 16 | 17 | /// 18 | /// The role of the message ("user" or "assistant"). 19 | /// 20 | [JsonPropertyName("role")] 21 | public required Role Role { get; init; } // "user" or "assistant" 22 | } 23 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/ServerCapabilities.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents the capabilities that a server may support. 7 | /// See the schema for details 8 | /// 9 | public record ServerCapabilities 10 | { 11 | /// 12 | /// Experimental, non-standard capabilities that the server supports. 13 | /// 14 | [JsonPropertyName("experimental")] 15 | public Dictionary? Experimental { get; init; } 16 | 17 | /// 18 | /// Present if the server supports sending log messages to the client. 19 | /// 20 | [JsonPropertyName("logging")] 21 | public LoggingCapability? Logging { get; init; } 22 | 23 | /// 24 | /// Present if the server offers any prompt templates. 25 | /// 26 | [JsonPropertyName("prompts")] 27 | public PromptsCapability? Prompts { get; init; } 28 | 29 | /// 30 | /// Present if the server offers any resources to read. 31 | /// 32 | [JsonPropertyName("resources")] 33 | public ResourcesCapability? Resources { get; init; } 34 | 35 | /// 36 | /// Present if the server offers any tools to call. 37 | /// 38 | [JsonPropertyName("tools")] 39 | public ToolsCapability? Tools { get; init; } 40 | } 41 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/SubscribeRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the client to request updated notifications from the server whenever a particular primitive changes. 5 | /// See the schema for details 6 | /// 7 | public class SubscribeRequestParams 8 | { 9 | /// 10 | /// The URI of the resource to subscribe to. The URI can use any protocol; it is up to the server how to interpret it. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("uri")] 13 | public string? Uri { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/Tool.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace McpDotNet.Protocol.Types; 4 | 5 | /// 6 | /// Represents a tool that the server is capable of calling. Part of the ListToolsResponse. 7 | /// See the schema for details 8 | /// 9 | public class Tool 10 | { 11 | /// 12 | /// The name of the tool. 13 | /// 14 | [JsonPropertyName("name")] 15 | public string Name { get; set; } = string.Empty; 16 | 17 | /// 18 | /// A human-readable description of the tool. 19 | /// 20 | [JsonPropertyName("description")] 21 | public string? Description { get; set; } 22 | 23 | /// 24 | /// A JSON Schema object defining the expected parameters for the tool. 25 | /// 26 | [JsonPropertyName("inputSchema")] 27 | public JsonSchema? InputSchema { get; set; } 28 | } 29 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/UnsubscribeFromResourceRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request. 5 | /// See the schema for details 6 | /// 7 | public class UnsubscribeFromResourceRequestParams 8 | { 9 | /// 10 | /// The URI of the resource to unsubscribe from. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("uri")] 13 | public string? Uri { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/mcpdotnet/Protocol/Types/UnsubscribeRequestParams.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Protocol.Types; 2 | 3 | /// 4 | /// Sent from the client to request not receiving updated notifications from the server whenever a primitive resource changes. 5 | /// See the schema for details 6 | /// 7 | public class UnsubscribeRequestParams 8 | { 9 | /// 10 | /// The URI of the resource to unsubscribe fro. The URI can use any protocol; it is up to the server how to interpret it. 11 | /// 12 | [System.Text.Json.Serialization.JsonPropertyName("uri")] 13 | public string? Uri { get; init; } 14 | } 15 | -------------------------------------------------------------------------------- /src/mcpdotnet/Server/IMcpServer.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | using McpDotNet.Protocol.Types; 3 | 4 | namespace McpDotNet.Server; 5 | 6 | /// 7 | /// Represents a server that can communicate with a client using the MCP protocol. 8 | /// 9 | public interface IMcpServer : IAsyncDisposable 10 | { 11 | /// 12 | /// Gets a value indicating whether the server has been initialized. 13 | /// 14 | bool IsInitialized { get; } 15 | 16 | /// 17 | /// Gets the capabilities supported by the client. 18 | /// 19 | ClientCapabilities? ClientCapabilities { get; } 20 | 21 | /// 22 | /// Gets the version and implementation information of the client. 23 | /// 24 | Implementation? ClientInfo { get; } 25 | 26 | /// 27 | /// Gets the service provider for the server. 28 | /// 29 | IServiceProvider? ServiceProvider { get; } 30 | 31 | /// 32 | /// Adds a handler for client notifications of a specific method. 33 | /// 34 | /// The notification method to handle. 35 | /// The async handler function to process notifications. 36 | /// 37 | /// 38 | /// Each method may have multiple handlers. Adding a handler for a method that already has one 39 | /// will not replace the existing handler. 40 | /// 41 | /// 42 | /// provides constants for common notification methods. 43 | /// 44 | /// 45 | void AddNotificationHandler(string method, Func handler); 46 | 47 | /// 48 | /// Starts the server and begins listening for client requests. 49 | /// 50 | Task StartAsync(CancellationToken cancellationToken = default); 51 | 52 | /// 53 | /// Sends a generic JSON-RPC request to the client. 54 | /// NB! This is a temporary method that is available to send not yet implemented feature messages. 55 | /// Once all MCP features are implemented this will be made private, as it is purely a convenience for those who wish to implement features ahead of the library. 56 | /// 57 | /// The expected response type. 58 | /// The JSON-RPC request to send. 59 | /// A token to cancel the operation. 60 | /// A task containing the client's response. 61 | Task SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken) where T : class; 62 | 63 | /// 64 | /// Sends a message to the server. 65 | /// 66 | /// The message. 67 | /// A token to cancel the operation. 68 | Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default); 69 | } 70 | -------------------------------------------------------------------------------- /src/mcpdotnet/Server/McpServerException.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Client; 2 | 3 | namespace McpDotNet.Server; 4 | 5 | /// 6 | /// Represents errors that occur in the MCP server. 7 | /// 8 | public class McpServerException : Exception 9 | { 10 | /// 11 | /// Gets the error code if this exception was caused by a JSON-RPC error response. 12 | /// 13 | public int? ErrorCode { get; } 14 | 15 | /// 16 | /// Initializes a new instance of the class with a specified error message. 17 | /// 18 | public McpServerException() 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the class with a specified error message. 24 | /// 25 | /// The message that describes the error. 26 | public McpServerException(string message) : base(message) 27 | { 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the class with a specified error message and error code. 32 | /// 33 | /// The message that describes the error. 34 | /// The error code associated with the JSON-RPC error response. 35 | public McpServerException(string message, int errorCode) : base(message) 36 | { 37 | ErrorCode = errorCode; 38 | } 39 | 40 | /// 41 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 42 | /// 43 | /// The message that describes the error. 44 | /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. 45 | public McpServerException(string message, Exception innerException) : base(message, innerException) 46 | { 47 | } 48 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Server/McpServerFactory.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Transport; 2 | using McpDotNet.Utils; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace McpDotNet.Server; 6 | 7 | /// 8 | /// Provides a factory for creating instances. 9 | /// 10 | public static class McpServerFactory 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// Transport to use for the server 16 | /// 17 | /// Configuration options for this server, including capabilities. 18 | /// Make sure to accurately reflect exactly what capabilities the server supports and does not support. 19 | /// 20 | /// Optional service provider to create new instances. 21 | /// Logger factory to use for logging 22 | /// An that's started and ready to receive connections. 23 | /// is . 24 | /// is . 25 | public static IMcpServer Create( 26 | ITransport serverTransport, 27 | McpServerOptions serverOptions, 28 | ILoggerFactory? loggerFactory = null, 29 | IServiceProvider? serviceProvider = null) 30 | { 31 | Throw.IfNull(serverTransport); 32 | Throw.IfNull(serverOptions); 33 | 34 | return new McpServer(serverTransport, serverOptions, loggerFactory, serviceProvider); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/mcpdotnet/Server/McpServerOptions.cs: -------------------------------------------------------------------------------- 1 |  2 | using McpDotNet.Protocol.Types; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace McpDotNet.Server; 6 | 7 | /// 8 | /// Configuration options for the MCP server. This is passed to the client during the initialization sequence, letting it know about the server's capabilities and 9 | /// protocol version. 10 | /// See the protocol specification for details on capability negotiation 11 | /// 12 | public class McpServerOptions 13 | { 14 | /// 15 | /// Information about this server implementation. 16 | /// 17 | public required Implementation ServerInfo { get; set; } 18 | 19 | /// 20 | /// Server capabilities to advertise to the server. 21 | /// 22 | public ServerCapabilities? Capabilities { get; set; } 23 | 24 | /// 25 | /// Protocol version to request from the server. 26 | /// 27 | public string ProtocolVersion { get; set; } = "2024-11-05"; 28 | 29 | /// 30 | /// Timeout for initialization sequence. 31 | /// 32 | public TimeSpan InitializationTimeout { get; set; } = TimeSpan.FromSeconds(60); 33 | 34 | /// 35 | /// Optional server instructions to send to clients 36 | /// 37 | public string ServerInstructions { get; set; } = string.Empty; 38 | 39 | /// 40 | /// Gets or sets the handler for get completion requests. 41 | /// 42 | [JsonIgnore] 43 | public Func, CancellationToken, Task>? GetCompletionHandler { get; set; } 44 | } 45 | -------------------------------------------------------------------------------- /src/mcpdotnet/Server/McpToolAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Server; 2 | 3 | /// 4 | /// Attribute to mark a method as an MCP tool. 5 | /// 6 | [AttributeUsage(AttributeTargets.Method)] 7 | public sealed class McpToolAttribute : Attribute 8 | { 9 | /// Gets the name of the tool. 10 | /// If not provided, the method name will be used. 11 | public string? Name { get; } 12 | /// 13 | /// Attribute to mark a method as an MCP tool. 14 | /// 15 | /// The name of the tool. If not provided, the class name will be used. 16 | public McpToolAttribute(string? name = null) 17 | { 18 | Name = name; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/mcpdotnet/Server/McpToolTypeAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Server; 2 | 3 | /// 4 | /// Attribute to mark a type as container for MCP tools. 5 | /// 6 | [AttributeUsage(AttributeTargets.Class)] 7 | public sealed class McpToolTypeAttribute : Attribute; 8 | -------------------------------------------------------------------------------- /src/mcpdotnet/Server/RequestContext.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Server; 2 | 3 | /// 4 | /// Container for the request context. 5 | /// 6 | /// Type of the request parameters 7 | public record RequestContext(IMcpServer Server, TParams? Params) 8 | { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/mcpdotnet/Utils/Json/JsonRpcMessageConverter.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | using System.ComponentModel; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace McpDotNet.Utils.Json; 7 | 8 | /// 9 | /// JSON converter for IJsonRpcMessage that handles polymorphic deserialization of different message types. 10 | /// 11 | [EditorBrowsable(EditorBrowsableState.Never)] 12 | public sealed class JsonRpcMessageConverter : JsonConverter 13 | { 14 | /// 15 | public override IJsonRpcMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 16 | { 17 | if (reader.TokenType != JsonTokenType.StartObject) 18 | { 19 | throw new JsonException("Expected StartObject token"); 20 | } 21 | 22 | using var doc = JsonDocument.ParseValue(ref reader); 23 | var root = doc.RootElement; 24 | 25 | // All JSON-RPC messages must have a jsonrpc property with value "2.0" 26 | if (!root.TryGetProperty("jsonrpc", out var versionProperty) || 27 | versionProperty.GetString() != "2.0") 28 | { 29 | throw new JsonException("Invalid or missing jsonrpc version"); 30 | } 31 | 32 | // Determine the message type based on the presence of id, method, and error properties 33 | bool hasId = root.TryGetProperty("id", out _); 34 | bool hasMethod = root.TryGetProperty("method", out _); 35 | bool hasError = root.TryGetProperty("error", out _); 36 | 37 | var rawText = root.GetRawText(); 38 | 39 | // Messages with an id but no method are responses 40 | if (hasId && !hasMethod) 41 | { 42 | // Messages with an error property are error responses 43 | if (hasError) 44 | { 45 | return JsonSerializer.Deserialize(rawText, options.GetTypeInfo()); 46 | } 47 | 48 | // Messages with a result property are success responses 49 | if (root.TryGetProperty("result", out _)) 50 | { 51 | return JsonSerializer.Deserialize(rawText, options.GetTypeInfo()); 52 | } 53 | 54 | throw new JsonException("Response must have either result or error"); 55 | } 56 | 57 | // Messages with a method but no id are notifications 58 | if (hasMethod && !hasId) 59 | { 60 | return JsonSerializer.Deserialize(rawText, options.GetTypeInfo()); 61 | } 62 | 63 | // Messages with both method and id are requests 64 | if (hasMethod && hasId) 65 | { 66 | return JsonSerializer.Deserialize(rawText, options.GetTypeInfo()); 67 | } 68 | 69 | throw new JsonException("Invalid JSON-RPC message format"); 70 | } 71 | 72 | /// 73 | public override void Write(Utf8JsonWriter writer, IJsonRpcMessage value, JsonSerializerOptions options) 74 | { 75 | switch (value) 76 | { 77 | case JsonRpcRequest request: 78 | JsonSerializer.Serialize(writer, request, options.GetTypeInfo()); 79 | break; 80 | case JsonRpcNotification notification: 81 | JsonSerializer.Serialize(writer, notification, options.GetTypeInfo()); 82 | break; 83 | case JsonRpcResponse response: 84 | JsonSerializer.Serialize(writer, response, options.GetTypeInfo()); 85 | break; 86 | case JsonRpcError error: 87 | JsonSerializer.Serialize(writer, error, options.GetTypeInfo()); 88 | break; 89 | default: 90 | throw new JsonException($"Unknown JSON-RPC message type: {value.GetType()}"); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/mcpdotnet/Utils/Json/JsonSerializerOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Messages; 2 | using McpDotNet.Protocol.Types; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Text.Json; 5 | using System.Text.Json.Nodes; 6 | using System.Text.Json.Serialization; 7 | using System.Text.Json.Serialization.Metadata; 8 | 9 | namespace McpDotNet.Utils.Json; 10 | 11 | /// 12 | /// Extensions for configuring System.Text.Json serialization options for MCP. 13 | /// 14 | internal static partial class JsonSerializerOptionsExtensions 15 | { 16 | public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions(); 17 | 18 | /// 19 | /// Creates default options to use for MCP-related serialization. 20 | /// 21 | /// The configured options. 22 | [UnconditionalSuppressMessage("AotAnalysis", "IL3050", Justification = "DefaultJsonTypeInfoResolver is only used when reflection-based serialization is enabled")] 23 | [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "DefaultJsonTypeInfoResolver is only used when reflection-based serialization is enabled")] 24 | private static JsonSerializerOptions CreateDefaultOptions() 25 | { 26 | // If reflection-based serialization is enabled by default, use it, as it's the most permissive in terms of what it can serialize, 27 | // and we want to be flexible in terms of what can be put into the various collections in the object model. 28 | // Otherwise, use the source-generated options to enable trimming and Native AOT. 29 | 30 | if (JsonSerializer.IsReflectionEnabledByDefault) 31 | { 32 | // Keep in sync with the JsonSourceGenerationOptions attribute on JsonContext below. 33 | JsonSerializerOptions options = new(JsonSerializerDefaults.Web) 34 | { 35 | TypeInfoResolver = new DefaultJsonTypeInfoResolver(), 36 | Converters = { new JsonStringEnumConverter() }, 37 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, 38 | NumberHandling = JsonNumberHandling.AllowReadingFromString 39 | }; 40 | 41 | options.MakeReadOnly(); 42 | return options; 43 | } 44 | 45 | return JsonContext.Default.Options; 46 | } 47 | 48 | internal static JsonTypeInfo GetTypeInfo(this JsonSerializerOptions options) => 49 | (JsonTypeInfo)options.GetTypeInfo(typeof(T)); 50 | 51 | // Keep in sync with CreateDefaultOptions above. 52 | [JsonSourceGenerationOptions(JsonSerializerDefaults.Web, 53 | UseStringEnumConverter = true, 54 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, 55 | NumberHandling = JsonNumberHandling.AllowReadingFromString)] 56 | [JsonSerializable(typeof(JsonDocument))] 57 | [JsonSerializable(typeof(JsonElement))] 58 | [JsonSerializable(typeof(JsonNode))] 59 | [JsonSerializable(typeof(IJsonRpcMessage))] 60 | [JsonSerializable(typeof(JsonRpcRequest))] 61 | [JsonSerializable(typeof(JsonRpcNotification))] 62 | [JsonSerializable(typeof(JsonRpcResponse))] 63 | [JsonSerializable(typeof(JsonRpcError))] 64 | [JsonSerializable(typeof(ServerCapabilities))] 65 | [JsonSerializable(typeof(Implementation))] 66 | [JsonSerializable(typeof(CreateMessageResult))] 67 | [JsonSerializable(typeof(ListRootsResult))] 68 | [JsonSerializable(typeof(InitializeResult))] 69 | [JsonSerializable(typeof(JsonSchema))] 70 | [JsonSerializable(typeof(CallToolResponse))] 71 | internal sealed partial class JsonContext : JsonSerializerContext; 72 | } 73 | -------------------------------------------------------------------------------- /src/mcpdotnet/Utils/ProcessHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace McpDotNet.Utils; 5 | 6 | /// 7 | /// Helper class for working with processes. 8 | /// 9 | internal static class ProcessHelper 10 | { 11 | private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 12 | private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); 13 | 14 | public static void KillTree(this Process process) => process.KillTree(_defaultTimeout); 15 | 16 | public static void KillTree(this Process process, TimeSpan timeout) 17 | { 18 | var pid = process.Id; 19 | if (_isWindows) 20 | { 21 | RunProcessAndWaitForExit( 22 | "taskkill", 23 | $"/T /F /PID {pid}", 24 | timeout, 25 | out var _); 26 | } 27 | else 28 | { 29 | var children = new HashSet(); 30 | GetAllChildIdsUnix(pid, children, timeout); 31 | foreach (var childId in children) 32 | { 33 | KillProcessUnix(childId, timeout); 34 | } 35 | KillProcessUnix(pid, timeout); 36 | } 37 | 38 | // wait until the process finishes exiting/getting killed. 39 | // We don't want to wait forever here because the task is already supposed to be dieing, we just want to give it long enough 40 | // to try and flush what it can and stop. If it cannot do that in a reasonable time frame then we will just ignore it. 41 | process.WaitForExit((int)timeout.TotalMilliseconds); 42 | } 43 | 44 | private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) 45 | { 46 | var exitcode = RunProcessAndWaitForExit( 47 | "pgrep", 48 | $"-P {parentId}", 49 | timeout, 50 | out var stdout); 51 | 52 | if (exitcode == 0 && !string.IsNullOrEmpty(stdout)) 53 | { 54 | using var reader = new StringReader(stdout); 55 | while (true) 56 | { 57 | var text = reader.ReadLine(); 58 | if (text == null) 59 | return; 60 | 61 | if (int.TryParse(text, out var id)) 62 | { 63 | children.Add(id); 64 | // Recursively get the children 65 | GetAllChildIdsUnix(id, children, timeout); 66 | } 67 | } 68 | } 69 | } 70 | 71 | private static void KillProcessUnix(int processId, TimeSpan timeout) 72 | { 73 | RunProcessAndWaitForExit( 74 | "kill", 75 | $"-TERM {processId}", 76 | timeout, 77 | out var _); 78 | } 79 | 80 | private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string? stdout) 81 | { 82 | var startInfo = new ProcessStartInfo 83 | { 84 | FileName = fileName, 85 | Arguments = arguments, 86 | RedirectStandardOutput = true, 87 | RedirectStandardError = true, 88 | UseShellExecute = false, 89 | }; 90 | 91 | stdout = null; 92 | 93 | var process = Process.Start(startInfo); 94 | if (process == null) 95 | return -1; 96 | 97 | if (process.WaitForExit((int)timeout.TotalMilliseconds)) 98 | { 99 | stdout = process.StandardOutput.ReadToEnd(); 100 | } 101 | else 102 | { 103 | process.Kill(); 104 | } 105 | 106 | return process.ExitCode; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/mcpdotnet/Utils/Throw.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace McpDotNet.Utils; 5 | 6 | /// Provides helper methods for throwing exceptions. 7 | internal static class Throw 8 | { 9 | // NOTE: Most of these should be replaced with extension statics for the relevant extension 10 | // type as downlevel polyfills once the C# 14 extension everything feature is available. 11 | 12 | public static void IfNull([NotNull] object? arg, [CallerArgumentExpression(nameof(arg))] string? parameterName = null) 13 | { 14 | if (arg is null) 15 | { 16 | ThrowArgumentNullException(parameterName); 17 | } 18 | } 19 | 20 | public static void IfNullOrWhiteSpace([NotNull] string? arg, [CallerArgumentExpression(nameof(arg))] string? parameterName = null) 21 | { 22 | if (arg is null || arg.AsSpan().IsWhiteSpace()) 23 | { 24 | ThrowArgumentNullOrWhiteSpaceException(arg); 25 | } 26 | } 27 | 28 | [DoesNotReturn] 29 | private static void ThrowArgumentNullOrWhiteSpaceException(string? parameterName) 30 | { 31 | if (parameterName is null) 32 | { 33 | ThrowArgumentNullException(parameterName); 34 | } 35 | 36 | throw new ArgumentException("Value cannot be empty or composed entirely of whitespace.", parameterName); 37 | } 38 | 39 | [DoesNotReturn] 40 | private static void ThrowArgumentNullException(string? parameterName) => throw new ArgumentNullException(parameterName); 41 | } 42 | -------------------------------------------------------------------------------- /src/mcpdotnet/mcpdotnet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0;netstandard2.0 5 | enable 6 | enable 7 | Latest 8 | true 9 | All 10 | 11 | 12 | 13 | true 14 | 15 | 16 | 17 | 18 | mcpdotnet 19 | 1.1.0.1 20 | PederHP 21 | .NET library for the Model Context Protocol (MCP) 22 | https://github.com/PederHP/mcpdotnet 23 | https://github.com/PederHP/mcpdotnet 24 | git 25 | mcp;ai;ModelContextProtocol;llm 26 | README.md 27 | MIT 28 | 29 | mcpdotnet 30 | McpDotNet 31 | true 32 | true 33 | snupkg 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /tests/mcpdotnet.TestServer/mcpdotnet.TestServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | TestServer 9 | McpDotNet.TestServer 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/mcpdotnet.TestSseServer/mcpdotnet.TestSseServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | TestSseServer 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/ClientIntegrationTestFixture.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Client; 2 | using McpDotNet.Configuration; 3 | using McpDotNet.Protocol.Transport; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace McpDotNet.Tests; 7 | 8 | public class ClientIntegrationTestFixture : IDisposable 9 | { 10 | public ILoggerFactory LoggerFactory { get; } 11 | public McpClientOptions DefaultOptions { get; } 12 | public McpServerConfig EverythingServerConfig { get; } 13 | public McpServerConfig TestServerConfig { get; } 14 | 15 | public static IEnumerable ClientIds => ["everything", "test_server"]; 16 | 17 | public ClientIntegrationTestFixture() 18 | { 19 | LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => 20 | builder.AddConsole() 21 | .SetMinimumLevel(LogLevel.Debug)); 22 | 23 | DefaultOptions = new() 24 | { 25 | ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" }, 26 | }; 27 | 28 | EverythingServerConfig = new() 29 | { 30 | Id = "everything", 31 | Name = "Everything", 32 | TransportType = TransportTypes.StdIo, 33 | TransportOptions = new Dictionary 34 | { 35 | ["command"] = "npx", 36 | // Change to ["arguments"] = "mcp-server-everything" if you want to run the server locally after creating a symlink 37 | ["arguments"] = "-y --verbose @modelcontextprotocol/server-everything" 38 | } 39 | }; 40 | 41 | TestServerConfig = new() 42 | { 43 | Id = "test_server", 44 | Name = "TestServer", 45 | TransportType = TransportTypes.StdIo, 46 | TransportOptions = new Dictionary 47 | { 48 | ["command"] = OperatingSystem.IsWindows() ? "TestServer.exe" : "dotnet", 49 | // Change to ["arguments"] = "mcp-server-everything" if you want to run the server locally after creating a symlink 50 | } 51 | }; 52 | 53 | if (!OperatingSystem.IsWindows()) 54 | { 55 | TestServerConfig.TransportOptions["arguments"] = "TestServer.dll"; 56 | } 57 | } 58 | 59 | public Task CreateClientAsync(string clientId, McpClientOptions? clientOptions = null) => 60 | McpClientFactory.CreateAsync(clientId switch 61 | { 62 | "everything" => EverythingServerConfig, 63 | "test_server" => TestServerConfig, 64 | _ => throw new ArgumentException($"Unknown client ID: {clientId}") 65 | }, clientOptions ?? DefaultOptions, loggerFactory: LoggerFactory); 66 | 67 | public void Dispose() 68 | { 69 | LoggerFactory?.Dispose(); 70 | GC.SuppressFinalize(this); 71 | } 72 | } -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/Configuration/McpServerBuilderExtensionsTransportsTests.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Configuration; 2 | using McpDotNet.Protocol.Transport; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Moq; 5 | 6 | namespace McpDotNet.Tests.Configuration; 7 | 8 | public class McpServerBuilderExtensionsTransportsTests 9 | { 10 | [Fact] 11 | public void WithStdioServerTransport_Sets_Transport() 12 | { 13 | var services = new ServiceCollection(); 14 | var builder = new Mock(); 15 | builder.SetupGet(b => b.Services).Returns(services); 16 | 17 | builder.Object.WithStdioServerTransport(); 18 | 19 | var transportType = services.FirstOrDefault(s => s.ServiceType == typeof(IServerTransport)); 20 | Assert.NotNull(transportType); 21 | Assert.Equal(typeof(StdioServerTransport), transportType.ImplementationType); 22 | } 23 | 24 | [Fact] 25 | public void WithHttpListenerSseServerTransport_Sets_Transport() 26 | { 27 | var services = new ServiceCollection(); 28 | var builder = new Mock(); 29 | builder.SetupGet(b => b.Services).Returns(services); 30 | 31 | builder.Object.WithHttpListenerSseServerTransport(); 32 | 33 | var transportType = services.FirstOrDefault(s => s.ServiceType == typeof(IServerTransport)); 34 | Assert.NotNull(transportType); 35 | Assert.Equal(typeof(HttpListenerSseServerTransport), transportType.ImplementationType); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/EverythingSseServerFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace McpDotNet.Tests; 4 | 5 | public class EverythingSseServerFixture : IAsyncDisposable 6 | { 7 | private int _port; 8 | private string _containerName; 9 | 10 | public EverythingSseServerFixture(int port) 11 | { 12 | _port = port; 13 | _containerName = $"mcp-everything-server-{_port}"; 14 | } 15 | 16 | public async Task StartAsync() 17 | { 18 | var processStartInfo = new ProcessStartInfo 19 | { 20 | FileName = "docker", 21 | Arguments = $"run -p {_port}:3001 --name {_containerName} --rm tzolov/mcp-everything-server:v1", 22 | RedirectStandardInput = true, 23 | RedirectStandardOutput = true, 24 | RedirectStandardError = true, 25 | UseShellExecute = false, 26 | }; 27 | 28 | _ = Process.Start(processStartInfo) 29 | ?? throw new InvalidOperationException($"Could not start process for {processStartInfo.FileName} with '{processStartInfo.Arguments}'."); 30 | 31 | // Wait for the server to start 32 | await Task.Delay(10000); 33 | } 34 | public async ValueTask DisposeAsync() 35 | { 36 | try 37 | { 38 | 39 | // Stop the container 40 | var stopInfo = new ProcessStartInfo 41 | { 42 | FileName = "docker", 43 | Arguments = $"stop {_containerName}", 44 | UseShellExecute = false 45 | }; 46 | 47 | using var stopProcess = Process.Start(stopInfo) 48 | ?? throw new InvalidOperationException($"Could not stop process for {stopInfo.FileName} with '{stopInfo.Arguments}'."); 49 | await stopProcess.WaitForExitAsync(); 50 | } 51 | catch (Exception ex) 52 | { 53 | // Log the exception but don't throw 54 | await Console.Error.WriteLineAsync($"Error stopping Docker container: {ex.Message}"); 55 | } 56 | 57 | GC.SuppressFinalize(this); 58 | } 59 | } -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/Server/McpServerDelegatesTests.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Types; 2 | using McpDotNet.Server; 3 | 4 | namespace McpDotNet.Tests.Server; 5 | 6 | public class McpServerHandlerTests 7 | { 8 | [Fact] 9 | public void AllPropertiesAreSettable() 10 | { 11 | var handlers = new McpServerHandlers(); 12 | 13 | Assert.Null(handlers.ListToolsHandler); 14 | Assert.Null(handlers.CallToolHandler); 15 | Assert.Null(handlers.ListPromptsHandler); 16 | Assert.Null(handlers.GetPromptHandler); 17 | Assert.Null(handlers.ListResourcesHandler); 18 | Assert.Null(handlers.ReadResourceHandler); 19 | Assert.Null(handlers.GetCompletionHandler); 20 | Assert.Null(handlers.SubscribeToResourcesHandler); 21 | Assert.Null(handlers.UnsubscribeFromResourcesHandler); 22 | 23 | handlers.ListToolsHandler = (p, c) => Task.FromResult(new ListToolsResult()); 24 | handlers.CallToolHandler = (p, c) => Task.FromResult(new CallToolResponse()); 25 | handlers.ListPromptsHandler = (p, c) => Task.FromResult(new ListPromptsResult()); 26 | handlers.GetPromptHandler = (p, c) => Task.FromResult(new GetPromptResult()); 27 | handlers.ListResourcesHandler = (p, c) => Task.FromResult(new ListResourcesResult()); 28 | handlers.ReadResourceHandler = (p, c) => Task.FromResult(new ReadResourceResult()); 29 | handlers.GetCompletionHandler = (p, c) => Task.FromResult(new CompleteResult()); 30 | handlers.SubscribeToResourcesHandler = (s, c) => Task.FromResult(new EmptyResult()); 31 | handlers.UnsubscribeFromResourcesHandler = (s, c) => Task.FromResult(new EmptyResult()); 32 | 33 | Assert.NotNull(handlers.ListToolsHandler); 34 | Assert.NotNull(handlers.CallToolHandler); 35 | Assert.NotNull(handlers.ListPromptsHandler); 36 | Assert.NotNull(handlers.GetPromptHandler); 37 | Assert.NotNull(handlers.ListResourcesHandler); 38 | Assert.NotNull(handlers.ReadResourceHandler); 39 | Assert.NotNull(handlers.GetCompletionHandler); 40 | Assert.NotNull(handlers.SubscribeToResourcesHandler); 41 | Assert.NotNull(handlers.UnsubscribeFromResourcesHandler); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/Server/McpServerFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Protocol.Transport; 2 | using McpDotNet.Protocol.Types; 3 | using McpDotNet.Server; 4 | using Microsoft.Extensions.Logging.Abstractions; 5 | using Moq; 6 | 7 | namespace McpDotNet.Tests.Server; 8 | 9 | public class McpServerFactoryTests 10 | { 11 | private readonly Mock _serverTransport; 12 | private readonly McpServerOptions _options; 13 | private readonly IServiceProvider _serviceProvider; 14 | 15 | public McpServerFactoryTests() 16 | { 17 | _serverTransport = new Mock(); 18 | _options = new McpServerOptions 19 | { 20 | ServerInfo = new Implementation { Name = "TestServer", Version = "1.0" }, 21 | ProtocolVersion = "1.0", 22 | InitializationTimeout = TimeSpan.FromSeconds(30) 23 | }; 24 | _serviceProvider = new Mock().Object; 25 | } 26 | 27 | [Fact] 28 | public async Task Create_Should_Initialize_With_Valid_Parameters() 29 | { 30 | // Arrange & Act 31 | await using IMcpServer server = McpServerFactory.Create(_serverTransport.Object, _options, NullLoggerFactory.Instance); 32 | 33 | // Assert 34 | Assert.NotNull(server); 35 | } 36 | 37 | [Fact] 38 | public void Constructor_Throws_For_Null_ServerTransport() 39 | { 40 | // Arrange, Act & Assert 41 | Assert.Throws("serverTransport", () => McpServerFactory.Create(null!, _options, NullLoggerFactory.Instance)); 42 | } 43 | 44 | [Fact] 45 | public void Constructor_Throws_For_Null_Options() 46 | { 47 | // Arrange, Act & Assert 48 | Assert.Throws("serverOptions", () => McpServerFactory.Create(_serverTransport.Object, null!, NullLoggerFactory.Instance)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/SseServerIntegrationTestFixture.cs: -------------------------------------------------------------------------------- 1 | using McpDotNet.Client; 2 | using McpDotNet.Configuration; 3 | using McpDotNet.Protocol.Transport; 4 | using Microsoft.Extensions.Logging; 5 | using System.Diagnostics; 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | namespace McpDotNet.Tests; 9 | 10 | public class SseServerIntegrationTestFixture : IDisposable 11 | { 12 | private Process _process; 13 | 14 | public ILoggerFactory LoggerFactory { get; } 15 | public McpClientOptions DefaultOptions { get; } 16 | public McpServerConfig DefaultConfig { get; } 17 | 18 | public SseServerIntegrationTestFixture() 19 | { 20 | LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => 21 | builder.AddConsole() 22 | .SetMinimumLevel(LogLevel.Debug)); 23 | 24 | DefaultOptions = new() 25 | { 26 | ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" }, 27 | }; 28 | 29 | DefaultConfig = new McpServerConfig 30 | { 31 | Id = "test_server", 32 | Name = "TestServer", 33 | TransportType = TransportTypes.Sse, 34 | TransportOptions = [], 35 | Location = "http://localhost:3001/sse" 36 | }; 37 | 38 | Start(); 39 | } 40 | 41 | [MemberNotNull(nameof(_process))] 42 | public void Start() 43 | { 44 | // Start the server (which is at TestSseServer.exe on windows and "dotnet TestSseServer.dll" on linux) 45 | var processStartInfo = new ProcessStartInfo 46 | { 47 | FileName = OperatingSystem.IsWindows() ? "TestSseServer.exe" : "dotnet", 48 | Arguments = "TestSseServer.dll", 49 | RedirectStandardInput = true, 50 | RedirectStandardOutput = true, 51 | RedirectStandardError = true, 52 | UseShellExecute = false, 53 | }; 54 | 55 | _process = Process.Start(processStartInfo) 56 | ?? throw new InvalidOperationException($"Could not start process for {processStartInfo.FileName} with '{processStartInfo.Arguments}'."); 57 | 58 | // Wait 1 second 59 | Thread.Sleep(1000); 60 | } 61 | 62 | public void Dispose() 63 | { 64 | try 65 | { 66 | var client = McpClientFactory.CreateAsync(DefaultConfig, DefaultOptions, loggerFactory: LoggerFactory).GetAwaiter().GetResult(); 67 | client.DisposeAsync().AsTask().Wait(); 68 | LoggerFactory?.Dispose(); 69 | } 70 | finally 71 | { 72 | // Kill the server process 73 | _process.Kill(); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/Transport/StdioServerTransportTests.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using McpDotNet.Protocol.Messages; 3 | using McpDotNet.Protocol.Transport; 4 | using McpDotNet.Protocol.Types; 5 | using McpDotNet.Server; 6 | using McpDotNet.Utils.Json; 7 | using Microsoft.Extensions.Logging.Abstractions; 8 | 9 | namespace McpDotNet.Tests.Transport; 10 | 11 | public class StdioServerTransportTests 12 | { 13 | private readonly McpServerOptions _serverOptions; 14 | 15 | public StdioServerTransportTests() 16 | { 17 | _serverOptions = new McpServerOptions 18 | { 19 | ServerInfo = new Implementation 20 | { 21 | Name = "Test Server", 22 | Version = "1.0" 23 | }, 24 | ProtocolVersion = "2.0", 25 | InitializationTimeout = TimeSpan.FromSeconds(10), 26 | ServerInstructions = "Test Instructions" 27 | }; 28 | } 29 | 30 | [Fact] 31 | public async Task Constructor_Should_Initialize_With_Valid_Parameters() 32 | { 33 | // Act 34 | await using var transport = new StdioServerTransport(_serverOptions); 35 | 36 | // Assert 37 | Assert.NotNull(transport); 38 | } 39 | 40 | [Fact] 41 | public void Constructor_Throws_For_Null_Options() 42 | { 43 | Assert.Throws("serverName", () => new StdioServerTransport((string)null!)); 44 | 45 | Assert.Throws("serverOptions", () => new StdioServerTransport((McpServerOptions)null!)); 46 | Assert.Throws("serverOptions.ServerInfo", () => new StdioServerTransport(new McpServerOptions() { ServerInfo = null! })); 47 | Assert.Throws("serverName", () => new StdioServerTransport(new McpServerOptions() { ServerInfo = new() { Name = null!, Version = "" } })); 48 | } 49 | 50 | [Fact] 51 | public async Task StartListeningAsync_Should_Set_Connected_State() 52 | { 53 | await using var transport = new StdioServerTransport(_serverOptions); 54 | 55 | await transport.StartListeningAsync(); 56 | 57 | Assert.True(transport.IsConnected); 58 | } 59 | 60 | [Fact] 61 | public async Task SendMessageAsync_Should_Send_Message() 62 | { 63 | TextReader oldIn = Console.In; 64 | TextWriter oldOut = Console.Out; 65 | try 66 | { 67 | var output = new StringWriter(); 68 | 69 | Console.SetIn(new StringReader("")); 70 | Console.SetOut(output); 71 | 72 | await using var transport = new StdioServerTransport(_serverOptions, NullLoggerFactory.Instance); 73 | await transport.StartListeningAsync(); 74 | 75 | var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) }; 76 | 77 | 78 | await transport.SendMessageAsync(message); 79 | 80 | var result = output.ToString()?.Trim(); 81 | var expected = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions); 82 | 83 | Assert.Equal(expected, result); 84 | } 85 | finally 86 | { 87 | Console.SetOut(oldOut); 88 | Console.SetIn(oldIn); 89 | } 90 | } 91 | 92 | [Fact] 93 | public async Task SendMessageAsync_Throws_Exception_If_Not_Connected() 94 | { 95 | await using var transport = new StdioServerTransport(_serverOptions); 96 | 97 | var message = new JsonRpcRequest { Method = "test" }; 98 | 99 | await Assert.ThrowsAsync(() => transport.SendMessageAsync(message)); 100 | } 101 | 102 | [Fact] 103 | public async Task DisposeAsync_Should_Dispose_Resources() 104 | { 105 | await using var transport = new StdioServerTransport(_serverOptions); 106 | 107 | await transport.DisposeAsync(); 108 | 109 | Assert.False(transport.IsConnected); 110 | } 111 | 112 | [Fact] 113 | public async Task ReadMessagesAsync_Should_Read_Messages() 114 | { 115 | var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) }; 116 | var json = JsonSerializer.Serialize(message, JsonSerializerOptionsExtensions.DefaultOptions); 117 | 118 | TextReader oldIn = Console.In; 119 | TextWriter oldOut = Console.Out; 120 | try 121 | { 122 | Console.SetIn(new StringReader(json)); 123 | Console.SetOut(new StringWriter()); 124 | 125 | await using var transport = new StdioServerTransport(_serverOptions); 126 | await transport.StartListeningAsync(); 127 | 128 | var canRead = await transport.MessageReader.WaitToReadAsync(); 129 | 130 | Assert.True(canRead, "Nothing to read here from transport message reader"); 131 | Assert.True(transport.MessageReader.TryPeek(out var readMessage)); 132 | Assert.NotNull(readMessage); 133 | Assert.IsType(readMessage); 134 | Assert.Equal(44, ((JsonRpcRequest)readMessage).Id.AsNumber); 135 | } 136 | finally 137 | { 138 | Console.SetOut(oldOut); 139 | Console.SetIn(oldIn); 140 | } 141 | } 142 | 143 | [Fact] 144 | public async Task CleanupAsync_Should_Cleanup_Resources() 145 | { 146 | var transport = new StdioServerTransport(_serverOptions); 147 | await transport.StartListeningAsync(); 148 | 149 | await transport.DisposeAsync(); 150 | 151 | Assert.False(transport.IsConnected); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/Utils/MockHttpHandler.cs: -------------------------------------------------------------------------------- 1 | namespace McpDotNet.Tests.Utils; 2 | 3 | public class MockHttpHandler : HttpMessageHandler 4 | { 5 | public Func>? RequestHandler { get; set; } 6 | 7 | protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 8 | { 9 | if (RequestHandler == null) 10 | throw new InvalidOperationException($"No {nameof(RequestHandler)} was set! Please set handler first and make request afterwards."); 11 | 12 | if (cancellationToken.IsCancellationRequested) 13 | throw new OperationCanceledException(cancellationToken); 14 | 15 | var result = await RequestHandler.Invoke(request); 16 | 17 | if (cancellationToken.IsCancellationRequested) 18 | throw new OperationCanceledException(cancellationToken); 19 | 20 | return result; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/Utils/TestServerTransport.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Channels; 2 | using McpDotNet.Protocol.Messages; 3 | using McpDotNet.Protocol.Transport; 4 | 5 | namespace McpDotNet.Tests.Utils; 6 | 7 | public class TestServerTransport : IServerTransport 8 | { 9 | private readonly Channel _messageChannel; 10 | private bool _isStarted; 11 | 12 | public bool IsConnected => _isStarted; 13 | 14 | public ChannelReader MessageReader => _messageChannel; 15 | 16 | public List SentMessages { get; } = []; 17 | 18 | public Action? OnMessageSent { get; set; } 19 | 20 | public TestServerTransport() 21 | { 22 | _messageChannel = Channel.CreateUnbounded(new UnboundedChannelOptions 23 | { 24 | SingleReader = true, 25 | SingleWriter = true, 26 | }); 27 | } 28 | 29 | public ValueTask DisposeAsync() => ValueTask.CompletedTask; 30 | 31 | public async Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default) 32 | { 33 | SentMessages.Add(message); 34 | if (message is JsonRpcRequest request) 35 | { 36 | if (request.Method == "roots/list") 37 | await ListRoots(request, cancellationToken); 38 | else if (request.Method == "sampling/createMessage") 39 | await Sampling(request, cancellationToken); 40 | else 41 | await WriteMessageAsync(request, cancellationToken); 42 | } 43 | else if (message is JsonRpcNotification notification) 44 | { 45 | await WriteMessageAsync(notification, cancellationToken); 46 | } 47 | 48 | OnMessageSent?.Invoke(message); 49 | } 50 | 51 | public Task StartListeningAsync(CancellationToken cancellationToken = default) 52 | { 53 | _isStarted = true; 54 | return Task.CompletedTask; 55 | } 56 | 57 | private async Task ListRoots(JsonRpcRequest request, CancellationToken cancellationToken) 58 | { 59 | await WriteMessageAsync(new JsonRpcResponse 60 | { 61 | Id = request.Id, 62 | Result = new McpDotNet.Protocol.Types.ListRootsResult 63 | { 64 | Roots = [] 65 | } 66 | }, cancellationToken); 67 | } 68 | 69 | private async Task Sampling(JsonRpcRequest request, CancellationToken cancellationToken) 70 | { 71 | await WriteMessageAsync(new JsonRpcResponse 72 | { 73 | Id = request.Id, 74 | Result = new Protocol.Types.CreateMessageResult { Content = new(), Model = "model", Role = "role" } 75 | }, cancellationToken); 76 | } 77 | 78 | private async Task Error(JsonRpcRequest request, CancellationToken cancellationToken) 79 | { 80 | await WriteMessageAsync(new JsonRpcError 81 | { 82 | Id = request.Id, 83 | Error = new JsonRpcErrorDetail() { Code = -32601, Message = $"Method '{request.Method}' not supported" } 84 | }, cancellationToken); 85 | } 86 | 87 | protected async Task WriteMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default) 88 | { 89 | await _messageChannel.Writer.WriteAsync(message, cancellationToken); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/mcpdotnet.Tests/mcpdotnet.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | Latest 8 | 9 | false 10 | true 11 | McpDotNet.Tests 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | runtime; build; native; contentfiles; analyzers; buildtransitive 28 | all 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | PreserveNewest 41 | 42 | 43 | PreserveNewest 44 | 45 | 46 | PreserveNewest 47 | 48 | 49 | PreserveNewest 50 | 51 | 52 | 53 | 54 | --------------------------------------------------------------------------------