Skip to content

Add --port flag to serve over http. #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

tillahoffmann
Copy link

@tillahoffmann tillahoffmann commented May 25, 2025

This PR adds a command line flag --port to serve DesktopCommanderMCP over http transport. Supporting http allows DesktopCommanderMCP to be used with language models other than the Claude desktop app. The behavior of the command remains unchanged if no --port is supplied.

I implemented this change to be able to use DesktopCommanderMCP with Google AI Studio through a userscript (see here for more details if interested).

Edit: Oh dear, just saw #115. Feel free to close if this one is just a duplicate.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added support for running the server as an HTTP server using a specified port, in addition to the existing stdio mode.
  • Documentation

    • Updated the README to include instructions for running the server as an HTTP server.
    • Improved formatting and cleaned up whitespace in the documentation.
  • Chores

    • Added Express and its type definitions as dependencies.
    • Enabled source map generation in the TypeScript configuration.
  • Tests

    • Introduced comprehensive tests validating HTTP server startup, MCP protocol communication over HTTP, and error handling for invalid requests.

Copy link
Contributor

coderabbitai bot commented May 25, 2025

Walkthrough

The changes introduce a new HTTP server mode to the project, allowing it to run as an Express-based web server in addition to the existing stdio mode. Documentation and configuration files were updated to reflect this new capability, and Express dependencies were added. Minor formatting and whitespace adjustments were also made.

Changes

File(s) Change Summary
README.md Added installation option for HTTP server mode; minor release note formatting and whitespace fixes.
package.json Added express and @types/express as dependencies.
src/index.ts Added HTTP server mode using Express; refactored server startup logic; improved error handling.
src/http-transport.ts Added new module to run HTTP server with Express, handling JSON-RPC requests via POST.
test/test-http-transport.js Added comprehensive tests for HTTP transport functionality, including startup, MCP calls, and error handling.
tsconfig.json Enabled TypeScript source maps; minor formatting update.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Server (index.ts)
    participant Express
    participant Transport

    User->>Server (index.ts): Start server with --port argument
    Server (index.ts)->>Express: Initialize HTTP server on specified port
    User->>Express: Send POST request
    Express->>Server (index.ts): Receive request
    Server (index.ts)->>Transport: Create StreamableHTTPServerTransport per request
    Transport->>Server (index.ts): Handle JSON-RPC request/response
    Server (index.ts)->>Express: Send response
    Express->>User: Return response
Loading

Poem

A rabbit hops, a server wakes,
Now HTTP requests it gladly takes.
With Express in its fluffy paws,
It listens close for every cause.
Whether port or stdio stream,
This bunny codes a clever scheme!
🐇✨

✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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
Contributor

@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

🧹 Nitpick comments (1)
README.md (1)

133-137: Fix minor grammar issue and approve documentation.

The new HTTP server installation option is well-documented and matches the implementation.

Apply this grammar fix:

-### Option 6: Running as a http server
+### Option 6: Running as an HTTP server
🧰 Tools
🪛 LanguageTool

[misspelling] ~133-~133: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...fig if needed ### Option 6: Running as a http server 1. Clone the repository us...

(EN_A_VS_AN)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2743fdc and 9e16162.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • README.md (3 hunks)
  • package.json (1 hunks)
  • src/index.ts (5 hunks)
  • tsconfig.json (2 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[misspelling] ~133-~133: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...fig if needed ### Option 6: Running as a http server 1. Clone the repository us...

(EN_A_VS_AN)

🔇 Additional comments (8)
tsconfig.json (1)

15-16: LGTM! Source map generation added for debugging support.

The addition of source map generation and formatting consistency with the trailing comma are appropriate changes that will support debugging the new HTTP server functionality.

package.json (1)

67-67:

✅ Verification successful

Verify Express 5.x stability for production use.

The Express dependencies are correctly added to support the new HTTP server functionality. However, ensure that Express 5.x is stable for your use case.

Please verify the stability and compatibility of Express 5.x:

Also applies to: 70-70


🌐 Web query:

Is Express 5.x stable for production use and what are the major changes from Express 4.x?

💡 Result:

Express 5.x Stability for Production

Express 5.x is now officially stable and recommended for production use. After more than a decade in development, the Express.js team released version 5.0.0 with a strong focus on stability and security, ensuring it is suitable for robust, production-grade Node.js applications[1][4][5]. The release also went through a security audit and incorporates modern best practices, giving additional assurance for its use in production systems[1][2][8].

Major Changes from Express 4.x

Express 5 is largely an evolutionary update, prioritizing stability, modernization, and security over radical new features. However, there are several important changes and improvements developers should note:

Node.js Support

  • Node.js 18+ Required: Express 5 drops support for Node.js versions earlier than v18, enabling better performance, security, and maintainability[1][5][8].

Routing and Path Matching

  • Path Matching Changes: Express 5 revises the route matching system for greater security and predictability. Sub-expressions in regular expressions are no longer supported, eliminating the risk of ReDoS (Regular Expression Denial of Service) attacks[1][3][8].
  • Wildcard and Optional Parameters: Syntax for wildcards and optional route parameters is clarified and updated. Wildcards must now be explicitly named or replaced with (.*), and optional parameters use {/:param} instead of :param?[3].
  • Named Parameters Required: All captured parameters in routes must be named, improving readability and maintainability[3].

Error Handling and Async Support

  • Automatic Async Error Handling: Rejected promises thrown in async middleware and route handlers are now automatically forwarded to Express’s error handler. Manual try/catch or calls to next(err) are no longer required for promise rejections, greatly simplifying async logic[1][7][9].

Core Stability and Security

  • Stricter HTTP Status Code Enforcement: The res.status() method now enforces valid HTTP status codes (100–999). Invalid codes will throw errors instead of failing silently[1][6].
  • Removal of Deprecated Methods: Several legacy and previously deprecated methods have been removed, such as:
    • app.del()
    • app.param(fn)
    • req.param(name)
    • res.send(status)
    • res.sendfile()
    • And others[6][7][9]
  • Integrated Body Parser: The body parser is now included in Express by default; no need for a separate body-parser package[7][8]. The default configuration for express.urlencoded is improved for convenience and security[9].

Miscellaneous Improvements

  • ES6+ Support: Express 5 is fully compatible with modern ES6+ features, including native import/export syntax[7].
  • Improved Defaults: For example, req.body returns undefined instead of {} if the body has not been parsed, and req.host now preserves the port number[6].
  • Brotli Compression Support: Express 5 adds support for Brotli encoding to improve response compression for supported clients[6].
  • Removal of Monkey-Patching and Legacy APIs: Express is deprecating and phasing out monkey-patching and other legacy patterns, improving future maintainability[2].

Migration Considerations

  • Express provides a migration guide to help developers update their applications from 4.x to 5.x and address breaking changes[6].

Summary Table: Express 4.x vs 5.x Key Differences

Feature/Change Express 4.x Express 5.x
Node.js Support v0.10+ v18+ only
Async Error Handling Manual (try/catch, next) Automatic for rejected promises
Body Parser Separate package needed Built-in
Routing Regex/Path Matching Sub-expressions allowed Sub-expressions disallowed; stricter
Wildcard/Optional Params *, :param? (.*), {/:param}
Deprecated Methods Some supported Removed
HTTP Status Validation Loose (silent fail) Strict (errors on invalid codes)
ES6+ Syntax Partial Full support
Brotli Compression No Yes

Express 5.x is stable, secure, and production-ready. Upgrading provides improved error handling, stricter routing, and better alignment with modern Node.js and JavaScript standards. Some migration effort may be required, especially for projects using deprecated or legacy APIs, but the new version is widely regarded as a solid foundation for the future of Express-based development[1][3][4][5][6][7][8][9].

Citations:


Express 5.x Is Production-Ready—Approved
Express 5.x has graduated from beta and is officially stable for production use. Your dependency updates ("express": "^5.1.0" and "@types/express": "^5.0.2") align correctly, and the caret ranges allow necessary minor/patch fixes.

No further action required.

src/index.ts (6)

4-6: LGTM! Imports correctly added for HTTP server functionality.

The new imports for Express and StreamableHTTPServerTransport are appropriate for the HTTP server implementation.


91-95: LGTM! Setup command handling properly maintained.

The setup command detection and early return logic is correctly preserved in the refactored code structure.


109-118: LGTM! Port parsing implementation is robust.

The port flag parsing includes proper validation and error handling for invalid port numbers.


120-125: LGTM! Conditional server startup logic is clean.

The branching logic correctly chooses between HTTP and stdio server modes based on the port flag.


143-190: HTTP server implementation looks good overall with one critical fix needed.

The HTTP server implementation correctly creates isolated transport instances per request and handles errors appropriately. The JSON-RPC error response format is correct, and the server lifecycle management is proper except for the server.close() issue flagged separately.


192-197: LGTM! Stdio server extraction maintains existing functionality.

The stdio server logic is properly extracted into a separate function while preserving the original behavior and logging.

src/index.ts Outdated
Comment on lines 160 to 162
transport.close();
server.close();
});
Copy link
Contributor

@coderabbitai coderabbitai bot May 25, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical bug: Don't close the main server instance on each request.

The code calls server.close() on every request close event, which will shut down the entire server after the first request completes. Only the transport should be closed, not the main server instance.

Apply this fix:

         res.on('close', () => {
           transport.close();
-          server.close();
         });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
transport.close();
server.close();
});
res.on('close', () => {
transport.close();
});
🤖 Prompt for AI Agents
In src/index.ts around lines 160 to 162, the code incorrectly calls
server.close() on each request close event, which shuts down the entire server
prematurely. To fix this, remove the call to server.close() inside the request
close handler and only call transport.close() to properly close the transport
without stopping the main server.

Copy link
Author

Choose a reason for hiding this comment

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

This follows the official modelcontextprotocol/typescript-sdk documentation for a stateless MCP server (see https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#without-session-management-stateless for details).

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

Let me also check the specific GitHub repository mentioned by the user:

@tillahoffmann Thank you for the clarification and the link to the official documentation. Let me verify this against the official TypeScript SDK documentation to ensure I understand the correct implementation pattern for stateless MCP servers.


🌐 Web query:

modelcontextprotocol typescript-sdk stateless server implementation without session management documentation

💡 Result:

To implement a stateless server using the Model Context Protocol (MCP) with the TypeScript SDK, you can configure the StreamableHTTPServerTransport to operate without session management. This approach is suitable for scenarios where each request is independent, such as simple API wrappers or horizontally scaled deployments without shared session state.

Implementation Steps:

  1. Install the MCP TypeScript SDK:

    Ensure you have the MCP SDK installed in your project:

    npm install @modelcontextprotocol/sdk
  2. Set Up the Server:

    Create an instance of McpServer and configure it with the desired capabilities:

    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    
    const server = new McpServer({
      name: "example-server",
      version: "1.0.0",
      capabilities: {
        resources: {},
        tools: {},
      },
    });
  3. Configure the Transport in Stateless Mode:

    Set up the StreamableHTTPServerTransport with statelessMode enabled and sessionIdGenerator set to undefined to disable session management:

    import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
    
    const transport = new StreamableHTTPServerTransport({
      statelessMode: true,
      sessionIdGenerator: undefined,
    });
  4. Handle Incoming Requests:

    Implement the logic to handle incoming requests. In stateless mode, each request is processed independently:

    import express, { Request, Response } from "express";
    
    const app = express();
    app.use(express.json());
    
    app.post("/mcp", async (req: Request, res: Response) => {
      try {
        // Handle the request with the transport
        await server.connect(transport);
        await transport.handleRequest(req, res, req.body);
      } catch (error) {
        console.error("Error handling MCP request:", error);
        if (!res.headersSent) {
          res.status(500).json({
            jsonrpc: "2.0",
            error: {
              code: -32603,
              message: "Internal server error",
            },
            id: null,
          });
        }
      }
    });
    
    app.listen(3000, () => {
      console.log("MCP Stateless Streamable HTTP Server listening on port 3000");
    });

Key Considerations:

  • Stateless Mode: By setting statelessMode: true and sessionIdGenerator: undefined, the server operates without maintaining session state between requests. This is beneficial for simple, independent interactions.

  • Request Handling: Each request is handled in isolation, ensuring that no session information is retained between requests. This approach is suitable for scenarios where each request is independent, such as simple API wrappers or horizontally scaled deployments without shared session state.

For more detailed information and advanced configurations, refer to the official MCP TypeScript SDK documentation and the GitHub repository. (github.com)

Citations:


🌐 Web query:

site:github.com modelcontextprotocol typescript-sdk stateless MCP server implementation example

💡 Result:

The Model Context Protocol (MCP) TypeScript SDK provides a way to build stateless MCP servers that can connect with LLM applications. Based on the search results, I'll provide a comprehensive explanation of implementing stateless MCP servers using the TypeScript SDK.

Understanding MCP and Stateless Servers

The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction[5]. A stateless MCP server handles each request independently without maintaining session state between requests, which is ideal for serverless architectures.

Implementing a Stateless MCP Server

Using Streamable HTTP Transport

The most recent version of the MCP protocol (2025-03-26) supports Streamable HTTP transport, which supersedes the older SSE transport[7]. Here's an example of implementing a stateless MCP server with Streamable HTTP:

import express from 'express';
import { Request, Response } from 'express';
import { Server } from "@modelcontextprotocol/sdk/server";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamablehttp.js";

// Configure server
const serverConfig = { 
  name: "StatelessMCPServer", 
  version: "1.0.0"
};

// Factory function to create a new server instance for each request
const getServer = () => new Server(serverConfig, {
  capabilities: {
    resources: {}
  }
});

const app = express();
app.use(express.json());

app.post('/mcp', async (req: Request, res: Response) => {
  // Create a new instance of transport and server for each request
  // to ensure complete isolation and avoid request ID collisions
  try {
    const server = getServer();
    const transport = new StreamableHTTPServerTransport({
      sessionIdGenerator: undefined,
    });
    
    res.on('close', () => {
      console.log('Request closed');
      transport.close();
      server.close();
    });

    // Set up request handlers
    server.setRequestHandler(ListResourcesRequestSchema, async () => {
      return {
        resources: [
          {
            uri: "example://resource",
            name: "Example Resource"
          }
        ]
      };
    });
    
    await server.connect(transport);
    await transport.handleRequest(req, res, req.body);
  } catch (error) {
    console.error('Error handling MCP request:', error);
    if (!res.headersSent) {
      res.status(500).json({
        jsonrpc: '2.0',
        error: {
          code: -32603,
          message: 'Internal server error',
        },
        id: null,
      });
    }
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`MCP Stateless Server listening on port ${PORT}`);
});

This example demonstrates creating a new server instance for each request, which is crucial for stateless operation[10]. The server and transport are created on demand and cleaned up after the request completes.

Key Components for Stateless Implementation

  1. Factory Function: Create a new server instance for each request to ensure complete isolation[10].

  2. Transport Configuration: The StreamableHTTPServerTransport is configured with sessionIdGenerator: undefined to indicate stateless operation[10].

  3. Request Cleanup: Ensure proper cleanup of resources by closing both the transport and server when the request ends[10].

  4. Error Handling: Implement robust error handling to prevent unhandled exceptions[10].

Recent Updates to MCP TypeScript SDK

The TypeScript SDK has been actively developed with recent improvements:

  • Support for Streamable HTTP transport (protocol version 2025-03-26)[7]
  • Tool Annotations for better describing tool behavior[7]
  • Improved stateless examples and documentation[7]
  • Fixes for SSE transport server examples[7]
  • Authentication flow improvements for StreamableHTTP transport[7]

Alternate Implementation Example

For comparison, here's another example of a basic MCP server implementation:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server({
  name: "example-server",
  version: "1.0.0"
}, {
  capabilities: {
    resources: {}
  }
});

// Handle requests
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "example://resource",
        name: "Example Resource"
      }
    ]
  };
});

// Connect transport
const transport = new StdioServerTransport();
await server.connect(transport);

This example uses the StdioServerTransport instead of Streamable HTTP, which might be suitable for different use cases[8].

Serverless Deployment

For serverless environments like AWS Lambda, a similar pattern can be followed:

import { Server } from "@modelcontextprotocol/sdk/server";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamablehttp.js";

// Create server factory
const createServer = () => {
  const server = new Server({
    name: "lambda-mcp-server",
    version: "1.0.0"
  });
  
  // Set up handlers
  // ...
  
  return server;
};

// Lambda handler
export const handler = async (event, context) => {
  const server = createServer();
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined
  });
  
  await server.connect(transport);
  
  // Process the Lambda event through the MCP transport
  // Implementation details depend on how you structure your Lambda event
  
  return {
    statusCode: 200,
    body: "Response from MCP server"
  };
};

This pattern aligns with the serverless deployment approach shown in the Lambda MCP Server Demo[6].

By following these examples and patterns, you can create stateless MCP servers that can be deployed in various environments, from traditional web servers to serverless functions.

Citations:


@tillahoffmann You're absolutely right, and I apologize for the incorrect analysis. After reviewing the official MCP TypeScript SDK documentation, I can confirm that the pattern in your code is indeed the recommended approach for stateless MCP servers.

The documentation clearly shows that for stateless implementations, you should create a new server instance for each request and clean up both the transport and server when the request completes:

res.on('close', () => {
  transport.close();
  server.close();
});

This ensures complete isolation between requests and prevents request ID collisions when multiple clients connect concurrently, which is exactly what your implementation does.

Thank you for the clarification and for providing the documentation link. I should have verified against the official documentation before flagging this as an issue.


✏️ Learnings added
Learnt from: tillahoffmann
PR: wonderwhy-er/DesktopCommanderMCP#137
File: src/index.ts:160-162
Timestamp: 2025-05-25T16:02:52.176Z
Learning: In stateless MCP server implementations using the modelcontextprotocol/typescript-sdk, the pattern of calling both transport.close() and server.close() in the request close event handler is the official recommended approach according to the documentation at https://github.com/modelcontextprotocol/typescript-sdk.

Learnt from: tillahoffmann
PR: wonderwhy-er/DesktopCommanderMCP#137
File: src/index.ts:160-162
Timestamp: 2025-05-25T16:02:52.176Z
Learning: In stateless MCP server implementations using the modelcontextprotocol/typescript-sdk, the correct pattern is to call both transport.close() and server.close() in the request close event handler. This is the official recommended approach according to the documentation for ensuring complete isolation between requests and preventing request ID collisions in concurrent scenarios.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@wonderwhy-er
Copy link
Owner

Yours looks like a more correct implementation than in that PR
I just need to test how it works.

@wonderwhy-er
Copy link
Owner

Will try to test tomorrow. Looks cool.

@wonderwhy-er
Copy link
Owner

Really love things you made here.
I have not fully tested it all.
Sat down late tired and one thing I did not like was not about code but about workflow of connecting to AI Studio

Felt like I want people to be able to run one command from terminal and get everything setup and working.
Seems possible to not so easy.
Was thinking to convert user script to bookmarklet, chrome extension or even just wrap in electron.

I did try to wrap AI Studio in electron and got errors :D
Thinking of bookmarklet now.

@wonderwhy-er
Copy link
Owner

One comment and one question.

  1. can we extract http server related things to separate file? So that in index we check if it should be stdio or http and run imported function that starts things from some httpserver.ts file?

  2. I did convert user script to bookmarklet and it seems to work but calling from ai studio to localhost resulted in cors errors
    Checking if its fixable

@tillahoffmann
Copy link
Author

  1. I'll have a go at splitting out the code into two separate files.
  2. The user script is indeed a bit ugly, but, as you mentioned, the CORS requests seem to not work unless they are run through an extension. Would be cool if we could get it to work through a bookmarklet!

Copy link
Contributor

@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: 3

🧹 Nitpick comments (3)
README.md (2)

132-132: Fix grammar: Add comma before "but"

Add a comma before "but" when connecting two independent clauses.

-**❌ Auto-Updates:** No - uses npx but config might not update automatically
+**❌ Auto-Updates:** No - uses npx, but config might not update automatically
🧰 Tools
🪛 LanguageTool

[uncategorized] ~132-~132: Use a comma before ‘but’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ning. ❌ Auto-Updates: No - uses npx but config might not update automatically *...

(COMMA_COMPOUND_SENTENCE_2)


155-155: Fix grammar: Use "an" instead of "a" before "http"

Use "an" before words that start with a vowel sound.

-### Option 6: Running as a http server
+### Option 6: Running as an HTTP server
🧰 Tools
🪛 LanguageTool

[misspelling] ~155-~155: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...Claude config ### Option 6: Running as a http server 1. Clone the repository us...

(EN_A_VS_AN)

test/test-http-transport.js (1)

231-232: Simplify conditional check with optional chaining

Use optional chaining to make the code more concise and readable.

-    if (testResult && testResult.serverInfo) {
+    if (testResult?.serverInfo) {
       await teardown(originalConfig, testResult.serverInfo.serverPromise);
🧰 Tools
🪛 Biome (1.9.4)

[error] 231-232: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9e16162 and 33b449a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • README.md (7 hunks)
  • package.json (1 hunks)
  • src/http-transport.ts (1 hunks)
  • src/index.ts (5 hunks)
  • test/test-http-transport.js (1 hunks)
  • tsconfig.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • package.json
  • tsconfig.json
  • src/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
test/test-http-transport.js (3)
src/server.ts (1)
  • server (44-56)
src/config-manager.ts (1)
  • configManager (212-212)
src/http-transport.ts (1)
  • runHttpServer (6-53)
src/http-transport.ts (2)
test/test-http-transport.js (1)
  • port (107-107)
src/server.ts (1)
  • server (44-56)
🪛 Biome (1.9.4)
test/test-http-transport.js

[error] 231-232: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🪛 LanguageTool
README.md

[uncategorized] ~132-~132: Use a comma before ‘but’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...ning. ❌ Auto-Updates: No - uses npx but config might not update automatically *...

(COMMA_COMPOUND_SENTENCE_2)


[misspelling] ~155-~155: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...Claude config ### Option 6: Running as a http server 1. Clone the repository us...

(EN_A_VS_AN)

🔇 Additional comments (6)
README.md (1)

155-159: Well-documented new HTTP server option

The new HTTP server installation option is clearly documented with appropriate instructions. This aligns well with the PR objective to add HTTP transport capability.

🧰 Tools
🪛 LanguageTool

[misspelling] ~155-~155: Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.
Context: ...Claude config ### Option 6: Running as a http server 1. Clone the repository us...

(EN_A_VS_AN)

src/http-transport.ts (2)

13-17: Excellent design: Stateless transport per request

Creating a new transport instance per request is a smart approach to ensure complete isolation and prevent request ID collisions between concurrent clients. This design choice aligns well with HTTP's stateless nature.


28-40: Proper error handling with JSON-RPC compliance

The error handling correctly follows JSON-RPC 2.0 specification with proper error codes and message format. The check for !res.headersSent prevents double responses.

test/test-http-transport.js (3)

18-32: Excellent port discovery implementation

Using the OS to assign an available port (port 0) is a robust approach that prevents port conflicts during testing. The promise-based implementation with proper error handling is well done.


37-52: Robust SSE parsing with good error handling

The SSE response parsing correctly handles the data: prefix and provides clear error messages when data is not found. This is essential for testing the StreamableHTTPServerTransport.


103-141: Comprehensive MCP protocol testing

The test properly validates the MCP initialize handshake with correct protocol version, capabilities verification, and proper client info. This ensures the HTTP transport correctly implements the MCP specification.

Comment on lines +6 to +8
export async function runHttpServer(port: number): Promise<void> {
// We return a new promise that never resolves to keep the server alive.
return new Promise((resolve, reject) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the intentional never-resolving promise

The comment indicates this is intentional to keep the server alive, but this design means the function can never be properly awaited or cleaned up programmatically.

Consider if there should be a way to gracefully shut down the server. This might be needed for testing or production deployments.


🏁 Script executed:

#!/bin/bash
# Check if there are any patterns in the codebase for server shutdown or cleanup
rg -A 5 -B 5 "server.*close|shutdown|stop|cleanup" --type ts --type js

Length of output: 14106


Add graceful shutdown support to runHttpServer

Currently runHttpServer returns a promise that never resolves, making it impossible to programmatically stop the HTTP server (e.g. in tests or controlled shutdowns).

• File: src/http-transport.ts
– Function: export async function runHttpServer(port: number): Promise<void>
– Lines 6–8: returning a never-resolving promise keeps the server alive indefinitely.

Suggested changes:

  • Change the API to expose the Server instance or return a shutdown handle.
  • Alternatively, accept an AbortSignal or callback to trigger server.close().

Example refactor:

-export async function runHttpServer(port: number): Promise<void> {
-  // ...never-resolving promise…
-  return new Promise(() => {/* keep alive */});
-}
+export function runHttpServer(port: number, signal?: AbortSignal) {
+  const server = http.createServer(/*…*/);
+  server.listen(port);
+
+  // on external signal, close the server
+  signal?.addEventListener('abort', () => server.close());
+
+  return server;
+}

This lets callers call server.close() (or abort via signal) to shut down cleanly.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/http-transport.ts around lines 6 to 8, the runHttpServer function returns
a promise that never resolves, preventing programmatic shutdown of the server.
Refactor the function to either return the HTTP Server instance or accept an
AbortSignal or callback parameter that can trigger server.close() for graceful
shutdown. This change will allow callers to properly stop the server when
needed, such as during testing or controlled shutdowns.

Comment on lines +84 to +98
async function teardown(originalConfig, httpServer) {
// Close HTTP server if it exists
if (httpServer) {
try {
httpServer.close();
} catch (error) {
// Ignore errors when closing server
}
}

// Reset configuration to original
if (originalConfig) {
await configManager.updateConfig(originalConfig);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify server cleanup in teardown function

The teardown function attempts to close the HTTP server, but the runHttpServer function returns a Promise that never resolves, not an HTTP server instance.

The httpServer parameter in teardown might not be the correct type for cleanup. Consider how to properly shutdown the HTTP server started by runHttpServer().


🏁 Script executed:

#!/bin/bash
# Check how the HTTP server instance should be properly closed
ast-grep --pattern 'httpServer.close()'

Length of output: 98


🏁 Script executed:

#!/bin/bash
# Locate runHttpServer definition and usage in the codebase
rg -n 'runHttpServer' .

Length of output: 409


🏁 Script executed:

#!/bin/bash
# Show teardown definition with surrounding context
rg -n "async function teardown" -A5 -B5 test/test-http-transport.js

# Show runHttpServer implementation
sed -n '1,200p' src/http-transport.ts

Length of output: 2259


Fix HTTP server cleanup in teardown

The teardown in test/test-http-transport.js (lines 84–98) calls httpServer.close() on whatever is passed in, but runHttpServer() returns a Promise that never resolves to the actual server instance. As a result, httpServer in teardown is a pending Promise, not the server to close.

Please update one of the following:

• Change runHttpServer (src/http-transport.ts) to return the httpServerInstance, e.g.:

 export async function runHttpServer(port: number): Promise<Server> {
-  return new Promise((resolve, reject) => {
+  return new Promise<Server>((resolve, reject) => {
     const app = express();
     // … setup routes …
     const httpServerInstance = app.listen(port, () => {
       console.log(`…listening on port ${port}`);
+      resolve(httpServerInstance);
     });
     httpServerInstance.on('error', reject);
   });
 }

• Or modify the test’s teardown to await the promise and then close the resolved server:

 async function teardown(originalConfig, httpServerPromise) {
   // Close HTTP server if it exists
-  if (httpServer) {
-    try {
-      httpServer.close();
-    } catch (error) {
-      // Ignore errors when closing server
-    }
-  }
+  if (httpServerPromise) {
+    try {
+      const serverInstance = await httpServerPromise;
+      serverInstance.close();
+    } catch (error) {
+      // Ignore errors when closing server
+    }
+  }

   // Reset configuration to original
   if (originalConfig) {
     await configManager.updateConfig(originalConfig);
   }
 }

Either change will ensure you’re closing the actual HTTP server, not a Promise.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function teardown(originalConfig, httpServer) {
// Close HTTP server if it exists
if (httpServer) {
try {
httpServer.close();
} catch (error) {
// Ignore errors when closing server
}
}
// Reset configuration to original
if (originalConfig) {
await configManager.updateConfig(originalConfig);
}
}
async function teardown(originalConfig, httpServerPromise) {
// Close HTTP server if it exists
if (httpServerPromise) {
try {
const serverInstance = await httpServerPromise;
serverInstance.close();
} catch (error) {
// Ignore errors when closing server
}
}
// Reset configuration to original
if (originalConfig) {
await configManager.updateConfig(originalConfig);
}
}
🤖 Prompt for AI Agents
In test/test-http-transport.js around lines 84 to 98, the teardown function
incorrectly calls httpServer.close() on a parameter that is actually a Promise
returned by runHttpServer(), not the HTTP server instance itself. To fix this,
modify the teardown function to await the httpServer Promise to get the actual
server instance before calling close(), or alternatively, update runHttpServer
in src/http-transport.ts to return the HTTP server instance instead of a
Promise<void>. This ensures the correct server object is closed during teardown.

@wonderwhy-er
Copy link
Owner

  1. I'll have a go at splitting out the code into two separate files.
  2. The user script is indeed a bit ugly, but, as you mentioned, the CORS requests seem to not work unless they are run through an extension. Would be cool if we could get it to work through a bookmarklet!

problem was with with http server, I fixed it and it worked trough bookmarklet
Tested it and UX is bit flaky
Not sure I fully understand everything what is happening there

Will test more
Thanks for splitting out server in to another file
Will test more tomorrow

@wonderwhy-er
Copy link
Owner

With new ChatGPT support for remote MCPs I think I will be merging this soon.
Still need to test few things

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