From e672c72fb940685d3b351e1f63e28986b3f60f8f Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 5 Aug 2025 10:13:25 +0100 Subject: [PATCH 1/6] fix: Move notification calls from internal _createRegisteredTool to public methods The _createRegisteredTool() method was calling setToolRequestHandlers() and sendToolListChanged() internally, which caused issues when registering tools after server connection due to capability registration restrictions. This moves the notification responsibility to the public registerTool() and tool() methods, making tool registration consistent and allowing registration after connection when capabilities have already been established. Fixes memory leak warnings caused by rapid tool registration after connection. --- src/server/mcp.test.ts | 116 +++++++++++++++++++++++++++++++++++++++++ src/server/mcp.ts | 17 ++++-- 2 files changed, 128 insertions(+), 5 deletions(-) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 10e550df4..638fff7ca 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4290,4 +4290,120 @@ describe("elicitInput()", () => { text: "No booking made. Original date not available." }]); }); + + /*** + * Test: Bulk Tool Registration - Memory Leak Prevention + */ + test("should handle multiple tool registrations without memory leak", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + // Register first tool before connection (should work fine) + mcpServer.tool("tool1", async () => ({ + content: [{ type: "text", text: "Tool 1" }] + })); + + const notifications: Notification[] = [] + const client = new Client({ + name: "test client", + version: "1.0", + }); + client.fallbackNotificationHandler = async (notification) => { + notifications.push(notification) + } + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.connect(serverTransport), + ]); + + // Clear any initial notifications + notifications.length = 0; + + // This should work since capabilities were already registered during first tool + mcpServer.tool("tool2", async () => ({ + content: [{ type: "text", text: "Tool 2" }] + })); + + // Yield event loop to let notifications process + await new Promise(process.nextTick); + + // Should have sent exactly one notification for the second tool + expect(notifications).toHaveLength(1); + expect(notifications[0]).toMatchObject({ + method: "notifications/tools/list_changed", + }); + + // Verify both tools are registered + const toolsResult = await client.request( + { method: "tools/list" }, + ListToolsResultSchema, + ); + expect(toolsResult.tools).toHaveLength(2); + expect(toolsResult.tools.map(t => t.name).sort()).toEqual(["tool1", "tool2"]); + }); + + /*** + * Test: registerTool() should work after connection (fixed behavior) + */ + test("registerTool() should work after connection", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + // Register first tool before connection to establish capabilities + mcpServer.tool("tool1", async () => ({ + content: [{ type: "text", text: "Tool 1" }] + })); + + const notifications: Notification[] = [] + const client = new Client({ + name: "test client", + version: "1.0", + }); + client.fallbackNotificationHandler = async (notification) => { + notifications.push(notification) + } + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.connect(serverTransport), + ]); + + // Clear any initial notifications + notifications.length = 0; + + // This should now work after the fix + mcpServer.registerTool("test2", { + description: "Test tool 2" + }, async () => ({ + content: [{ type: "text", text: "Test 2" }] + })); + + // Yield event loop to let notifications process + await new Promise(process.nextTick); + + // Should have sent exactly one notification + expect(notifications).toHaveLength(1); + expect(notifications[0]).toMatchObject({ + method: "notifications/tools/list_changed", + }); + + // Verify both tools are registered + const toolsResult = await client.request( + { method: "tools/list" }, + ListToolsResultSchema, + ); + expect(toolsResult.tools).toHaveLength(2); + expect(toolsResult.tools.map(t => t.name).sort()).toEqual(["test2", "tool1"]); + }); }); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 791facef1..39029530e 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -803,9 +803,6 @@ export class McpServer { }; this._registeredTools[name] = registeredTool; - this.setToolRequestHandlers(); - this.sendToolListChanged() - return registeredTool } @@ -914,7 +911,12 @@ export class McpServer { } const callback = rest[0] as ToolCallback; - return this._createRegisteredTool(name, undefined, description, inputSchema, outputSchema, annotations, callback) + const result = this._createRegisteredTool(name, undefined, description, inputSchema, outputSchema, annotations, callback); + + this.setToolRequestHandlers(); + this.sendToolListChanged(); + + return result; } /** @@ -937,7 +939,7 @@ export class McpServer { const { title, description, inputSchema, outputSchema, annotations } = config; - return this._createRegisteredTool( + const result = this._createRegisteredTool( name, title, description, @@ -946,6 +948,11 @@ export class McpServer { annotations, cb as ToolCallback ); + + this.setToolRequestHandlers(); + this.sendToolListChanged(); + + return result; } /** From 903bf387c931e036a08ddf95efef339124771245 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 5 Aug 2025 10:16:56 +0100 Subject: [PATCH 2/6] feat: Add registerTools() bulk method for efficient tool registration - Adds registerTools() method that accepts an array of tools - Registers all tools and sends only one list_changed notification - Prevents memory leak warnings when registering many tools - Uses same pattern as individual registerTool() method - Includes comprehensive test coverage This solves the GitHub issue where registering 80+ tools caused EventEmitter memory leak warnings due to multiple rapid notifications. --- src/server/mcp.test.ts | 109 +++++++++++++++++++++++++++++++++++++++++ src/server/mcp.ts | 49 ++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 638fff7ca..cf4b8068b 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4406,4 +4406,113 @@ describe("elicitInput()", () => { expect(toolsResult.tools).toHaveLength(2); expect(toolsResult.tools.map(t => t.name).sort()).toEqual(["test2", "tool1"]); }); + + /*** + * Test: registerTools() bulk method + */ + test("should register multiple tools with single notification using registerTools()", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + // Register first tool to establish capabilities + mcpServer.tool("initial", async () => ({ + content: [{ type: "text", text: "Initial" }] + })); + + const notifications: Notification[] = [] + const client = new Client({ + name: "test client", + version: "1.0", + }); + client.fallbackNotificationHandler = async (notification) => { + notifications.push(notification) + } + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.connect(serverTransport), + ]); + + // Clear initial notifications + notifications.length = 0; + + // Register multiple tools at once using the bulk method + const tools = mcpServer.registerTools([ + { + name: "bulk1", + config: { + title: "Bulk Tool 1", + description: "First bulk tool" + }, + callback: async () => ({ + content: [{ type: "text" as const, text: "Bulk 1" }] + }) + }, + { + name: "bulk2", + config: { + title: "Bulk Tool 2", + description: "Second bulk tool" + }, + callback: async () => ({ + content: [{ type: "text" as const, text: "Bulk 2" }] + }) + }, + { + name: "bulk3", + config: { + description: "Third bulk tool" + }, + callback: async () => ({ + content: [{ type: "text" as const, text: "Bulk 3" }] + }) + } + ]); + + // Yield event loop to let notifications process + await new Promise(process.nextTick); + + // Should have sent exactly ONE notification for all three tools + expect(notifications).toHaveLength(1); + expect(notifications[0]).toMatchObject({ + method: "notifications/tools/list_changed", + }); + + // Should return array of registered tools + expect(tools).toHaveLength(3); + expect(tools[0].title).toBe("Bulk Tool 1"); + expect(tools[1].title).toBe("Bulk Tool 2"); + expect(tools[2].title).toBeUndefined(); // No title provided + + // Verify all tools are registered and functional + const toolsResult = await client.request( + { method: "tools/list" }, + ListToolsResultSchema, + ); + expect(toolsResult.tools).toHaveLength(4); // initial + 3 bulk tools + + const toolNames = toolsResult.tools.map(t => t.name).sort(); + expect(toolNames).toEqual(["bulk1", "bulk2", "bulk3", "initial"]); + + // Test that the tools actually work + const callResult = await client.request( + { + method: "tools/call", + params: { + name: "bulk1", + arguments: {} + } + }, + CallToolResultSchema, + ); + expect(callResult.content[0]).toMatchObject({ + type: "text", + text: "Bulk 1" + }); + }); }); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 39029530e..37fe4040a 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -955,6 +955,55 @@ export class McpServer { return result; } + /** + * Registers multiple tools at once with a single notification. + * This is more efficient than calling registerTool() multiple times, + * especially when registering many tools, as it sends only one list_changed notification. + */ + registerTools; + }>>(tools: T): RegisteredTool[] { + const results: RegisteredTool[] = []; + + // First, validate that none of the tools are already registered + for (const { name } of tools) { + if (this._registeredTools[name]) { + throw new Error(`Tool ${name} is already registered`); + } + } + + // Register all tools without sending notifications + for (const { name, config, callback } of tools) { + const { title, description, inputSchema, outputSchema, annotations } = config; + + const result = this._createRegisteredTool( + name, + title, + description, + inputSchema, + outputSchema, + annotations, + callback as ToolCallback + ); + + results.push(result); + } + + // Set up handlers and send single notification at the end + this.setToolRequestHandlers(); + this.sendToolListChanged(); + + return results; + } + /** * Registers a zero-argument prompt `name`, which will run the given function when the client calls it. */ From 263f3f8e6b039fccd8983aebffc32eb369fa8a48 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 5 Aug 2025 10:19:18 +0100 Subject: [PATCH 3/6] Add registerResources() bulk method - Accepts array of resource configurations - Registers all resources with single notification - Supports both string URIs and ResourceTemplate objects - Consistent with existing registration patterns --- src/server/mcp.test.ts | 109 +++++++++++++++++++++++++++++++++++++++++ src/server/mcp.ts | 56 +++++++++++++++++++++ 2 files changed, 165 insertions(+) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index cf4b8068b..f82907ab5 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4515,4 +4515,113 @@ describe("elicitInput()", () => { text: "Bulk 1" }); }); + + /*** + * Test: registerResources() bulk method + */ + test("should register multiple resources with single notification using registerResources()", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + // Register first resource to establish capabilities + mcpServer.resource("initial", "initial://resource", async () => ({ + contents: [{ + uri: "initial://resource", + text: "Initial resource" + }] + })); + + const notifications: Notification[] = [] + const client = new Client({ + name: "test client", + version: "1.0", + }); + client.fallbackNotificationHandler = async (notification) => { + notifications.push(notification) + } + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.connect(serverTransport), + ]); + + // Clear initial notifications + notifications.length = 0; + + // Register multiple resources at once using the bulk method + const resources = mcpServer.registerResources([ + { + name: "bulk1", + uriOrTemplate: "bulk://resource1", + config: { + title: "Bulk Resource 1", + description: "First bulk resource" + }, + callback: async () => ({ + contents: [{ + uri: "bulk://resource1", + text: "Bulk resource 1 content" + }] + }) + }, + { + name: "bulk2", + uriOrTemplate: "bulk://resource2", + config: { + title: "Bulk Resource 2", + description: "Second bulk resource" + }, + callback: async () => ({ + contents: [{ + uri: "bulk://resource2", + text: "Bulk resource 2 content" + }] + }) + } + ]); + + // Yield event loop to let notifications process + await new Promise(process.nextTick); + + // Should have sent exactly ONE notification for all resources + expect(notifications).toHaveLength(1); + expect(notifications[0]).toMatchObject({ + method: "notifications/resources/list_changed", + }); + + // Should return array of registered resources + expect(resources).toHaveLength(2); + expect(resources[0].title).toBe("Bulk Resource 1"); + expect(resources[1].title).toBe("Bulk Resource 2"); + + // Verify all resources are registered and functional + const resourcesResult = await client.request( + { method: "resources/list" }, + ListResourcesResultSchema, + ); + expect(resourcesResult.resources).toHaveLength(3); // initial + 2 bulk resources + + const resourceNames = resourcesResult.resources.map(r => r.name).sort(); + expect(resourceNames).toEqual(["bulk1", "bulk2", "initial"]); + + // Test that a resource actually works + const readResult = await client.request( + { + method: "resources/read", + params: { + uri: "bulk://resource1" + } + }, + ReadResourceResultSchema, + ); + expect(readResult.contents[0]).toMatchObject({ + uri: "bulk://resource1", + text: "Bulk resource 1 content" + }); + }); }); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 37fe4040a..8e1e2aeb9 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -666,6 +666,62 @@ export class McpServer { } } + /** + * Registers multiple resources at once with a single notification. + * This is more efficient than calling registerResource() multiple times, + * especially when registering many resources, as it sends only one list_changed notification. + */ + registerResources>(resources: T): (RegisteredResource | RegisteredResourceTemplate)[] { + const results: (RegisteredResource | RegisteredResourceTemplate)[] = []; + + // First, validate that none of the resources are already registered + for (const { name, uriOrTemplate } of resources) { + if (typeof uriOrTemplate === "string") { + if (this._registeredResources[uriOrTemplate]) { + throw new Error(`Resource ${uriOrTemplate} is already registered`); + } + } else { + if (this._registeredResourceTemplates[name]) { + throw new Error(`Resource template ${name} is already registered`); + } + } + } + + // Register all resources without sending notifications + for (const { name, uriOrTemplate, config, callback } of resources) { + if (typeof uriOrTemplate === "string") { + const result = this._createRegisteredResource( + name, + (config as BaseMetadata).title, + uriOrTemplate, + config, + callback as ReadResourceCallback + ); + results.push(result); + } else { + const result = this._createRegisteredResourceTemplate( + name, + (config as BaseMetadata).title, + uriOrTemplate, + config, + callback as ReadResourceTemplateCallback + ); + results.push(result); + } + } + + // Set up handlers and send single notification at the end + this.setResourceRequestHandlers(); + this.sendResourceListChanged(); + + return results; + } + private _createRegisteredResource( name: string, title: string | undefined, From 4710c04d7710cf0ed2ae15e4e136ae358b0a3276 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 5 Aug 2025 10:24:05 +0100 Subject: [PATCH 4/6] Add registerPrompts() bulk method - Accepts array of prompt configurations - Registers all prompts with single notification - Follows existing registration patterns - Comprehensive test coverage with notification validation --- src/server/mcp.test.ts | 116 +++++++++++++++++++++++++++++++++++++++++ src/server/mcp.ts | 43 +++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index f82907ab5..9bec316d1 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4624,4 +4624,120 @@ describe("elicitInput()", () => { text: "Bulk resource 1 content" }); }); + + test("registerPrompts() should register multiple prompts with single notification", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + const notifications: Notification[] = [] + const client = new Client({ + name: "test client", + version: "1.0", + }); + client.fallbackNotificationHandler = async (notification) => { + notifications.push(notification) + } + + // Register a single prompt first to test notification behavior + mcpServer.registerPrompt( + "initial", + { + title: "Initial Prompt", + description: "An initial prompt", + argsSchema: { input: z.string() } + }, + ({ input }) => ({ + messages: [{ + role: "user" as const, + content: { type: "text" as const, text: `Initial: ${input}` } + }] + }) + ); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.connect(serverTransport), + ]); + + // Clear any initial notifications + notifications.length = 0; + + // Register multiple prompts in bulk + const registeredPrompts = mcpServer.registerPrompts([ + { + name: "bulk1", + config: { + title: "Bulk Prompt One", + description: "First bulk prompt", + argsSchema: { input: z.string() } + }, + callback: (args, _extra) => ({ + messages: [{ + role: "user" as const, + content: { type: "text" as const, text: `Bulk 1: ${args.input}` } + }] + }) + }, + { + name: "bulk2", + config: { + title: "Bulk Prompt Two", + description: "Second bulk prompt", + argsSchema: { value: z.string() } + }, + callback: (args, _extra) => ({ + messages: [{ + role: "assistant" as const, + content: { type: "text" as const, text: `Bulk 2: ${args.value}` } + }] + }) + } + ]); + + // Yield event loop to let notifications process + await new Promise(process.nextTick); + + // Should return array of registered prompts + expect(registeredPrompts).toHaveLength(2); + expect(registeredPrompts[0].title).toBe("Bulk Prompt One"); + expect(registeredPrompts[1].title).toBe("Bulk Prompt Two"); + + // Should have sent exactly ONE notification for all prompts + expect(notifications).toHaveLength(1); + expect(notifications[0]).toMatchObject({ + method: "notifications/prompts/list_changed", + }); + + // Test list prompts shows all prompts + const promptsResult = await client.request( + { + method: "prompts/list", + params: {} + }, + ListPromptsResultSchema, + ); + + const promptNames = promptsResult.prompts.map(p => p.name).sort(); + expect(promptNames).toEqual(["bulk1", "bulk2", "initial"]); + + // Test that a prompt actually works + const getResult = await client.request( + { + method: "prompts/get", + params: { + name: "bulk1", + arguments: { input: "test" } + } + }, + GetPromptResultSchema, + ); + expect(getResult.messages[0]).toMatchObject({ + role: "user", + content: { type: "text", text: "Bulk 1: test" } + }); + }); }); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 8e1e2aeb9..1b4a68c84 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -722,6 +722,49 @@ export class McpServer { return results; } + /** + * Register multiple prompts in a single operation with a single notification. + * This is more efficient than calling registerPrompt() multiple times when registering many prompts, + * especially when registering many prompts, as it sends only one list_changed notification. + */ + registerPrompts; + }>>(prompts: T): RegisteredPrompt[] { + const results: RegisteredPrompt[] = []; + + // First, validate that none of the prompts are already registered + for (const { name } of prompts) { + if (this._registeredPrompts[name]) { + throw new Error(`Prompt ${name} is already registered`); + } + } + + // Register all prompts without sending notifications + for (const { name, config, callback } of prompts) { + const { title, description, argsSchema } = config; + const result = this._createRegisteredPrompt( + name, + title, + description, + argsSchema, + callback + ); + results.push(result); + } + + // Set up handlers and send single notification at the end + this.setPromptRequestHandlers(); + this.sendPromptListChanged(); + + return results; + } + private _createRegisteredResource( name: string, title: string | undefined, From c827e15b249c85ab9b1de26a2d1c195e70fda6b2 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 5 Aug 2025 10:31:40 +0100 Subject: [PATCH 5/6] Improve: Add empty array handling to bulk registration methods - Add early return for empty arrays to avoid unnecessary notifications - Add comprehensive tests for empty array edge cases - Addresses O3 feedback from code review --- src/server/mcp.test.ts | 30 ++++++++++++++++++++++++++++++ src/server/mcp.ts | 12 ++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 9bec316d1..b4146f029 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4625,6 +4625,36 @@ describe("elicitInput()", () => { }); }); + test("registerTools() should handle empty array gracefully", () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + const result = mcpServer.registerTools([]); + expect(result).toEqual([]); + }); + + test("registerResources() should handle empty array gracefully", () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + const result = mcpServer.registerResources([]); + expect(result).toEqual([]); + }); + + test("registerPrompts() should handle empty array gracefully", () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + const result = mcpServer.registerPrompts([]); + expect(result).toEqual([]); + }); + test("registerPrompts() should register multiple prompts with single notification", async () => { const mcpServer = new McpServer({ name: "test server", diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 1b4a68c84..d3ef6a270 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -677,6 +677,10 @@ export class McpServer { config: ResourceMetadata; callback: ReadResourceCallback | ReadResourceTemplateCallback; }>>(resources: T): (RegisteredResource | RegisteredResourceTemplate)[] { + if (resources.length === 0) { + return []; + } + const results: (RegisteredResource | RegisteredResourceTemplate)[] = []; // First, validate that none of the resources are already registered @@ -736,6 +740,10 @@ export class McpServer { }; callback: PromptCallback; }>>(prompts: T): RegisteredPrompt[] { + if (prompts.length === 0) { + return []; + } + const results: RegisteredPrompt[] = []; // First, validate that none of the prompts are already registered @@ -1070,6 +1078,10 @@ export class McpServer { }; callback: ToolCallback; }>>(tools: T): RegisteredTool[] { + if (tools.length === 0) { + return []; + } + const results: RegisteredTool[] = []; // First, validate that none of the tools are already registered From 675efe0d1f0e7e52e96b8e8eaa7ff9e958b63226 Mon Sep 17 00:00:00 2001 From: Cameron Cooke Date: Tue, 5 Aug 2025 13:18:00 +0100 Subject: [PATCH 6/6] fix: resolve TS2589 "Type instantiation is excessively deep" error in registerTools Simplifies the registerTools() method signature to prevent TypeScript from attempting complex tuple type inference when registering large numbers of tools with complex Zod schemas. The simplified interface maintains full runtime safety through Zod validation while avoiding compile-time type complexity issues. --- src/server/mcp.ts | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/server/mcp.ts b/src/server/mcp.ts index d3ef6a270..1964080d7 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -47,6 +47,24 @@ import { UriTemplate, Variables } from "../shared/uriTemplate.js"; import { RequestHandlerExtra } from "../shared/protocol.js"; import { Transport } from "../shared/transport.js"; +/** + * Simple interface for tool registration that avoids TypeScript "Type instantiation is excessively deep" + * errors when registering large numbers of tools with complex schemas. + * + * Uses unknown types to prevent TypeScript from attempting deep type inference on complex schemas. + */ +export interface ToolRegistration { + name: string; + config: { + title?: string; + description?: string; + inputSchema?: unknown; + outputSchema?: unknown; + annotations?: ToolAnnotations; + }; + callback: (args: unknown, extra: RequestHandlerExtra) => CallToolResult | Promise; +} + /** * High-level MCP server that provides a simpler API for working with resources, tools, and prompts. * For advanced usage (like sending notifications or setting custom request handlers), use the underlying @@ -671,12 +689,12 @@ export class McpServer { * This is more efficient than calling registerResource() multiple times, * especially when registering many resources, as it sends only one list_changed notification. */ - registerResources>(resources: T): (RegisteredResource | RegisteredResourceTemplate)[] { + }>): (RegisteredResource | RegisteredResourceTemplate)[] { if (resources.length === 0) { return []; } @@ -731,7 +749,7 @@ export class McpServer { * This is more efficient than calling registerPrompt() multiple times when registering many prompts, * especially when registering many prompts, as it sends only one list_changed notification. */ - registerPrompts; - }>>(prompts: T): RegisteredPrompt[] { + }>): RegisteredPrompt[] { if (prompts.length === 0) { return []; } @@ -1066,18 +1084,10 @@ export class McpServer { * Registers multiple tools at once with a single notification. * This is more efficient than calling registerTool() multiple times, * especially when registering many tools, as it sends only one list_changed notification. + * + * Uses simplified types to avoid TypeScript compilation issues with large numbers of complex tools. */ - registerTools; - }>>(tools: T): RegisteredTool[] { + registerTools(tools: ToolRegistration[]): RegisteredTool[] { if (tools.length === 0) { return []; } @@ -1099,8 +1109,8 @@ export class McpServer { name, title, description, - inputSchema, - outputSchema, + inputSchema as ZodRawShape | undefined, + outputSchema as ZodRawShape | undefined, annotations, callback as ToolCallback );