Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .changeset/add-tool-annotations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
"mcp-lite": minor
---

Add tool annotations support per MCP specification 2025-06-18.

Tools can now include optional `annotations` field with behavioral hints and metadata:

- **Behavioral hints**: `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` - Help clients understand tool behavior and potential side effects
- **Audience targeting**: `audience` array to specify intended users (assistant, user, or both)
- **Priority**: Optional `priority` field (0-1) for relative importance hints
- **Timestamps**: `lastModified` for tracking tool updates
- **Display name**: `title` field as alternative to top-level title

Example usage:

```typescript
server.tool("deleteDatabase", {
description: "Permanently deletes the database",
annotations: {
destructiveHint: true,
audience: ["user"],
priority: 0.3,
},
handler: async (args) => {
// implementation
}
});
```

All annotation fields are optional. Tools without annotations continue to work unchanged (backwards compatible).
66 changes: 66 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,72 @@ server.tool("experimental-feature", {

The `_meta` and `title` from the definition appear in `tools/list` responses. Tool handlers can also return `_meta` in the result for per-call metadata like execution time or cache status.

### Tool with Annotations

Annotations provide metadata about tool behavior and usage hints to help clients make informed decisions. Annotations include behavioral hints (readOnlyHint, destructiveHint, idempotentHint, openWorldHint), audience targeting, priority levels, and timestamps.

```typescript
// Read-only tool
server.tool("getConfig", {
description: "Retrieves configuration settings",
annotations: {
readOnlyHint: true,
audience: ["assistant"],
priority: 0.8,
},
handler: () => ({
content: [{ type: "text", text: JSON.stringify(config) }],
}),
});

// Destructive tool
server.tool("deleteDatabase", {
description: "Permanently deletes the database",
annotations: {
destructiveHint: true,
readOnlyHint: false,
audience: ["user"],
priority: 0.3,
openWorldHint: true,
},
inputSchema: z.object({ confirm: z.boolean() }),
handler: async (args) => {
if (args.confirm) {
await dropDatabase();
}
return { content: [{ type: "text", text: "Database deleted" }] };
},
});

// Idempotent tool
server.tool("setConfig", {
description: "Updates a configuration value",
annotations: {
idempotentHint: true,
readOnlyHint: false,
priority: 0.5,
lastModified: "2025-01-15T10:00:00Z",
},
inputSchema: z.object({ key: z.string(), value: z.string() }),
handler: (args) => ({
content: [{ type: "text", text: `Set ${args.key} to ${args.value}` }],
}),
});
```

**Annotation Fields:**

- **`readOnlyHint`** (boolean): Tool only reads data without making modifications
- **`destructiveHint`** (boolean): Tool makes potentially irreversible changes
- **`idempotentHint`** (boolean): Repeated calls produce the same result
- **`openWorldHint`** (boolean): Tool interacts with external systems beyond the server's control
- **`audience`** (string[]): Intended users - `["assistant"]`, `["user"]`, or both
- **`priority`** (number): Importance level from 0 (lowest) to 1 (highest)
- **`lastModified`** (string): ISO 8601 timestamp of last modification
- **`title`** (string): Human-readable display name (alternative to top-level `title`)

Annotations appear in `tools/list` responses and help clients understand tool capabilities, risks, and appropriate usage patterns. See the [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) for more details.

### Resources

Resources are URI-identified content.
Expand Down
53 changes: 53 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import type {
ResourceVarValidators,
SchemaAdapter,
Tool,
ToolAnnotations,
ToolCallResult,
ToolEntry,
} from "./types.js";
Expand Down Expand Up @@ -441,6 +442,14 @@ export class McpServer {
* @param def - Tool definition with schema, description, handler, and optional metadata
* @param def.description - Human-readable description of what the tool does
* @param def.title - Optional display title for the tool
* @param def.annotations - Optional annotations for tool behavior and usage hints
* @param def.annotations.audience - Intended users (e.g., ["assistant"], ["user"], or ["assistant", "user"])
* @param def.annotations.priority - Importance level from 0 (lowest) to 1 (highest)
* @param def.annotations.title - Alternative display title (prioritized over def.title)
* @param def.annotations.readOnlyHint - Whether the tool only reads data without modifications
* @param def.annotations.destructiveHint - Whether the tool makes potentially irreversible changes
* @param def.annotations.idempotentHint - Whether repeated calls produce the same result
* @param def.annotations.openWorldHint - Whether the tool interacts with external systems
* @param def._meta - Optional arbitrary metadata object passed through to clients via tools/list
* @param def.inputSchema - Schema for validating input arguments (JSON Schema or Standard Schema)
* @param def.outputSchema - Schema for validating structured output (JSON Schema or Standard Schema)
Expand Down Expand Up @@ -520,6 +529,42 @@ export class McpServer {
* })
* });
* ```
*
* @example With annotations (read-only tool)
* ```typescript
* server.tool("getConfig", {
* description: "Retrieves configuration settings",
* annotations: {
* readOnlyHint: true,
* audience: ["assistant"],
* priority: 0.8
* },
* handler: () => ({
* content: [{ type: "text", text: JSON.stringify(config) }]
* })
* });
* ```
*
* @example With annotations (destructive tool)
* ```typescript
* server.tool("deleteDatabase", {
* description: "Permanently deletes the database",
* annotations: {
* destructiveHint: true,
* readOnlyHint: false,
* audience: ["user"],
* priority: 0.3,
* openWorldHint: true
* },
* inputSchema: z.object({ confirm: z.boolean() }),
* handler: async (args) => {
* if (args.confirm) {
* await dropDatabase();
* }
* return { content: [{ type: "text", text: "Database deleted" }] };
* }
* });
* ```
*/
// Overload 1: Both input and output are Standard Schema (full type inference)
tool<
Expand All @@ -530,6 +575,7 @@ export class McpServer {
def: {
description?: string;
title?: string;
annotations?: ToolAnnotations;
_meta?: { [key: string]: unknown };
inputSchema: SInput;
outputSchema: SOutput;
Expand All @@ -548,6 +594,7 @@ export class McpServer {
def: {
description?: string;
title?: string;
annotations?: ToolAnnotations;
_meta?: { [key: string]: unknown };
inputSchema: S;
outputSchema?: unknown;
Expand All @@ -564,6 +611,7 @@ export class McpServer {
def: {
description?: string;
title?: string;
annotations?: ToolAnnotations;
_meta?: { [key: string]: unknown };
inputSchema?: unknown;
outputSchema: S;
Expand All @@ -582,6 +630,7 @@ export class McpServer {
def: {
description?: string;
title?: string;
annotations?: ToolAnnotations;
_meta?: { [key: string]: unknown };
inputSchema?: unknown;
outputSchema?: unknown;
Expand All @@ -598,6 +647,7 @@ export class McpServer {
def: {
description?: string;
title?: string;
annotations?: ToolAnnotations;
_meta?: { [key: string]: unknown };
inputSchema?: unknown | StandardSchemaV1<TArgs>;
outputSchema?: unknown | StandardSchemaV1<unknown>;
Expand Down Expand Up @@ -631,6 +681,9 @@ export class McpServer {
if (def.title) {
metadata.title = def.title;
}
if (def.annotations) {
metadata.annotations = def.annotations;
}
if (def._meta) {
metadata._meta = def._meta;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ export interface Tool {
inputSchema: unknown;
outputSchema?: unknown;
title?: string;
annotations?: ToolAnnotations;
_meta?: { [key: string]: unknown };
}

Expand Down Expand Up @@ -385,6 +386,14 @@ export interface Annotations {
priority?: number;
}

export interface ToolAnnotations extends Annotations {
title?: string;
readOnlyHint?: boolean;
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
}

export type TextResourceContents = {
_meta?: { [key: string]: unknown };
uri: string;
Expand Down
Loading
Loading