using System.Text.Json; using Microsoft.Extensions.AI; using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; string endpoint = Environment.GetEnvironmentVariable("MCP_ENDPOINT") ?? throw new InvalidOperationException( "Set MCP_ENDPOINT to your reviewed https://.../mcp endpoint."); static Dictionary Args(params (string Name, object? Value)[] values) => values.ToDictionary(value => value.Name, value => value.Value); var transport = new HttpClientTransport(new HttpClientTransportOptions { Endpoint = new Uri(endpoint), Name = "open-for-agents", TransportMode = HttpTransportMode.StreamableHttp, ConnectionTimeout = TimeSpan.FromSeconds(20), OwnsSession = false, }); await using McpClient client = await McpClient.CreateAsync(transport); IList tools = await client.ListToolsAsync(); foreach (McpClientTool tool in tools) { Tool protocolTool = tool.ProtocolTool; if (protocolTool.Annotations?.ReadOnlyHint != true || protocolTool.Annotations?.OpenWorldHint != false || tool is not AIFunction) { throw new InvalidOperationException( $"Unexpected metadata for {tool.Name}."); } } var calls = new Dictionary { ["get_site_info"] = await client.CallToolAsync("get_site_info"), ["list_posts"] = await client.CallToolAsync("list_posts", Args(("per_page", 3))), ["woo_search_products"] = await client.CallToolAsync("woo_search_products", Args(("per_page", 3))), }; if (calls.Values.Any(result => result.IsError == true)) { throw new InvalidOperationException("A representative read returned an error."); } CallToolResult large = await client.CallToolAsync( "woo_search_products", Args(("per_page", 6))); if (large.IsError == true || large.Content.FirstOrDefault() is not TextContentBlock textBlock) { throw new InvalidOperationException("The large read was not successful."); } string[] textParts = textBlock.Text.Split("\n\n", 2); using JsonDocument projection = JsonDocument.Parse(textParts[1]); JsonElement bounded = projection.RootElement.GetProperty("_open_for_agents"); if (!bounded.GetProperty("truncated").GetBoolean() || bounded.GetProperty("reason").GetString() != "output_budget" || bounded.GetProperty("max_chars").GetInt32() != 1500) { throw new InvalidOperationException( "The bounded output contract did not match."); } Console.WriteLine(JsonSerializer.Serialize(new { endpoint, tools = tools.Select(tool => new { tool.Name, annotations = tool.ProtocolTool.Annotations, agentFrameworkFunction = tool is AIFunction, }), representativeCalls = calls.ToDictionary( item => item.Key, item => item.Value.StructuredContent), boundedOutput = JsonSerializer.Deserialize(bounded.GetRawText()), }, new JsonSerializerOptions { WriteIndented = true }));