diff --git a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs index 562a8476c..6b0cf34b2 100644 --- a/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs +++ b/src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs @@ -162,7 +162,7 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions( { Name = options?.Name ?? function.Name, Description = options?.Description ?? function.Description, - InputSchema = function.JsonSchema, + InputSchema = FilterJsonSchema(function.JsonSchema), }; if (options is not null) @@ -220,6 +220,45 @@ options.OpenWorld is not null || return newOptions; } + /// + /// Filters a JsonElement containing a schema to only include allowed properties: "type", "properties", and "required". + /// + private static JsonElement FilterJsonSchema(JsonElement schema) + { + using var memoryStream = new MemoryStream(); + using var writer = new Utf8JsonWriter(memoryStream); + + writer.WriteStartObject(); + + // Include "type" property if it exists + if (schema.TryGetProperty("type", out var typeElement)) + { + writer.WritePropertyName("type"); + typeElement.WriteTo(writer); + } + + // Include "properties" property if it exists + if (schema.TryGetProperty("properties", out var propertiesElement)) + { + writer.WritePropertyName("properties"); + propertiesElement.WriteTo(writer); + } + + // Include "required" property if it exists + if (schema.TryGetProperty("required", out var requiredElement)) + { + writer.WritePropertyName("required"); + requiredElement.WriteTo(writer); + } + + writer.WriteEndObject(); + writer.Flush(); + + memoryStream.Position = 0; + using var document = JsonDocument.Parse(memoryStream.ToArray()); + return document.RootElement.Clone(); + } + /// Gets the wrapped by this tool. internal AIFunction AIFunction { get; } diff --git a/src/ModelContextProtocol/Utils/Json/McpJsonUtilities.cs b/src/ModelContextProtocol/Utils/Json/McpJsonUtilities.cs index b759ba975..b5b7ac066 100644 --- a/src/ModelContextProtocol/Utils/Json/McpJsonUtilities.cs +++ b/src/ModelContextProtocol/Utils/Json/McpJsonUtilities.cs @@ -68,8 +68,12 @@ internal static bool IsValidMcpToolSchema(JsonElement element) { return false; } - - return true; // No need to check other properties + } + else if (!(property.NameEquals("properties") || + property.NameEquals("required"))) + { + // Only "type", "properties", and "required" are allowed. + return false; } } diff --git a/tests/ModelContextProtocol.Tests/Protocol/ProtocolTypeTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ProtocolTypeTests.cs index 7821d2ae6..1ad709749 100644 --- a/tests/ModelContextProtocol.Tests/Protocol/ProtocolTypeTests.cs +++ b/tests/ModelContextProtocol.Tests/Protocol/ProtocolTypeTests.cs @@ -30,6 +30,7 @@ public static void ToolInputSchema_HasValidDefaultSchema() [InlineData("""{"type":"number"}""")] [InlineData("""{"type":"array"}""")] [InlineData("""{"type":["object"]}""")] + [InlineData("""{"type":"object", "title": "MyAwesomeTool", "description": "It's awesome!", "properties": {}, "required" : ["NotAParam"] }""")] public static void ToolInputSchema_RejectsInvalidSchemaDocuments(string invalidSchema) { using var document = JsonDocument.Parse(invalidSchema); @@ -41,7 +42,7 @@ public static void ToolInputSchema_RejectsInvalidSchemaDocuments(string invalidS [Theory] [InlineData("""{"type":"object"}""")] [InlineData("""{"type":"object", "properties": {}, "required" : [] }""")] - [InlineData("""{"type":"object", "title": "MyAwesomeTool", "description": "It's awesome!", "properties": {}, "required" : ["NotAParam"] }""")] + [InlineData("""{"type":"object", "properties": {}, "required" : ["NotAParam"] }""")] public static void ToolInputSchema_AcceptsValidSchemaDocuments(string validSchema) { using var document = JsonDocument.Parse(validSchema);