From 2590d40e74d4a4c3212714f59ac9bba07e499de0 Mon Sep 17 00:00:00 2001
From: Justin Spahr-Summers <justin@anthropic.com>
Date: Tue, 21 Jan 2025 09:14:14 +0000
Subject: [PATCH 1/2] Fix registering multiple tools/resources/prompts

Request handlers were being re-registered every time a new tool/resource/prompt was added, which would fail since the handlers were already registered. Now we track whether handlers have been initialized and only register them once.

Added tests to verify that multiple registrations work correctly.

Resolves #124.
---
 src/server/mcp.test.ts | 73 ++++++++++++++++++++++++++++++++++++++++++
 src/server/mcp.ts      | 24 ++++++++++++++
 2 files changed, 97 insertions(+)

diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts
index 33fdeb91..4154e7dd 100644
--- a/src/server/mcp.test.ts
+++ b/src/server/mcp.test.ts
@@ -310,6 +310,19 @@ describe("tool()", () => {
     }).toThrow(/already registered/);
   });
 
+  test("should allow registering multiple tools", () => {
+    const mcpServer = new McpServer({
+      name: "test server",
+      version: "1.0",
+    });
+
+    // This should succeed
+    mcpServer.tool("tool1", () => ({ content: [] }));
+    
+    // This should also succeed and not throw about request handlers
+    mcpServer.tool("tool2", () => ({ content: [] }));
+  });
+
   test("should allow client to call server tools", async () => {
     const mcpServer = new McpServer({
       name: "test server",
@@ -734,6 +747,33 @@ describe("resource()", () => {
     }).toThrow(/already registered/);
   });
 
+  test("should allow registering multiple resources", () => {
+    const mcpServer = new McpServer({
+      name: "test server",
+      version: "1.0",
+    });
+
+    // This should succeed
+    mcpServer.resource("resource1", "test://resource1", async () => ({
+      contents: [
+        {
+          uri: "test://resource1",
+          text: "Test content 1",
+        },
+      ],
+    }));
+    
+    // This should also succeed and not throw about request handlers
+    mcpServer.resource("resource2", "test://resource2", async () => ({
+      contents: [
+        {
+          uri: "test://resource2",
+          text: "Test content 2",
+        },
+      ],
+    }));
+  });
+
   test("should prevent duplicate resource template registration", () => {
     const mcpServer = new McpServer({
       name: "test server",
@@ -1210,6 +1250,39 @@ describe("prompt()", () => {
     }).toThrow(/already registered/);
   });
 
+  test("should allow registering multiple prompts", () => {
+    const mcpServer = new McpServer({
+      name: "test server",
+      version: "1.0",
+    });
+
+    // This should succeed
+    mcpServer.prompt("prompt1", async () => ({
+      messages: [
+        {
+          role: "assistant",
+          content: {
+            type: "text",
+            text: "Test response 1",
+          },
+        },
+      ],
+    }));
+    
+    // This should also succeed and not throw about request handlers
+    mcpServer.prompt("prompt2", async () => ({
+      messages: [
+        {
+          role: "assistant",
+          content: {
+            type: "text",
+            text: "Test response 2",
+          },
+        },
+      ],
+    }));
+  });
+
   test("should throw McpError for invalid prompt name", async () => {
     const mcpServer = new McpServer({
       name: "test server",
diff --git a/src/server/mcp.ts b/src/server/mcp.ts
index 4b484465..36d9341d 100644
--- a/src/server/mcp.ts
+++ b/src/server/mcp.ts
@@ -81,7 +81,13 @@ export class McpServer {
     await this.server.close();
   }
 
+  private _toolHandlersInitialized = false;
+
   private setToolRequestHandlers() {
+    if (this._toolHandlersInitialized) {
+      return;
+    }
+    
     this.server.assertCanSetRequestHandler(
       ListToolsRequestSchema.shape.method.value,
     );
@@ -165,6 +171,8 @@ export class McpServer {
         }
       },
     );
+
+    this._toolHandlersInitialized = true;
   }
 
   private setCompletionRequestHandler() {
@@ -249,7 +257,13 @@ export class McpServer {
     return createCompletionResult(suggestions);
   }
 
+  private _resourceHandlersInitialized = false;
+
   private setResourceRequestHandlers() {
+    if (this._resourceHandlersInitialized) {
+      return;
+    }
+
     this.server.assertCanSetRequestHandler(
       ListResourcesRequestSchema.shape.method.value,
     );
@@ -342,9 +356,17 @@ export class McpServer {
     );
 
     this.setCompletionRequestHandler();
+    
+    this._resourceHandlersInitialized = true;
   }
 
+  private _promptHandlersInitialized = false;
+
   private setPromptRequestHandlers() {
+    if (this._promptHandlersInitialized) {
+      return;
+    }
+
     this.server.assertCanSetRequestHandler(
       ListPromptsRequestSchema.shape.method.value,
     );
@@ -406,6 +428,8 @@ export class McpServer {
     );
 
     this.setCompletionRequestHandler();
+    
+    this._promptHandlersInitialized = true;
   }
 
   /**

From 38c361a0c2441e77e71bf1bb6e60c36d8cc920bd Mon Sep 17 00:00:00 2001
From: Justin Spahr-Summers <justin@anthropic.com>
Date: Tue, 21 Jan 2025 09:14:44 +0000
Subject: [PATCH 2/2] Preemptively bump package version

---
 package-lock.json | 4 ++--
 package.json      | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 6f8490b3..a4e2e7ec 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@modelcontextprotocol/sdk",
-  "version": "1.3.0",
+  "version": "1.3.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@modelcontextprotocol/sdk",
-      "version": "1.3.0",
+      "version": "1.3.1",
       "license": "MIT",
       "dependencies": {
         "content-type": "^1.0.5",
diff --git a/package.json b/package.json
index bc7fb42f..e051f1a3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@modelcontextprotocol/sdk",
-  "version": "1.3.0",
+  "version": "1.3.1",
   "description": "Model Context Protocol implementation for TypeScript",
   "license": "MIT",
   "author": "Anthropic, PBC (https://anthropic.com)",