Skip to content

fix: cleanup idle mcp stream sessions#459

Merged
akitaSummer merged 3 commits into
masterfrom
fix/mcp-stream-session-cleanup
Jun 23, 2026
Merged

fix: cleanup idle mcp stream sessions#459
akitaSummer merged 3 commits into
masterfrom
fix/mcp-stream-session-cleanup

Conversation

@akitaSummer

@akitaSummer akitaSummer commented Jun 23, 2026

Copy link
Copy Markdown
Contributor
Checklist
  • npm test passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)
Description of change

Summary by CodeRabbit

Release Notes

  • Improvements
    • Streaming sessions that remain idle are now automatically cleaned up server-side after a configurable timeout period (default 60 seconds), improving server resource utilization.
    • Enhanced stream shutdown handling to ensure safer and more graceful closure of MCP streaming connections.
    • Improved async cleanup in request lifecycle handling to ensure proper resource release.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a configurable streamSessionIdleTimeout to MCPConfig and wires idle-session cleanup into MCPControllerRegister: new per-session maps track servers, cleanup timers, and in-flight close promises; try/finally blocks schedule cleanup after each response; closeStreamMcpServer deduplicates concurrent closes. The ctxLifecycleMiddleware cleanup is changed to await doDestory().

Changes

MCP Stream Idle Session Cleanup

Layer / File(s) Summary
MCPConfig idle timeout config and accessor
plugin/controller/lib/impl/mcp/MCPConfig.ts
MCPConfigOptions adds streamSessionIdleTimeout?: number; MCPConfig stores it defaulting to 60_000; new getStreamSessionIdleTimeout(name?) returns per-server override or instance default.
Per-session state maps and shutdown helpers
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts
Adds streamServers, streamCleanupTimers, streamClosePromises maps; extends clean() to reset them; implements clearStreamCleanupTimer, scheduleStreamMcpServerCleanup, closeStreamMcpServer (deduped), and extends clearStreamMcpServer.
Session lifecycle integration
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts
onsessioninitialized clears pending timers and stores server; transport onclose and ping cleanup route through closeStreamMcpServer; new and existing session request paths wrap processing in try/finally to schedule idle cleanup after response closes.
Idle session cleanup test
plugin/controller/test/mcp/mcp.test.ts
New test reduces idle timeout, connects a streamable client, calls listTools, closes client without terminate, waits, and asserts streamTransports and pingIntervals entries are cleared.

ctx Lifecycle Middleware Await Fix

Layer / File(s) Summary
Await doDestory() in middleware finally block
plugin/tegg/lib/ctx_lifecycle_middleware.ts
doDestory() is now awaited in the finally block so stream closing and teggCtx.destroy complete before the middleware returns.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant StreamInit as mcpStreamServerInit
  participant Timers as streamCleanupTimers
  participant Closer as closeStreamMcpServer
  participant State as streamServers / streamTransports

  Client->>StreamInit: POST (new session)
  StreamInit->>Timers: clearStreamCleanupTimer(sessionId)
  StreamInit->>State: store server + transport on onsessioninitialized
  StreamInit->>StreamInit: handle request (try/finally)
  StreamInit->>Timers: scheduleStreamMcpServerCleanup(sessionId, name)

  Client->>StreamInit: POST (existing sessionId)
  StreamInit->>Timers: clearStreamCleanupTimer(sessionId)
  StreamInit->>StreamInit: handle request (try/finally)
  StreamInit->>Timers: scheduleStreamMcpServerCleanup(sessionId, name)

  note over Timers: idle timeout elapses (no new requests)
  Timers->>Closer: closeStreamMcpServer(sessionId)
  Closer->>Closer: dedupe via streamClosePromises
  Closer->>State: close server + transport, clearStreamMcpServer
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • eggjs/tegg#319: Modifies MCPControllerRegister stream-session response close awaiting, which directly affects when idle cleanup is scheduled in this PR.
  • eggjs/tegg#357: Adds per-session/server tracking maps to MCPControllerRegister that this PR extends with idle-timer and close-promise maps.
  • eggjs/tegg#453: Modifies MCPControllerRegister stream-session transport.onclose and per-session cleanup, which this PR replaces with the new closeStreamMcpServer coordinated shutdown.

Suggested reviewers

  • jerryliang64

Poem

🐇 Hops the rabbit, timer in paw,
When clients leave without a claw,
The idle session fades away,
await ensures there's no delay—
Clean streams and servers, hip hooray! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: cleanup idle mcp stream sessions' directly and specifically describes the main change: implementing cleanup logic for idle MCP stream sessions. This aligns with the primary modifications across multiple files that add idle session tracking, cleanup timers, and shutdown mechanisms.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-stream-session-cleanup

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

plugin/controller/lib/impl/mcp/MCPConfig.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

plugin/controller/lib/impl/mcp/MCPControllerRegister.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

plugin/controller/test/mcp/mcp.test.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 1 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces idle session cleanup for streamable Model Context Protocol (MCP) sessions. It adds a configurable streamSessionIdleTimeout option (defaulting to 60 seconds) to MCPConfig and implements the scheduling and execution of session cleanup within MCPControllerRegister. Additionally, a test case is added to verify the cleanup of idle sessions, and doDestory() is now properly awaited in the TEgg context lifecycle middleware. There are no review comments, so no further feedback is provided.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugin/controller/lib/impl/mcp/MCPConfig.ts`:
- Line 51: The `streamSessionIdleTimeout` assignment in the MCPConfig
constructor is accepting any numeric value without validation, which can cause
issues with idle cleanup behavior. Add validation logic to ensure the
`streamSessionIdleTimeout` value from options is a valid positive number before
assigning it to `this._streamSessionIdleTimeout`. If the value is invalid, fall
back to the default of 60 * 1000. Also ensure the getter method for
`streamSessionIdleTimeout` (around lines 180-188) returns the properly validated
value so downstream timer logic receives only valid timeouts.

In `@plugin/controller/lib/impl/mcp/MCPControllerRegister.ts`:
- Around line 595-597: Remove the sessionId parameter from all three error
logging statements in the MCPControllerRegister class where cleanup operations
fail. Update the logger.error calls at lines 595-597, 621-623, and 627-629 to
omit the sessionId and related formatting placeholder from the log messages,
keeping only the error message details. This prevents exposure of
request-derived session identifiers in error logs.
- Around line 160-167: The clean() method is clearing streamCleanupTimers and
resetting several state maps, but it is not clearing the pingIntervals timers.
Add code after the clearTimeout loop for streamCleanupTimers to iterate through
Object.values(this.instance.pingIntervals) and call clearInterval() on each
timer, then reset this.instance.pingIntervals to an empty object like the other
maps being reset in the clean() method. This ensures that all interval callbacks
are stopped and stale state references are eliminated when the controller is
cleaned up.

In `@plugin/controller/test/mcp/mcp.test.ts`:
- Around line 467-473: The test uses a fixed 200ms sleep which creates timing
sensitivity and potential flakiness. Replace the setTimeout promise with a
bounded polling mechanism that repeatedly checks if
register.streamTransports['custom-session-id-idle'] becomes undefined rather
than relying on a fixed delay. Additionally, the pingIntervals assertion is
vacuous because it doesn't confirm the key existed before the close operation;
either capture the initial state of
register.pingIntervals['custom-session-id-idle'] before calling
streamableClient.close() to verify cleanup actually occurred, or remove the
assertion if meaningful verification is not intended.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 645cc5bc-5bbb-4581-bdba-4ffd457c7b2f

📥 Commits

Reviewing files that changed from the base of the PR and between 5829c60 and a808863.

📒 Files selected for processing (4)
  • plugin/controller/lib/impl/mcp/MCPConfig.ts
  • plugin/controller/lib/impl/mcp/MCPControllerRegister.ts
  • plugin/controller/test/mcp/mcp.test.ts
  • plugin/tegg/lib/ctx_lifecycle_middleware.ts

Comment thread plugin/controller/lib/impl/mcp/MCPConfig.ts
Comment thread plugin/controller/lib/impl/mcp/MCPControllerRegister.ts
Comment thread plugin/controller/lib/impl/mcp/MCPControllerRegister.ts
Comment thread plugin/controller/test/mcp/mcp.test.ts

@jerryliang64 jerryliang64 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@akitaSummer akitaSummer merged commit bc57964 into master Jun 23, 2026
12 checks passed
@akitaSummer akitaSummer deleted the fix/mcp-stream-session-cleanup branch June 23, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants