Skip to content

tools list does not contain descriptions for nested properties in the input schema #1240

@Jarryd-Lansdell

Description

@Jarryd-Lansdell

Is your feature request related to a problem? Please describe.
When calling list_tools the json structure generated does not have descriptions for the models properties and nested properties. In the image below the left-hand side is what I have used with a custom tools registration to include various metadata about the properties. I made the code traverse all nested classes and include their xml summary as descriptions for the properties.

Image

Describe the solution you'd like
I would like to remove my tool registration and use the standard 'WithToolsFromAssembly' and let it handle everything for me.

Describe alternatives you've considered
I've created my own work around for now.

Additional context
Here is a class and a unit test(Xunit) that I am using to verify when the fix is in.


 /// <summary>
 /// A sample MCP tool container used to verify extraction of XML summaries from instance methods.
 /// </summary>
 [McpServerToolType]
 public partial class McpTestToolsForXMLSummary
 {
     public string Prefix { get; set; } = "Hello from C#: ";

     /// <summary>
     /// Returns the provided message with the configured `Prefix` to validate summary extraction
     /// for instance methods when generating MCP tool descriptions.
     /// </summary>
     /// <param name="message">A non-empty message to be returned with the current prefix.</param>
     /// <param name="testObject">A sample object used to confirm parameter documentation is included in tool metadata.</param>
     /// <returns>A string that concatenates `Prefix` and `message`.</returns>
     [McpServerTool]
     public partial string SeeSummary([Required] string message, [Required] TestObject? testObject) => $"{Prefix}{message}";
 }
public void BuilderRegistration_ToolHasRequiredParameters_RequiredIsReflectedInSchema()
{
    // Arrange: use the test tools assembly that defines the 'echo' MCP tool
    var exampleTypeFromApplication = typeof(McpTestToolsForXMLSummary);
    var path = (exampleTypeFromApplication.Assembly.GetName().Name ?? "") + ".xml";
    XmlDocHelper.LoadXmlDocumentation(path);
    var toolAssembly = Assembly.GetAssembly(exampleTypeFromApplication)!;

    // Build via McpServerBuilder extensions instead of McpToolHandler
    var sc = new ServiceCollection();
    sc.AddMcpServer().WithToolsFromAssembly(toolAssembly);

    // Act
    var serviceProvider = sc.BuildServiceProvider();
    var toolsAsServices = serviceProvider.GetServices<McpServerTool>();
    var echoTool = toolsAsServices.FirstOrDefault(x => x?.ProtocolTool?.Name == "see_summary");

    // Assert
    Assert.NotNull(echoTool);
    // Tool-level description from XML summary
    Assert.False(string.IsNullOrWhiteSpace(echoTool!.ProtocolTool.Description));
    Assert.True(echoTool!.ProtocolTool.InputSchema.TryGetProperty("properties", out var properties));

    // Prefer canonical JSON Schema: parent-level required array
    Assert.True(echoTool!.ProtocolTool.InputSchema.TryGetProperty("required", out var requiredArray));
    var requiredList = requiredArray.EnumerateArray().Select(e => e.GetString()).ToList();
    Assert.Contains("message", requiredList);
    Assert.Contains("testObject", requiredList);

    // Also check nullable hints are present on nodes (implementation detail)
    Assert.True(properties.TryGetProperty("message", out var messageSchema));
    if (messageSchema.TryGetProperty("nullable", out var messageNullable))
    {
        Assert.False(messageNullable.GetBoolean());
    }
    // Property description for 'message'
    Assert.True(messageSchema.TryGetProperty("description", out var messageDescription));
    Assert.False(string.IsNullOrWhiteSpace(messageDescription.GetString()));
    Assert.True(properties.TryGetProperty("testObject", out var objSchema));
    if (objSchema.TryGetProperty("nullable", out var objNullable))
    {
        Assert.False(objNullable.GetBoolean());
    }
    // Property description for 'testObject'
    Assert.True(objSchema.TryGetProperty("description", out var objDescription));
    Assert.False(string.IsNullOrWhiteSpace(objDescription.GetString()));

    // Check nested property description matches XML summary on TestObject.TestProperty
    Assert.True(objSchema.TryGetProperty("properties", out var objProperties));
    Assert.True(objProperties.TryGetProperty("TestProperty", out var nestedPropSchema));
    Assert.True(nestedPropSchema.TryGetProperty("description", out var nestedDescription));
    Assert.Equal("An example property used to validate XML documentation propagation to schema nodes.", nestedDescription.GetString());
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions