modelcontextprotocol/typescript-sdk

Use types for tool annotations to reduce confusion

Opened this issue · 0 comments

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 😅