From be3c68afdcd575ef264a72c9e8b11c78f5e60829 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 23 Mar 2025 15:02:21 -0400 Subject: [PATCH 1/3] Make client config optional to McpClientFactory.CreateAsync Unless you're specifying capabilities (sampling/rooting) or need a specific client name/version, the client information can be inferred from the assembly, simplifying the getting-started experience. --- README.MD | 14 ++------- samples/ChatWithTools/Program.cs | 3 +- .../Client/McpClientFactory.cs | 23 +++++++++++++-- .../Client/McpClientFactoryTests.cs | 29 ++++++++++++++----- 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/README.MD b/README.MD index 0ebc2f1f1..985524175 100644 --- a/README.MD +++ b/README.MD @@ -20,16 +20,10 @@ For more information about MCP: ## Getting Started (Client) To get started writing a client, the `McpClientFactory.CreateAsync` method is used to instantiate and connect an `IMcpClient` -to a server, with details about the client and server specified in `McpClientOptions` and `McpServerConfig` objects. -Once you have an `IMcpClient`, you can interact with it, such as to enumerate all available tools and invoke tools. +to a server. Once you have an `IMcpClient`, you can interact with it, such as to enumerate all available tools and invoke tools. ```csharp -McpClientOptions options = new() -{ - ClientInfo = new() { Name = "TestClient", Version = "1.0.0" } -}; - -McpServerConfig config = new() +var client = await McpClientFactory.CreateAsync(new() { Id = "everything", Name = "Everything", @@ -39,9 +33,7 @@ McpServerConfig config = new() ["command"] = "npx", ["arguments"] = "-y @modelcontextprotocol/server-everything", } -}; - -var client = await McpClientFactory.CreateAsync(config, options); +}); // Print the list of tools available from the server. await foreach (var tool in client.ListToolsAsync()) diff --git a/samples/ChatWithTools/Program.cs b/samples/ChatWithTools/Program.cs index feeb06a04..2dcf06fae 100644 --- a/samples/ChatWithTools/Program.cs +++ b/samples/ChatWithTools/Program.cs @@ -15,8 +15,7 @@ { ["command"] = "npx", ["arguments"] = "-y @modelcontextprotocol/server-everything", } - }, - new() { ClientInfo = new() { Name = "ChatClient", Version = "1.0.0" } }); + }); // Get all available tools Console.WriteLine("Tools available:"); diff --git a/src/ModelContextProtocol/Client/McpClientFactory.cs b/src/ModelContextProtocol/Client/McpClientFactory.cs index 0fdbc3f0c..892ed868b 100644 --- a/src/ModelContextProtocol/Client/McpClientFactory.cs +++ b/src/ModelContextProtocol/Client/McpClientFactory.cs @@ -6,6 +6,7 @@ using ModelContextProtocol.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using System.Reflection; namespace ModelContextProtocol.Client; @@ -14,7 +15,10 @@ public static class McpClientFactory { /// Creates an , connecting it to the specified server. /// Configuration for the target server to which the client should connect. - /// A client configuration object which specifies client capabilities and protocol version. + /// + /// A client configuration object which specifies client capabilities and protocol version. + /// If , details based on the current process will be employed. + /// /// An optional factory method which returns transport implementations based on a server configuration. /// A logger factory for creating loggers for clients. /// A token to cancel the operation. @@ -25,13 +29,26 @@ public static class McpClientFactory /// returns an invalid transport. public static async Task CreateAsync( McpServerConfig serverConfig, - McpClientOptions clientOptions, + McpClientOptions? clientOptions = null, Func? createTransportFunc = null, ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default) { Throw.IfNull(serverConfig); - Throw.IfNull(clientOptions); + + if (clientOptions is null) + { + var asmName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName(); + + clientOptions ??= new() + { + ClientInfo = new() + { + Name = asmName.Name ?? "McpClient", + Version = asmName.Version?.ToString() ?? "1.0.0", + }, + }; + } createTransportFunc ??= CreateTransport; diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs index ce4c038a9..0053e8da2 100644 --- a/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs +++ b/tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs @@ -19,13 +19,6 @@ public async Task CreateAsync_WithInvalidArgs_Throws() { await Assert.ThrowsAsync("serverConfig", () => McpClientFactory.CreateAsync((McpServerConfig)null!, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken)); - await Assert.ThrowsAsync("clientOptions", () => McpClientFactory.CreateAsync(new McpServerConfig() - { - Name = "name", - Id = "id", - TransportType = TransportTypes.StdIo, - }, (McpClientOptions)null!, cancellationToken: TestContext.Current.CancellationToken)); - await Assert.ThrowsAsync("serverConfig", () => McpClientFactory.CreateAsync(new McpServerConfig() { Name = "name", @@ -41,6 +34,28 @@ await Assert.ThrowsAsync(() => McpClientFactory.Creat }, _defaultOptions, (_, __) => null!, cancellationToken: TestContext.Current.CancellationToken)); } + [Fact] + public async Task CreateAsync_NullOptions_EntryAssemblyInferred() + { + // Arrange + var serverConfig = new McpServerConfig + { + Id = "test-server", + Name = "Test Server", + TransportType = TransportTypes.StdIo, + Location = "/path/to/server", + }; + + // Act + await using var client = await McpClientFactory.CreateAsync( + serverConfig, + null, + (_, __) => new NopTransport(), + cancellationToken: TestContext.Current.CancellationToken); + + Assert.NotNull(client); + } + [Fact] public async Task CreateAsync_WithValidStdioConfig_CreatesNewClient() { From 1906b3697eac65dcbcdc3d8b25accfc7a8425fbf Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 24 Mar 2025 09:59:03 -0400 Subject: [PATCH 2/3] Cache default options --- .../Client/McpClientFactory.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/ModelContextProtocol/Client/McpClientFactory.cs b/src/ModelContextProtocol/Client/McpClientFactory.cs index 892ed868b..141ff38ef 100644 --- a/src/ModelContextProtocol/Client/McpClientFactory.cs +++ b/src/ModelContextProtocol/Client/McpClientFactory.cs @@ -13,6 +13,23 @@ namespace ModelContextProtocol.Client; /// Provides factory methods for creating MCP clients. public static class McpClientFactory { + /// Default client options to use when none are supplied. + private static readonly McpClientOptions s_defaultClientOptions = CreateDefaultClientOptions(); + + /// Creates default client options to use when no options are supplied. + private static McpClientOptions CreateDefaultClientOptions() + { + var asmName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName(); + return new() + { + ClientInfo = new() + { + Name = asmName.Name ?? "McpClient", + Version = asmName.Version?.ToString() ?? "1.0.0", + }, + }; + } + /// Creates an , connecting it to the specified server. /// Configuration for the target server to which the client should connect. /// @@ -36,20 +53,7 @@ public static async Task CreateAsync( { Throw.IfNull(serverConfig); - if (clientOptions is null) - { - var asmName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName(); - - clientOptions ??= new() - { - ClientInfo = new() - { - Name = asmName.Name ?? "McpClient", - Version = asmName.Version?.ToString() ?? "1.0.0", - }, - }; - } - + clientOptions ??= s_defaultClientOptions; createTransportFunc ??= CreateTransport; string endpointName = $"Client ({serverConfig.Id}: {serverConfig.Name})"; From 61f13bea148bcbcafaea5ad06aa8bc1a679784e4 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 24 Mar 2025 10:52:24 -0400 Subject: [PATCH 3/3] Update src/ModelContextProtocol/Client/McpClientFactory.cs Co-authored-by: Eirik Tsarpalis --- src/ModelContextProtocol/Client/McpClientFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ModelContextProtocol/Client/McpClientFactory.cs b/src/ModelContextProtocol/Client/McpClientFactory.cs index 141ff38ef..691d165af 100644 --- a/src/ModelContextProtocol/Client/McpClientFactory.cs +++ b/src/ModelContextProtocol/Client/McpClientFactory.cs @@ -19,7 +19,7 @@ public static class McpClientFactory /// Creates default client options to use when no options are supplied. private static McpClientOptions CreateDefaultClientOptions() { - var asmName = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName(); + var asmName = (Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly()).GetName(); return new() { ClientInfo = new()