modelcontextprotocol / modelcontextprotocol/typescript-sdk

Bulk tool registration causes EventEmitter memory leak warnings

Open
#842 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug fix proposed P2 ready for work
Dominant language
TypeScript
Stars
13.4k
Forks
2.2k
Avg merge
3d 15h
Merged PRs (30d)
4

Description

Problem Description

When registering many tools dynamically (80+ tools), the MCP SDK triggers EventEmitter memory leak warnings due to individual sendToolListChanged() calls per tool registration.

Root Cause

  • Each server.tool() call automatically triggers sendToolListChanged()
  • Rapid notifications overwhelm stdout buffer in StdioServerTransport
  • Multiple 'drain' listeners accumulate temporarily, exceeding Node.js default limit (10)

Stack Trace

MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 drain listeners added to [Socket]. MaxListeners is 10.
    at genericNodeError (node:internal/errors:983:15)
    at wrappedFn (node:internal/errors:537:14)
    at _addListener (node:events:581:17)
    at Socket.addListener (node:events:599:10)
    at Readable.on (node:internal/streams/readable:1131:35)
    at Socket.once (node:events:643:8)
    at StdioServerTransport.send (/@modelcontextprotocol/sdk/dist/esm/server/stdio.js:73:30)
    at Promise.resolve.then.then._a (/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js:151:83)

Reproduction Steps

  1. Create an MCP server with dynamic tool registration
  2. Register 80+ tools rapidly in a loop:
    for (const tool of manyTools) {
      server.tool(tool.name, tool.description, tool.schema, tool.handler);
    }
    
  3. Observe EventEmitter memory leak warnings in stderr

Expected Behavior

  • Bulk tool registration should not trigger memory leak warnings
  • Efficient batch operations without notification spam

Current Workaround

// Increase maxListeners before bulk registration
process.stdout.setMaxListeners(100);

Proposed Solutions

  1. Batch Notification API: Add optional defer parameter to tool() method
  2. Bulk Registration: Add registerTools(tools[]) method with single notification
  3. Increase Default MaxListeners: Set higher default for stdio transport
  4. Documentation: Document bulk registration patterns and best practices

Use Case

Servers like XcodeBuildMCP that load 80+ tools dynamically based on user context need efficient bulk registration without warning spam. Dynamic tool loading is becoming common for context-aware MCP servers.

Environment

  • MCP SDK Version: 1.17.1
  • Node.js Version: 18+
  • Transport: StdioServerTransport
  • Platform: macOS/Linux

Impact

  • False positive memory leak warnings confuse developers
  • Performance degradation from stdout buffer backpressure
  • Client receives excessive individual ToolListChangedNotification messages

The core issue is that the MCP SDK wasn't designed for bulk tool registration scenarios common in dynamic, context-aware servers.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the server.tool() registration flow and StdioServerTransport.send, using the provided 80+ tool reproduction to observe drain listeners and notifications. Compare the proposed batching or deferred-notification approaches, then verify that bulk registration avoids warning spam while preserving the expected tool-list update behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.