MCP Server Design · ~7 min
A tool's input schema is only half its contract. The other half — what it returns and what it promises about side effects — is where you let an agent trust a call without running it to find out.
The same craft that makes inputs unambiguous applies to outputs and side effects. Two fields carry it:
outputSchema for the shape of what you return, and tool annotations for the
behavior a call implies. Both are optional — and both change how a host treats your tool.
inputSchema tells the agent how to call. outputSchema tells the client what to expect
back, so the result can be validated rather than parsed hopefully out of a text blob. When you declare
one, return both structuredContent (the validated object) and a serialized JSON copy in
content for backwards compatibility with clients that don't read structured output.
A typed return lets the client check fields before the agent reasons over them, and lets downstream tools consume the object directly. It's the output-side of the same poka-yoke idea: remove the guesswork.
Annotations are optional metadata on a tool that describe its behavior, so a host can decide how much ceremony a call needs before running it. The spec defines four:
| Annotation | Asserts the tool… | Host can… |
|---|---|---|
readOnlyHint | Does not modify state | Auto-approve, run speculatively |
destructiveHint | May make irreversible changes | Require confirmation first |
idempotentHint | Repeating is safe — same effect | Retry without fear of duplication |
openWorldHint | Touches systems beyond a closed set | Treat its reach as unbounded |
Set idempotentHint: true on tools you've designed to be safely repeatable, so a host can retry a flaky
call without creating a second issue or charging a card twice.
Annotations are metadata only — the protocol does not enforce them, and the spec is explicit that
they are not trustable from untrusted servers. A malicious server can label a destructive tool
readOnlyHint: true to slip past an auto-approve policy.
Treat annotations as a claim by the server, useful for hosts you trust and worthless as a
security control for ones you don't. This is the same lesson as Part 2: trust is architectural.
A readOnlyHint from a third-party server is a hint, not a sandbox — gate egress and writes regardless.
So annotate honestly for the hosts that trust you, and never rely on another server's annotations to bound what it can do. The two readers of your tool's contract — a trusting host and a hostile auditor — need different things, and the protocol only helps the first.
outputSchema so clients validate the result instead of parsing prose.structuredContent and a JSON content copy for old clients.idempotentHint on safely-repeatable tools so hosts retry without duplication.Retrieval practice — recall, don't peek
Question 1The job of outputSchema on a tool is to…
Question 2When you declare an output schema, you should return…
Question 3A tool a host can safely retry without duplicating effects is marked…
Question 4Tool annotations from an untrusted server should be treated as…
Question 5 · spaced recall from Lesson 5Anthropic's floor for enabling tool search is roughly…
outputSchema for a tool you have, or deciding
which annotations to set on a write-tool? Next in Part 3: The Server Talks Back — when your server pauses a
call to ask the user for input, or asks the host's model to reason — and what each adds to the trust surface.