From f1c3238e296a635e2417c4c62fe3129b0ab66247 Mon Sep 17 00:00:00 2001
From: Felix Weinberger <fweinberger@anthropic.com>
Date: Thu, 20 Nov 2025 17:54:21 +0000
Subject: [PATCH] fix: restore extensibility for ToolAnnotations schema

The ToolAnnotations schema should allow additional properties per the
MCP specification. The JSON Schema at:
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.json

does NOT include "additionalProperties": false for ToolAnnotations,
which means additional properties are allowed by default per JSON Schema
semantics.

This was accidentally removed in commit f59996a (SEP-1319) when
.passthrough() was dropped during schema cleanup. This broke consumers
like Cloudflare's x402 integration that rely on extending annotations
with custom fields (e.g., paymentHint, paymentPriceUSD).

Using .catchall(z.unknown()) rather than .passthrough() for consistency
with other schemas per commit 913ca2a.
---
 src/types.ts | 80 +++++++++++++++++++++++++++-------------------------
 1 file changed, 41 insertions(+), 39 deletions(-)

diff --git a/src/types.ts b/src/types.ts
index 64153094d0..9828ff0395 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -944,49 +944,51 @@ export const PromptListChangedNotificationSchema = NotificationSchema.extend({
  * Clients should never make tool use decisions based on ToolAnnotations
  * received from untrusted servers.
  */
-export const ToolAnnotationsSchema = z.object({
-    /**
-     * A human-readable title for the tool.
-     */
-    title: z.string().optional(),
+export const ToolAnnotationsSchema = z
+    .object({
+        /**
+         * A human-readable title for the tool.
+         */
+        title: z.string().optional(),
 
-    /**
-     * If true, the tool does not modify its environment.
-     *
-     * Default: false
-     */
-    readOnlyHint: z.boolean().optional(),
+        /**
+         * If true, the tool does not modify its environment.
+         *
+         * Default: false
+         */
+        readOnlyHint: z.boolean().optional(),
 
-    /**
-     * If true, the tool may perform destructive updates to its environment.
-     * If false, the tool performs only additive updates.
-     *
-     * (This property is meaningful only when `readOnlyHint == false`)
-     *
-     * Default: true
-     */
-    destructiveHint: z.boolean().optional(),
+        /**
+         * If true, the tool may perform destructive updates to its environment.
+         * If false, the tool performs only additive updates.
+         *
+         * (This property is meaningful only when `readOnlyHint == false`)
+         *
+         * Default: true
+         */
+        destructiveHint: z.boolean().optional(),
 
-    /**
-     * If true, calling the tool repeatedly with the same arguments
-     * will have no additional effect on the its environment.
-     *
-     * (This property is meaningful only when `readOnlyHint == false`)
-     *
-     * Default: false
-     */
-    idempotentHint: z.boolean().optional(),
+        /**
+         * If true, calling the tool repeatedly with the same arguments
+         * will have no additional effect on the its environment.
+         *
+         * (This property is meaningful only when `readOnlyHint == false`)
+         *
+         * Default: false
+         */
+        idempotentHint: z.boolean().optional(),
 
-    /**
-     * If true, this tool may interact with an "open world" of external
-     * entities. If false, the tool's domain of interaction is closed.
-     * For example, the world of a web search tool is open, whereas that
-     * of a memory tool is not.
-     *
-     * Default: true
-     */
-    openWorldHint: z.boolean().optional()
-});
+        /**
+         * If true, this tool may interact with an "open world" of external
+         * entities. If false, the tool's domain of interaction is closed.
+         * For example, the world of a web search tool is open, whereas that
+         * of a memory tool is not.
+         *
+         * Default: true
+         */
+        openWorldHint: z.boolean().optional()
+    })
+    .catchall(z.unknown());
 
 /**
  * Definition for a tool the client can call.
