Skip to content

Conversation

tzolov
Copy link
Contributor

@tzolov tzolov commented Sep 14, 2025

  • Change CallToolResult.structuredContent type from Map<String,Object> to Object to support both objects and arrays
  • Update JsonSchemaValidator to validate any Object type, not just Maps
  • Add test cases for array-type structured output validation
  • Fix type casting in existing tests to handle the more generic Object type
  • Deprecate CallToolResult constructors in favor of builder pattern
  • Update server implementations to use CallToolResult builder

This allows tools to return arrays as structured content, not just objects, which is required by the MCP specification for tools with array-type output schemas.

It is related to MPC spec fix: modelcontextprotocol/modelcontextprotocol#834

Fixes #550

This PR extends the MCP Java SDK to support arrays as structured content output from tools, not just objects. The main change is updating CallToolResult.structuredContent from Map<String,Object> to Object to accommodate both object and array return types.

Motivation and Context

This change addresses issue #550. Previously, the SDK only supported tools that returned objects as structured content, but the MCP specification allows tools to define output schemas that return arrays. This limitation prevented developers from creating tools that return lists of items (e.g., a tool that returns a list of users, search results, or any collection of structured data).

Without this change, tools with array-type output schemas would fail validation or cause runtime errors when trying to return array data.

How Has This Been Tested?

  • Added new test cases testStructuredOutputOfObjectArrayValidationSuccess in multiple test classes to verify array support
  • Existing tests updated to handle the new Object type with proper casting
  • Test coverage includes:
    • HttpClient transport
    • WebFlux transport (where applicable)
    • Stateless and stateful server implementations
    • JSON schema validation for array types

Breaking Changes

Yes, this is a breaking change. Users will need to update their code when:

  1. Accessing CallToolResult.structuredContent(): Add explicit casting since it now returns Object instead of Map<String,Object>

    // Before
    result.structuredContent().get("key")
    
    // After
    ((Map<String,Object>) result.structuredContent()).get("key")
  2. Creating CallToolResult instances: The constructors are deprecated, use the builder pattern

    // Before
    new CallToolResult(content, isError, structuredContent)
    
    // After
    CallToolResult.builder()
      .content(content)
      .isError(isError)
      .structuredContent(structuredContent)
      .build()
  3. Implementing custom JsonSchemaValidator: Update the validate method signature

    // Before
    validate(Map<String,Object> schema, Map<String,Object> structuredContent)
    
    // After
    validate(Map<String,Object> schema, Object structuredContent)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Design Decisions:

  • Changed structuredContent to Object type to support both Maps and Lists, aligning with JSON's two container types
  • Used builder pattern for CallToolResult to provide a cleaner API and better forward compatibility
  • Maintained backward compatibility where possible by keeping deprecated constructors (marked with @Deprecated)
  • Updated all server implementations (McpAsyncServer, McpStatelessAsyncServer) to use the builder pattern consistently

Migration Path:

The deprecated constructors are retained for backward compatibility but should be removed in a future major version. Users are encouraged to migrate to the builder pattern immediately.

Related MCP Specification:

This change aligns with the MCP Tools Specification which states that structured content can be any valid JSON value, including arrays.

modelcontextprotocol/modelcontextprotocol#834

- Change CallToolResult.structuredContent type from Map<String,Object> to Object
  to support both objects and arrays
- Update JsonSchemaValidator to validate any Object type, not just Maps
- Add test cases for array-type structured output validation
- Fix type casting in existing tests to handle the more generic Object type
- Deprecate CallToolResult constructors in favor of builder pattern
- Update server implementations to use CallToolResult builder

This allows tools to return arrays as structured content, not just objects,
which is required by the MCP specification for tools with array-type output schemas.

It is realated to MPC spec fix: modelcontextprotocol/modelcontextprotocol#834

Fixes modelcontextprotocol#550

BREAKING CHANGE!

Migration Guide:
----------------
1. If you're accessing CallToolResult.structuredContent():
   - Add explicit casting when you know it's a Map:
     Before: result.structuredContent().get("key")
     After:  ((Map<String,Object>) result.structuredContent()).get("key")

   - Or check the type first:
     if (result.structuredContent() instanceof Map) {
         Map<String,Object> map = (Map<String,Object>) result.structuredContent();
         // use map
     } else if (result.structuredContent() instanceof List) {
         List<?> list = (List<?>) result.structuredContent();
         // use list
     }

2. If you're creating CallToolResult instances:
   - Switch to using the builder pattern:
     Before: new CallToolResult(content, isError, structuredContent)
     After:  CallToolResult.builder()
               .content(content)
               .isError(isError)
               .structuredContent(structuredContent)
               .build()

3. If you're implementing JsonSchemaValidator:
   - Update your validate() method signature:
     Before: validate(Map<String,Object> schema, Map<String,Object> structuredContent)
     After:  validate(Map<String,Object> schema, Object structuredContent)

Signed-off-by: Christian Tzolov <[email protected]>
@tzolov tzolov added this to the 0.13.0 milestone Sep 14, 2025
@Kehrlann
Copy link
Contributor

@tzolov looks good.

The title could be improved. As you state in "Related MCP specification", structured content be any valid JSON, not just arrays (incl: booleans, strings, numbers)

@tzolov tzolov changed the title fix: support arrays in tool structured content output fix: support valid JSON value in tool structured content output Sep 15, 2025
@tzolov tzolov merged commit d8959ef into modelcontextprotocol:main Sep 15, 2025
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SEP-834 Support full JSON Schema 2020-12 The CallToolResult#structuredContent type is insufficient to represent list or array of objects.
2 participants