diff --git a/docs/specification/draft/server/tools.mdx b/docs/specification/draft/server/tools.mdx index 075550b2a..c5cf1c59b 100644 --- a/docs/specification/draft/server/tools.mdx +++ b/docs/specification/draft/server/tools.mdx @@ -181,6 +181,7 @@ A tool definition includes: - `name`: Unique identifier for the tool - `description`: Human-readable description of functionality - `inputSchema`: JSON Schema defining expected parameters +- `outputSchema`: Optional JSON Schema defining expected output structure - `annotations`: optional properties describing tool behavior For trust & safety and security, clients **MUST** consider @@ -188,7 +189,11 @@ tool annotations to be untrusted unless they come from trusted servers. +For backwards compatibility, a tool that declares an `outputSchema` may also return unstructured results in the `content` field. +* If present, the unstructured result should be functionally equivalent to the structured result. (For example, serialized JSON can be returned in a `TextContent` block.) +* Clients that support `structuredContent` should ignore the `content` field if present. + + +Example tool with output schema: + +```json +{ + "name": "get_weather_data", + "description": "Get current weather conditions and forecast data for a location", + "inputSchema": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "City name or zip code" + }, + "units": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "default": "celsius", + "description": "Temperature unit" + } + }, + "required": ["location"] + }, + "outputSchema": { + "type": "object", + "properties": { + "current": { + "type": "object", + "properties": { + "temperature": { "type": "number" }, + "humidity": { "type": "number" }, + "conditions": { "type": "string" }, + "wind": { + "type": "object", + "properties": { + "speed": { "type": "number" }, + "direction": { "type": "string" } + }, + "required": ["speed", "direction"] + } + }, + "required": ["temperature", "humidity", "conditions", "wind"] + }, + "forecast": { + "type": "array", + "items": { + "type": "object", + "properties": { + "date": { "type": "string", "format": "date" }, + "high": { "type": "number" }, + "low": { "type": "number" }, + "conditions": { "type": "string" } + }, + "required": ["date", "high", "low", "conditions"] + } + }, + "location": { + "type": "object", + "properties": { + "city": { "type": "string" }, + "country": { "type": "string" }, + "coordinates": { + "type": "object", + "properties": { + "latitude": { "type": "number" }, + "longitude": { "type": "number" } + }, + "required": ["latitude", "longitude"] + } + }, + "required": ["city", "country", "coordinates"] + } + }, + "required": ["current", "forecast", "location"] + } +} +``` + +Example valid response for this tool: + +```json +{ + "jsonrpc": "2.0", + "id": 5, + "result": { + "structuredContent": { + "current": { + "temperature": 22.5, + "humidity": 65, + "conditions": "Partly cloudy", + "wind": { + "speed": 12, + "direction": "NW" + } + }, + "forecast": [ + { + "date": "2024-03-28", + "high": 25, + "low": 18, + "conditions": "Sunny" + }, + { + "date": "2024-03-29", + "high": 23, + "low": 17, + "conditions": "Cloudy" + } + ], + "location": { + "city": "San Francisco", + "country": "US", + "coordinates": { + "latitude": 37.7749, + "longitude": -122.4194 + } + } + } + } +} +``` + +The `outputSchema` helps clients and LLMs understand and properly handle structured tool outputs by: + +- Enabling strict schema validation of responses +- Providing type information for better integration with programming languages +- Guiding clients and LLMs to properly parse and utilize the returned data +- Supporting better documentation and developer experience + ## Error Handling Tools use two error reporting mechanisms: diff --git a/schema/draft/schema.json b/schema/draft/schema.json index a1e3f2679..ad03810d7 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -101,7 +101,61 @@ "type": "object" }, "CallToolResult": { - "description": "The server's response to a tool call.\n\nAny errors that originate from the tool SHOULD be reported inside the result\nobject, with `isError` set to true, _not_ as an MCP protocol-level error\nresponse. Otherwise, the LLM would not be able to see that an error occurred\nand self-correct.\n\nHowever, any errors in _finding_ the tool, an error indicating that the\nserver does not support tool calls, or any other exceptional conditions,\nshould be reported as an MCP error response.", + "anyOf": [ + { + "$ref": "#/definitions/CallToolUnstructuredResult" + }, + { + "$ref": "#/definitions/CallToolStructuredResult" + } + ], + "description": "The server's response to a tool call.\n\nAny errors that originate from the tool SHOULD be reported inside the result\nobject, with `isError` set to true, _not_ as an MCP protocol-level error\nresponse. Otherwise, the LLM would not be able to see that an error occurred\nand self-correct.\n\nHowever, any errors in _finding_ the tool, an error indicating that the\nserver does not support tool calls, or any other exceptional conditions,\nshould be reported as an MCP error response." + }, + "CallToolStructuredResult": { + "description": "Tool result for tools that do declare an outputSchema.", + "properties": { + "_meta": { + "additionalProperties": {}, + "description": "This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.", + "type": "object" + }, + "content": { + "description": "If the Tool defines an outputSchema, this field MAY be present in the result.\nTools should use this field to provide compatibility with older clients that do not support structured content.\nClients that support structured content should ignore this field.", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/TextContent" + }, + { + "$ref": "#/definitions/ImageContent" + }, + { + "$ref": "#/definitions/AudioContent" + }, + { + "$ref": "#/definitions/EmbeddedResource" + } + ] + }, + "type": "array" + }, + "isError": { + "description": "Whether the tool call ended in an error.\n\nIf not set, this is assumed to be false (the call was successful).", + "type": "boolean" + }, + "structuredContent": { + "additionalProperties": {}, + "description": "An object containing structured tool output.\n\nIf the Tool defines an outputSchema, this field MUST be present in the result, and contain a JSON object that matches the schema.", + "type": "object" + } + }, + "required": [ + "structuredContent" + ], + "type": "object" + }, + "CallToolUnstructuredResult": { + "description": "Tool result for tools that do not declare an outputSchema.", "properties": { "_meta": { "additionalProperties": {}, @@ -109,6 +163,7 @@ "type": "object" }, "content": { + "description": "A list of content objects that represent the result of the tool call.\n\nIf the Tool does not define an outputSchema, this field MUST be present in the result.", "items": { "anyOf": [ { @@ -358,6 +413,25 @@ ], "type": "object" }, + "ContentList": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/TextContent" + }, + { + "$ref": "#/definitions/ImageContent" + }, + { + "$ref": "#/definitions/AudioContent" + }, + { + "$ref": "#/definitions/EmbeddedResource" + } + ] + }, + "type": "array" + }, "CreateMessageRequest": { "description": "A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it.", "properties": { @@ -1904,7 +1978,10 @@ "$ref": "#/definitions/ListToolsResult" }, { - "$ref": "#/definitions/CallToolResult" + "$ref": "#/definitions/CallToolUnstructuredResult" + }, + { + "$ref": "#/definitions/CallToolStructuredResult" }, { "$ref": "#/definitions/CompleteResult" @@ -2049,6 +2126,12 @@ "name": { "description": "The name of the tool.", "type": "string" + }, + "outputSchema": { + "additionalProperties": true, + "description": "An optional JSON Schema object defining the structure of the tool's output.\n\nIf set, a CallToolResult for this Tool MUST contain a structuredContent field whose contents validate against this schema.\nIf not set, a CallToolResult for this Tool MUST contain a content field.", + "properties": {}, + "type": "object" } }, "required": [ diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 1f6c3b1e2..61de0de46 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -695,8 +695,51 @@ export interface ListToolsResult extends PaginatedResult { * server does not support tool calls, or any other exceptional conditions, * should be reported as an MCP error response. */ -export interface CallToolResult extends Result { - content: (TextContent | ImageContent | AudioContent | EmbeddedResource)[]; +export type CallToolResult = CallToolUnstructuredResult | CallToolStructuredResult; + +export type ContentList = (TextContent | ImageContent | AudioContent | EmbeddedResource)[]; + +/** + * Tool result for tools that do not declare an outputSchema. + */ +export interface CallToolUnstructuredResult extends Result { + /** + * A list of content objects that represent the result of the tool call. + * + * If the Tool does not define an outputSchema, this field MUST be present in the result. + */ + content: ContentList; + + /** + * Structured output must not be provided in an unstructured tool result. + */ + structuredContent: never; + + /** + * Whether the tool call ended in an error. + * + * If not set, this is assumed to be false (the call was successful). + */ + isError?: boolean; +} + +/** + * Tool result for tools that do declare an outputSchema. + */ +export interface CallToolStructuredResult extends Result { + /** + * An object containing structured tool output. + * + * If the Tool defines an outputSchema, this field MUST be present in the result, and contain a JSON object that matches the schema. + */ + structuredContent: { [key: string]: unknown }; + + /** + * If the Tool defines an outputSchema, this field MAY be present in the result. + * Tools should use this field to provide compatibility with older clients that do not support structured content. + * Clients that support structured content should ignore this field. + */ + content?: ContentList; /** * Whether the tool call ended in an error. @@ -706,6 +749,7 @@ export interface CallToolResult extends Result { isError?: boolean; } + /** * Used by the client to invoke a tool provided by the server. */ @@ -803,6 +847,14 @@ export interface Tool { required?: string[]; }; + /** + * An optional JSON Schema object defining the structure of the tool's output. + * + * If set, a CallToolResult for this Tool MUST contain a structuredContent field whose contents validate against this schema. + * If not set, a CallToolResult for this Tool MUST contain a content field. + */ + outputSchema?: object; + /** * Optional additional tool information. */