Skip to content

Integrate mcp-gateway (awmg) with awf firewall using MCP streamable-http protocol #7429

Description

@Mossaka

Summary

Enable MCP servers running on the host to be accessible by agents running inside the awf (aw-firewall) container through the mcp-gateway, using the standard MCP streamable-http protocol.

Current State

mcp-gateway (awmg) currently:

  • Exposes custom HTTP endpoints: /health, /servers, /mcp/{server} (POST)
  • Uses simple JSON-RPC over HTTP, NOT the standard MCP streamable-http protocol
  • Only supports command/stdio transport for backend MCP servers
  • Has config rewriting that generates http://host.docker.internal:PORT/mcp/{server} URLs

awf (firewall) currently:

  • Runs agents in isolated containers on dedicated network (172.30.0.0/24)
  • Routes HTTP traffic through Squid proxy with domain whitelisting
  • Allows localhost traffic (stdio MCP servers work automatically)
  • Supports host.docker.internal for accessing host services

The Problem

The current gateway's /mcp/{server} endpoint is a custom JSON POST API, not a proper MCP streamable-http endpoint. An MCP client using StreamableClientTransport (which is the standard way to connect via HTTP) won't be able to communicate with it correctly.

Proposed Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        HOST MACHINE                              │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │              mcp-gateway (awmg)                           │   │
│  │                                                           │   │
│  │   HTTP Server (:8080)                                     │   │
│  │   └── /mcp  ← StreamableHTTPHandler (NEW)                │   │
│  │       └── routes to appropriate backend MCP server        │   │
│  │                                                           │   │
│  │   Backend MCP Sessions (stdio):                           │   │
│  │   ├── gh-aw mcp-server                                    │   │
│  │   ├── filesystem server                                   │   │
│  │   └── other MCP servers...                                │   │
│  └──────────────────────────────────────────────────────────┘   │
│                           ▲                                      │
│                           │ host.docker.internal:8080            │
│  ┌────────────────────────┼─────────────────────────────────┐   │
│  │        awf container   │                                  │   │
│  │                        │                                  │   │
│  │   Agent (Copilot/Claude/Codex)                            │   │
│  │   └── MCP Client                                          │   │
│  │       └── StreamableClientTransport                       │   │
│  │           Endpoint: http://host.docker.internal:8080/mcp  │   │
│  │                                                           │   │
│  └───────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

Required Changes

1. Gateway: Add StreamableHTTPHandler endpoint

The gateway needs to expose a proper MCP streamable-http endpoint. The go-sdk already has StreamableHTTPHandler available (used in mcp_server.go:762-794):

handler := mcp.NewStreamableHTTPHandler(func(req *http.Request) *mcp.Server {
    // Route based on request path or header to appropriate backend
    return server
}, &mcp.StreamableHTTPOptions{
    SessionTimeout: 2 * time.Hour,
})

The gateway would need to:

  1. Create a virtual "aggregated" MCP server that combines tools from all backends
  2. Expose this via StreamableHTTPHandler on /mcp
  3. When tools are called, route to the appropriate backend MCP session

2. Tool Aggregation Strategy

Two approaches to consider:

Option A - Namespace prefixing: Tools from different servers get prefixed (e.g., gh-aw:list_files, filesystem:read_file)

Option B - Flat namespace: All tools exposed without prefix, conflicts resolved by priority

3. Agent Configuration

The agent inside awf would use a single MCP config entry:

{
  "mcpServers": {
    "gateway": {
      "url": "http://host.docker.internal:8080/mcp"
    }
  }
}

4. Firewall Whitelist

host.docker.internal needs to be whitelisted in the Squid proxy configuration for the awf container.

Benefits

  1. Security: MCP servers run on host with full access; agent runs in restricted container
  2. Simplicity: Agent sees single MCP endpoint with all tools aggregated
  3. Standard Protocol: Uses proper MCP streamable-http, not custom API
  4. Existing Patterns: Both StreamableClientTransport and StreamableHTTPHandler are already used in the codebase

Implementation Scope

Main work in pkg/cli/mcp_gateway_command.go:

  1. Create an aggregated mcp.Server that combines tools from all backend sessions
  2. Replace current HTTP handlers with StreamableHTTPHandler
  3. Implement tool routing logic to dispatch calls to appropriate backends

References

  • Existing StreamableHTTPHandler usage: pkg/cli/mcp_server.go:762-794
  • Existing StreamableClientTransport usage: pkg/cli/mcp_inspect_mcp.go:238-276
  • Current gateway implementation: pkg/cli/mcp_gateway_command.go
  • Gateway spec: specs/mcp-gateway.md

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions