Skip to content

[Bug]: [mcp] read_skill returns the skill body twice, in two different encodings #9290

Description

@dev-aditya-hub

What happened?

read_skill is the only one of the nine MCP tools that sets CallToolResult.Content itself:

return &mcp.CallToolResult{
    Content: []mcp.Content{&mcp.TextContent{Text: content}},
}, types.ReadSkillOutput{Instructions: content}, nil

The SDK then unconditionally populates StructuredContent from the typed output, so the response carries the skill body twice — once as raw markdown in content[0].text, once JSON-escaped inside structuredContent.instructions.

The other eight handlers return nil, output, nil and also carry their payload twice, but that is the spec's backward-compat duplicate and both copies are identical JSON. read_skill is different: the duplication is hand-written, and the two copies are different encodings of the same bytes.

Measured against the embedded skills, wire bytes per call:

path file wire ratio
SKILL.md 800 B 1723 B 2.15×
error-root-cause/SKILL.md 1427 B 3021 B 2.12×
detect-n-plus-one/SKILL.md 1535 B 3243 B 2.11×

INSTRUCTIONS.md points agents at SKILL.md first, so a skill-driven run pays this on at least two calls before it touches any telemetry.

Steps to reproduce

  1. Call read_skill with path: "SKILL.md" over the MCP endpoint.
  2. Compare content[0].text with structuredContent.instructions — same body, two encodings.

Expected behavior

One copy. Which one is a design call, so I'd rather ask than guess. Measured on a 347-byte skill:

option wire note
today 797 B
drop the hand-set Content 832 B worsecontent becomes the JSON copy
Content: []mcp.Content{} 413 B one copy, but no content block for clients that only read content
markdown in Content, metadata (path, truncated) in the typed output 473 B keeps readable markdown and a content block

The last one looks best to me — it keeps the agent-facing markdown unescaped and drops the body from the structured half — but it changes ReadSkillOutput's schema, so it's your call. Happy to implement whichever you prefer, with a test pinning the wire shape (the current tests assert output.Instructions only, which is why this went unnoticed).

Worth noting the same 2× applies to the other eight tools; that half is inherited from the spec's backward-compat guidance, so I've left it out of this issue and raised the measurement side in #9135 instead.

Relevant log output

No response

Screenshot

No response

Additional context

No response

Jaeger backend version

main (2487d69)

SDK

Not applicable — this is in the MCP server handler layer (mcptools), not ingestion.

Pipeline

Not applicable.

Stogage backend

Not applicable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions