Skip to content

McpServer.connect() Appears to Return null/undefined with Per-Request Server Instantiation and await; Leads to "Method not found" #508

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
chiragp2411 opened this issue May 17, 2025 · 0 comments

Comments

@chiragp2411
Copy link

chiragp2411 commented May 17, 2025

Hello MCP SDK Team,

I'm encountering an issue where McpServer.connect(transport) appears to return null or undefined instead of a valid ServerConnection object when using StreamableHTTPServerTransport. This subsequently prevents the transport's request listener from being set, leading to "Method not found" errors for all MCP calls (including mcp.describe).

Environment:

  • @modelcontextprotocol/sdk Version: 1.11.4 (also observed with 1.11.0)
  • Node.js Version: v20.16.0
  • Operating System: macOS Sonoma 14.4 (Apple M1 Pro)
  • Express.js Version: 4.19.2 (Please update if different)
  • TypeScript Version: 5.4.5 (Please update if different)

Problem Description:

In my application, which uses Express.js, I'm setting up an McpServer with StreamableHTTPServerTransport. The primary issue is that the synchronous call to server.connect(transport) seems to complete without throwing an immediate error, but the returned value assigned to our connection variable is observed to be null or undefined through logging.

Because the connection object is not valid:

  1. The transport.setRequestListener(...) call, which normally happens inside McpServer.connect(), is likely not being effectively executed for the transport instance.
  2. When transport.handleRequest(...) is later called, transport.requestListener is undefined.
  3. This results in either:
    • A "Method not found" JSON-RPC error if sessionIdGenerator for StreamableHTTPServerTransport is undefined.
    • A "Bad Request: Server not initialized" JSON-RPC error (HTTP 400) if sessionIdGenerator is explicitly set (e.g., to randomUUID), as transport.handleRequest has a specific check for this.requestListener.

The core problem is that McpServer.connect() is not returning a valid ServerConnection object as expected by its type signature, thus breaking the link with the transport.

My Application Code Structure:

Here's a simplified representation of the relevant parts of my application code where the issue is observed.

1. src/mcp/mcpServer.ts:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { Application, Request, Response } from "express";
import logger from "../logger"; // My application's logger (e.g., Winston)
// import { randomUUID } from "crypto"; // If using randomUUID for sessionIdGenerator

async function getCountryList() {
  return [
    { name: "United States", code: "US" },
    { name: "Canada", code: "CA" },
  ];
}

// Original problematic approach: Factory function called per request
function getServer(): McpServer {
  const server = new McpServer({ name: "AppTools", version: "1.0.0" });
  server.tool("country-list", "Returns a list of all countries in the system", {}, async () => {
    const countries = await getCountryList();
    return {
      content: [
        {
          type: "resource",
          resource: { mimeType: "application/json", text: JSON.stringify(countries) },
        },
      ],
    };
  });
  logger.info("Registered tool 'country-list' on new server instance.");
  return server;
}

export function setupMCPServer(app: Application) {
  app.post("/mcp", async (req: Request, res: Response) => {
    try {
      const server = getServer(); // Problem: New server instance on every request
      const transport = new StreamableHTTPServerTransport({
        sessionIdGenerator: undefined, // Also tested with randomUUID
      });

      logger.info("Connecting transport to server (per-request instance)...");
      console.log("Connecting transport to server (per-request instance)..."); // For visibility

      // Problem: 'await' on a synchronous function.
      // This is where 'connection' effectively becomes null/undefined based on logs.
      const connection = await server.connect(transport); 
      
      if (connection) {
        logger.info("server.connect seemed to return a truthy connection object.");
        console.log("server.connect seemed to return a truthy connection object.");
      } else {
        logger.error("CRITICAL: server.connect returned null or undefined.");
        console.error("CRITICAL: server.connect returned null or undefined.");
      }

      logger.info("Transport supposedly connected to server.");
      console.log("Transport supposedly connected to server.");

      await transport.handleRequest(req, res, req.body);
      logger.info("Request handled successfully by transport.");
      console.log("Request handled successfully by transport.");

    } catch (error: any) {
      logger.error("Error handling MCP request:", { message: error.message, stack: error.stack });
      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: req.body?.id || null,
        });
      }
    }
  });

  app.get("/mcp", (req: Request, res: Response) => { 
    res.status(405).json({ jsonrpc: "2.0", error: { code: -32000, message: "Method Not Allowed. Use POST." }, id: null });
  });
  app.delete("/mcp", (req: Request, res: Response) => { 
    res.status(405).json({ jsonrpc: "2.0", error: { code: -32000, message: "Method Not Allowed. Use POST." }, id: null });
  });
  logger.info("Original MCP routes configured.");
}
  1. src/app.ts (How setupMCPServer is called):
import express from "express";
import * as mcpServerSetup from "./mcp/mcpServer"; // Path to the mcpServer.ts above
import logger from "./logger"; // My application's logger

const app = express();
const port = process.env.API_PORT;

app.use(express.json());

try {
  mcpServerSetup.setupMCPServer(app);
  logger.info("Main MCP server setup completed.");
} catch (error) {
  logger.error("Failed to set up main MCP server:", error);
  process.exit(1);
}

app.listen(port, () => {
  logger.info(`Main Server is running at http://localhost:${port}`);
  logger.info(`Main MCP server available at http://localhost:${port}/mcp`);
});
  1. tsconfig.json:
{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "rootDir": "./src",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "allowJs": true,
    "outDir": "./dist/",
    "downlevelIteration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitAny": false,
    "strictNullChecks": false,
    "noImplicitReturns": false,
    "skipLibCheck": true,
    "types": ["./src/types/express"] 
  },
  "exclude": ["node_modules"],
  "include": ["src/**/*", "src/**/*.json"]
}
  1. package.json (Key Dependencies - please specify your exact versions):
{
  "name": "my-application",
  "version": "1.0.0",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.11.4", 
    "express": "^4.19.2", 
    "typescript": "^5.4.5" 
  }
}

Steps to Reproduce (Using the application structure above):

Set up the project with the described files and relevant dependencies.

Compile TypeScript.

Start the server.

Send a POST request:

curl -v -X POST http://localhost:3777/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":"test-country","method":"AppTools.country-list","params":{}}'

Or for mcp.describe:

curl -v -X POST http://localhost:3777/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":"test-describe","method":"mcp.describe","params":{}}'

Observed Behavior:

The curl command receives a "Method not found" error:

{"jsonrpc":"2.0","id":"test-country","error":{"code":-32601,"message":"Method not found"}}

Server logs show the CRITICAL: server.connect returned null or undefined. message (or similar if connection logging is added).

Expected Behavior:
McpServer.connect(transport) should return a valid ServerConnection object. The curl command should successfully invoke the AppTools.country-list method or mcp.describe.

Debugging Notes & Attempts:

We have confirmed that McpServer.connect is a synchronous method, so the await in my original code is incorrect.

Even when correcting my application structure to use a singleton McpServer instance and calling server.connect(transport) synchronously (as demonstrated in separate minimal tests), the core issue of connect() returning null/undefined persisted. This suggests the problem is not solely due to my initial incorrect usage patterns but might indicate a deeper issue within the SDK's connect logic when interacting with StreamableHTTPServerTransport.

Could you please investigate why McpServer.connect() might be failing to establish a proper connection and return null/undefined in this scenario with StreamableHTTPServerTransport?

Thank you for your time and assistance!

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

No branches or pull requests

1 participant