From 62d39c9049c8f4be45ba4ec192b1465949e62ac0 Mon Sep 17 00:00:00 2001 From: Kyle Rubenok Date: Fri, 13 Mar 2026 11:01:05 -0700 Subject: [PATCH 1/4] Rebase SEP-2200 onto current main Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/docs.json | 6 + ...clarify-tool-result-content-visibility.mdx | 190 ++++++++++++++++++ docs/seps/index.mdx | 2 + ...-clarify-tool-result-content-visibility.md | 172 ++++++++++++++++ 4 files changed, 370 insertions(+) create mode 100644 docs/seps/2200-clarify-tool-result-content-visibility.mdx create mode 100644 seps/2200-clarify-tool-result-content-visibility.md diff --git a/docs/docs.json b/docs/docs.json index 411dae729..6f8d99aef 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -421,6 +421,12 @@ "seps/2207-oidc-refresh-token-guidance", "seps/2260-Require-Server-requests-to-be-associated-with-Client-requests" ] + }, + { + "group": "Draft", + "pages": [ + "seps/2200-clarify-tool-result-content-visibility" + ] } ] }, diff --git a/docs/seps/2200-clarify-tool-result-content-visibility.mdx b/docs/seps/2200-clarify-tool-result-content-visibility.mdx new file mode 100644 index 000000000..3e30a3fdc --- /dev/null +++ b/docs/seps/2200-clarify-tool-result-content-visibility.mdx @@ -0,0 +1,190 @@ +--- +title: "SEP-2200: Clarify Tool Result Content and Model Visibility" +sidebarTitle: "SEP-2200: Clarify Tool Result Content and Model V…" +description: "Clarify Tool Result Content and Model Visibility" +--- + +
+ + Draft + + + Standards Track + +
+ +| Field | Value | +| ------------- | ------------------------------------------------------------------------------- | +| **SEP** | 2200 | +| **Title** | Clarify Tool Result Content and Model Visibility | +| **Status** | Draft | +| **Type** | Standards Track | +| **Created** | 2026-02-03 | +| **Author(s)** | Kyle Rubenok ([@krubenok](https://github.com/krubenok)) | +| **Sponsor** | Ola Hungerford ([@olaservo](https://github.com/olaservo)) | +| **PR** | [#2200](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2200) | + +--- + +## Abstract + +This SEP updates the intended usage and model visibility of `CallToolResult.content` and +`CallToolResult.structuredContent`. It formalizes the distinction between model-oriented output +(`content`) and machine-oriented output (`structuredContent`), provides guidance on semantic +alignment when both fields are present, and clarifies client behavior for selecting which field to +expose to models. The change is especially important now that MCP Apps is an official extension, +where tool results may drive interactive UI experiences and should not inadvertently flood model +context. This SEP does not change the protocol wire format or field types, but it does change the +normative guidance for what servers are expected to return in `content` when `structuredContent` +is present. Implementations that treated `content` as if it were always a JSON-serialized copy of +`structuredContent`—an assumption made in some tooling and implementations, but not a universally +safe one in practice—may need to be updated. + +## Motivation + +Since `structuredContent` was introduced in +[PR #371](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/371) and back-compat was +relaxed in [PR #559](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/559), servers +and clients have implemented divergent interpretations of how `content` and `structuredContent` +should be used: + +- Clients disagree on which field to pass to models, with some preferring `content`, others + preferring `structuredContent`, and some forwarding both. +- Server authors have returned different semantic information in each field, causing inconsistent + behavior across clients. +- Tool authors receive mixed guidance, including specification text and inspector checks that + reflect the prior recommendation that `content` contain a JSON-serialized copy of + `structuredContent`. + +The official MCP Apps extension +(https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/) makes the ambiguity more +urgent. Tools can now return interactive UIs and richer data for hosts to render. Without explicit +guidance, model context can be flooded with UI-oriented data or the UI can lack a reliable +structured payload. The ext-apps issue +[#380](https://github.com/modelcontextprotocol/ext-apps/issues/380) and related client issues (for +example, [python-sdk #1796](https://github.com/modelcontextprotocol/python-sdk/issues/1796) and +[vscode #290063](https://github.com/microsoft/vscode/issues/290063)) highlight persistent confusion +about what is model-visible and what is host-only. + +Several SDK issues are also currently blocked on this clarification, including +[typescript-sdk #911](https://github.com/modelcontextprotocol/typescript-sdk/issues/911), +[python-sdk #1332](https://github.com/modelcontextprotocol/python-sdk/issues/1332), +[csharp-sdk #747](https://github.com/modelcontextprotocol/csharp-sdk/issues/747), and +[csharp-sdk #930](https://github.com/modelcontextprotocol/csharp-sdk/issues/930). + +The MCP Inspector check that expects `content` to JSON-stringify `structuredContent` is tracked in +[inspector #1089](https://github.com/modelcontextprotocol/inspector/issues/1089). That behavior +followed the pre-existing specification guidance; this SEP proposes changing that guidance. + +This SEP proposes updated guidance from issue #1624 and related discussions: `content` is the +model-facing representation, `structuredContent` is for programmatic or UI use, and clients should +choose the appropriate field for their use case rather than forwarding both verbatim. + +## Specification + +This SEP proposes the following updates to the MCP specification (draft): + +1. **Tool result audiences** + - `content` is the model-oriented representation of a tool result, optimized for readability + and token efficiency. It is the default field to pass into model context for conversational + and agent experiences. + - `structuredContent` is the machine-oriented representation for programmatic use, code + generation, typed orchestration, and UI hydration (including MCP Apps). + +2. **Semantic alignment** + - When both `content` and `structuredContent` are present, they SHOULD be semantically + equivalent (same information, different presentation). `content` may summarize or + textualize `structuredContent` without listing every field verbatim, but it SHOULD NOT + contradict it. + - For backwards compatibility, tools that return `structuredContent` SHOULD also return a + `content` representation of the same information. JSON-serialized text is acceptable, but + not required and may be suboptimal depending on the use case. + +3. **Client selection guidance** + - Clients SHOULD choose the field that best matches their experience: + - Conversational/agent UX: prefer `content`. + - Programmatic/code mode: prefer `structuredContent`. + - When providing tool results to a model context, clients SHOULD use `content` when present + and only fall back to `structuredContent` if `content` is empty or omitted. + - If the preferred field is missing or empty, clients MAY fall back to the other field. + - Clients SHOULD NOT forward both fields verbatim to the model as separate inputs. + +4. **MCP Apps and model visibility** + - For MCP Apps hosts, `content` is the model-visible summary, while `structuredContent` and + `_meta` are typically used for UI rendering or host-specific metadata. Model visibility is + ultimately client-controlled; `_meta` should be treated as host-only metadata unless a + client explicitly exposes it. + - Apps can update model context explicitly using the MCP Apps API (e.g., `ui/update-model-context`). + - Example pattern: for a tool that renders an interactive view, `content` can be a concise + model-facing summary such as "A view was displayed for item 123, and the user can now + interact with it," while detailed UI state can remain in `structuredContent` or `_meta`. + - Implementers SHOULD ensure `content` still includes the pertinent result information the + model needs, even when the format is more concise or tailored for model use than for + populating UI controls. + +5. **Output schema documentation** + - The output schema section should emphasize that `outputSchema` applies to + `structuredContent`, while `content` remains a model-oriented representation. + +6. **Error results (`isError: true`)** + - When a tool call results in an error (`isError: true`), the error message SHOULD be + returned in `content`. + - Servers MAY omit `structuredContent` for error results, since the error is unlikely to + conform to the tool's `outputSchema`. + - Clients SHOULD NOT validate `structuredContent` against `outputSchema` when `isError` is + `true`. + +## Rationale + +This proposal adopts a clearer separation between model-facing and machine-facing tool result +representations in response to how MCP Apps and related integrations are being built in practice. +Historically, the protocol evolved from an initial design where `structuredContent` and `content` +were more tightly coupled, and the current specification text preserved a compatibility-oriented +duplication pattern. This SEP intentionally changes that guidance toward semantically aligned but +presentation-specific outputs. It resolves cross-client inconsistencies and prevents the same tool +result from being interpreted differently depending on which field a client prefers. + +With MCP Apps now official, clarifying model visibility is essential. UI hosts need a reliable, +structured payload for rendering, while models benefit from concise summaries rather than raw UI +data. Explicit guidance reduces token waste, improves context quality, and avoids inadvertent +exposure of UI-only data to the model. + +Alternatives such as introducing new audience annotations or side-channel fields were considered +but rejected for this SEP due to higher complexity and breaking-change risk. The goal is to revise +guidance for existing fields without adding new protocol surface area. + +## Backward Compatibility + +This SEP does not change the protocol wire format or field types, so existing servers and clients +remain schema-compatible. However, it does change the normative guidance for what servers are +expected to return in `content` when `structuredContent` is present. Implementations that treated +`content` as if it were always a JSON-serialized copy of `structuredContent`—an assumption made in +some tooling and implementations, but not a universally safe one in practice—may need to be +updated to handle model-optimized representations that remain semantically aligned without being +structurally identical. Implementations that already return only `structuredContent` or only +`content` continue to be allowed, though tool authors are encouraged to provide both when feasible +for maximum compatibility. + +## Security Implications + +Clarifying model visibility reduces accidental leakage of large or sensitive UI data into model +context. No new protocol features or attack surfaces are introduced. + +## Reference Implementation + +This SEP is implemented through documentation updates in the specification. SDK and inspector +updates are expected to follow but are not required for adoption. + +## Alternatives Considered + +- **Audience annotations or explicit visibility flags**: would require new protocol fields and + capability negotiation. +- **Deprecating one field**: would break existing implementations. +- **Treating `structuredContent` as a purely host-only side channel**: would conflict with + programmatic/code-mode use cases that benefit from structured model inputs. + +## Open Questions + +- Should future extensions define explicit audience selectors (model/host/user) for tool results? +- Should empirical benchmarks on model performance with structured vs unstructured tool outputs be + published to guide best practices? diff --git a/docs/seps/index.mdx b/docs/seps/index.mdx index 184370543..c9b5326df 100644 --- a/docs/seps/index.mdx +++ b/docs/seps/index.mdx @@ -13,6 +13,7 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi ## Summary - **Accepted**: 2 +- **Draft**: 1 - **Final**: 27 ## All SEPs @@ -21,6 +22,7 @@ Specification Enhancement Proposals (SEPs) are the primary mechanism for proposi | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ------------------------------------------------- | ---------------- | ---------- | | [SEP-2260](/seps/2260-Require-Server-requests-to-be-associated-with-Client-requests) | Require Server requests to be associated with a Client request. | Accepted | Standards Track | 2026-02-16 | | [SEP-2207](/seps/2207-oidc-refresh-token-guidance) | OIDC-Flavored Refresh Token Guidance | Accepted | Standards Track | 2026-02-04 | +| [SEP-2200](/seps/2200-clarify-tool-result-content-visibility) | Clarify Tool Result Content and Model Visibility | Draft | Standards Track | 2026-02-03 | | [SEP-2149](/seps/2149-working-group-charter-template) | MCP Group Governance and Charter Template | Final | Process | 2025-01-15 | | [SEP-2148](/seps/2148-contributor-ladder) | MCP Contributor Ladder | Final | Process | 2026-01-15 | | [SEP-2133](/seps/2133-extensions) | Extensions | Final | Standards Track | 2025-01-21 | diff --git a/seps/2200-clarify-tool-result-content-visibility.md b/seps/2200-clarify-tool-result-content-visibility.md new file mode 100644 index 000000000..051c77e9f --- /dev/null +++ b/seps/2200-clarify-tool-result-content-visibility.md @@ -0,0 +1,172 @@ +# SEP-2200: Clarify Tool Result Content and Model Visibility + +- **Status**: Draft +- **Type**: Standards Track +- **Created**: 2026-02-03 +- **Author(s)**: Kyle Rubenok (@krubenok) +- **Sponsor**: Ola Hungerford (@olaservo) +- **PR**: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2200 +- **Related Issues**: [#1624](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1624), [#1411](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1411), [ext-apps #380](https://github.com/modelcontextprotocol/ext-apps/issues/380), [inspector #1089](https://github.com/modelcontextprotocol/inspector/issues/1089) + +## Abstract + +This SEP updates the intended usage and model visibility of `CallToolResult.content` and +`CallToolResult.structuredContent`. It formalizes the distinction between model-oriented output +(`content`) and machine-oriented output (`structuredContent`), provides guidance on semantic +alignment when both fields are present, and clarifies client behavior for selecting which field to +expose to models. The change is especially important now that MCP Apps is an official extension, +where tool results may drive interactive UI experiences and should not inadvertently flood model +context. This SEP does not change the protocol wire format or field types, but it does change the +normative guidance for what servers are expected to return in `content` when `structuredContent` +is present. Implementations that treated `content` as if it were always a JSON-serialized copy of +`structuredContent`—an assumption made in some tooling and implementations, but not a universally +safe one in practice—may need to be updated. + +## Motivation + +Since `structuredContent` was introduced in +[PR #371](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/371) and back-compat was +relaxed in [PR #559](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/559), servers +and clients have implemented divergent interpretations of how `content` and `structuredContent` +should be used: + +- Clients disagree on which field to pass to models, with some preferring `content`, others + preferring `structuredContent`, and some forwarding both. +- Server authors have returned different semantic information in each field, causing inconsistent + behavior across clients. +- Tool authors receive mixed guidance, including specification text and inspector checks that + reflect the prior recommendation that `content` contain a JSON-serialized copy of + `structuredContent`. + +The official MCP Apps extension +(https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/) makes the ambiguity more +urgent. Tools can now return interactive UIs and richer data for hosts to render. Without explicit +guidance, model context can be flooded with UI-oriented data or the UI can lack a reliable +structured payload. The ext-apps issue +[#380](https://github.com/modelcontextprotocol/ext-apps/issues/380) and related client issues (for +example, [python-sdk #1796](https://github.com/modelcontextprotocol/python-sdk/issues/1796) and +[vscode #290063](https://github.com/microsoft/vscode/issues/290063)) highlight persistent confusion +about what is model-visible and what is host-only. + +Several SDK issues are also currently blocked on this clarification, including +[typescript-sdk #911](https://github.com/modelcontextprotocol/typescript-sdk/issues/911), +[python-sdk #1332](https://github.com/modelcontextprotocol/python-sdk/issues/1332), +[csharp-sdk #747](https://github.com/modelcontextprotocol/csharp-sdk/issues/747), and +[csharp-sdk #930](https://github.com/modelcontextprotocol/csharp-sdk/issues/930). + +The MCP Inspector check that expects `content` to JSON-stringify `structuredContent` is tracked in +[inspector #1089](https://github.com/modelcontextprotocol/inspector/issues/1089). That behavior +followed the pre-existing specification guidance; this SEP proposes changing that guidance. + +This SEP proposes updated guidance from issue #1624 and related discussions: `content` is the +model-facing representation, `structuredContent` is for programmatic or UI use, and clients should +choose the appropriate field for their use case rather than forwarding both verbatim. + +## Specification + +This SEP proposes the following updates to the MCP specification (draft): + +1. **Tool result audiences** + - `content` is the model-oriented representation of a tool result, optimized for readability + and token efficiency. It is the default field to pass into model context for conversational + and agent experiences. + - `structuredContent` is the machine-oriented representation for programmatic use, code + generation, typed orchestration, and UI hydration (including MCP Apps). + +2. **Semantic alignment** + - When both `content` and `structuredContent` are present, they SHOULD be semantically + equivalent (same information, different presentation). `content` may summarize or + textualize `structuredContent` without listing every field verbatim, but it SHOULD NOT + contradict it. + - For backwards compatibility, tools that return `structuredContent` SHOULD also return a + `content` representation of the same information. JSON-serialized text is acceptable, but + not required and may be suboptimal depending on the use case. + +3. **Client selection guidance** + - Clients SHOULD choose the field that best matches their experience: + - Conversational/agent UX: prefer `content`. + - Programmatic/code mode: prefer `structuredContent`. + - When providing tool results to a model context, clients SHOULD use `content` when present + and only fall back to `structuredContent` if `content` is empty or omitted. + - If the preferred field is missing or empty, clients MAY fall back to the other field. + - Clients SHOULD NOT forward both fields verbatim to the model as separate inputs. + +4. **MCP Apps and model visibility** + - For MCP Apps hosts, `content` is the model-visible summary, while `structuredContent` and + `_meta` are typically used for UI rendering or host-specific metadata. Model visibility is + ultimately client-controlled; `_meta` should be treated as host-only metadata unless a + client explicitly exposes it. + - Apps can update model context explicitly using the MCP Apps API (e.g., `ui/update-model-context`). + - Example pattern: for a tool that renders an interactive view, `content` can be a concise + model-facing summary such as "A view was displayed for item 123, and the user can now + interact with it," while detailed UI state can remain in `structuredContent` or `_meta`. + - Implementers SHOULD ensure `content` still includes the pertinent result information the + model needs, even when the format is more concise or tailored for model use than for + populating UI controls. + +5. **Output schema documentation** + - The output schema section should emphasize that `outputSchema` applies to + `structuredContent`, while `content` remains a model-oriented representation. + +6. **Error results (`isError: true`)** + - When a tool call results in an error (`isError: true`), the error message SHOULD be + returned in `content`. + - Servers MAY omit `structuredContent` for error results, since the error is unlikely to + conform to the tool's `outputSchema`. + - Clients SHOULD NOT validate `structuredContent` against `outputSchema` when `isError` is + `true`. + +## Rationale + +This proposal adopts a clearer separation between model-facing and machine-facing tool result +representations in response to how MCP Apps and related integrations are being built in practice. +Historically, the protocol evolved from an initial design where `structuredContent` and `content` +were more tightly coupled, and the current specification text preserved a compatibility-oriented +duplication pattern. This SEP intentionally changes that guidance toward semantically aligned but +presentation-specific outputs. It resolves cross-client inconsistencies and prevents the same tool +result from being interpreted differently depending on which field a client prefers. + +With MCP Apps now official, clarifying model visibility is essential. UI hosts need a reliable, +structured payload for rendering, while models benefit from concise summaries rather than raw UI +data. Explicit guidance reduces token waste, improves context quality, and avoids inadvertent +exposure of UI-only data to the model. + +Alternatives such as introducing new audience annotations or side-channel fields were considered +but rejected for this SEP due to higher complexity and breaking-change risk. The goal is to revise +guidance for existing fields without adding new protocol surface area. + +## Backward Compatibility + +This SEP does not change the protocol wire format or field types, so existing servers and clients +remain schema-compatible. However, it does change the normative guidance for what servers are +expected to return in `content` when `structuredContent` is present. Implementations that treated +`content` as if it were always a JSON-serialized copy of `structuredContent`—an assumption made in +some tooling and implementations, but not a universally safe one in practice—may need to be +updated to handle model-optimized representations that remain semantically aligned without being +structurally identical. Implementations that already return only `structuredContent` or only +`content` continue to be allowed, though tool authors are encouraged to provide both when feasible +for maximum compatibility. + +## Security Implications + +Clarifying model visibility reduces accidental leakage of large or sensitive UI data into model +context. No new protocol features or attack surfaces are introduced. + +## Reference Implementation + +This SEP is implemented through documentation updates in the specification. SDK and inspector +updates are expected to follow but are not required for adoption. + +## Alternatives Considered + +- **Audience annotations or explicit visibility flags**: would require new protocol fields and + capability negotiation. +- **Deprecating one field**: would break existing implementations. +- **Treating `structuredContent` as a purely host-only side channel**: would conflict with + programmatic/code-mode use cases that benefit from structured model inputs. + +## Open Questions + +- Should future extensions define explicit audience selectors (model/host/user) for tool results? +- Should empirical benchmarks on model performance with structured vs unstructured tool outputs be + published to guide best practices? From c14325abc109cda0e205ece84ce77d85b7e0cf1a Mon Sep 17 00:00:00 2001 From: Kyle Rubenok Date: Fri, 27 Mar 2026 08:57:56 -0700 Subject: [PATCH 2/4] docs: refine SEP 2200 audience annotation rationale --- docs/seps/2200-clarify-tool-result-content-visibility.mdx | 7 +++++-- seps/2200-clarify-tool-result-content-visibility.md | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/seps/2200-clarify-tool-result-content-visibility.mdx b/docs/seps/2200-clarify-tool-result-content-visibility.mdx index 3e30a3fdc..8136db490 100644 --- a/docs/seps/2200-clarify-tool-result-content-visibility.mdx +++ b/docs/seps/2200-clarify-tool-result-content-visibility.mdx @@ -177,8 +177,11 @@ updates are expected to follow but are not required for adoption. ## Alternatives Considered -- **Audience annotations or explicit visibility flags**: would require new protocol fields and - capability negotiation. +- **Extending audience annotations**: the existing audience annotation on content blocks provides + per-item visibility hints, but does not address the top-level distinction between `content` and + `structuredContent` as fields with different intended consumers. Extending audience annotations + to cover that distinction was considered but would add complexity without resolving the + fundamental question of which field clients should prefer for model context. - **Deprecating one field**: would break existing implementations. - **Treating `structuredContent` as a purely host-only side channel**: would conflict with programmatic/code-mode use cases that benefit from structured model inputs. diff --git a/seps/2200-clarify-tool-result-content-visibility.md b/seps/2200-clarify-tool-result-content-visibility.md index 051c77e9f..66379952c 100644 --- a/seps/2200-clarify-tool-result-content-visibility.md +++ b/seps/2200-clarify-tool-result-content-visibility.md @@ -159,8 +159,11 @@ updates are expected to follow but are not required for adoption. ## Alternatives Considered -- **Audience annotations or explicit visibility flags**: would require new protocol fields and - capability negotiation. +- **Extending audience annotations**: the existing audience annotation on content blocks provides + per-item visibility hints, but does not address the top-level distinction between `content` and + `structuredContent` as fields with different intended consumers. Extending audience annotations + to cover that distinction was considered but would add complexity without resolving the + fundamental question of which field clients should prefer for model context. - **Deprecating one field**: would break existing implementations. - **Treating `structuredContent` as a purely host-only side channel**: would conflict with programmatic/code-mode use cases that benefit from structured model inputs. From 3031d548e7ffadd7ab7e33312d56652878a911ec Mon Sep 17 00:00:00 2001 From: Kyle Rubenok Date: Tue, 14 Apr 2026 12:08:10 -0700 Subject: [PATCH 3/4] Restore SEP-2200 draft spec changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/specification/draft/schema.mdx | 13 +++++--- docs/specification/draft/server/tools.mdx | 32 ++++++++++++++++--- .../result-with-structured-content.json | 2 +- schema/draft/schema.json | 10 +++--- schema/draft/schema.ts | 23 ++++++++++--- 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/docs/specification/draft/schema.mdx b/docs/specification/draft/schema.mdx index 988b0d656..fe445a215 100644 --- a/docs/specification/draft/schema.mdx +++ b/docs/specification/draft/schema.mdx @@ -1394,8 +1394,8 @@ means intelligence is the most important factor.

### `ToolResultContent` -
interface ToolResultContent {
  type: "tool_result";
  toolUseId: string;
  content: ContentBlock[];
  structuredContent?: { [key: string]: unknown };
  isError?: boolean;
  _meta?: MetaObject;
}

The result of a tool use, provided by the user back to the assistant.

Example: `get_weather` tool result
{
"type": "tool_result",
"toolUseId": "call_abc123",
"content": [
{
"type": "text",
"text": "Weather in Paris: 18°C, partly cloudy"
}
]
}

The ID of the tool use this result corresponds to.

This MUST match the ID from a previous ToolUseContent.

The unstructured result content of the tool use.

This has the same format as CallToolResult.content and can include text, images, -audio, resource links, and embedded resources.

An optional structured result object.

If the tool defined an Tool.outputSchema, this SHOULD conform to that schema.

Whether the tool use resulted in an error.

If true, the content typically describes the error that occurred. +

interface ToolResultContent {
  type: "tool_result";
  toolUseId: string;
  content: ContentBlock[];
  structuredContent?: { [key: string]: unknown };
  isError?: boolean;
  _meta?: MetaObject;
}

The result of a tool use, provided by the user back to the assistant.

Example: `get_weather` tool result
{
"type": "tool_result",
"toolUseId": "call_abc123",
"content": [
{
"type": "text",
"text": "Weather in Paris: 18°C, partly cloudy"
}
]
}

The ID of the tool use this result corresponds to.

This MUST match the ID from a previous ToolUseContent.

The model-oriented result content of the tool use.

This has the same format as CallToolResult.content and can include text, images, +audio, resource links, and embedded resources.

An optional structured, machine-oriented result object.

If the tool defined an Tool.outputSchema, this SHOULD conform to that schema unless ToolResultContent.isError is true.

Whether the tool use resulted in an error.

If true, the content typically describes the error that occurred. Default: false

Optional metadata about the tool result. Clients SHOULD preserve this field when including tool results in subsequent sampling requests to enable caching optimizations.

@@ -1444,7 +1444,12 @@ for task augmentation of specific request types in their capabilities.

interface CallToolResult {
  _meta?: MetaObject;
  content: ContentBlock[];
  structuredContent?: { [key: string]: unknown };
  isError?: boolean;
  [key: string]: unknown;
}

The result returned by the server for a tools/call request.

Example: Result with unstructured text
{
"content": [
{
"type": "text",
"text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
}
],
"isError": false
}
Example: Result with structured content
{
"content": [
{
"type": "text",
"text": "{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\", \"humidity\": 65}"
}
],
"structuredContent": {
"temperature": 22.5,
"conditions": "Partly cloudy",
"humidity": 65
}
}
Example: Invalid tool input error
{
"content": [
{
"type": "text",
"text": "Invalid departure date: must be in the future. Current date is 08/08/2025."
}
],
"isError": true
}

A list of content objects that represent the unstructured result of the tool call.

An optional JSON object that represents the structured result of the tool call.

Whether the tool call ended in an error.

If not set, this is assumed to be false (the call was successful).

Any errors that originate from the tool SHOULD be reported inside the result +

interface CallToolResult {
  _meta?: MetaObject;
  content: ContentBlock[];
  structuredContent?: { [key: string]: unknown };
  isError?: boolean;
  [key: string]: unknown;
}

The result returned by the server for a tools/call request.

Example: Result with unstructured text
{
"content": [
{
"type": "text",
"text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
}
],
"isError": false
}
Example: Result with structured content
{
"content": [
{
"type": "text",
"text": "Current weather: 22.5C, partly cloudy, 65% humidity."
}
],
"structuredContent": {
"temperature": 22.5,
"conditions": "Partly cloudy",
"humidity": 65
}
}
Example: Invalid tool input error
{
"content": [
{
"type": "text",
"text": "Invalid departure date: must be in the future. Current date is 08/08/2025."
}
],
"isError": true
}

A list of content objects that represent the model-oriented result of the tool call.

Clients SHOULD use this field when providing tool results to conversational or agentic model +context, if it is present.

An optional JSON object that represents the structured, machine-oriented result of the tool +call.

When both this field and CallToolResult.content are present, they SHOULD be +semantically equivalent. For backwards compatibility, tools that return structured content +SHOULD also return a content representation of the same information. JSON-serialized text is +acceptable, but not required.

Whether the tool call ended in an error.

If not set, this is assumed to be false (the call was successful).

Any errors that originate from the tool SHOULD be reported inside the result object, with isError set to true, not as an MCP protocol-level error response. Otherwise, the LLM would not be able to see that an error occurred and self-correct.

However, any errors in finding the tool, an error indicating that the @@ -1489,7 +1494,7 @@ If present, there may be more results available.

A human-readable description of the tool.

This can be used by clients to improve the LLM's understanding of available tools. It can be thought of like a "hint" to the model.

A JSON Schema object defining the expected parameters for the tool.

Execution-related properties for this tool.

An optional JSON Schema object defining the structure of the tool's output returned in -the structuredContent field of a CallToolResult.

Defaults to JSON Schema 2020-12 when no explicit $schema is provided. +the structuredContent field of a CallToolResult.

This schema applies to structuredContent and does not constrain the format of CallToolResult.content.

Defaults to JSON Schema 2020-12 when no explicit $schema is provided. Currently restricted to type: "object" at the root level.

Optional additional tool information.

Display name precedence order is: title, annotations.title, then name.

diff --git a/docs/specification/draft/server/tools.mdx b/docs/specification/draft/server/tools.mdx index d9aadd02e..1f9681294 100644 --- a/docs/specification/draft/server/tools.mdx +++ b/docs/specification/draft/server/tools.mdx @@ -345,18 +345,40 @@ Embedded resources support the same [Resource annotations](/specification/draft/ #### Structured Content -**Structured** content is returned as a JSON object in the `structuredContent` field of a result. +Tool results can include both a model-oriented representation and a machine-oriented representation: -For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block. +- `content` is the model-oriented representation of the result. Clients **SHOULD** use this field + when providing tool results to conversational or agentic model context, if it is present. +- `structuredContent` is the machine-oriented representation of the same result for programmatic + consumers, typed orchestration, and UI hydration. + +When both `content` and `structuredContent` are present, they **SHOULD** be semantically +equivalent. `content` may summarize or textualize `structuredContent` without reproducing every +field verbatim, but it **SHOULD NOT** contradict it. + +For backwards compatibility, a tool that returns structured content **SHOULD** also return a +`content` representation of the same information. JSON-serialized text is acceptable, but it is +not required and may be suboptimal depending on the use case. + +If `content` is missing or empty, clients **MAY** fall back to `structuredContent`. Clients +**SHOULD NOT** forward both fields verbatim to the model as separate inputs. + +When `isError` is `true`, the error message **SHOULD** be returned in `content`. Servers **MAY** +omit `structuredContent` for error results. #### Output Schema -Tools may also provide an output schema for validation of structured results. -If an output schema is provided: +Tools may also provide an output schema for validation of `structuredContent`. This schema applies +to the structured, machine-oriented representation and does not constrain the format of `content`. + +If an output schema is provided and a non-error result includes `structuredContent`: - Servers **MUST** provide structured results that conform to this schema. - Clients **SHOULD** validate structured results against this schema. +Clients **SHOULD NOT** validate `structuredContent` against `outputSchema` when `isError` is +`true`. + Example tool with output schema: ```json @@ -405,7 +427,7 @@ Example valid response for this tool: "content": [ { "type": "text", - "text": "{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\", \"humidity\": 65}" + "text": "Current weather: 22.5C, partly cloudy, 65% humidity." } ], "structuredContent": { diff --git a/schema/draft/examples/CallToolResult/result-with-structured-content.json b/schema/draft/examples/CallToolResult/result-with-structured-content.json index b398586ca..b4c00ee21 100644 --- a/schema/draft/examples/CallToolResult/result-with-structured-content.json +++ b/schema/draft/examples/CallToolResult/result-with-structured-content.json @@ -2,7 +2,7 @@ "content": [ { "type": "text", - "text": "{\"temperature\": 22.5, \"conditions\": \"Partly cloudy\", \"humidity\": 65}" + "text": "Current weather: 22.5C, partly cloudy, 65% humidity." } ], "structuredContent": { diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 83997a14e..4a11a440b 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -177,7 +177,7 @@ "$ref": "#/$defs/MetaObject" }, "content": { - "description": "A list of content objects that represent the unstructured result of the tool call.", + "description": "A list of content objects that represent the model-oriented result of the tool call.\n\nClients SHOULD use this field when providing tool results to conversational or agentic model\ncontext, if it is present.", "items": { "$ref": "#/$defs/ContentBlock" }, @@ -189,7 +189,7 @@ }, "structuredContent": { "additionalProperties": {}, - "description": "An optional JSON object that represents the structured result of the tool call.", + "description": "An optional JSON object that represents the structured, machine-oriented result of the tool\ncall.\n\nWhen both this field and {@link CallToolResult.content} are present, they SHOULD be\nsemantically equivalent. For backwards compatibility, tools that return structured content\nSHOULD also return a `content` representation of the same information. JSON-serialized text is\nacceptable, but not required.", "type": "object" } }, @@ -4048,7 +4048,7 @@ "type": "string" }, "outputSchema": { - "description": "An optional JSON Schema object defining the structure of the tool's output returned in\nthe structuredContent field of a {@link CallToolResult}.\n\nDefaults to JSON Schema 2020-12 when no explicit `$schema` is provided.\nCurrently restricted to `type: \"object\"` at the root level.", + "description": "An optional JSON Schema object defining the structure of the tool's output returned in\nthe structuredContent field of a {@link CallToolResult}.\n\nThis schema applies to structuredContent and does not constrain the format of\n{@link CallToolResult.content}.\n\nDefaults to JSON Schema 2020-12 when no explicit `$schema` is provided.\nCurrently restricted to `type: \"object\"` at the root level.", "properties": { "$schema": { "type": "string" @@ -4171,7 +4171,7 @@ "description": "Optional metadata about the tool result. Clients SHOULD preserve this field when\nincluding tool results in subsequent sampling requests to enable caching optimizations." }, "content": { - "description": "The unstructured result content of the tool use.\n\nThis has the same format as {@link CallToolResult.content} and can include text, images,\naudio, resource links, and embedded resources.", + "description": "The model-oriented result content of the tool use.\n\nThis has the same format as {@link CallToolResult.content} and can include text, images,\naudio, resource links, and embedded resources.", "items": { "$ref": "#/$defs/ContentBlock" }, @@ -4183,7 +4183,7 @@ }, "structuredContent": { "additionalProperties": {}, - "description": "An optional structured result object.\n\nIf the tool defined an {@link Tool.outputSchema}, this SHOULD conform to that schema.", + "description": "An optional structured, machine-oriented result object.\n\nIf the tool defined an {@link Tool.outputSchema}, this SHOULD conform to that schema unless\n{@link ToolResultContent.isError} is true.", "type": "object" }, "toolUseId": { diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 29a6cecf6..990b37661 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -1548,12 +1548,21 @@ export interface ListToolsResultResponse extends JSONRPCResultResponse { */ export interface CallToolResult extends Result { /** - * A list of content objects that represent the unstructured result of the tool call. + * A list of content objects that represent the model-oriented result of the tool call. + * + * Clients SHOULD use this field when providing tool results to conversational or agentic model + * context, if it is present. */ content: ContentBlock[]; /** - * An optional JSON object that represents the structured result of the tool call. + * An optional JSON object that represents the structured, machine-oriented result of the tool + * call. + * + * When both this field and {@link CallToolResult.content} are present, they SHOULD be + * semantically equivalent. For backwards compatibility, tools that return structured content + * SHOULD also return a `content` representation of the same information. JSON-serialized text is + * acceptable, but not required. */ structuredContent?: { [key: string]: unknown }; @@ -1754,6 +1763,9 @@ export interface Tool extends BaseMetadata, Icons { * An optional JSON Schema object defining the structure of the tool's output returned in * the structuredContent field of a {@link CallToolResult}. * + * This schema applies to structuredContent and does not constrain the format of + * {@link CallToolResult.content}. + * * Defaults to JSON Schema 2020-12 when no explicit `$schema` is provided. * Currently restricted to `type: "object"` at the root level. */ @@ -2467,7 +2479,7 @@ export interface ToolResultContent { toolUseId: string; /** - * The unstructured result content of the tool use. + * The model-oriented result content of the tool use. * * This has the same format as {@link CallToolResult.content} and can include text, images, * audio, resource links, and embedded resources. @@ -2475,9 +2487,10 @@ export interface ToolResultContent { content: ContentBlock[]; /** - * An optional structured result object. + * An optional structured, machine-oriented result object. * - * If the tool defined an {@link Tool.outputSchema}, this SHOULD conform to that schema. + * If the tool defined an {@link Tool.outputSchema}, this SHOULD conform to that schema unless + * {@link ToolResultContent.isError} is true. */ structuredContent?: { [key: string]: unknown }; From 190c6dc6724639ed9415bf7d2948777392b6c349 Mon Sep 17 00:00:00 2001 From: Kyle Rubenok Date: Fri, 1 May 2026 17:04:11 -0700 Subject: [PATCH 4/4] PR Comments, thanks Den! --- ...clarify-tool-result-content-visibility.mdx | 7 ++++--- docs/specification/draft/changelog.mdx | 1 + docs/specification/draft/schema.mdx | 2 +- docs/specification/draft/server/tools.mdx | 20 ++++++++++++------- schema/draft/schema.json | 2 +- schema/draft/schema.ts | 2 +- ...-clarify-tool-result-content-visibility.md | 7 ++++--- 7 files changed, 25 insertions(+), 16 deletions(-) diff --git a/docs/seps/2200-clarify-tool-result-content-visibility.mdx b/docs/seps/2200-clarify-tool-result-content-visibility.mdx index 8136db490..d3c3d5f64 100644 --- a/docs/seps/2200-clarify-tool-result-content-visibility.mdx +++ b/docs/seps/2200-clarify-tool-result-content-visibility.mdx @@ -104,9 +104,10 @@ This SEP proposes the following updates to the MCP specification (draft): - Clients SHOULD choose the field that best matches their experience: - Conversational/agent UX: prefer `content`. - Programmatic/code mode: prefer `structuredContent`. - - When providing tool results to a model context, clients SHOULD use `content` when present - and only fall back to `structuredContent` if `content` is empty or omitted. - - If the preferred field is missing or empty, clients MAY fall back to the other field. + - When providing tool results to a model context, clients SHOULD use `content` and only fall + back to `structuredContent` if `content` is empty. + - For programmatic/code mode, clients SHOULD use `structuredContent` when present and MAY fall + back to `content` if `structuredContent` is absent. - Clients SHOULD NOT forward both fields verbatim to the model as separate inputs. 4. **MCP Apps and model visibility** diff --git a/docs/specification/draft/changelog.mdx b/docs/specification/draft/changelog.mdx index d7599580a..84b06fa29 100644 --- a/docs/specification/draft/changelog.mdx +++ b/docs/specification/draft/changelog.mdx @@ -17,6 +17,7 @@ N/A 2. Document OpenTelemetry trace context propagation conventions for `_meta` keys (`traceparent`, `tracestate`, `baggage`) ([SEP-414](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414)). 3. Servers **SHOULD** return tools from `tools/list` in a deterministic order to enable client-side caching and improve LLM prompt cache hit rates. 4. Require standard MCP request headers (`Mcp-Method`, `Mcp-Name`) on Streamable HTTP POST requests, and add support for custom headers from tool parameters via `x-mcp-header` ([SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)). +5. Clarify the intended audiences for `content` and `structuredContent` in tool results, including model visibility, fallback behavior, and `outputSchema` validation for non-error results ([SEP-2200](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2200)). ## Other schema changes diff --git a/docs/specification/draft/schema.mdx b/docs/specification/draft/schema.mdx index fe445a215..523c3fcb5 100644 --- a/docs/specification/draft/schema.mdx +++ b/docs/specification/draft/schema.mdx @@ -1445,7 +1445,7 @@ for task augmentation of specific request types in their capabilities.

interface CallToolResult {
  _meta?: MetaObject;
  content: ContentBlock[];
  structuredContent?: { [key: string]: unknown };
  isError?: boolean;
  [key: string]: unknown;
}

The result returned by the server for a tools/call request.

Example: Result with unstructured text
{
"content": [
{
"type": "text",
"text": "Current weather in New York:\nTemperature: 72°F\nConditions: Partly cloudy"
}
],
"isError": false
}
Example: Result with structured content
{
"content": [
{
"type": "text",
"text": "Current weather: 22.5C, partly cloudy, 65% humidity."
}
],
"structuredContent": {
"temperature": 22.5,
"conditions": "Partly cloudy",
"humidity": 65
}
}
Example: Invalid tool input error
{
"content": [
{
"type": "text",
"text": "Invalid departure date: must be in the future. Current date is 08/08/2025."
}
],
"isError": true
}

A list of content objects that represent the model-oriented result of the tool call.

Clients SHOULD use this field when providing tool results to conversational or agentic model -context, if it is present.

An optional JSON object that represents the structured, machine-oriented result of the tool +context.

An optional JSON object that represents the structured, machine-oriented result of the tool call.

When both this field and CallToolResult.content are present, they SHOULD be semantically equivalent. For backwards compatibility, tools that return structured content SHOULD also return a content representation of the same information. JSON-serialized text is diff --git a/docs/specification/draft/server/tools.mdx b/docs/specification/draft/server/tools.mdx index 6debcd695..a61b72268 100644 --- a/docs/specification/draft/server/tools.mdx +++ b/docs/specification/draft/server/tools.mdx @@ -315,9 +315,9 @@ PII) with `x-mcp-header`, as header values are visible to network intermediaries ### Tool Result -Tool results may contain [**structured**](#structured-content) or **unstructured** content. - -**Unstructured** content is returned in the `content` field of a result, and can contain multiple content items of different types: +Tool results always include `content`, a list of content blocks that can contain multiple content +items of different types. `content` is the model-oriented representation of the result. Tool +results may also include [`structuredContent`](#structured-content) for machine-oriented consumers. All content types (text, image, audio, resource links, and embedded resources) @@ -410,7 +410,7 @@ Embedded resources support the same [Resource annotations](/specification/draft/ Tool results can include both a model-oriented representation and a machine-oriented representation: - `content` is the model-oriented representation of the result. Clients **SHOULD** use this field - when providing tool results to conversational or agentic model context, if it is present. + when providing tool results to conversational or agentic model context. - `structuredContent` is the machine-oriented representation of the same result for programmatic consumers, typed orchestration, and UI hydration. @@ -422,8 +422,14 @@ For backwards compatibility, a tool that returns structured content **SHOULD** a `content` representation of the same information. JSON-serialized text is acceptable, but it is not required and may be suboptimal depending on the use case. -If `content` is missing or empty, clients **MAY** fall back to `structuredContent`. Clients -**SHOULD NOT** forward both fields verbatim to the model as separate inputs. +Clients providing tool results to conversational or agentic model context **SHOULD** use `content`. +If `content` is empty, clients **MAY** fall back to `structuredContent`. + +Clients using tool results for programmatic, code, typed orchestration, or UI hydration workflows +**SHOULD** use `structuredContent` when present. If `structuredContent` is absent, clients **MAY** +fall back to `content`. + +Clients **SHOULD NOT** forward both fields verbatim to the model as separate inputs. When `isError` is `true`, the error message **SHOULD** be returned in `content`. Servers **MAY** omit `structuredContent` for error results. @@ -433,7 +439,7 @@ omit `structuredContent` for error results. Tools may also provide an output schema for validation of `structuredContent`. This schema applies to the structured, machine-oriented representation and does not constrain the format of `content`. -If an output schema is provided and a non-error result includes `structuredContent`: +If an output schema is provided and the result is not an error: - Servers **MUST** provide structured results that conform to this schema. - Clients **SHOULD** validate structured results against this schema. diff --git a/schema/draft/schema.json b/schema/draft/schema.json index 4a11a440b..407ce7bfc 100644 --- a/schema/draft/schema.json +++ b/schema/draft/schema.json @@ -177,7 +177,7 @@ "$ref": "#/$defs/MetaObject" }, "content": { - "description": "A list of content objects that represent the model-oriented result of the tool call.\n\nClients SHOULD use this field when providing tool results to conversational or agentic model\ncontext, if it is present.", + "description": "A list of content objects that represent the model-oriented result of the tool call.\n\nClients SHOULD use this field when providing tool results to conversational or agentic model\ncontext.", "items": { "$ref": "#/$defs/ContentBlock" }, diff --git a/schema/draft/schema.ts b/schema/draft/schema.ts index 990b37661..d2f72c104 100644 --- a/schema/draft/schema.ts +++ b/schema/draft/schema.ts @@ -1551,7 +1551,7 @@ export interface CallToolResult extends Result { * A list of content objects that represent the model-oriented result of the tool call. * * Clients SHOULD use this field when providing tool results to conversational or agentic model - * context, if it is present. + * context. */ content: ContentBlock[]; diff --git a/seps/2200-clarify-tool-result-content-visibility.md b/seps/2200-clarify-tool-result-content-visibility.md index 66379952c..8da0af310 100644 --- a/seps/2200-clarify-tool-result-content-visibility.md +++ b/seps/2200-clarify-tool-result-content-visibility.md @@ -86,9 +86,10 @@ This SEP proposes the following updates to the MCP specification (draft): - Clients SHOULD choose the field that best matches their experience: - Conversational/agent UX: prefer `content`. - Programmatic/code mode: prefer `structuredContent`. - - When providing tool results to a model context, clients SHOULD use `content` when present - and only fall back to `structuredContent` if `content` is empty or omitted. - - If the preferred field is missing or empty, clients MAY fall back to the other field. + - When providing tool results to a model context, clients SHOULD use `content` and only fall + back to `structuredContent` if `content` is empty. + - For programmatic/code mode, clients SHOULD use `structuredContent` when present and MAY fall + back to `content` if `structuredContent` is absent. - Clients SHOULD NOT forward both fields verbatim to the model as separate inputs. 4. **MCP Apps and model visibility**