Advanced Tools
Tool Annotations
Tool annotations are metadata attached to each tool definition. They help clients and users understand the tool's behavior, especially regarding side effects, safety, and intended use. Annotations do not affect the tool's execution. They are hints for UI, approval flows, and documentation.
Annotations are not security features. They are advisory only and should not
be relied on for access control or sandboxing.
Why Use Annotations?
- UX clarity: Help users understand what a tool does before approving its use.
- Safety: Warn about potentially destructive or open-world actions.
- Automation: Allow clients to group, filter, or require extra approval for certain tools.
Example Tool Definition: Launch Real-Life Confetti
Here's a fun (and slightly dangerous) example of a tool that launches a real confetti cannon in the physical world:
{
name: 'launch_confetti',
description:
'Launch a real confetti cannon in the physical world to celebrate! (Warning: may make a mess)',
inputSchema: {
type: 'object',
properties: {
color: { type: 'string', description: 'The color of the confetti' },
intensity: {
type: 'string',
enum: ['low', 'medium', 'high'],
description: 'How much confetti to launch',
},
location: {
type: 'string',
description:
"Where to launch the confetti (e.g., 'main office', 'living room')",
},
},
required: ['color', 'location'],
},
annotations: {
title: 'Launch Real-Life Confetti',
readOnlyHint: false,
destructiveHint: true,
idempotentHint: false,
openWorldHint: true,
},
}
readOnlyHint: false
- This tool physically changes the environment (and makes a mess!).destructiveHint: true
- Launching confetti can be considered a destructive action (cleanup required).openWorldHint: true
- This tool literally interacts with the real world, not just the local system/service provider.idempotentHint: false
- This tool is not idempotent because it makes a mess (and the cannon may need a reload to work again).
A tool like this should require explicit user approval and be used with
caution. Imagine the consequences of launching confetti in the wrong place at
the wrong time!
Table of Annotations
Annotation | Default | Description | Relevance |
---|---|---|---|
readOnlyHint | false | If true, the tool does not modify its environment. | Always relevant |
destructiveHint | true | If true, the tool may perform destructive updates to its environment. | Irrelevant when readOnlyHint is true |
idempotentHint | false | If true, calling the tool repeatedly with the same arguments will have no additional effect on its environment. | Irrelevant when readOnlyHint is true |
openWorldHint | true | If true, this tool may interact with an "open world" of external entities (outside of the tool's domain). | Always relevant |
Pragmatic Annotation Guidelines
For practical tool design, consider these guidelines:
- Use
destructiveHint: true
for tools that delete records or data - Use
destructiveHint: false
for tools that modify content (even if original is overwritten). - Use
idempotentHint: true
for tools that produce the same logical result regardless of call count. Ignore metadata changes (timestamps, access logs, etc.). Focus on the core operation outcome. - Use
idempotentHint: false
for tools that produce a different result each time they are called. - Use
openWorldHint: true
for tools that interact with systems external to your application.
Examples:
delete_user(id: 123)
- Destructive, Idempotentupdate_user(id: 123, {name: "John"})
- Non-destructive, Idempotentincrement_counter(id: 123)
- Non-destructive, Not idempotent
This approach prioritizes practical usability over theoretical precision because being pedantic about
updatedAt
timestamps makes annotations effectively meaningless.How Annotations Affect the Client
Clients can use annotations to:
- Display warnings or require confirmation for destructive tools
- Group or filter tools (e.g., show only read-only tools)
- Provide friendlier names in the UI
- Decide when to allow automation or require human approval
Annotations make it easier to build safe, user-friendly interfaces for tool
invocation.
Recommended Practices
- Be accurate about side effects: Mark tools as
readOnlyHint: true
only if they never modify state. - Use descriptive titles: The
title
annotation should be clear and human-friendly. - Indicate idempotency: Use
idempotentHint: true
only if repeated calls with the same arguments are safe and have no extra effect. - Set open/closed world hints: Use
openWorldHint: true
for tools that interact with the internet or external systems. - Remember: annotations are hints! Never rely on them for security or correctness.
Annotations are for humans and UIs, not for enforcing security or correctness.
Learn More ๐
For a full list of available annotations and their meanings, see the official MCP documentation on tool annotations.
Structured Output and Output Schemas
Structured output allows tools to return rich, machine-validated data instead of just plain text. By defining an
outputSchema
for a tool, the server ensures that all tool responses conform to a specific structure, making it easier for clients and LLMs to consume, validate, and act on the results.Why Use Structured Output?
- Reliability: Ensures tool responses are predictable and machine-parseable.
- Validation: Automatic schema validation prevents malformed or incomplete data from propagating.
- Automation: Enables downstream automation, UI rendering, and chaining of tool results.
- Safety: Reduces the risk of misinterpretation or injection by strictly defining expected output.
How It Works
- Tool Definition: The tool specifies an
outputSchema
(JSON Schema) describing the expected result structure. - Tool Execution: When the tool is called, the server validates the output against the schema before returning it to the client.
- Client Consumption: Clients and LLMs can safely parse and use the structured result, knowing it matches the schema.
- Error Handling: If the output does not match the schema, an error is returned instead of invalid data.
Example Tool with Output Schema
Suppose we have a tool that generates a random fantasy character profile:
{
name: 'generate_fantasy_character',
description:
'Creates a random fantasy character profile for games or stories.',
inputSchema: {
type: 'object',
properties: {
theme: {
type: 'string',
description:
'Optional theme for the character (e.g., "forest", "fire", "ice")',
},
},
required: [],
},
outputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: "The character's name" },
species: {
type: 'string',
description: 'The fantasy species (e.g., elf, orc, dragon)',
},
characterClass: {
type: 'string',
description:
"The character's class or role (e.g., wizard, rogue, paladin)",
},
abilities: {
type: 'array',
items: { type: 'string' },
description: 'A list of special abilities or powers',
},
},
required: ['name', 'species', 'characterClass', 'abilities'],
},
}
Example Request/Response with Structured Content
Request
{
"jsonrpc": "2.0",
"id": 99,
"method": "tools/call",
"params": {
"name": "generate_fantasy_character",
"arguments": {
"theme": "forest"
}
}
}
Response
{
"jsonrpc": "2.0",
"id": 99,
"result": {
"content": [
{
"type": "text",
"text": "{\"name\": \"Lirael Mosswhisper\", \"species\": \"Elf\", \"characterClass\": \"Druid\", \"abilities\": [\"Speak with Animals\", \"Vine Whip\", \"Forest Camouflage\"]}"
}
],
"structuredContent": {
"name": "Lirael Mosswhisper",
"species": "Elf",
"characterClass": "Druid",
"abilities": ["Speak with Animals", "Vine Whip", "Forest Camouflage"]
}
}
}
Validation Flow
Below is a sequence diagram showing how structured content is validated:
Recommended Practices
- Define clear output schemas: Use JSON Schema to describe all possible fields and types.
- Validate on the server: Always validate tool output before returning to the client (the SDK does this for us).
- Handle validation errors gracefully: Inform users or clients when output does not match the schema (the SDK does this for us).
For more details, see the ๐ official MCP documentation on structured content and output schemas.