Skip to content

feat(everything): add tool-level annotations to get-annotated-message#3246

Open
olaservo wants to merge 9 commits intomodelcontextprotocol:mainfrom
olaservo:fix/tool-annotations
Open

feat(everything): add tool-level annotations to get-annotated-message#3246
olaservo wants to merge 9 commits intomodelcontextprotocol:mainfrom
olaservo:fix/tool-annotations

Conversation

@olaservo
Copy link
Copy Markdown
Member

@olaservo olaservo commented Jan 24, 2026

Description

Add ToolAnnotations to all 18 tools in the Everything example server. Previously only get-annotated-message had annotations; now every tool explicitly declares readOnlyHint, destructiveHint, idempotentHint, and openWorldHint so the server serves as a complete reference for developers implementing tool annotations per the MCP spec.

Server Details

  • Server: everything
  • Changes to: tools, docs

Motivation and Context

Tool annotations are part of the MCP specification and provide hints to clients about tool behavior:

Annotation Purpose
readOnlyHint Indicates the tool does not modify its environment
destructiveHint Indicates whether the tool may perform destructive updates
idempotentHint Indicates whether repeated calls with same args have no additional effect
openWorldHint Indicates whether the tool interacts with external entities

The Everything server is the canonical MCP reference/demo server, yet only 1 of its 18 tools had ToolAnnotations. Adding annotations to all tools makes it a complete reference for:

  • Client developers testing annotation support
  • Server developers learning proper tool annotation usage
  • Integration testing of annotation handling

Tool Annotations Added

Tool readOnlyHint destructiveHint idempotentHint openWorldHint Rationale
echo true false true false Pure function, echoes input back
get-annotated-message true false true false (already existed) Returns annotated messages
get-env true false true false Reads process.env, no modifications
get-resource-links true false true false Returns links to internal resources
get-resource-reference true false true false Returns internal resource references
get-roots-list true false true false Reads client roots, no modifications
get-structured-content true false true false Returns simulated weather data
get-sum true false true false Pure arithmetic computation
get-tiny-image true false true false Returns a static encoded image
trigger-long-running-operation true false true false Simulates work with progress, no lasting state change
toggle-simulated-logging false false false false Toggles server-side logging state (not idempotent)
toggle-subscriber-updates false false false false Toggles resource subscription updates (not idempotent)
trigger-elicitation-request false false false false Triggers user-facing dialog via client
trigger-elicitation-request-async false false false false Async task-based elicitation via client
trigger-sampling-request false false false true Triggers LLM inference (external API)
trigger-sampling-request-async false false false true Async task-based LLM inference (external API)
gzip-file-as-resource false false true true Fetches URLs and creates session resources; same input produces same output
simulate-research-query false false false false Creates background tasks with internal state

How Has This Been Tested?

  • TypeScript compilation passes (npm run build)
  • Verified annotations are accepted by SDK types with no type errors
  • Tested with MCP Inspector to confirm annotations appear in tools/list responses

Tested with this version of Inspector in this open PR: modelcontextprotocol/inspector#1066

Breaking Changes

None. Annotations are optional metadata hints and do not affect tool behavior.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Protocol Documentation
  • My changes follows MCP security best practices
  • I have updated the documentation accordingly
  • I have tested this with an LLM client
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have documented all environment variables and configuration options

Additional context

The annotation values follow the MCP spec semantics:

  • readOnlyHint: true when the tool only returns data with no side effects
  • destructiveHint: false for all tools since none delete or overwrite external data
  • idempotentHint: true when repeated calls with the same args produce the same result and no additional environmental effect; false for toggles, user-interactive tools, and task-creating tools
  • openWorldHint: true only for tools that interact with external systems (URL fetching, LLM sampling); false for tools operating within the closed MCP server/client domain
olaservo and others added 2 commits January 12, 2026 07:48
Add readOnlyHint, destructiveHint, idempotentHint, and openWorldHint
annotations to the tool config. Update docs to reflect that this tool
now demonstrates both tool-level and content annotations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@olaservo olaservo marked this pull request as ready for review January 25, 2026 23:00
olaservo and others added 4 commits January 27, 2026 05:08
Previously only get-annotated-message had ToolAnnotations. Now all 18
tools explicitly declare readOnlyHint, destructiveHint, idempotentHint,
and openWorldHint so the server serves as a complete reference for
developers implementing tool annotations per the MCP spec.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
olaservo and others added 2 commits February 7, 2026 17:32
- Update get-annotated-message description: all tools now have
  tool-level annotations, not just this one
- Add missing tool entries: simulate-research-query,
  trigger-elicitation-request-async, trigger-sampling-request-async
- Add these tools to the file tree listing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@olaservo olaservo marked this pull request as ready for review February 8, 2026 01:09
│ ├── get-sum.ts
│ ├── get-tiny-image.ts
│ ├── gzip-file-as-resource.ts
│ ├── simulate-research-query.ts
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated updates - these were just missing from the docs.

@cliffhall cliffhall added enhancement New feature or request server-everything Reference implementation for the Everything MCP server - src/everything labels Apr 20, 2026
@cliffhall
Copy link
Copy Markdown
Member

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 21, 2026

Claude finished @cliffhall's task in 2m 15s —— View job


PR Review: feat(everything): add tool-level annotations

Tasks:

  • Gather context from PR description and changed files
  • Read tool annotation files to verify correctness
  • Review docs changes
  • Check annotation semantics against MCP spec
  • Post review findings

Summary

This PR adds ToolAnnotations to all 18 tools in the Everything server, making it a complete reference for MCP annotation semantics. The changes are non-breaking and the scope is clean. The annotation choices are well-reasoned and the PR description documents each decision clearly.

Annotation Semantics

The annotation values are correct across the board. A few noteworthy choices worth confirming:

trigger-long-running-operationreadOnlyHint: true, idempotentHint: true
The tool sends notifications/progress back to the client during execution, which is a side effect within the session. However, it doesn't persist any state externally, so readOnlyHint: true is defensible per spec. idempotentHint: true is also acceptable since repeated calls don't accumulate lasting environmental changes. This is a borderline case worth noting for consumers of this reference server — calling it twice sends two rounds of progress notifications and holds the connection twice as long.

gzip-file-as-resourceidempotentHint: true
The tool fetches a URL and registers a session resource. Same input re-registers the same resource URI with the same content, so "no additional effect on the environment" holds. ✅

trigger-elicitation-requestopenWorldHint: false
Elicitation interacts with the user, which could be considered an "external entity." However, elicitation is a first-class MCP protocol primitive (not an external system call), so false aligns with the intended distinction between MCP interactions and truly external systems. ✅

get-envidempotentHint: true
idempotentHint concerns environmental effects, not output determinism. Since get-env is read-only, calling it repeatedly has no additional effect. ✅

Minor Issue: Comment Inconsistency

get-annotated-message.ts (lines 25–28) is the only tool with inline explanatory comments on its annotation block:

annotations: {
  readOnlyHint: true,       // This tool only returns data, no side effects
  destructiveHint: false,   // Does not delete or modify anything
  idempotentHint: true,     // Same input always produces same output
  openWorldHint: false,     // Does not interact with external systems
},

None of the other 17 tools have these comments. For a reference server, this inconsistency is noticeable — developers scanning multiple tool files will see comments in one and not others. Since this file is specifically designed to demonstrate annotations, keeping the comments here is understandable, but the pattern could confuse readers into thinking the comments only matter for get-annotated-message. A brief note in the AGENTS.md or a comment in the get-annotated-message.ts file clarifying it's intentionally the most-commented example would help. Alternatively, adding a one-line comment to all tools would make the reference more uniform.

Fix this →

Doc Changes

The structure.md updates are good quality:

  • Adds missing tools to the file tree (simulate-research-query.ts, trigger-elicitation-request-async.ts, trigger-sampling-request-async.ts)
  • Fixes incorrect tool names: annotated-messageget-annotated-message, long-running-operationtrigger-long-running-operation, an get-suma get-sum
  • Expands tool descriptions for the newer tools

Verdict

Approve with nit. The core goal is well-executed — all 18 tools now have semantically correct annotations. The only actionable item is the comment inconsistency in get-annotated-message.ts, which is a style concern rather than a functional one. The docs fixes are a welcome bonus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request server-everything Reference implementation for the Everything MCP server - src/everything

2 participants