Skip to content

Add Support for NodeStreamableHTTPServerTransport Session Management from official MCP SDK source #250

@jetstartai

Description

@jetstartai

Summary

Thanks for your great work on a very useful server! This is a suggested enhancement to adopt a new 'Hybrid' transport mode recently posted by the people at MCP SDK project: MCP SDK SSE with HTTP - added Jan 2026

MetaMCP currently creates StreamableHTTPClientTransport connections correctly, but doesn't maintain session state across requests. This prevents compatibility with MCP servers using the new NodeStreamableHTTPServerTransport (added to MCP SDK in January 2026).

Context

Timeline:

  • September 2025: MetaMCP last updated in this area
  • January 2026: Anthropic added NodeStreamableHTTPServerTransport to official MCP SDK
  • This new transport requires clients to send mcp-session-id header on subsequent requests

Current Behavior:
When connecting to a server using NodeStreamableHTTPServerTransport:

  1. ✅ Initial initialize request succeeds
  2. ❌ Subsequent requests (e.g., notifications/initialized) fail with "Bad Request: No valid session ID provided"
  3. ❌ MetaMCP doesn't include mcp-session-id header after initialization

Root Cause

MetaMCP's createMetaMcpClient() in /apps/backend/src/lib/metamcp/client.ts creates the transport but doesn't track or propagate session IDs:

// Current implementation (lines 108-133)
transport = new StreamableHTTPClientTransport(new URL(transformedUrl), {
  requestInit: { headers }
});
// Session ID is never captured or reused

## Proposed Solution
File: [client.ts](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)

## Changes Required:

Store session ID after connection
Include mcp-session-id header in subsequent requests
Clear session ID on connection close

### Implementation Example:


// Add session tracking (module-level or per-server)
const sessionIds = new Map<string, string>();

export const createMetaMcpClient = (
  serverParams: ServerParameters,
): { client: Client | undefined; transport: Transport | undefined } => {
  // ... existing code ...

  } else if (serverParams.type === "STREAMABLE_HTTP" && serverParams.url) {
    const transformedUrl = transformDockerUrl(serverParams.url);
    const headers: Record<string, string> = {
      ...(serverParams.headers || {}),
    };

    const authToken =
      serverParams.oauth_tokens?.access_token || serverParams.bearerToken;
    if (authToken) {
      headers["Authorization"] = `Bearer ${authToken}`;
    }

    // *** NEW: Include session ID if we have one ***
    const storedSessionId = sessionIds.get(serverParams.name);
    if (storedSessionId) {
      headers['mcp-session-id'] = storedSessionId;
    }

    transport = new StreamableHTTPClientTransport(new URL(transformedUrl), {
      requestInit: { headers },
    });

    // *** NEW: Capture session ID after initialization ***
    const originalStart = transport.start.bind(transport);
    transport.start = async () => {
      await originalStart();
      // Access the session ID from the transport (available after connection)
      const sessionId = (transport as any).sessionId;
      if (sessionId) {
        sessionIds.set(serverParams.name, sessionId);
        console.log(`Session ${sessionId} established for ${serverParams.name}`);
      }
    };

    // *** NEW: Clean up session ID on close ***
    const originalClose = transport.close.bind(transport);
    transport.close = async () => {
      sessionIds.delete(serverParams.name);
      await originalClose();
    };
  }

## Benefits
-Standards Compliance: Aligns with latest MCP SDK (Jan 2026)
-Performance: Enables use of more efficient hybrid transport servers
-Future-Proof: Compatible with upcoming MCP server implementations
-Backward Compatible: SSE and STDIO transports unaffected

## Testing
-Tested against a server using NodeStreamableHTTPServerTransport:

-Without fix: Connection fails on second request

-With fix: Full session maintained, sub-10ms performance

## Impact
-Estimated Lines of Code: ~30 lines
-Breaking Changes: None
-Files Modified: 1 file ([client.ts](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html))
Related

## Official MCP SDK: [https://github.com/modelcontextprotocol/typescript-sdk](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)

## Example hybrid server: [https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/standaloneSseWithGetStreamableHttp.ts](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)

## Environment:

MetaMCP Version: September 2025
MCP SDK: @modelcontextprotocol/sdk (latest)
Node: 20+

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions