Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Gw

FreeNot checked

Aggregates multiple MCP servers into a single HTTP endpoint with tool namespacing, dashboard, and REST API for management.

GitHubEmbed

About

Aggregates multiple MCP servers into a single HTTP endpoint with tool namespacing, dashboard, and REST API for management.

README

An aggregating MCP (Model Context Protocol) gateway that runs as a Docker container. Claude Code connects to one endpoint and gets tools from all registered backend MCP servers combined.

Architecture

Claude Code
    │  HTTP MCP (localhost:3000/mcp)
    ▼
┌─────────────────────────────┐
│       mcp-gateway           │  ← Alpine Docker container
│  ┌─────────┐ ┌──────────┐  │
│  │ MCP srv │ │Dashboard │  │
│  └────┬────┘ └──────────┘  │
│       │  mcp-gw-net         │
└───────┼─────────────────────┘
        │
        ├── backend-a:8080          ← Docker container on mcp-gw-net
        ├── backend-b:8080          ← Docker container on mcp-gw-net
        └── 10.10.7.22:8123        ← External host (e.g. Home Assistant)

Tools from all backends are aggregated and namespaced as backend__tool_name so Claude Code sees them in a single tool list.

Quick Start

git clone https://github.com/gpimthong/mcp-gw
cd mcp-gw
docker compose up -d

The gateway starts on port 3000.

Connect Claude Code

Add to ~/.mcp.json:

{
  "mcpServers": {
    "gateway": {
      "type": "http",
      "url": "http://localhost:3000/mcp"
    }
  }
}

Restart Claude Code to pick up the change.

Homelab Deployment

First-time setup on a new machine

config/backends.json is gitignored (to prevent credentials from leaking). You must create it manually on every fresh clone:

mkdir -p config
cat > config/backends.json << 'EOF'
{
  "backends": []
}
EOF

Then start the gateway and register your backends via the dashboard or API.

Persistent config across restarts

The config/ directory is bind-mounted into the container (see docker-compose.yml). Any backends you register via the dashboard or API are written to config/backends.json on the host and survive container restarts automatically.

Network topology

The gateway runs on a dedicated bridge network mcp-gw-net (172.22.0.0/16). Backend containers must be on this network. External hosts (like Home Assistant on your LAN) are reachable directly by IP — no network joining needed for those.

Adding Backend MCP Servers

Docker container backends

The container must be on the mcp-gw-net network:

# Attach an existing container
docker network connect mcp-gw-net <container-name>

# Or start a new backend directly on the network
docker run -d --name my-mcp --network mcp-gw-net my-mcp-image

Then register it:

curl -X POST http://localhost:3000/api/backends \
  -H 'Content-Type: application/json' \
  -d '{"name":"my-mcp","url":"http://my-mcp:8080/mcp","transport":"http","enabled":true}'

External host backends

Backends that run outside Docker (on a LAN IP, VM, or another machine) are registered by IP. No network setup needed — the gateway container reaches them over the host network.

curl -X POST http://localhost:3000/api/backends \
  -H 'Content-Type: application/json' \
  -d '{"name":"my-service","url":"http://192.168.1.50:8080/mcp","transport":"http","enabled":true}'

Authenticated backends (Bearer token / custom headers)

Pass a headers object to send arbitrary HTTP headers on every request to a backend. This is how you authenticate against services like Home Assistant:

curl -X POST http://localhost:3000/api/backends \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "my-service",
    "url": "http://192.168.1.50:8080/mcp",
    "transport": "sse",
    "enabled": true,
    "headers": {
      "Authorization": "Bearer <your-token-here>"
    }
  }'

The headers field is optional. Without it, requests are sent unauthenticated.

Home Assistant Setup

Home Assistant's built-in MCP server is exposed at /mcp_server/sse and uses SSE transport. It requires a long-lived access token.

Get a long-lived access token

  1. Open Home Assistant → Profile (bottom-left avatar)
  2. Scroll to Long-Lived Access TokensCreate Token
  3. Give it a name (e.g. mcp-gateway) and copy the token

Register Home Assistant as a backend

curl -X POST http://localhost:3000/api/backends \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "homeassistant",
    "url": "http://<HA_IP>:8123/mcp_server/sse",
    "transport": "sse",
    "enabled": true,
    "headers": {
      "Authorization": "Bearer <your-long-lived-token>"
    }
  }'

Replace <HA_IP> with your Home Assistant host IP (e.g. 10.10.7.22) and <your-long-lived-token> with the token from the previous step.

Resulting tools in Claude Code

Once connected, Home Assistant tools appear namespaced as homeassistant__<tool>, for example:

  • homeassistant__HassTurnOn
  • homeassistant__HassTurnOff
  • homeassistant__HassSetVolume

Dashboard

The dashboard at http://localhost:3000 shows:

  • Backend cards — connection status, tool count, latency, last ping
  • Live request log — real-time stream of every tool call with duration and status
  • Tool catalog — all tools from all backends, expandable by backend

Use the + Add Backend button to register new backends, or click Remove / Reconnect on each card.

REST API

Method Path Description
GET /api/health Status, version, uptime, tool count
GET /api/backends List all backends with state
POST /api/backends Add / reconnect a backend
DELETE /api/backends/:name Remove a backend
POST /api/backends/:name/reconnect Force reconnect
GET /api/logs Last 200 log entries
GET /api/logs/stream SSE live log stream

Tool Namespacing

Backend memory with tool search_notes → exposed to Claude Code as memory__search_notes.

The double-underscore separator avoids collisions between backends. The gateway parses the prefix at call time to route to the right backend client.

Transport Support

Backend transport Config value
MCP Streamable HTTP "transport": "http"
MCP SSE (legacy) "transport": "sse"

Home Assistant uses SSE. Most modern MCP servers use Streamable HTTP.

config/backends.json

This file is gitignored because it typically contains auth tokens. It is created and maintained at runtime by the gateway — every dashboard or API action writes through to this file.

Template for a fresh deployment:

{
  "backends": []
}

Start from the empty template and re-register your backends after cloning. The config/ directory is bind-mounted so the file persists across docker compose down / up cycles.

Development

Requires Node.js 22+.

npm install
npm run dev        # tsx hot-reload
npm run build      # compile TypeScript → dist/

Config is read from ./config/backends.json in dev mode (set CONFIG_PATH env var to override).

Versioning

Version is set in package.json. The gateway advertises it in the MCP initialize handshake and at /api/health. Git tags follow v<semver> (e.g. v0.1.0).

Project Structure

src/
  index.ts            — entry point
  version.ts          — reads version from package.json
  types.ts            — shared interfaces
  logger.ts           — circular log buffer + SSE emitter
  config.ts           — load/save backends.json
  backend-registry.ts — MCP client connections + heartbeat
  tool-aggregator.ts  — namespace + route tool calls
  mcp-server.ts       — MCP HTTP server (session management)
  dashboard.ts        — Express app (REST API + SSE)
public/
  index.html          — dashboard SPA (no build step)
config/
  backends.json       — persisted backend definitions (gitignored)

from github.com/gpimthong/mcp-gw

Installing Gw

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/gpimthong/mcp-gw

FAQ

Is Gw MCP free?

Yes, Gw MCP is free — one-click install via Unyly at no cost.

Does Gw need an API key?

No, Gw runs without API keys or environment variables.

Is Gw hosted or self-hosted?

A hosted option is available: Unyly runs the server in the cloud, no local setup required.

How do I install Gw in Claude Desktop, Claude Code or Cursor?

Open Gw on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Gw with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All development MCPs