Skip to content

MCP tool-call parser concatenates <parameter> tail into first string arg when model emits <TAG>…</TAG> instead of <parameter name="TAG">…</parameter> #345

@uniclawassistant

Description

@uniclawassistant

Summary

On some MCP tool-calls, the SDK appears to emit (or fail to fully parse) a tool-call where the model wrote <prompt>…</prompt> instead of <parameter name="prompt">…</parameter>. The result reaching the MCP server is that the first string parameter contains the whole trailing </prompt>\n<parameter name="X">VALUE… blob, and the rest of the parameters arrive as undefined.

Environment

  • @anthropic-ai/claude-agent-sdk 0.2.92
  • Model: claude-opus-4-8
  • Transport: stdio MCP server from @modelcontextprotocol/sdk
  • Tools registered with server.tool(name, desc, zod-shape, handler) — three string args (prompt, preset?, caption?)

Observed payload

Three independent calls in the same session — every one of them stored the same shape in our DB (real values, untouched):

prompt:
  "Flat 2D UI design of an iOS 26 music player … dark mode.</prompt>\n<parameter name=\"preset\">[\"1024x1536\",\"format=png\",\"quality=high\"]"

preset: undefined
caption: undefined

i.e. the model wrote <prompt>…</prompt> rather than <parameter name="prompt">…</parameter>, the parser didn't find a matching </parameter>, and instead of bouncing the whole call or treating the next <parameter name="…"> as the start of a new arg, it concatenated the tail into args.prompt.

Expected behaviour

One of:

  1. Treat <TAG>…</TAG> (where TAG matches a declared parameter name) as a valid alternate form of <parameter name="TAG">…</parameter>, OR
  2. Return a structured tool-call parse error so the agent can self-correct, OR
  3. Stop the value at the next <parameter name="…"> boundary even when the current </parameter> is missing.

Current behaviour silently corrupts the args.

Why it bites users

In our case this caused FED-32 — an image_generation tool whose preset arrived as undefined, so the host's defaults kicked in and every call returned a 1024×1024 square regardless of the explicit ["portrait"] / ["1024x1536"] requested. Without inspecting the model's raw tool-call we'd have spent a long time looking for the bug in our preset parser.

We've shipped a workaround that detects the boundary, restores known params, and refuses calls where the boundary fired but no recognized parameter could be recovered (so the failure becomes visible instead of silently defaulting). It's a clear compensation — happy to share the normalizer code if useful.

Asks

  1. Confirm whether this is a known issue (couldn't find a direct match).
  2. Decide whether the right fix lives in the SDK's tool-call parser (option 3 above seems lowest-risk).
  3. Anything you'd like us to capture next time we see it in the wild — full request trace, MCP frames, model output, etc?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    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