From abacb03486d83e5fff232196104d84ae8e437006 Mon Sep 17 00:00:00 2001 From: Krzysztof Cwalina Date: Thu, 26 Jun 2025 16:36:57 -0700 Subject: [PATCH 1/3] mcp usage sample --- docs/guides/mcp/chat_with_mcp.cs | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/guides/mcp/chat_with_mcp.cs diff --git a/docs/guides/mcp/chat_with_mcp.cs b/docs/guides/mcp/chat_with_mcp.cs new file mode 100644 index 000000000..68ed83f15 --- /dev/null +++ b/docs/guides/mcp/chat_with_mcp.cs @@ -0,0 +1,92 @@ +// SAMPLE: call the official MCP library for .NET + +#:package OpenAI@2.2.*-* +#:package ModelContextProtocol.Core@*-* +#:property PublishAot=false + +using ModelContextProtocol.Client; +using OpenAI.Chat; + +string mcpCommand = Environment.GetEnvironmentVariable("MCP_SERVER_COMMAND")!; // path to a stdio mcp server +string key = Environment.GetEnvironmentVariable("OPENAI_KEY")!; + +ChatClient chatClient = new("gpt-4.1", key); + +// Create MCP client to connect to the server +IMcpClient mcpClient = await McpClientFactory.CreateAsync(new StdioClientTransport(new StdioClientTransportOptions +{ + Name = "MyServer", + Command = mcpCommand, + Arguments = [], +})); + +// get avaliable tools and add them to completion options +ChatCompletionOptions options = new(); +await foreach (McpClientTool chatTool in mcpClient.EnumerateToolsAsync()) +{ + options.Tools.Add(chatTool.AsOpenAIChatTool()); +} + +// conversation thread +List conversation = new() +{ + ChatMessage.CreateUserMessage("Use an MCP tool") +}; + +start: +ChatCompletion chatCompletion = chatClient.CompleteChat(conversation, options); +conversation.Add(ChatMessage.CreateAssistantMessage(chatCompletion)); +switch (chatCompletion.FinishReason) +{ + case ChatFinishReason.ToolCalls: + foreach(var call in chatCompletion.ToolCalls) + { + var mcpArguments = call.FunctionArguments.ToObjectFromJson>(); + Console.WriteLine("Tool call detected, calling MCP server..."); + ModelContextProtocol.Protocol.CallToolResult result = await mcpClient.CallToolAsync(call.FunctionName, mcpArguments!); + Console.WriteLine($"tool call result {result.Content[0]}"); + ChatMessage message = result.ToMessage(call); + conversation.Add(message); + } + goto start; + case ChatFinishReason.Stop: + Console.WriteLine(chatCompletion.Content[0].Text); + break; + default: + throw new NotImplementedException(); +} + +#region TEMPORARY +// this is temporary. all these APIs will endup being in one of the packages used here. +public static class TemporaryExtensions +{ + public static ChatMessage ToMessage(this ModelContextProtocol.Protocol.CallToolResult mcpCallResult, ChatToolCall openaiCall) + { + List parts = new(); + var sc = mcpCallResult.StructuredContent; + + foreach (ModelContextProtocol.Protocol.ContentBlock block in mcpCallResult.Content) + { + if (block is ModelContextProtocol.Protocol.TextContentBlock textContent) + { + parts.Add(ChatMessageContentPart.CreateTextPart(textContent.Text)); + } + else + { + throw new NotImplementedException(); + } + } + ToolChatMessage message = ChatMessage.CreateToolMessage(openaiCall.Id, parts); + return message; + } + + // this is in the adapter package; waiting for package to be dropped. + public static ChatTool AsOpenAIChatTool(this Microsoft.Extensions.AI.AIFunction mcpTool) + { + return ChatTool.CreateFunctionTool( + mcpTool.Name, + mcpTool.Description, + BinaryData.FromString(mcpTool.JsonSchema.GetRawText())); + } +} +#endregion \ No newline at end of file From 422fb5daa91d7c6906402be32bde61500564bb6d Mon Sep 17 00:00:00 2001 From: Krzysztof Cwalina Date: Fri, 27 Jun 2025 13:56:44 -0700 Subject: [PATCH 2/3] simplified dependencies of ToMessage --- docs/guides/mcp/chat_with_mcp.cs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/guides/mcp/chat_with_mcp.cs b/docs/guides/mcp/chat_with_mcp.cs index 68ed83f15..a67fcdbf2 100644 --- a/docs/guides/mcp/chat_with_mcp.cs +++ b/docs/guides/mcp/chat_with_mcp.cs @@ -4,6 +4,8 @@ #:package ModelContextProtocol.Core@*-* #:property PublishAot=false +using System.Text.Json; +using ModelContextProtocol; using ModelContextProtocol.Client; using OpenAI.Chat; @@ -45,7 +47,7 @@ Console.WriteLine("Tool call detected, calling MCP server..."); ModelContextProtocol.Protocol.CallToolResult result = await mcpClient.CallToolAsync(call.FunctionName, mcpArguments!); Console.WriteLine($"tool call result {result.Content[0]}"); - ChatMessage message = result.ToMessage(call); + ChatMessage message = call.ToMessage(result.Content.ToAIContents()); conversation.Add(message); } goto start; @@ -60,27 +62,23 @@ // this is temporary. all these APIs will endup being in one of the packages used here. public static class TemporaryExtensions { - public static ChatMessage ToMessage(this ModelContextProtocol.Protocol.CallToolResult mcpCallResult, ChatToolCall openaiCall) + // this needs to be in the adapter package + public static ChatMessage ToMessage(this ChatToolCall openaiCall, IEnumerable contents) { List parts = new(); - var sc = mcpCallResult.StructuredContent; - - foreach (ModelContextProtocol.Protocol.ContentBlock block in mcpCallResult.Content) + foreach (Microsoft.Extensions.AI.AIContent content in contents) { - if (block is ModelContextProtocol.Protocol.TextContentBlock textContent) - { - parts.Add(ChatMessageContentPart.CreateTextPart(textContent.Text)); - } - else - { - throw new NotImplementedException(); - } + string serialized = JsonSerializer.Serialize(content.RawRepresentation); + using JsonDocument json = JsonDocument.Parse(serialized); + JsonElement text = json.RootElement.GetProperty("text"); + string textValue = text.GetString() ?? string.Empty; + parts.Add(ChatMessageContentPart.CreateTextPart(textValue)); } ToolChatMessage message = ChatMessage.CreateToolMessage(openaiCall.Id, parts); return message; } - // this is in the adapter package; waiting for package to be dropped. + // this is in the adapter package public static ChatTool AsOpenAIChatTool(this Microsoft.Extensions.AI.AIFunction mcpTool) { return ChatTool.CreateFunctionTool( From f8d5c6f86873342cf95a72df14a740951900b8ab Mon Sep 17 00:00:00 2001 From: Krzysztof Cwalina Date: Fri, 27 Jun 2025 14:52:34 -0700 Subject: [PATCH 3/3] cleaned up usings --- docs/guides/mcp/chat_with_mcp.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/guides/mcp/chat_with_mcp.cs b/docs/guides/mcp/chat_with_mcp.cs index a67fcdbf2..620c8520e 100644 --- a/docs/guides/mcp/chat_with_mcp.cs +++ b/docs/guides/mcp/chat_with_mcp.cs @@ -4,7 +4,6 @@ #:package ModelContextProtocol.Core@*-* #:property PublishAot=false -using System.Text.Json; using ModelContextProtocol; using ModelContextProtocol.Client; using OpenAI.Chat; @@ -68,9 +67,9 @@ public static ChatMessage ToMessage(this ChatToolCall openaiCall, IEnumerable parts = new(); foreach (Microsoft.Extensions.AI.AIContent content in contents) { - string serialized = JsonSerializer.Serialize(content.RawRepresentation); - using JsonDocument json = JsonDocument.Parse(serialized); - JsonElement text = json.RootElement.GetProperty("text"); + string serialized = System.Text.Json.JsonSerializer.Serialize(content.RawRepresentation); + using System.Text.Json.JsonDocument json = System.Text.Json.JsonDocument.Parse(serialized); + System.Text.Json.JsonElement text = json.RootElement.GetProperty("text"); string textValue = text.GetString() ?? string.Empty; parts.Add(ChatMessageContentPart.CreateTextPart(textValue)); }