Skip to content

Add OutputSchemaType and OutputSchema for tools returning CallToolResult#1272

Open
Copilot wants to merge 6 commits intomainfrom
copilot/support-output-schema-independently
Open

Add OutputSchemaType and OutputSchema for tools returning CallToolResult#1272
Copilot wants to merge 6 commits intomainfrom
copilot/support-output-schema-independently

Conversation

Copy link
Contributor

Copilot AI commented Feb 14, 2026

  • Add Type? OutputSchemaType property to McpServerToolAttribute
  • Add JsonElement? OutputSchema property to McpServerToolCreateOptions
  • Update DeriveOptions in AIFunctionMcpServerTool to generate schema from OutputSchemaType via AIJsonUtilities.CreateJsonSchema
  • Update CreateOutputSchema in AIFunctionMcpServerTool to use the explicit OutputSchema from options when provided
  • Force UseStructuredContent = true when OutputSchemaType is set in DeriveOptions
  • Force UseStructuredContent = true when OutputSchema is set via options
  • Add Debug.Assert in CreateOutputSchema verifying UseStructuredContent is true
  • Update Clone() method to include OutputSchema
  • Add tests for OutputSchemaType on the attribute using lambda syntax
  • Add tests for OutputSchema on create options with using for JsonDocument
  • Add test for OutputSchemaType with UseStructuredContent=false to verify it's overridden
  • Update documentation comments to say "to a non-null value"
  • Run full test suite - all tests pass
  • Run code review
  • Run CodeQL security check - no alerts
  • Refactor OutputSchemaType check to use ??= pattern per review feedback
  • Remove unnecessary [DynamicallyAccessedMembers] from OutputSchemaType
Original prompt

This section details on the original issue you should resolve

<issue_title>Support specifying OutputSchema type independently of return type for tools returning CallToolResult</issue_title>
<issue_description>Is your feature request related to a problem? Please describe.
When a tool method returns CallToolResult directly (to control properties like Meta, IsError, or manually populate StructuredContent), the SDK cannot generate a meaningful OutputSchema for the tool. The output schema either reflects the shape of CallToolResult itself—which is an internal protocol type and useless to clients—or is omitted entirely.

This forces developers to choose between:

  • Returning a strongly-typed object to get a proper OutputSchema, but losing control over Meta, IsError, and other CallToolResult properties.
  • Returning CallToolResult for full response control, but with no way to advertise the actual shape of StructuredContent to clients.

Describe the solution you'd like
Allow developers to specify the output schema type independently of the return type, for example via an attribute parameter or a create option:

// Option A: Attribute-based
[McpServerTool(UseStructuredContent = true, OutputSchemaType = typeof(MyResponseType))]
public static CallToolResult my_tool(string input) { ... }



// Option B: Via McpServerToolCreateOptions
McpServerToolCreateOptions options = new()
{
    UseStructuredContent = true,
    OutputSchemaType = typeof(MyResponseType),
};

When OutputSchemaType is set, the SDK would generate the OutputSchema from that type while still allowing the method to return CallToolResult with full control over the response.

Describe alternatives you've considered

  • Returning a typed object: Works for simple cases, but precludes setting Meta, IsError, or customizing Content independently of StructuredContent.
  • Schema node transformation workaround: Creating a stub tool with McpServerTool.Create(() => new T()) solely to extract its output schema, then using SchemaCreateOptions.TransformSchemaNode on the real tool to replace the CallToolResult schema with the stub's. This works but is fragile, non-obvious, and requires significant boilerplate.
  • Manually setting StructuredContent without UseStructuredContent: Provides structured data to clients but no OutputSchema is advertised in tools/list, so clients can't validate or understand the response shape ahead of time.

Additional context
This gap is particularly impactful in scenarios where tools need to return both structured content and out-of-band metadata via CallToolResult.Meta. 
</issue_description>

<agent_instructions>Add a Type? OutputSchemaType property to McpServerToolAttribute and a JsonElement? OutputSchema property to McpServerToolCreateOptions. The OutputSchema property, if it exists, will just be used to initialize the OutputSchema on the creation options using AIJsonUtilities. This let's someone using the attribute specify a type, and someone using the options bag directly either specify a type (with a call to AIJsonUtilities to create the schema) or just supply their own custom schema. If the schema is specified, UseStructuredOutput should be forced to true in the options bag. Make sure to add good test coverage of this in the existing test files. Also ensure any existing docs about UseStructuredOutput and [McpServerTool] are updated appropriately.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…pServerToolCreateOptions

- Added Type? OutputSchemaType property to McpServerToolAttribute
- Added JsonElement? OutputSchema property to McpServerToolCreateOptions
- Updated DeriveOptions to generate schema from OutputSchemaType via AIJsonUtilities.CreateJsonSchema
- Updated CreateOutputSchema to use explicit OutputSchema when provided (takes precedence)
- OutputSchema forces structured content behavior even if UseStructuredContent is false
- Updated Clone() to preserve OutputSchema
- Added 8 new tests covering various scenarios
- Updated XML documentation

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Support specifying OutputSchema type for CallToolResult Add OutputSchemaType and OutputSchema for tools returning CallToolResult Feb 14, 2026
Copilot AI requested a review from stephentoub February 14, 2026 13:53
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…namicallyAccessedMembers

- In DeriveOptions, force UseStructuredContent=true when OutputSchemaType is set
- Simplify GetToolDescription to only check UseStructuredContent (no longer needs OutputSchema check)
- Remove unnecessary [DynamicallyAccessedMembers] from OutputSchemaType property

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copy link
Contributor

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…tests, add using for JsonDocument

- Add Debug.Assert in CreateOutputSchema to verify UseStructuredContent is true when OutputSchema is set
- Force UseStructuredContent=true in DeriveOptions when OutputSchema is set via options
- Update docs to say "to a non-null value" for UseStructuredContent auto-enable
- Add using statements for all JsonDocument.Parse calls in tests
- Rewrite attribute-based tests to use lambdas instead of reflection
- Add test for OutputSchemaType with UseStructuredContent=false to verify it's overridden
- Remove OutputSchemaTypeTools helper class (no longer needed)

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
@stephentoub stephentoub enabled auto-merge (squash) February 14, 2026 22:52
@mikekistler
Copy link
Contributor

@copilot Please give a short explanation of the feature added in this PR from the perspective of a user of the SDK.

Copy link
Contributor Author

Copilot AI commented Feb 15, 2026

@copilot Please give a short explanation of the feature added in this PR from the perspective of a user of the SDK.

When a tool method returns CallToolResult directly (e.g. to set IsError, custom Content, or StructuredContent), the SDK previously couldn't generate a meaningful OutputSchema for it — the schema would either reflect CallToolResult itself or be omitted entirely.

This PR lets you specify the output schema type independently of the return type:

Via attribute:

[McpServerTool(OutputSchemaType = typeof(MyResponse))]
public static CallToolResult MyTool(string input)
{
    var response = new MyResponse { Name = "test", Score = 42 };
    return new CallToolResult
    {
        Content = [new TextContentBlock { Text = "done" }],
        StructuredContent = JsonSerializer.SerializeToNode(response),
    };
}

Via create options:

McpServerTool.Create(myDelegate, new McpServerToolCreateOptions
{
    OutputSchema = myJsonSchemaElement, // a JsonElement with the schema
});

In both cases, the tool's OutputSchema in tools/list will reflect the specified type (e.g. MyResponse), so clients can understand and validate the structured content shape — while the method still has full control over CallToolResult properties. Setting either property automatically enables UseStructuredContent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support specifying OutputSchema type independently of return type for tools returning CallToolResult

3 participants