-
Notifications
You must be signed in to change notification settings - Fork 772
Prototype support for outputSchema
/structuredContent
#454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2ab965e
to
3a83e40
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some concerns around protocol version bump and questions
{ method: "tools/list", params }, | ||
ListToolsResultSchema, | ||
options, | ||
); | ||
|
||
// Cache the tools and their output schemas for future validation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit worried about this part related to caching tools
there are some efforts to refresh tool list on client like this one. Which will work fine as listTools
is used there
I think my question here is, do we need to have cached output types - how expensive it is to convert it on the fly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think _cachedTools
is not used, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[edited] the problem is that the client isn't actually storing tool definitions anywhere - so unless we grab and stash on an organic listTools
call, we'd need to do one prior to the tool call itself (with no filter, and maybe multiple pages of results).
So yeah, this would be the first real connective state between tool definitions and tool calls in the client, I think. (And thus the first state that could go out of sync.)
But I'm not sure it's different in kind from, say, calling a tool based on a stale param schema... though ofc in that case the error would be on the server rather than the client.
drafting until new api is up |
3a83e40
to
d3ed226
Compare
// Check if the tool has an outputSchema | ||
const outputSchema = this.getToolOutputSchema(params.name); | ||
if (outputSchema) { | ||
// If tool has outputSchema, it MUST return structuredContent (unless it's an error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if we want to relax this. What if an SDK implemented some server changes by mistake - would the client then be unable to use it at all? Is this something we want? I think enforcing those requirements on the server would definitely be a good thing, but I'm not sure it should apply to the client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it depends on how strictly we want to enforce the "trusted output" aspect of the structured result contract, so I don't think we should unconditionally allow potentially malicious stuff through. But we could make it discretionary if this seems too inflexible - maybe add a strictness/trust flag to RequestOptions?
More radically we could not do validation here at all and have a separate "validate" method that the user of the client can call at will - not sure I feel about making validation opt-in rather than opt-out but it's an option.
src/client/index.ts
Outdated
} | ||
} else { | ||
// If tool doesn't have outputSchema, it MUST NOT return structuredContent | ||
if (result.structuredContent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, do we need to throw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nah you're right this one seems overly pedantic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
followup: this restriction is actually referenced in the spec types. I think we should relax the language there, but keep the type distinction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should relax the language there, but keep the type distinction.
@@ -0,0 +1,93 @@ | |||
#!/usr/bin/env node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need this one, I think mcpServerOutputSchema.ts
can be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, mcpServerOutputSchema.ts and lowLevelOutputSchema.ts are servers (high- and low-level respectively), and this is just a test script that runs a client against either of them.
/**
* Client to test outputSchema servers
* This client connects to either the high-level or low-level outputSchema server
* and calls the get_weather tool to demonstrate structured output
*/
It was useful for me to test the examples, but your call whether it's worth including in the SDK itself!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, should it be move do client folder?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
- consider moving testOutputSchemaServers to client folder
- looks like there is a merge conflict
- Add outputSchema field to Tool type and RegisteredTool interface - Make content field optional in CallToolResult - Update ListToolsRequestSchema handler to include outputSchema in tool list responses - Add support for structuredContent in tool results - Update examples to handle optional content field - Add tests for new outputSchema and structuredContent functionality - Update ToolCallback documentation to clarify when to use structuredContent vs content This change enables tools to define structured output schemas and return structured JSON content, providing better type safety and validation for tool outputs. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- Cache tool output schemas when listing tools - Validate structuredContent against outputSchema during callTool - Enforce that tools with outputSchema must return structuredContent - Add json-schema-to-zod dependency for schema conversion - Add comprehensive tests for validation scenarios 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
…tured tool output - Add outputSchema support to Tool interface with proper documentation - Split CallToolResult into structured and unstructured variants - Change structuredContent from string to object type - Add validation that tools without outputSchema cannot return structuredContent - Add validation that tools with outputSchema must return structuredContent - Update client to validate structured content as object (no JSON parsing) - Update tests to use object format for structuredContent - Add tests for new validation constraints - Update LATEST_PROTOCOL_VERSION to DRAFT-2025-v2 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
…patibility - Update McpServer to support registering tools with outputSchema - Add automatic content generation from structuredContent for backward compatibility - Add validation to ensure proper usage of structuredContent vs content - Add comprehensive tests for outputSchema functionality - Add example servers demonstrating structured output usage - Update existing test to match new backward compatibility behavior 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
- fix schema validation - comment headers on tests
…ured-output restriction
- Remove tool() overload that takes a config object with callback - Add new registerTool() method that separates config from callback - Extract common tool registration logic into _createRegisteredTool() helper - Update all tests to use registerTool() for tools with output schemas - Update examples to use registerTool() and high-level Client API - Add comment noting that output schema validation only works with high-level API This change improves API clarity by using a distinct method for tools with output schemas while maintaining backward compatibility. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
This file is a client implementation that tests outputSchema servers, so it belongs in the client examples directory rather than server examples. Updated file paths in error messages to reflect the new location. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
…without outputSchema Co-Authored-By: Claude <[email protected]>
1363c86
to
f470f09
Compare
Prototype support for schema validation additions proposed in modelcontextprotocol/modelcontextprotocol#371
Design choices (RFC)
new
McpServer.registerTool()
API: uses aconfig
object for all tool properties except name and the tool callback. Oldtool()
API (optional arguments for all properties + overloads for every combination + argument parsing via type sniffing in implementation) was already stretched to the breaking point, andoutputSchema
would've been unavoidably ambiguous with this approach due to type overlap.tool()
is still supported, butregisterTool()
is preferred.tool outputSchema caching in
Client
: aClient
needs a tool'soutputSchema
to do validation of tool call results. This is the first instance of the Client needing to remember something about a tool in order to complete a tool call successfully: previously, the client retained no information from a "tools/list" request, and tool calls would be validated entirely on the server side. This PR does this by caching output schemas from incoming tool listings, which adds state to the Client that must be kept up to date.outputSchema
s are cached as a side effect ofClient.listTools()
, which means that bare requests to "tools/list" will not retain them. This confines support ofoutputSchema
andstructuredContent
to the high level API.Reviewer notes
registerTool()
API on all existing tests, and addition of comment headers for readability.types.ts
: addedoutputSchema
toToolSchema
, splitCallToolResult
into structured and unstructured variantsclient/
: addedstructuredContent
handling (incl. schema validation) and testsserver/': added
structuredContentand compatibility output, **new
tool()api**, tests (new api and
outputSchema`)package.json
,package-lock.json
: addedjson-schema-to-zod
for output validation againstoutputSchema
examples/client
: minor changes needed to handle optionality ofresult.content
examples/server
: new examples exercising structured output capabilityMotivation and Context
See above
How Has This Been Tested?
Added tests for new functionality
Breaking Changes
No
Types of changes
Checklist
Additional context