Roadmap
Planned features and architectural improvements for MCP Gateway.
Env Hash Process Pooling (Planned)
Process pooling based on env hash, not workspace/project names. Two requests with identical resolved environment variables share the same server process.
Core Concept
process_key = hash(server_name + resolved_env)
| Scenario | Result |
|---|---|
| Same env hash | Share process |
| Different env hash | Different process |
| Workspace/project name | Irrelevant at runtime |
Example
Workspace A: /work/client-a → {SUPABASE_URL: prod, TOKEN: xxx}
Workspace B: /work/client-b → {SUPABASE_URL: prod, TOKEN: xxx}
→ Same hash, share process
Workspace C: /work/staging → {SUPABASE_URL: staging, TOKEN: yyy}
→ Different hash, different process
Config Resolution
- Request with
cwd - Load
.envfrom cwd (walk up if needed) - Check
MCP_PROFILEenv var - Load profile config if set
- Merge env (highest priority first):
- cwd
.env - Profile
env:block ~/.mcp-gateway/.env- System env
- cwd
- Hash resolved env for process key
Forced Isolation
For servers needing per-cwd isolation:
servers:
filesystem:
scope: isolated
Key becomes: hash(server_name + resolved_env + cwd)
Capability Filtering (Planned)
Currently, capabilities are tags on profiles. A future enhancement will add strict include/exclude filtering on profiles:
profiles:
# Whitelist mode - only these servers accessible
restricted:
capabilities:
include: [supabase, docs]
# Blacklist mode - all except these
developer:
capabilities:
exclude: [production-db]
# No restrictions (current behavior)
admin:
# No capabilities block = access to all
Filtering Logic
if profile.capabilities.include:
# Whitelist - only listed servers
allowed = server_name in include
elif profile.capabilities.exclude:
# Blacklist - all except listed
allowed = server_name not in exclude
else:
# No restrictions
allowed = true
Use Cases
| Profile | Configuration | Effect |
|---|---|---|
contractor | include: [docs, github] | Can only access docs and github servers |
developer | exclude: [production-db] | Access to everything except production database |
admin | (no capabilities block) | Full access |
Client Mode (Planned)
A separate mcpg-client package for connecting to a remote gateway over the network. For team/enterprise scenarios where a central gateway is shared.
Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Claude Code │ ───── │ mcpg-client │ ───── │ mcpg (remote) │
│ │ local │ localhost:8989 │ mTLS │ gateway.co:8989│
└─────────────────┘ └─────────────────┘ └─────────────────┘
Local HTTP proxy Central gateway
with auth certs with MCP servers
When to Use
| Scenario | What to run |
|---|---|
| Local dev (solo) | mcpg start - full gateway locally |
| Team/network | mcpg-client locally → connects to shared remote gateway |
Client Responsibilities
- Run local HTTP server (same MCP interface as gateway)
- Authenticate to remote gateway with certs/API keys
- Fetch server list/config from remote
- Proxy MCP requests through to remote
- Pass project env context from local
.env
Local Configuration
Project directory structure:
/work/my-project/
├── .env # Project env vars (passed to gateway)
├── .mcpg/
│ ├── config.yaml # Gateway URL, auth method
│ ├── client.crt # Client cert (mTLS)
│ └── client.key
└── src/
Example .mcpg/config.yaml:
gateway:
url: https://gateway.company.com:8989
auth:
type: mtls
cert: ./client.crt
key: ./client.key
# Or with bearer token
gateway:
url: https://gateway.company.com:8989
auth:
type: bearer
token: ${GATEWAY_TOKEN}
Claude Code Configuration
{
"mcpServers": {
"gateway": {
"command": "npx",
"args": ["-y", "mcpg-client"]
}
}
}
Auto-Detection
The client auto-detects the working directory when spawned:
- Gets cwd from process
- Walks up directories looking for
.mcpg/config.yaml - Loads
.envfrom project root - Passes env context to remote gateway
Process Pooling
The remote gateway handles process pooling using env hash:
- Two clients with same resolved env share processes on the gateway
- Different env = different processes
scope: isolatedon a server forces per-cwd isolation
Security
- mTLS preferred for production
- Client certs issued per-developer or per-project
- Gateway validates client cert before accepting requests
- Project env vars are trusted (client-side)
Workspace-Based Profile Resolution (Planned)
Workspaces map directory paths to profile lists. Agents never pass project or profile parameters - resolution is automatic via cwd.
Profile Structure
Profiles bundle env and servers:
profiles:
supabase-prod:
env:
SUPABASE_ACCESS_TOKEN: ${P1_SUPABASE_ACCESS_TOKEN}
SUPABASE_PROJECT_REF: prod-abcd1234
servers:
supabase:
url: https://mcp.supabase.com/mcp?project_ref=${SUPABASE_PROJECT_REF}
auth:
type: bearer
token: ${SUPABASE_ACCESS_TOKEN}
github-work:
env:
GITHUB_TOKEN: ${WORK_GITHUB_TOKEN}
servers:
github:
transport: stdio
command: npx
args: [-y, "@modelcontextprotocol/server-github"]
Workspace Configuration
Workspaces map cwd paths to profile lists (order matters - later overrides earlier):
workspaces:
/work/my-app:
profiles: [supabase-prod, github-work]
/work/staging-app:
profiles: [supabase-staging, github-work]
Tool Calls
No project or profile parameter. Only cwd:
list_servers(cwd="/work/my-app")
call(server="supabase", tool="query", cwd="/work/my-app")
The gateway:
- Matches cwd to workspace (longest prefix)
- Loads profiles in order
- Merges env and servers
- Applies cwd
.envoverrides (highest priority)
Env Resolution Order
| Priority | Source |
|---|---|
| 1 (highest) | cwd .env |
| 2 | Profile env: block |
| 3 | ~/.mcp-gateway/.env |
| 4 (lowest) | System environment |
Process Pooling
Process identity based on env hash, not workspace name:
process_key = hash(server_name + resolved_env)
Two workspaces with identical resolved env share the same server process.