# MCP Server Hub
Aggregate and proxy multiple [Model Context Protocol](https://modelcontextprotocol.io) servers into unified Streamable HTTP endpoints. Connect to any combination of stdio, SSE, and Streamable HTTP MCP servers and expose them through a single HTTP interface.
## Features
- **Aggregate endpoint** (`/mcp`) — merges tools, resources, and prompts from all configured servers
- **Individual server endpoints** (`/mcp/server/:id`) — proxy a single downstream server
- **Group endpoints** (`/mcp/group/:id`) — proxy a logical group of servers
- **Namespacing** — tools are prefixed as `serverId__toolName` to avoid collisions
- **Per-endpoint auth** — optional bearer token authentication on any endpoint
- **Auto-reconnect** — exponential backoff with jitter for dropped connections
- **Health monitoring** — `GET /health` shows connection status for all servers
- **Transport support** — stdio, SSE, and Streamable HTTP downstream transports
## Requirements
- Node.js >= 18
## Install
```bash
npm install
npm run build
```
## Usage
```bash
# Start with a config file
node dist/index.js --config config.json
# Override the port
node dist/index.js --config config.json --port 8080
# Development mode (no build step)
npm run dev -- --config config.json
```
## Configuration
Create a `config.json` file (see `config.example.json` for a working example):
```json
{
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
"transport": "stdio"
},
"remote-api": {
"url": "http://localhost:3001/mcp",
"transport": "streamable-http",
"headers": { "Authorization": "Bearer token" }
},
"legacy": {
"url": "http://localhost:3002/sse",
"transport": "sse"
}
},
"groups": {
"dev-tools": ["filesystem", "remote-api"]
},
"hub": {
"port": 7070,
"auth": {
"default": { "enabled": false },
"endpoints": {
"/mcp": { "enabled": true, "token": "my-secret-token" }
}
},
"reconnect": {
"maxAttempts": 3,
"initialDelayMs": 1000,
"maxDelayMs": 30000
}
}
}
```
### Server transports
| Transport | Required fields | Optional fields |
|-----------|----------------|-----------------|
| `stdio` | `command` | `args`, `env` |
| `streamable-http` | `url` | `headers` |
| `sse` | `url` | `headers` |
### Groups
Groups define named subsets of servers. Each group lists server IDs that must exist in the `servers` section.
### Auth
Auth can be configured per-endpoint or as a default. When `enabled` is `true`, requests must include an `Authorization: Bearer <token>` header.
Endpoint-specific config takes priority over the default. Prefix matching is used for parameterized routes (e.g., a rule for `/mcp` also covers `/mcp/server/filesystem`).
### Reconnect
Controls automatic reconnection to downstream servers when connections drop:
- `maxAttempts` — maximum retry attempts before giving up (default: 3)
- `initialDelayMs` — base delay for exponential backoff (default: 1000)
- `maxDelayMs` — maximum delay cap (default: 30000)
## Endpoints
| Method | Path | Description |
|--------|------|-------------|
| POST | `/mcp` | Send MCP requests to all servers (aggregated) |
| GET | `/mcp` | SSE stream for server-to-client notifications |
| DELETE | `/mcp` | Close the session |
| POST | `/mcp/server/:id` | Send MCP requests to a single server |
| GET | `/mcp/server/:id` | SSE stream for a single server |
| DELETE | `/mcp/server/:id` | Close the session |
| POST | `/mcp/group/:id` | Send MCP requests to a server group |
| GET | `/mcp/group/:id` | SSE stream for a server group |
| DELETE | `/mcp/group/:id` | Close the session |
| GET | `/health` | Health check with per-server connection status |
All MCP endpoints use the [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http). Sessions are tracked via the `mcp-session-id` header.
## Examples
### Health check
```bash
curl http://localhost:7070/health
```
```json
{
"status": "healthy",
"servers": [
{
"id": "filesystem",
"connected": true,
"tools": 14,
"resources": 0,
"prompts": 0
}
]
}
```
### MCP initialize
```bash
curl -X POST http://localhost:7070/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "my-client", "version": "1.0.0" }
}
}'
```
### List tools (with session)
```bash
curl -X POST http://localhost:7070/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: <session-id-from-initialize>" \
-d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}'
```
Tools are namespaced with the server ID:
```
filesystem__read_file
filesystem__write_file
filesystem__list_directory
...
```
### Call a tool
```bash
curl -X POST http://localhost:7070/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: <session-id>" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "filesystem__list_directory",
"arguments": { "path": "/tmp" }
}
}'
```
### Authenticated request
```bash
curl -X POST http://localhost:7070/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer my-secret-token" \
-d '{ ... }'
```
## Project Structure
```
src/
├── index.ts # CLI entry point (Commander)
├── types.ts # Shared TypeScript types
├── config/
│ ├── schema.ts # Zod schemas + inferred types
│ ├── loader.ts # Load and parse JSON config
│ └── validator.ts # Semantic validation
├── client/
│ ├── manager.ts # ServerManager — lifecycle of all connections
│ ├── connection.ts # ClientConnection — single MCP client wrapper
│ ├── transports.ts # Transport factory (stdio/SSE/HTTP)
│ └── reconnect.ts # Exponential backoff with jitter
├── server/
│ ├── proxy.ts # Creates MCP Server delegating to Aggregator
│ ├── aggregator.ts # Merges tools/resources/prompts, routes calls
│ └── namespacing.ts # serverId__toolName prefix utilities
├── http/
│ ├── app.ts # Express app setup
│ ├── router.ts # MCP endpoint routes + session management
│ ├── auth.ts # Per-endpoint bearer token middleware
│ └── health.ts # GET /health endpoint
└── utils/
├── logger.ts # Winston logger
├── errors.ts # Custom error classes
└── shutdown.ts # Graceful SIGINT/SIGTERM handling
```
## License
MIT