diff --git a/src/ModelContextProtocol/McpJsonUtilities.cs b/src/ModelContextProtocol/McpJsonUtilities.cs index ca9748437..fe7b8970c 100644 --- a/src/ModelContextProtocol/McpJsonUtilities.cs +++ b/src/ModelContextProtocol/McpJsonUtilities.cs @@ -2,6 +2,7 @@ using ModelContextProtocol.Protocol; using System.Diagnostics.CodeAnalysis; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -75,6 +76,25 @@ internal static bool IsValidMcpToolSchema(JsonElement element) return false; // No type keyword found. } + private static readonly string[] s_rootSchemaKeywordsToRemove = ["title", "description"]; + internal static AIJsonSchemaCreateOptions DefaultSchemaCreateOptions { get; } = new() + { + TransformOptions = new() + { + TransformSchemaNode = static (ctx, node) => + { + if (ctx.Path is [] && node is JsonObject obj) + { + foreach (string keywordToRemove in s_rootSchemaKeywordsToRemove) + { + obj.Remove(keywordToRemove); + } + } + return node; + }, + } + }; + // Keep in sync with CreateDefaultOptions above. [JsonSourceGenerationOptions(JsonSerializerDefaults.Web, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, diff --git a/src/ModelContextProtocol/Server/AIFunctionMcpServerPrompt.cs b/src/ModelContextProtocol/Server/AIFunctionMcpServerPrompt.cs index 9e1cdbcea..ed1bd3cc8 100644 --- a/src/ModelContextProtocol/Server/AIFunctionMcpServerPrompt.cs +++ b/src/ModelContextProtocol/Server/AIFunctionMcpServerPrompt.cs @@ -151,7 +151,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions( return null; } }, - JsonSchemaCreateOptions = options?.SchemaCreateOptions, + JsonSchemaCreateOptions = options?.SchemaCreateOptions ?? McpJsonUtilities.DefaultSchemaCreateOptions, }; /// Creates an that wraps the specified . diff --git a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs index 366eb23cd..60820c254 100644 --- a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs +++ b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs @@ -160,7 +160,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions( return null; } }, - JsonSchemaCreateOptions = options?.SchemaCreateOptions, + JsonSchemaCreateOptions = options?.SchemaCreateOptions ?? McpJsonUtilities.DefaultSchemaCreateOptions, }; /// Creates an that wraps the specified . diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs index cb98d9bce..b33d50705 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs @@ -3,6 +3,7 @@ using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; using Moq; +using System.ComponentModel; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; @@ -381,6 +382,23 @@ public async Task SupportsSchemaCreateOptions() ); } + [Fact] + public void TrimsDescriptionAndTitleKeywordsFromRootSchema() + { + McpServerTool tool = McpServerTool.Create(Add); + + Assert.Equal("My awesome adding tool", tool.ProtocolTool.Description); + Assert.False(tool.ProtocolTool.InputSchema.TryGetProperty("description", out _)); + Assert.False(tool.ProtocolTool.InputSchema.TryGetProperty("title", out _)); + + // Preserves any nested description keywords + Assert.Equal("The first argument", tool.ProtocolTool.InputSchema.GetProperty("properties").GetProperty("a").GetProperty("description").GetString()); + Assert.Equal("The second argument", tool.ProtocolTool.InputSchema.GetProperty("properties").GetProperty("b").GetProperty("description").GetString()); + + [Description("My awesome adding tool")] + static int Add([Description("The first argument")] int a , [Description("The second argument")] int b) => a + b; + } + private sealed class MyService; private class DisposableToolType : IDisposable