Skip to content

feat: add mcp stateless#309

Merged
akitaSummer merged 4 commits intoeggjs:masterfrom
akitaSummer:feat/mcp-stateless
Apr 29, 2025
Merged

feat: add mcp stateless#309
akitaSummer merged 4 commits intoeggjs:masterfrom
akitaSummer:feat/mcp-stateless

Conversation

@akitaSummer
Copy link
Copy Markdown
Contributor

@akitaSummer akitaSummer commented Apr 28, 2025

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

  • New Features
    • Introduced stateless stream transport support for MCP controllers, accessible via a new endpoint for streamable HTTP interactions.
    • Added centralized configuration for MCP controller endpoints and stream transport options.
  • Documentation
    • Expanded documentation to include detailed guidance and examples on MCP annotations, controller setup, and usage of new stream endpoints.
  • Bug Fixes
    • Middleware updated to properly handle request body parsing for the new stateless stream endpoint.
  • Tests
    • Added comprehensive tests to verify stateless streamable MCP controller functionality.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2025

Walkthrough

This update introduces stateless stream transport support for MCP controllers, centralizes MCP configuration in a new MCPConfig class, and extends the documentation to cover MCP annotations and endpoints. The application boot process now conditionally initializes the stateless stream transport. Middleware and configuration files are updated to recognize the new stateless stream path. The MCP controller registration logic is refactored to use the centralized configuration and to initialize the stateless stream server and transport. A comprehensive test is added to verify the new stateless streamable endpoint and its operations.

Changes

File(s) Change Summary
plugin/controller/README.md Expanded documentation with a new section on MCP annotations, detailed usage of decorators, schema definitions, and an example of an MCP controller. Documents the new stateless stream endpoint and describes supported controller endpoints.
plugin/controller/app.ts In the ControllerAppBootHook.didLoad method, added logic to initialize stateless stream transport if app.mcpProxy is enabled. Added statelessStreamPath to CSRF ignore list in configWillLoad.
plugin/controller/app/middleware/mcp_body_middleware.ts Added support for the new statelessStreamPath in the middleware's path checks to disable the body parser for this route.
plugin/controller/config/config.default.ts Added statelessStreamPath: '/mcp/stateless/stream' to the MCP configuration object.
plugin/controller/lib/impl/mcp/MCPConfig.ts Introduced new module defining MCPConfigOptions interface and MCPConfig class, encapsulating all MCP-related configuration, including new stateless stream path and related options.
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts Refactored to use MCPConfig for all configuration access. Added stateless stream transport support: new static method connectStatelessStreamTransport, new method mcpStatelessStreamServerInit, and related properties. Updated registration and initialization logic to support and route stateless stream requests.
plugin/controller/test/mcp/mcp.test.ts Added a new test case to verify the functionality of the stateless streamable endpoint, including tool listing, tool invocation, notification handling, resource and prompt operations.
plugin/controller/package.json Added "config/**/*.js" and "config/**/*.d.ts" patterns to the package files array to include config files in the published package.
plugin/mcp-proxy/app.ts Added configWillLoad lifecycle method to register MCPProxyHook with MCPControllerRegister before config load.
plugin/mcp-proxy/app/extend/agent.ts Added isAgent: true property to the options object when instantiating MCPProxyApiClient in the mcpProxy getter.
plugin/mcp-proxy/index.ts Extracted MCPProxyHook from inside MCPProxyApiClient class and exported it as a standalone constant. Added isAgent property to MCPProxyApiClient. Modified _init method to conditionally start HTTP server only if isAgent is false. Removed addProxyHook method.
plugin/mcp-proxy/package.json Expanded files array to include additional JavaScript and TypeScript declaration files under agent, index, config, app, and typings directories.

Sequence Diagram(s)

sequenceDiagram
    participant AppBootHook
    participant MCPControllerRegister
    participant MCPConfig
    participant ExpressApp
    participant Client

    AppBootHook->>MCPControllerRegister: doRegister()
    AppBootHook->>MCPControllerRegister: connectStatelessStreamTransport() (if mcpProxy enabled)
    MCPControllerRegister->>MCPConfig: Load configuration (including statelessStreamPath)
    MCPControllerRegister->>ExpressApp: Register /mcp/stateless/stream POST route
    Client->>ExpressApp: POST /mcp/stateless/stream (stateless stream request)
    ExpressApp->>MCPControllerRegister: Handle request via StreamableHTTPServerTransport
    MCPControllerRegister-->>Client: Responds with streamable/stateless data
Loading

Possibly related PRs

  • feat: add mcp #307: Introduces the foundational MCP decorators, metadata builders, and core MCP controller registration infrastructure, which this PR extends with stateless stream support and configuration centralization.

Suggested labels

enhancement

Suggested reviewers

  • killagu

Poem

In the warren of code, a new stream flows,
Stateless and nimble, where the fresh carrot grows.
Configs now gathered in one cozy den,
Controllers annotated with a clever pen.
Tests hop along, verifying each route—
The MCP rabbit leaps forward, no doubt!
🥕✨

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/mcp-proxy/app.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-eggache".

(The package "eslint-plugin-eggache" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-eggache@latest --save-dev

The plugin "eslint-plugin-eggache" was referenced from the config file in ".eslintrc » eslint-config-egg/typescript » ./index.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

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

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-eggache".

(The package "eslint-plugin-eggache" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-eggache@latest --save-dev

The plugin "eslint-plugin-eggache" was referenced from the config file in ".eslintrc » eslint-config-egg/typescript » ./index.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

plugin/mcp-proxy/index.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the plugin "eslint-plugin-eggache".

(The package "eslint-plugin-eggache" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-eggache@latest --save-dev

The plugin "eslint-plugin-eggache" was referenced from the config file in ".eslintrc » eslint-config-egg/typescript » ./index.js".

If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.

  • 1 others
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (7)
plugin/controller/test/mcp/mcp.test.ts (1)

279-370: Test case is comprehensive but contains duplicated code

The test case thoroughly verifies all aspects of the stateless stream transport functionality, which is excellent. It properly tests tools, notifications, resources, and prompts interactions.

Consider refactoring the "streamable should work" and "stateless streamable should work" test cases to reduce code duplication. You could extract a common testing function that accepts the endpoint URL as a parameter:

-  it('streamable should work', async () => {
-    const streamableClient = new Client({
-      name: 'streamable-demo-client',
-      version: '1.0.0',
-    });
-    const baseUrl = await app.httpRequest()
-      .post('/mcp/stream').url;
-    // ... rest of test code
-  });
-
-  it('stateless streamable should work', async () => {
-    const streamableClient = new Client({
-      name: 'streamable-demo-client',
-      version: '1.0.0',
-    });
-    const baseUrl = await app.httpRequest()
-      .post('/mcp/stateless/stream').url;
-    // ... rest of test code
-  });
+  async function testStreamable(endpoint, testName) {
+    it(`${testName} should work`, async () => {
+      const streamableClient = new Client({
+        name: 'streamable-demo-client',
+        version: '1.0.0',
+      });
+      const baseUrl = await app.httpRequest()
+        .post(endpoint).url;
+      // ... rest of test code
+    });
+  }
+
+  testStreamable('/mcp/stream', 'streamable');
+  testStreamable('/mcp/stateless/stream', 'stateless streamable');

This would make the tests more maintainable and reduce the risk of inconsistencies when implementing future changes.

plugin/controller/README.md (2)

239-246: Incorrect Zod import in the sample code

zod does not expose a default export; attempting import z from 'zod' will raise TS1192: Module 'zod' has no default export.
Use the named export instead:

-import z from 'zod';
+import { z } from 'zod';

Readers copy-pasting the sample will otherwise get a compile-time error.


214-222: Minor language/formatting issues reduce readability

The Chinese/English mix (“使用 @ToolArgsSchema @PromptArgsSchema @Extra 来 schema 和 extra”) is a bit hard to parse.
Consider re-phrasing:

使用 @ToolArgsSchema@PromptArgsSchema@Extra 来声明参数 schema 与额外元数据。

No functional impact, but clearer docs help onboarding.

plugin/controller/lib/impl/mcp/MCPConfig.ts (1)

34-60: Explicit return types improve IDE feedback

All getters currently rely on inference. Declaring return types is a tiny effort that improves DX and prevents accidental changes:

-  get sseInitPath() {
+  get sseInitPath(): string {

Repeat for all getters.

plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (3)

125-129: Use ctx instead of self.app.currentContext

Inside the route handler you already have ctx; storing self.app.currentContext is unnecessary and triggered Biome’s noUselessThisAlias.
Just pass ctx to hooks:

-  await hook.preHandle?.(self.app.currentContext);
+  await hook.preHandle?.(ctx);

Applies to multiple handlers (preHandle, onStreamSessionInitialized, etc.).


175-218: Optional chaining simplifies deep-hook traversal

Biome flagged the manual length > 0 check.
You can condense:

-if (MCPControllerRegister.hooks.length > 0) {
-  for (const hook of MCPControllerRegister.hooks) {
-    await hook.preHandle?.(self.app.currentContext);
-  }
-}
+for (const hook of MCPControllerRegister.hooks ?? []) {
+  await hook.preHandle?.(ctx);
+}

Reduces nesting and noise.

🧰 Tools
🪛 Biome (1.9.4)

[error] 179-180: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)


280-282: Use configured streamPath once, avoid alias

self aliasing is noisy and error-prone (Biome hint). Arrow functions inherit this; you can drop self and access this.mcpConfig.streamPath directly inside initHandler, or capture const { mcpConfig } = this;.

Not blocking, but consider cleaning up for readability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9a57b4 and 9023359.

📒 Files selected for processing (7)
  • plugin/controller/README.md (1 hunks)
  • plugin/controller/app.ts (1 hunks)
  • plugin/controller/app/middleware/mcp_body_middleware.ts (1 hunks)
  • plugin/controller/config/config.default.ts (1 hunks)
  • plugin/controller/lib/impl/mcp/MCPConfig.ts (1 hunks)
  • plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (11 hunks)
  • plugin/controller/test/mcp/mcp.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
plugin/controller/app.ts (1)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (1)
  • MCPControllerRegister (46-547)
plugin/controller/lib/impl/mcp/MCPConfig.ts (1)
plugin/mcp-proxy/test/fixtures/apps/mcp-proxy/app/controller/app.ts (1)
  • InMemoryEventStore (59-127)
🪛 Biome (1.9.4)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts

[error] 86-86: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 120-120: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)


[error] 294-294: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Runner-ubuntu (22)
  • GitHub Check: Runner-macos (16)
  • GitHub Check: Runner-ubuntu (18)
  • GitHub Check: Runner-ubuntu (20)
  • GitHub Check: Runner-macos (18)
  • GitHub Check: Runner-ubuntu (16)
  • GitHub Check: Runner-macos (20)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (typescript)
🔇 Additional comments (3)
plugin/controller/config/config.default.ts (1)

10-10: LGTM: Config addition follows existing naming conventions

The new statelessStreamPath configuration property is correctly added to the mcp config object, maintaining consistent path structure with other MCP endpoints.

plugin/controller/app/middleware/mcp_body_middleware.ts (1)

6-6: Properly updated middleware to handle new endpoint

Adding the stateless stream path to the body parser exclusion list ensures consistent behavior with other MCP streaming endpoints.

plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (1)

150-172: Duplicate route-name strings may shadow each other

Reflect.apply registers three routes (POST/GET/DEL) all named 'chairMcpStatelessStreamInit'.
Egg exploits the name as a unique identifier; re-using it with different HTTP verbs risks the later calls overwriting the first, depending on router internals.

Prefer unique names, e.g.:

- 'chairMcpStatelessStreamInit',
+ 'chairMcpStatelessStreamInitPost',

(and change accordingly for GET/DEL).

Comment thread plugin/controller/app.ts
Comment thread plugin/controller/lib/impl/mcp/MCPConfig.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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (1)

120-122: ⚠️ Potential issue

Pass eventStore & sessionIdGenerator into stateless transport

Unlike the stream mode, the stateless transport is created without explicit options for eventStore and sessionIdGenerator.

This loses the custom eventStore and sessionIdGenerator configured in MCPConfig.

const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
-  sessionIdGenerator: undefined,
+  sessionIdGenerator: () => this.mcpConfig.sessionIdGenerator(),
+  eventStore: this.mcpConfig.eventStore,
});
🧰 Tools
🪛 Biome (1.9.4)

[error] 120-120: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)

🧹 Nitpick comments (4)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (4)

85-115: Implementation of stateless transport connection and initialization.

The static method correctly connects the MCP server to the stateless transport and performs an initialization request with appropriate protocol details.

However, there's an opportunity to use optional chaining for more concise code.

-  if (MCPControllerRegister.instance && MCPControllerRegister.instance.statelessTransport) {
+  if (MCPControllerRegister.instance?.statelessTransport) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 86-86: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


120-120: Remove unnecessary this aliasing.

Arrow functions inherit this from their enclosing scope, making this aliasing unnecessary.

-    const self = this;
-    const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
+    const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({

And then use this instead of self in the following code.

🧰 Tools
🪛 Biome (1.9.4)

[error] 120-120: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)


294-294: Remove unnecessary this aliasing.

Similar to the issue at line 120, this self variable is redundant as arrow functions inherit this.

-    const transport = new SSEServerTransport(self.mcpConfig.sseMessagePath, ctx.res);
+    const transport = new SSEServerTransport(this.mcpConfig.sseMessagePath, ctx.res);
🧰 Tools
🪛 Biome (1.9.4)

[error] 294-294: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)


117-173: Consider refactoring long methods for better maintainability.

The mcpStatelessStreamServerInit method is quite long and performs multiple responsibilities. Consider breaking it down into smaller, focused methods to improve readability and maintainability.

For example, you could extract the handler functions and router registration into separate methods:

private createStatelessStreamHandler(): (ctx: Context) => Promise<void> {
  // Extract the initHandler logic
}

private createMethodNotAllowedHandler(): (ctx: Context) => Promise<void> {
  // Extract the notHandler logic
}

private registerStatelessStreamRoutes(
  initHandler: (ctx: Context) => Promise<void>,
  notHandler: (ctx: Context) => Promise<void>
) {
  // Register the routes
}

mcpStatelessStreamServerInit() {
  const initHandler = this.createStatelessStreamHandler();
  const notHandler = this.createMethodNotAllowedHandler();
  this.registerStatelessStreamRoutes(initHandler, notHandler);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 120-120: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9023359 and fe0fd58.

📒 Files selected for processing (2)
  • plugin/controller/app.ts (3 hunks)
  • plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugin/controller/app.ts
🧰 Additional context used
🪛 Biome (1.9.4)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts

[error] 86-86: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 120-120: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)


[error] 294-294: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Runner-ubuntu (20)
  • GitHub Check: Runner-ubuntu (22)
  • GitHub Check: Runner-ubuntu (18)
  • GitHub Check: Runner-macos (16)
  • GitHub Check: Runner-macos (20)
  • GitHub Check: Runner-ubuntu (16)
🔇 Additional comments (9)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (9)

4-5: Appropriate imports added for stateless transport implementation.

The new imports from 'node:http' and 'node:net' are necessary for constructing the synthetic HTTP request used to initialize the stateless stream transport.


28-28: Good practice: Centralizing configuration in MCPConfig.

Centralizing configuration in a dedicated class improves code organization and maintainability.


57-58: Class property additions correctly setup for stateless transport.

The new properties store the centralized configuration and stateless transport instance needed for the new feature.


78-78: Configuration initialization looks good.

Properly initializes the MCPConfig instance with the app's MCP configuration.


94-94: Correct usage of configured stateless stream path.

The code now correctly uses the path from MCPConfig, addressing a previous issue where the wrong path was hardcoded.


147-172: Good error handling for unsupported HTTP methods.

The implementation properly handles unsupported HTTP methods with a 405 status code and clear error message, which is a good security practice.


213-214: Properly using centralized config for stream transport.

The code now correctly uses the MCPConfig properties for eventStore and sessionIdGenerator in the stream transport, providing consistency and centralized configuration management.

Also applies to: 216-217


309-309: Using centralized config for SSE heartbeat timing.

Correctly uses the MCPConfig for the SSE heartbeat interval, which improves configurability.


532-532: Initialization of stateless stream server in register method.

The register method now correctly initializes the stateless stream server alongside the existing stream and SSE servers.

@akitaSummer akitaSummer changed the title feat: add stateless feat: add mcp stateless Apr 28, 2025
Comment thread plugin/controller/README.md Outdated
Comment thread plugin/mcp-proxy/index.ts Dismissed
@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​eggjs/​tegg-controller-plugin@​3.55.068100100100100
Added@​eggjs/​core@​6.5.07510010097100
Addedsdk-base@​5.0.110010010083100

View full report

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (1)

121-123: Pass eventStore & sessionIdGenerator into stateless transport

Unlike the stream mode, the stateless transport is created without explicit options:

const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
-  sessionIdGenerator: undefined,
+  sessionIdGenerator: () => this.mcpConfig.sessionIdGenerator(),
+  eventStore: this.mcpConfig.eventStore,
});

Keeps behavior consistent across transport types.

🧰 Tools
🪛 Biome (1.9.4)

[error] 121-121: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)

🧹 Nitpick comments (4)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (4)

87-115: Ensure optional chaining for better robustness

The conditional check could be simplified using optional chaining for better maintainability.

-    if (MCPControllerRegister.instance && MCPControllerRegister.instance.statelessTransport) {
+    if (MCPControllerRegister.instance?.statelessTransport) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 87-87: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


121-121: Remove unnecessary this aliasing in arrow function context

Arrow functions inherit this from their enclosing scope, making the self variable unnecessary.

-    const self = this;
🧰 Tools
🪛 Biome (1.9.4)

[error] 121-121: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)


125-131: Move hook processing logic to a shared helper function

The hook processing logic is duplicated in several places. Consider extracting it to a helper method to reduce duplication.

+ private async processHooks(ctx: Context, hookMethod: keyof MCPControllerHook) {
+   if (MCPControllerRegister.hooks.length > 0) {
+     for (const hook of MCPControllerRegister.hooks) {
+       await hook[hookMethod]?.(ctx);
+     }
+   }
+ }

// Then use it like:
// await this.processHooks(self.app.currentContext, 'preHandle');

295-295: Remove unnecessary this aliasing

The variable self is redundant as arrow functions inherit this from their enclosing scope.

-      const transport = new SSEServerTransport(self.mcpConfig.sseMessagePath, ctx.res);
+      const transport = new SSEServerTransport(this.mcpConfig.sseMessagePath, ctx.res);
🧰 Tools
🪛 Biome (1.9.4)

[error] 295-295: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe0fd58 and 727d876.

📒 Files selected for processing (7)
  • plugin/controller/README.md (1 hunks)
  • plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (14 hunks)
  • plugin/controller/package.json (1 hunks)
  • plugin/mcp-proxy/app.ts (2 hunks)
  • plugin/mcp-proxy/app/extend/agent.ts (1 hunks)
  • plugin/mcp-proxy/index.ts (1 hunks)
  • plugin/mcp-proxy/package.json (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • plugin/controller/package.json
  • plugin/mcp-proxy/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugin/controller/README.md
🧰 Additional context used
🪛 GitHub Check: CodeQL
plugin/mcp-proxy/index.ts

[failure] 230-230: Unvalidated dynamic method call
Invocation of method with user-controlled name may dispatch to unexpected target and cause an exception.

🪛 Biome (1.9.4)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts

[error] 87-87: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 121-121: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)


[error] 295-295: This aliasing of this is unnecessary.

Arrow functions inherits this from their enclosing scope.
Safe fix: Use this instead of an alias.

(lint/complexity/noUselessThisAlias)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Runner-macos (18)
  • GitHub Check: Runner-ubuntu (18)
  • GitHub Check: Runner-macos (20)
  • GitHub Check: Runner-ubuntu (20)
  • GitHub Check: Runner-macos (16)
  • GitHub Check: Runner-ubuntu (22)
  • GitHub Check: Runner-ubuntu (16)
  • GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (5)
plugin/controller/lib/impl/mcp/MCPControllerRegister.ts (1)

536-540: LGTM: Good initialization of both servers

The code properly initializes both the regular MCP server and the new stateless MCP server with consistent configurations.

plugin/mcp-proxy/app/extend/agent.ts (1)

9-14: LGTM: Setting isAgent flag for agent context

The addition of isAgent: true is appropriate for the agent context and aligns with the changes in the MCPProxyApiClient class to conditionally start the HTTP server.

plugin/mcp-proxy/app.ts (1)

12-14: LGTM: Early hook registration in the application lifecycle

Early registration of MCPProxyHook in the configWillLoad method ensures the hook is available when MCP controllers are initialized.

plugin/mcp-proxy/index.ts (2)

55-203: LGTM: Well-organized extraction of MCPProxyHook

The extraction of MCPProxyHook to a standalone exported constant improves code organization and modularity. This separation of concerns allows the hook to be registered separately from the client instance.


227-238: LGTM: Conditional server initialization based on agent mode

The conditional initialization of the HTTP server based on the isAgent flag is appropriate and integrates well with the changes in the agent.ts file.

🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 230-230: Unvalidated dynamic method call
Invocation of method with user-controlled name may dispatch to unexpected target and cause an exception.

Comment thread plugin/mcp-proxy/index.ts
@akitaSummer akitaSummer merged commit b79d313 into eggjs:master Apr 29, 2025
13 checks passed
Copy link
Copy Markdown
Contributor

@killagu killagu left a comment

Choose a reason for hiding this comment

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

LGTM

@coderabbitai coderabbitai Bot mentioned this pull request Jul 26, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Aug 6, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Nov 3, 2025
4 tasks
This was referenced Nov 18, 2025
@coderabbitai coderabbitai Bot mentioned this pull request Nov 26, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Dec 16, 2025
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Jan 27, 2026
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Apr 6, 2026
4 tasks
@coderabbitai coderabbitai Bot mentioned this pull request May 6, 2026
4 tasks
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.

3 participants