Skip to content

Improve HttpSseFilterChainFactory: Cannot Configure HTTP Path and Host for MCP Servers #177

@bettercallsaulj

Description

@bettercallsaulj

Issue Details

Problem Description

The HttpSseFilterChainFactory had hardcoded values for HTTP path and host, making it impossible to:

  1. Connect to different endpoints: All clients were forced to use /rpc path, but MCP servers may use /sse, /events, /api/mcp, etc.
  2. Specify actual server host: The host was hardcoded to localhost, preventing connections to remote MCP servers
  3. Configure per-connection settings: Each factory instance couldn't be customized for different server configurations

Root Cause

The factory constructor only accepted three parameters:

HttpSseFilterChainFactory(event::Dispatcher& dispatcher,
                          McpProtocolCallbacks& message_callbacks,
                          bool is_server = true)

The http_path and http_host values used by the internal filters were either:

  • Hardcoded as /rpc and localhost
  • Not passed through from the factory to the filter chain

Technical Flow (Before Fix)

// User wants to connect to https://mcp.example.com/api/sse
auto factory = std::make_shared<HttpSseFilterChainFactory>(
    dispatcher, callbacks, false /* client mode */);

// Factory creates filters with hardcoded values:
// - http_path_ = "/rpc"      ❌ Should be "/api/sse"
// - http_host_ = "localhost" ❌ Should be "mcp.example.com"

// Result: Client sends request to wrong endpoint
// GET /rpc HTTP/1.1
// Host: localhost

Technical Flow (After Fix)

// User wants to connect to https://mcp.example.com/api/sse
auto factory = std::make_shared<HttpSseFilterChainFactory>(
    dispatcher, callbacks,
    false,              // client mode
    "/api/sse",         // custom path ✅
    "mcp.example.com"); // custom host ✅

// Factory creates filters with configured values
// Result: Client sends correct request
// GET /api/sse HTTP/1.1
// Host: mcp.example.com

How to Reproduce

Prerequisites

  • gopher-mcp client code
  • An MCP server running on a non-default endpoint

Steps to Reproduce

1. Create factory for remote MCP server:

#include "mcp/filter/http_sse_filter_chain_factory.h"

// Before fix: No way to specify path/host
auto factory = std::make_shared<HttpSseFilterChainFactory>(
    dispatcher, callbacks, false /* client mode */);
// Path is hardcoded to "/rpc"
// Host is hardcoded to "localhost"

2. Attempt to connect to server at different endpoint:

// Server is at: https://mcp.example.com:8080/api/events
// But factory will send requests to: /rpc on localhost

3. Observe the failure:

Expected HTTP Request:
  GET /api/events HTTP/1.1
  Host: mcp.example.com:8080

Actual HTTP Request:
  GET /rpc HTTP/1.1
  Host: localhost

Result: Connection fails or connects to wrong server

Minimal Reproduction Test

TEST(HttpSseFilterChainFactory, CannotConfigureEndpoint) {
  MockMcpCallbacks callbacks;
  auto dispatcher = createDispatcher();

  // Before fix: Constructor doesn't accept path/host
  // This would fail to compile or use wrong defaults
  auto factory = std::make_shared<HttpSseFilterChainFactory>(
      *dispatcher, callbacks, false, "/sse", "server.example.com:8080");

  // Before fix: Compilation error - constructor only takes 3 params
  // After fix: Compiles and uses custom path/host
}

TEST(HttpSseFilterChainFactory, DefaultsToWrongEndpoint) {
  MockMcpCallbacks callbacks;
  auto dispatcher = createDispatcher();

  auto factory = std::make_shared<HttpSseFilterChainFactory>(
      *dispatcher, callbacks, false);

  // Create filter chain and inspect HTTP requests
  // Before fix: Uses /rpc and localhost regardless of server
  // After fix: Uses configurable defaults, can be overridden
}

Key Changes in the Fix

Component Change
Constructor signature Added http_path and http_host parameters with defaults
Member variables Added http_path_ and http_host_ storage
Include directive Added <string> for std::string member types
Default values http_path="/rpc", http_host="localhost" for backward compatibility

Updated Constructor

// Before
HttpSseFilterChainFactory(event::Dispatcher& dispatcher,
                          McpProtocolCallbacks& message_callbacks,
                          bool is_server = true)

// After
HttpSseFilterChainFactory(event::Dispatcher& dispatcher,
                          McpProtocolCallbacks& message_callbacks,
                          bool is_server = true,
                          const std::string& http_path = "/rpc",
                          const std::string& http_host = "localhost")

Usage Examples

// Server mode with defaults
auto server_factory = std::make_shared<HttpSseFilterChainFactory>(
    dispatcher, callbacks, true);

// Client mode for Claude Desktop MCP
auto claude_factory = std::make_shared<HttpSseFilterChainFactory>(
    dispatcher, callbacks, false, "/sse", "localhost:3000");

// Client mode for remote MCP server
auto remote_factory = std::make_shared<HttpSseFilterChainFactory>(
    dispatcher, callbacks, false, "/api/events", "mcp.example.com:8080");

// Client mode for custom enterprise MCP
auto enterprise_factory = std::make_shared<HttpSseFilterChainFactory>(
    dispatcher, callbacks, false, "/v2/mcp/stream", "internal.corp.com:443");

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