modelcontextprotocol / modelcontextprotocol/typescript-sdk

Use types for tool annotations to reduce confusion

Open
#927 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement needs decision P3
Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 15h
Merged PRs (30d)
4

Description

Is your feature request related to a problem? Please describe.

The way that tool annotations work is confusing because some of the properties only matter if you have read-only set to false.

Describe the solution you'd like

I would like to have type warnings if I am setting destructiveHint or idempotentHint to anything when readOnly is set to false.

Something like this:

Implement a discriminated union type that:

  1. Enforces the read-only constraint: When readOnlyHint: true, destructiveHint and idempotentHint cannot be specified
  2. Prevents redundant defaults: Only allows specifying properties when they differ from their default values
  3. Uses defaults: destructiveHint: true, idempotentHint: false, openWorldHint: true, readOnlyHint: false

Proposed Type Definition:

type ToolAnnotations = 
  | {
      readOnlyHint: true;
      openWorldHint?: boolean;
    }
  | {
      destructiveHint?: false;  // Only allow false (default is true)
      idempotentHint?: true;   // Only allow true (default is false)  
      openWorldHint?: false;   // Only allow false (default is true)
    };

Benefits:

  • Cleaner configurations: No need to specify default values
  • Type safety: Prevents invalid combinations at compile time
  • Explicit intent: Only specify what differs from defaults
  • Constraint enforcement: Cannot set destructive/idempotent on read-only tools

Examples:

type ToolAnnotations = 
  | {
      readOnlyHint: true;
      openWorldHint?: boolean;
    }
  | {
      destructiveHint?: false;  // Only allow false (default is true)
      idempotentHint?: true;   // Only allow true (default is false)  
      openWorldHint?: false;   // Only allow false (default is true)
    };

// ✅ Clean - all defaults
const defaultTool: ToolAnnotations = {};

// ✅ Only specify non-defaults
const nonDestructiveTool: ToolAnnotations = {
  destructiveHint: false  // Only specify when false (default is true)
};

// ✅ Read-only tool
const readOnlyTool: ToolAnnotations = {
  readOnlyHint: true,
  openWorldHint: false
};

// ❌ TypeScript error - cannot specify defaults
const invalidDefaults: ToolAnnotations = {
  destructiveHint: true,  // Error: Type 'true' is not assignable to type 'false'
  idempotentHint: false,   // Error: Type 'false' is not assignable to type 'true'
  openWorldHint: true      // Error: Type 'true' is not assignable to type 'false'
};

// ❌ TypeScript error - cannot set destructive/idempotent on read-only
const invalidReadOnly: ToolAnnotations = {
  readOnlyHint: true,
  destructiveHint: true  // Error: Property 'destructiveHint' does not exist
};

Describe alternatives you've considered

The status quo is me being pretty confused 😅

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No file or test is named in the issue. Start by locating the existing ToolAnnotations type and the tool registration entry point, then compare their current defaults and read-only behavior with the proposed discriminated union. Done means the public types reject invalid combinations at compile time while accepting valid default and non-default annotations, with the relevant type checks passing.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.