Skip to content

SEP-1624: Clarify structuredContent vs content Usage Guidance #1624

@krubenok

Description

@krubenok

SEP-1624: Clarify structuredContent vs content Usage Guidance

Status: proposal
Type: Informational
Created: 2025-10-07
Related Issues: #1411
Related PRs: #371, #559

Abstract

This SEP proposes clarifying the specification around structuredContent and content fields in CallToolResult to address inconsistent client and server implementations. The proposal focuses on non-breaking clarifications to the existing spec language to better communicate the intended design and usage patterns for both fields.

Motivation

Current State of Confusion

Since the introduction of structuredContent in PR #371, there has been significant confusion about when and how clients and servers should use these fields:

  1. Inconsistent Client Behavior (#1411):

    • Cursor recently changed to prefering content for the model input per @msfeldstein
    • VS Code prefers structuredContent over content for model context. Both are shown in Chat UI.
    • Most other clients (CC/Windsurf) ignore structuredContent entirely
    • Some clients forward both fields verbatim, causing redundant context (Citation? Mentioned in Discord discussion)
  2. Server Implementation Issues:

    • Servers returning different semantic information in each field (violating backwards compatibility intent)
    • Unclear guidance leading to JSON stringification in content in structuredContent resulting in at best indentical output, at worst semanitcally duplicated output in structuredContent
  3. Misaligned Use Cases:

Historical Context

PR #371 introduced structuredContent with these design goals:

  • Enable strict validation of structured tool results for programmatic tool use
  • Support use cases like agent code generation, tool composition, and typed orchestration
  • Originally made content and structuredContent mutually exclusive

PR #559 relaxed the requirements for backwards compatibility:

  • Made content non-optional again (additive change only)
  • Changed validation requirement from MUST to SHOULD
  • Introduced the backwards compatibility guidance: "For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block."

Discord Discussion (Aug-Sep 2025):

  • structuredContent is primarily valuable at for "Code Mode" / programmatic MCP usage (per Cloudflare Code Mode blog, @domdomegg prototypes, etc...)
  • For conversational/agent use with language models directly, content is often preferred (lower token cost, better readability)
  • Current spec language inadvertently leads to treating structuredContent as superior in all contexts

Specification

Proposed Changes

1. Clarify Client Guidance in /specification/draft/server/tools.mdx

Current Language (lines 298-305):

#### Structured Content

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

For backwards compatibility, a tool that returns structured content SHOULD also return the serialized JSON in a TextContent block.

Proposed Change:

#### Structured Content

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

The `structuredContent` and `content` fields serve different use cases:

- **`content`**: Model-oriented output optimized for readability and token efficiency. Preferred for conversational agents and direct model prompting. Does not require strict schematisation. A plain text or markdown representation of the output for example.
- **`structuredContent`**: Machine-oriented output for programmatic tool use, code generation, type-safe orchestration, and strict schema validation.

**Server Requirements**:

- When both fields are present, they MUST be semantically equivalent (same information, different presentation)
  - In some cases, an explicit field in `structuredContent` can be semantically present in `content` without specific mention. For example if expected fields are returned and no error is mentioned, a `structuredContent` response that contains a `"status": "success",` object is evident without the status being verbatim mentioned as successful in `content`.
- For backwards compatibility, a tool that returns structured content SHOULD also return a response in `content`.

**Client Guidance**:

- Clients SHOULD choose the appropriate field to expose to the model based on their use case:
  - **Conversational/Agent UX**: Prefer `content` for lower token cost and readability (by both humans and models).
  - **Programmatic/Code Mode**: Prefer `structuredContent` for type safety and schema validation
- Clients SHOULD NOT forward both fields verbatim to models as semantically distinct inputs.
- Clients that use `content` MUST still perform `outputSchema` validation against `structuredContent` when present

2. Update Tool Output Schema Documentation

Add Section After Output Schema Example (around line 345):

### Designing Tools with Complementary Output Formats

Tool developers should design outputs where `content` and `structuredContent` serve complementary roles, enabling both conversational and programmatic use cases:

- **`content`** provides a human/model-oriented representation:

  - Natural language content, context, and explanations
  - Optimized for readability and token efficiency
  - Includes suggestions, interpretations, or additional guidance
  - Suitable for direct consumption by conversational agents or end-users

- **`structuredContent`** provides a machine-oriented representation:
  - Strict schema validation via `outputSchema`
  - Type safety for programmatic tool composition
  - Consistent structure for code generation and scripting
  - Enables reliable data extraction and transformation

**Design patterns for complementary outputs:**

- Define an `outputSchema` that captures all data fields, ensuring that the `text` field from `content` is present as a substring of `structuredContent`.
- Provide semantically equivalent `content` with enhanced readability
- Format `content` with context that helps models use the information effectively
- Example: A weather tool returns structured temperature/humidity data in `structuredContent`, plus a natural language summary in `content`, i.e:

  structuredContent (machine-oriented):

  ```json
  {
    "temperature": 72,
    "units": "F",
    "condition": "sunny",
    "city": "Seattle",
    "summary": "It's 72 degrees F and sunny in Seattle"
  }
  ```

content (model / human-oriented):

```json
{
  "type": "text",
  "text": "It's 72 degrees F and sunny in Seattle"
}
```

Open Question: would it be beneficial to provide some examples here or cite resources? Writing Effective Tools for Agents for example.

3. Clarify Schema Validation Requirements

Update outputSchema description in schema.ts (draft/schema.ts around line 975):

Current:

/**
 * An optional JSON Schema object defining the structure of the tool's output returned in
 * the structuredContent field of a CallToolResult.
 */
outputSchema?: {
  type: "object";
  properties?: { [key: string]: object };
  required?: string[];
};

Proposed:

/**
 * An optional JSON Schema object defining the structure of the tool's output returned in
 * the structuredContent field of a CallToolResult.
 *
 * When outputSchema is provided:
 * - Servers MUST populate structuredContent with data conforming to this schema
 * - Clients SHOULD validate structuredContent against this schema
 * - Servers SHOULD also provide a human-readable representation in content
 *
 * Use outputSchema for programmatic tool use, code generation, and type-safe integrations.
 * For conversational agents, consider whether unstructured content provides better model performance.
 */
outputSchema?: {
  type: "object";
  properties?: { [key: string]: object };
  required?: string[];
};

Open Question: if we continue to expect that content is present in structuredContent as a substring, rather than the stringified JSON we have today, we will have semantic duplication in structuredContent. By the letter of the proposed spec this is not an issue as codeful use of structuredContent can ignore the embedded content field. In practice with clients currently preferring structuredContent as model input, this duplication could lead to increased token usage and duplicate information being provided.

Non-Changes (Explicitly Out of Scope)

This SEP intentionally does NOT propose:

  1. Breaking changes to field optionality - Both fields remain as currently specified
  2. Changes to validation semantics - SHOULD/MUST requirements remain unchanged
  3. New protocol fields or capabilities - Pure clarification only
  4. Deprecation of either field - Both remain first-class citizens for their use cases

Rationale

Why These Changes

  1. Aligns with Original Design Intent: PR RFC: add Tool.outputSchema and CallToolResult.structuredContent #371 was explicitly designed for programmatic tool use, not as a universal replacement for content

  2. Addresses Real-World Pain Points:

    • Cursor has already reverted to preferring content based on the Discord discussion
    • Developers are confused by Inspector warnings that contradict best practices
    • Server authors need clear guidance on semantic equivalence requirement
  3. Prevents Future Issues:

    • Clear use-case guidance prevents clients from implementing misguided behavior
    • Semantic equivalence requirement prevents the current situation where different clients get different outputs
  4. Maintains Backwards Compatibility:

    • All existing valid implementations remain valid
    • Changes are purely additive clarifications
    • No protocol changes required

Alternative Approaches Considered

  1. Making structuredContent a side-channel for metadata

    • OpenAI Apps SDK implements this pattern as of 10/7 and also leverages _meta for additional data.
    • Would require breaking changes
    • Better addressed in future SEP after more real-world usage with more explicit side channels or audience hints (i.e. [model|host|user])
  2. Deprecating one field in favor of the other

    • Premature - both serve legitimate use cases though implementation of the fields is perhaps sub-optimal.
    • Would break existing implementations
  3. Adding capability negotiation for structured content

    • Overly complex for current problem
    • Can be added later if needed

Backward Compatibility

This SEP introduces no backward incompatibilities.

All proposed changes are purely clarifications of existing behavior:

  1. No Protocol Changes: No changes to message structure, field types, or optionality
  2. No Breaking Semantics: MUST/SHOULD requirements remain as specified in PR make CallToolResult.content non-optional and relax output schema validation requirement #559
  3. Additive Guidance Only: All existing valid implementations remain valid
  4. Clarification of Intent: Documents the original design intent from PR RFC: add Tool.outputSchema and CallToolResult.structuredContent #371

Impact on Existing Implementations

Servers:

  • Servers already returning both fields with semantic equivalence: ✅ No change needed
  • Servers returning only content: ✅ Still valid
  • Servers returning only structuredContent with outputSchema: ✅ Still valid
  • Servers returning different semantics in each field: ⚠️ Should align to guidance (was always problematic)

Clients:

  • Clients preferring content: ✅ Now explicitly supported
  • Clients preferring structuredContent: ✅ Still valid for their use case
  • Clients validating against outputSchema: ✅ No change needed
  • Clients forwarding both fields to models: ⚠️ Should align to guidance (was always problematic)

Security Implications

This SEP has no direct security implications as it introduces no protocol changes. Existing security considerations from the original specification for structuredContent remain applicable.

  • Clients should consider tool annotations for trust decisions
  • Validation of untrusted server data remains critical
  • Schema validation is a defense mechanism, not a security boundary

Reference Implementation

Proposed throughout the issue and purely documentation-driven. Related, though different changes would be beneficial in the SDKs as mentioned in #1411.

References

Appendix: Example Tool Resposes

Below are valid, semantically equivalent responses for a hypothetical get_email tool that retrieves an email for both conten and structuredContent

Note: I am taking the liberty of putting new lines in in place of \n in the outputs of both structuredContent and content to better communicate the differences. In a real implementation, these would be \n within the strings.

structuredContent

284 tokens of output excluding the semantically duplicative content object embedded inside.

{
  "status": "success",
  "response_format": "detailed",
  "mailItem": {
    "subject": "Project Update – Q3 Metrics Review",
    "bodyText":
    "Hi Kyle Rubenok,

    Summary of the requested project artifacts.
    Key Points:
    - Revenue +14% QoQ
    - Churn down 0.6 pts
    - Infra cost stable while traffic increased

    Next Actions:
    1. Prepare draft reply summarizing highlights.
    2. Schedule follow‑up review.

    Thanks,
    Automated System",
    "conversationId": "CONV-EXAMPLE-123456",
    "receivedTime": "2025-10-07T19:45:23-07:00",
    "from": {
      "name": "Kyle Rubenok",
      "email": "sender@example.com"
    },
    "to": [
      {
        "name": "Recipient",
        "email": "recipient@example.com"
      }
    ],
    "cc": [
      {
        "name": "Review Bot",
        "email": "review.bot@example.com"
      }
    ]
  },
  "suggestions": [
    "Use email_draft_message to start a reply when you have enough context.",
    "Call email_get_conversations_in_folder to gather thread context before acting."
  ],
  "content":"Subject: 'Project Update – Q3 Metrics Review' from Kyle Rubenok (received 2025-10-07 19:45 UTC). conversationId 'CONV-EXAMPLE-123456'\n  To: Recipient <recipient@example.com>, CC: Review Bot <review.bot@example.com>\n  Status: unread (labels: inbox, unread).\n  Attachments: metrics-summary.xlsx (47 KB).\n\n  -- Message body --\n\n  Hi Kyle Rubenok,\n\n    Summary of the requested project artifacts.\n    Key Points:\n    - Revenue +14% QoQ\n    - Churn down 0.6 pts\n    - Infra cost stable while traffic increased\n\n    Next Actions:\n    1. Prepare draft reply summarizing highlights.\n    2. Schedule follow‑up review.\n\n    Thanks,\n    Automated System\n\n    -- End Message --\n\n    Potential actions:\n    - Use email_draft_message to start a reply when you have enough context.\n    - Call email_get_conversations_in_folder to gather thread context before acting."}

content

189 tokens of output

{
  "type": "text",
  "text": "
  Subject: 'Project Update – Q3 Metrics Review' from Kyle Rubenok (received 2025-10-07 19:45 UTC). conversationId 'CONV-EXAMPLE-123456'
  To: Recipient <recipient@example.com>, CC: Review Bot <review.bot@example.com>

  -- Message body --

  Hi Kyle Rubenok,

    Summary of the requested project artifacts.
    Key Points:
    - Revenue +14% QoQ
    - Churn down 0.6 pts
    - Infra cost stable while traffic increased

    Next Actions:
    1. Prepare draft reply summarizing highlights.
    2. Schedule follow‑up review.

    Thanks,
    Automated System

    -- End Message --

    Potential actions:
    - Use email_draft_message to start a reply when you have enough context.
    - Call email_get_conversations_in_folder to gather thread context before acting."
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions