Skip to content

SEP-834 Support full JSON Schema 2020-12 #834

Closed
modelcontextprotocol/java-sdk
#551

Description

@jpmcb

Preamble

Title: Support full JSON Schema 2020-12
Co-author: John McBride - @jpmcb
Co-author: Ola Hungerford - @olaservo (adopted from #655 per #881 (comment))
Status: proposal
PR: #881

Abstract

There's a major discrepancy with how 2025-06-18 handles structuredContent in relation to outputSchema being defined as JSON Schema.

Because structuredContent must be an object, the types that can be returned by servers and validated by clients (like type: array which is fully supported by JSON Schema but throws errors in Inspector, the Typescript-SDK, etc) is dramatically narrow.

Further, which draft of JSON Schema clients and servers use is not well defined leading to general drift among client, SDK, and server implementations. For example, JSON Schema specs in one client that uses JSON Schema draft-07 will be very different from another that uses 2020-12.

Related:

This SEP loosens JSON schema usage across clients and servers as well as defines using JSON Schema 2020-12

Motivation

In 5.1 Data Types: Tool, the spec states:

outputSchema: Optional JSON Schema defining expected output structure

JSON Schema may be of type object, array, string, null, etc. etc. If it's valid JSON, then JSON Schema can validate it.

But the spec then goes on, in 5.2.6 Structured Content, to state:

Structured content is returned as a JSON object in the structuredContent field of a result.

This represents a dramatic discrepency and severly limits what type of content can be returned by a server and validated in the structured content.

Consider the following valid JSON Schema:

{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "id": {
        "type": "string",
        "description": "A unique UUID"
      }
    },
    "required": ["id"],
    "additionalProperties": false
  }
}

which defines an array of objects where the objects must have an id property. This is pretty typical, especially in "list" type of operations.

Now, suppose a server responds with the following JSON as structured content:

[
  { "id": "abc123" },
  { "id": "xyz987" }
]

The spec would consider this structured content invalid and non-verifiable by the output schema.


Originally, the MCP specification didn't explicitly state which JSON Schema version to use for tool inputSchema/outputSchema and elicitation requestedSchema, causing compatibility issues between implementations. Different clients and servers assume different versions, leading to validation failures and runtime errors. See: https://github.com/orgs/modelcontextprotocol/discussions/366 (discussion includes links to related issues)

(original context before merged with this SEP: #655)

Specification

  • Clarifies JSON Schema dialect usage for embedded schemas within MCP messages by establishing 2020-12 as the default dialect and allowing explicit dialect declaration via the $schema field.
  • Loosens inputSchema to be an object with type: object and any additional property to support JSON schema Draft 2020-12 and more powerful validation compositions (like anyOf, oneOf, etc.)
  • Loosens outputSchema to fully support JSON Schema 2020-12 since MCP servers may return any valid JSON.
    • Loosens structuredContent to support any JSON validated by outputSchema's JSON Schema.

PR: #881

Backward Compatibility

No breaking changes.

Existing schemas without $schema will default to 2020-12. Servers can optionally add $schema to use other dialects.

Object schemas will continue to work as expected while the loosened specification can accept other valid JSON schema (like arrays of objects, etc.)

Rationale

The expectation is that any 2020-12 JSON Schema should be valid for fields that expect JSON schema. This includes outputSchema and structuredContent to be authentically validated by outputSchema, not just expected to always be an object: this is far too strict and narrows how tools can respond in ways that backends may not conform to. Further, 2020-12 has been widely adopted by the industry as the current standard but servers can define the schema they expect via the $schema metadata field.

Reference Implementation

TODO

Security Implications

There are no security implications.

Activity

  1. jpmcb commented on Jun 24, 2025

    @jpmcb
    ContributorAuthor

    Related discussion in the original RFC I'd like to surface: #371 (comment)

    Thinking about this, given that outputSchema is any arbitrary json schema (which makes sense), should this be of type any rather than an object,i.e. should we omit the type=object? It would make sense for eg if the tool just returns an int or even an array. This as is will prevent an array of objects from being returned for eg. even though you could describe it in json schema.

    @sambhav

    on balance I think the sticking with the top-level-object restriction in the rest of the protocol (and standard practice more generally) is worth the extra trouble of wrapping top-level primitives/arrays

    @bhosmer-ant


    In my opinion, keeping an object top level primitive opinion in the specification makes for a really challenging end-developer experience and will narrow the capabilities of downstream clients: are we to expect that clients can't use generic JSON Schema libraries and, instead, have to stick with a custom solution that expects objects throughout?

  2. bhosmer-ant commented on Jun 27, 2025

    @bhosmer-ant
    Contributor

    @jpmcb Hi John - not sure what you mean by "can't use generic JSON Schema libraries", the usual ones should work fine, this just constrains what gets fed through them. The motivation for the object restriction was to observe what seems to be a convention on the part of many libraries and frameworks in this space (that transferred data will always be objects). The extra burden - tools wanting to return non-object data need to wrap it in an object - seems pretty minor compared to being in a position where an intake pipeline would need to be customized to accept non-object data.

    That said, if you're thinking of concrete examples where this will cause real pain of a kind we hadn't anticipated, can you elaborate?

  3. jpmcb commented on Jun 29, 2025

    @jpmcb
    ContributorAuthor

    Arrays of objects are probably the most likely use case.

    Let's take some weather data I've been working with that gets returned by an API in a MCP server as an example. The idea is that this is the aggregate over a number of hours of weather forecasts.

    MCP client <--> MCP server <--> Weather API
    

    Say I want to feed back this array of data to an MCP client as a structured array of objects:

    [
      { hour: 0, precip: 0.82, cloud_cover: 0.99 ... },
      { hour: 1, precip: 0.82, cloud_cover: 0.99 ... },
    
      // etc. etc.
    
      { hour: 6, precip: 0.82, cloud_cover: 0.99 ... },
      { hour: 7, precip: 0.82, cloud_cover: 0.99 ... },
    ]

    This is clean, easy to understand, and conforms to the API usage that the MCP server has.

    I can't do that out of the box if it's forced to be an object.

    Wrapping it in an object seems like an unnecessary conformity with 1 key in the object:

    {
      results: [
        { hour: 0, precip: 0.82, cloud_cover: 0.99 ... },
        { hour: 0, precip: 0.82, cloud_cover: 0.99 ... },
    
        // etc. etc.
    
        { hour: 6, precip: 0.82, cloud_cover: 0.99 ... },
        { hour: 7, precip: 0.82, cloud_cover: 0.99 ... },
      ]
    }

    results is basically meaningless since it could just as easily be destructured to a single array of objects. Good API design would dictate that we remove `results and not have an unnecessary top level object.

    You can find this all over the place:


    tools wanting to return non-object data need to wrap it in an object

    Why the restriction? It seems like an unnecessary piece of abstraction when JSON schema already supports valid JSON. My point being, outputSchema cannot really be "JSON schema" if it doesn't even support arrays.

    This overall leads to poor server developer experience because I can't take advantage of the full breadth and depth of outputSchema being JSON schema because structuredContent is restricted to an object.

  4. bhosmer-ant commented on Jul 3, 2025

    @bhosmer-ant
    Contributor

    @jpmcb fair point about API endpoints that return lists being easy to find. Since the main motivation for the restriction was reducing client burden, I think it's worth taking the temperature of client implementors w.r.t. how well-grounded this consideration is (started a thread about this on Discord). And yeah, definitely worth considering relaxing the restriction if it turns out not to be useful from their POV.

  5. dsp-ant commented on Jul 15, 2025

    @dsp-ant
    Member

    Thanks for bringing this up. I think it's a reasonable request and @bhosmer-ant appraoch to asking client implementors seems worth it. @jpmcb would you be up to creating a SEP (you can just change this issue with the more formal template).

  6. jpmcb commented on Jul 15, 2025

    @jpmcb
    ContributorAuthor

    @dsp-ant - I've already cut an RFC here: #881 before the SEP process was in place.

    I can go back and transform it into a SEP if you'd like but this RFC is ready to go in.

  7. changed the title [-]`2025-06-18` Discrepancy: `structuredContent` does not support full `outputSchema` JSON Schema[/-] [+]Support full JSON Schema `2020-12`[/+] on Jul 21, 2025
  8. JMLX42 commented on Aug 25, 2025

    @JMLX42

    Further, which draft of JSON Schema clients and servers use is not well defined leading to general drift among client, SDK, and server implementations. For example, JSON Schema specs in one client that uses JSON Schema draft-07 will be very different from another that uses 2020-12.

    Implementor here. I'm the developer of rmcp-openapi, which uses the rmcp Rust crate (the official Rust MCP SDK) to create an MCP <=> OpenAPI bridge.

    To workaround the discrepancies with what the MCP schema expects, I had to implement the following quirks:

    To make things simpler for implementors, IMHO the best solution is to use what OpenAPI does to leverage the existing ecosystem. Which is 2020-12 based on the OpenAPI specification:

    In order to properly handle Schema Objects, OAS 3.1 inherits the parsing requirements of JSON Schema Specification Draft 2020-12, with appropriate modifications regarding base URIs as specified in Relative References In URIs.

  9. jpmcb commented on Sep 9, 2025

    @jpmcb
    ContributorAuthor

    Good feedback @JMLX42 - I agree, aligning to 2020-12 is critical for clarity on both the server and client side.

    cc @dsp-ant @cliffhall - what's holding this up? This SEP has yet to be assigned a sponsor and has been waiting for nearly 3 months (this even pre-dates the SEP process!)

  10. He-Pin commented on Sep 9, 2025

    @He-Pin
    Contributor

    I think the old one is better.
    Then the input schema should be any JSON type too, I think an object is better for future extension, you can't add a field to a boolean type.

  11. cliffhall commented on Sep 9, 2025

    @cliffhall
    Member

    cc @dsp-ant @cliffhall - what's holding this up? This SEP has yet to be assigned a sponsor and has been waiting for nearly 3 months (this even pre-dates the SEP process!)

    Hi @jpmcb I think this message reveals the way forward. We need some feedback from client developers about this. I suggest starting a discussion thread about it in the client-implementors channel in the MCP Contributors Discord.

  12. 41 remaining items

  13. handrews commented on Mar 6, 2026

    @handrews

    @JMLX42 yes you'd think that, but someone probably needs to connect the dots for them. We at the OpenAPI Foundation have been struggling to get VSCode to update for years, and when I was involved with the JSON Schema Org, we had been struggling to get them to do anything to update past draft-07 as well.

    Having someone who carries weight in the MCP world get in touch with them might finally get them moving. We (OpenAPI) are also hoping that the positive press on how much the OpenAPI Specification 3.2 (which depends on 2020-12) helps LLM use cases, including MCP servers, will motivate them, but we don't have as much buzz as MCP (to put it mildly).

  14. localden commented on Mar 11, 2026

    @localden
    Contributor

    @handrews @JMLX42 we have a pretty good relationship with the Microsoft folks working on VS Code, so we'll work with them and other clients help update things.

    @olaservo - do you happen to have the new SEP PR we can take a peek at? Assuming it's #2106?

  15. royalpinto333 commented on Mar 22, 2026

    @royalpinto333

    +1 for this. We have a tool that returns two different response shapes depending on the mode,
    which requires anyOf to express accurately in the output schema. Without it we're forced to
    either skip the output schema entirely or merge both shapes into one bloated nullable schema,
    which is misleading for the LLM. Would love to see anyOf/oneOf support in McpSchema.JsonSchema.

  16. sep-automation-bot commented on Mar 30, 2026

    @sep-automation-bot

    Maintainer Activity Check

    Hi @olaservo!

    You're assigned to this SEP but there hasn't been any activity from you in 18 days.

    Please provide an update on:

    • Current status of your review/work
    • Any blockers or concerns
    • Expected timeline for next steps

    If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


    This is an automated message from the SEP lifecycle bot.

  17. sep-automation-bot commented on Apr 13, 2026

    @sep-automation-bot

    Maintainer Activity Check

    Hi @olaservo!

    You're assigned to this SEP but there hasn't been any activity from you in 14 days.

    Please provide an update on:

    • Current status of your review/work
    • Any blockers or concerns
    • Expected timeline for next steps

    If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


    This is an automated message from the SEP lifecycle bot.

  18. sep-automation-bot commented on Apr 27, 2026

    @sep-automation-bot

    Maintainer Activity Check

    Hi @olaservo!

    You're assigned to this SEP but there hasn't been any activity from you in 14 days.

    Please provide an update on:

    • Current status of your review/work
    • Any blockers or concerns
    • Expected timeline for next steps

    If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


    This is an automated message from the SEP lifecycle bot.

  19. sep-automation-bot commented on May 11, 2026

    @sep-automation-bot

    Maintainer Activity Check

    Hi @olaservo!

    You're assigned to this SEP but there hasn't been any activity from you in 14 days.

    Please provide an update on:

    • Current status of your review/work
    • Any blockers or concerns
    • Expected timeline for next steps

    If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


    This is an automated message from the SEP lifecycle bot.

  20. sep-automation-bot commented on May 25, 2026

    @sep-automation-bot

    Maintainer Activity Check

    Hi @olaservo!

    You're assigned to this SEP but there hasn't been any activity from you in 14 days.

    Please provide an update on:

    • Current status of your review/work
    • Any blockers or concerns
    • Expected timeline for next steps

    If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


    This is an automated message from the SEP lifecycle bot.

  21. sep-automation-bot commented on Jun 8, 2026

    @sep-automation-bot

    Maintainer Activity Check

    Hi @olaservo!

    You're assigned to this SEP but there hasn't been any activity from you in 14 days.

    Please provide an update on:

    • Current status of your review/work
    • Any blockers or concerns
    • Expected timeline for next steps

    If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


    This is an automated message from the SEP lifecycle bot.

  22. sep-automation-bot commented on Jun 22, 2026

    @sep-automation-bot

    Maintainer Activity Check

    Hi @olaservo!

    You're assigned to this SEP but there hasn't been any activity from you in 14 days.

    Please provide an update on:

    • Current status of your review/work
    • Any blockers or concerns
    • Expected timeline for next steps

    If you're no longer able to sponsor this SEP, please let us know so we can find another maintainer.


    This is an automated message from the SEP lifecycle bot.

  23. localden commented on Jun 26, 2026

    @localden
    Contributor

    #1613 and #2106 should now cover this. Thank you again for the contribution, @jpmcb!

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

Metadata

Metadata

Assignees

Labels

SEPbugSomething isn't workingin-reviewSEP proposal ready for review.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions