Skip to content

HTTP/SSE Filter Chain Lacks Extensibility #204

@bettercallsaulj

Description

@bettercallsaulj

What's the Issue

The HttpSseFilterChainFactory was a closed system with no extension points for customization. This created two significant limitations:

Issue 1: Cannot Add Custom Filters Before Protocol Handling

There was no way to inject custom filters (e.g., authentication, logging, rate limiting) that run before the HTTP/SSE/JSON-RPC protocol filters. Users who needed to validate JWT tokens or log requests had no integration point.

// Desired: Add auth filter that runs before protocol filters
McpServerConfig config;
config.??? = auth_filter;  // No API exists!

// The filter chain was hardcoded:
// [TCP] → [Metrics] → [HTTP/SSE/JSON-RPC] → [Application]
//
// Needed:
// [TCP] → [Auth] → [Metrics] → [HTTP/SSE/JSON-RPC] → [Application]

Issue 2: Cannot Register Custom HTTP Endpoints

There was no way to register custom HTTP routes (e.g., OAuth discovery endpoints, custom health checks) when the filter chain is created. The HttpRoutingFilter was internal and inaccessible.

// Desired: Register OAuth discovery endpoint
// GET /.well-known/oauth-protected-resource
//
// But HttpRoutingFilter is created internally with no access point

How to Reproduce

Reproducing Issue 1 (No Custom Filter Support):

  1. Create an MCP server that requires OAuth authentication:

    auto auth_filter = std::make_shared<OAuthAuthFilter>(auth_client, config);
    
    McpServerConfig server_config;
    // No way to add auth_filter to the filter chain!
    // server_config.??? = auth_filter;  // API doesn't exist
    
    auto server = createMcpServer(server_config);
  2. Expected: Ability to add custom filters before protocol handling

  3. Actual: No API available; must fork and modify the library

Reproducing Issue 2 (No Custom Route Registration):

  1. Try to add OAuth discovery endpoint:

    McpServerConfig server_config;
    // Need to register: GET /.well-known/oauth-protected-resource
    // But no access to HttpRoutingFilter during setup!
    
    auto server = createMcpServer(server_config);
    // Too late - filter chain already created
  2. Expected: Callback to register custom routes during filter chain creation

  3. Actual: No API available; internal routes only

How to Fix

Fix 1: Add Filter Factory Support

Add filter_factories to allow custom filters before protocol filters, using the existing FilterFactoryCb pattern for consistency:

In http_sse_filter_chain_factory.h:

class HttpSseFilterChainFactory {
 public:
  // Add filter factory (follows existing FilterFactoryCb pattern)
  void addFilterFactory(network::FilterFactoryCb factory) {
    filter_factories_.push_back(std::move(factory));
  }

  const std::vector<network::FilterFactoryCb>& getFilterFactories() const {
    return filter_factories_;
  }

 private:
  std::vector<network::FilterFactoryCb> filter_factories_;
};

In mcp_server.h (McpServerConfig):

struct McpServerConfig {
  // Filter factories for custom filters (auth, logging, etc.)
  std::vector<network::FilterFactoryCb> filter_factories;
};

Usage:

auto auth_filter = std::make_shared<OAuthAuthFilter>(auth_client, config);

McpServerConfig server_config;
server_config.filter_factories.push_back([auth_filter]() {
  return auth_filter;
});

Fix 2: Add Route Registration Callback

Add callback type and setter for custom route registration:

In http_sse_filter_chain_factory.h:

// Callback type for registering custom HTTP routes
using HttpRouteRegistrationCallback = std::function<void(HttpRoutingFilter*)>;

class HttpSseFilterChainFactory {
 public:
  void setRouteRegistrationCallback(HttpRouteRegistrationCallback callback) {
    route_registration_callback_ = std::move(callback);
  }

 private:
  HttpRouteRegistrationCallback route_registration_callback_;
};

In filter chain creation - invoke callback:

// In setupRoutingHandlers() or createFilterChain()
if (route_registration_callback_) {
  route_registration_callback_(routing_filter_.get());
}

Usage:

McpServerConfig server_config;
server_config.route_registration_callback = [](HttpRoutingFilter* router) {
  // Register OAuth discovery endpoint
  router->registerHandler("GET", "/.well-known/oauth-protected-resource",
    [](const RequestContext& req) {
      Response resp;
      resp.status_code = 200;
      resp.headers["content-type"] = "application/json";
      resp.body = R"({"resource": "mcp-server", "scopes": ["read", "write"]})";
      return resp;
    });
};

Files changed:

  • include/mcp/filter/http_sse_filter_chain_factory.h - Add extensibility APIs
  • include/mcp/server/mcp_server.h - Add config options
  • src/filter/http_sse_filter_chain_factory.cc - Invoke factories and callbacks
  • src/server/mcp_server.cc - Apply config to factory

Tests added:

  • tests/filter/test_http_sse_filter_chain_factory.cc
    • AddFilterFactory - Verifies filter factory registration
    • MultipleFilterFactories - Verifies order preservation
    • RouteRegistrationCallback - Verifies callback setter
    • RouteCallbackInvokedOnCreate - Verifies callback invocation during chain creation

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