OpenZiti / MCP-Gateway
OfficialProvides integration with GitHub, enabling tools such as create_issue through a GitHub MCP backend.
MCP Gateway
Zero-trust access to MCP tools over OpenZiti
MCP Gateway lets AI assistants securely access internal tools without exposing public endpoints. Built on OpenZiti, zrok, and Agora, it provides cryptographically secure, zero trust connectivity with no attack surface.
MCP Gateway is sponsored by NetFoundry as part of its portfolio of solutions for secure workloads and agentic computing. NetFoundry is the creator of OpenZiti and zrok.
The Trifecta
Three simple components that work together:
Component | Purpose |
mcp-tools | Connects MCP clients to remote zrok shares or Agora tunnels (stdio or HTTP) |
mcp-gateway | Aggregates multiple backends into one secure endpoint over zrok and/or Agora |
mcp-bridge | Exposes a single MCP server to the network over zrok or Agora |
flowchart LR
A[Agent] -->|stdio| B[mcp-tools]
B -->|zrok or Agora| C[mcp-gateway]
C -->|stdio / zrok / Agora / HTTP| D[MCP Servers]
C -->|https| E[Remote MCP APIs]Related MCP server: protect-mcp
Why?
Problem: MCP servers typically run locally via stdio. To access tools on remote machines or share them across a team, you need to expose endpoints—creating security risks. Securing exposed MCP tooling can be complicated.
Solution: MCP Gateway uses OpenZiti's overlay network to create "dark services" that:
Never listen on public IPs
Require cryptographic identity to access
Work through NATs and firewalls without port forwarding
Can publish and serve through Agora catalogs and Layer 1 tunnels
Are incredibly simple to deploy securely
Quick Start
New to MCP Gateway? See the Getting Started Guide for a complete walkthrough.
1. Install
go install github.com/openziti/mcp-gateway/cmd/...@latest2. Enable zrok
Note: mcp-gateway requires zrok
v2.0.xor later. Currently the best release is zrok v2.0.0-rc7
zrok2 enable <your-zrok-token> # get token at https://api-v2.zrok.io3. Run a Gateway
Create config.yml:
aggregator:
name: "my-gateway"
version: "1.0.0"
backends:
- id: filesystem
transport:
type: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/documents"]
- id: github
transport:
type: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_TOKEN: "ghp_xxx"mcp-gateway run config.yml
# outputs: {"share_token":"abc123..."}4. Connect from Agent
Add to agent config:
{
"mcpServers": {
"my-tools": {
"command": "mcp-tools",
"args": ["run", "abc123..."]
}
}
}That's it. Your agent can now use tools from both backends through a single secure connection.
Use Cases
Aggregate Multiple Tool Servers
Combine filesystem, GitHub, database, and custom tools into one connection:
backends:
- id: fs
transport: { type: stdio, command: mcp-server-filesystem, args: ["/data"] }
- id: github
transport: { type: stdio, command: mcp-server-github }
- id: postgres
transport: { type: stdio, command: mcp-server-postgres }Tools are namespaced automatically: fs:read_file, github:create_issue, postgres:query.
Expose a Remote Tool Server
Run mcp-bridge on a remote machine to expose a local MCP server:
# on remote server
mcp-bridge mcp-server-custom --config /etc/custom.yml
# outputs share token
# from anywhere
mcp-tools run <share_token>Chain Bridges and Gateways
Gateway can connect to remote bridges (or other gateways) as backends:
backends:
- id: remote-tools
transport:
type: zrok
share_token: "token-from-bridge"Serve and Discover Through Agora
Gateway, bridge, and tools can use Agora Layer 1 tunnels in addition to zrok. A gateway can serve over zrok and Agora at the same time, publish a catalog advertisement, and connect to backends exposed by mcp-bridge --network=agora.
agora:
enabled: true
serve:
enabled: true
advertisement:
publish: true
backends:
- id: remote-filesystem
transport:
type: agora
agora_tunnel: filesystem-relaymcp-tools run --agora mcp-gateway-engineeringSee Agora Integration for configuration, CLI flags, integration files, and smoke scenarios.
Connect to HTTP and HTTPS MCP Servers
Gateway can aggregate remote MCP servers over HTTP(S), using either Streamable HTTP (the default) or the legacy SSE transport. Set protocol: sse explicitly for an SSE endpoint. type: https is strict and only accepts https:// endpoints. type: http supports both http:// and https://, but plaintext HTTP requires explicit opt-in.
backends:
- id: remote-api
transport:
type: https
endpoint: "https://mcp.example.com/mcp"
headers:
Authorization: "Bearer sk-abc123"
- id: legacy-api
transport:
type: https
endpoint: "https://mcp.internal.corp/sse"
protocol: "sse"
tls:
ca_cert_file: "/etc/ssl/certs/internal-ca.pem"This works alongside stdio and zrok backends — mix and match as needed.
For local development or trusted internal networks, you can opt into plaintext HTTP explicitly:
backends:
- id: local-dev
transport:
type: http
endpoint: "http://localhost:8080/mcp"
allow_insecure: trueHTTP backend clients connect directly and refuse redirects by default. Set allow_environment_proxy: true or allow_redirects: true on the transport only when that wider network behavior is deliberate.
Persistent Shares
By default, mcp-gateway and mcp-bridge create an ephemeral share that disappears when the process exits. Persistent shares are stored server-side in zrok, so a gateway or bridge can stop and restart without changing the share token.
Ephemeral shares are closed and owner-only by default. A bridge can grant other zrok accounts with a repeatable flag:
mcp-bridge --access-grant teammate@example.com mcp-filesystem /dataFor mcp-gateway, put the same account emails under zrok.share.access_grants in the gateway configuration. The creating account does not belong in the list; it already has access.
# create a persistent share with a chosen name
zrok2 create share -s my-gateway
# the chosen name is the share token
# use the token in a gateway config (share_token: my-gateway) or bridge
mcp-gateway run config.yml
mcp-bridge --share-token my-gateway npx -y @modelcontextprotocol/server-filesystem /home/user
# the gateway/bridge can restart and reconnect to the same share
# when done, delete the share
zrok2 delete share my-gatewayIf you omit the name, zrok generates a random token:
zrok2 create share
# outputs the share tokenThe token name must be 3–32 characters, lowercase alphanumeric and hyphens ([a-z0-9-]).
HTTP Transport
All components support HTTP-based MCP transport in addition to stdio.
Serve via HTTP with mcp-tools:
# expose a zrok share as a local HTTP server
mcp-tools http <share_token> --bind 127.0.0.1:8080
# expose an Agora tunnel as a local HTTP server
mcp-tools http --agora <tunnel> --bind 127.0.0.1:8080Options:
--stateless- Stateless mode (no session persistence)--json-response- Prefer JSON responses over streamed responses--session-idle-timeout <duration>- Close inactive local sessions after this long; default30m,0disables
The gateway and bridge natively serve MCP over Streamable HTTP through zrok and Agora. Use mcp-tools http when you need a local Streamable HTTP endpoint for clients that don't support the stdio transport provided by mcp-tools directly.
mcp-tools http serves any number of local agents, and each one gets its own MCP session on the remote gateway or bridge. The zrok access or Agora attachment is opened once and held for the life of the process; a fresh fabric session is opened when a local agent connects and closed when it disconnects, its session expires, its initialization fails, or mcp-tools shuts down. Backend state stays isolated per agent, exactly as it is for agents connecting to the gateway directly. In --stateless mode there is no session to own, so a fabric session is opened and closed per request — correct, but a full MCP handshake across the overlay on every call.
On shutdown mcp-tools waits up to five seconds for handshakes still in flight, so sessions completing during shutdown are released rather than stranded. The fabric HTTP clients set no timeout, so that wait is bounded rather than open-ended, and the bound carries an accepted cost: a remote session is created when its initialize request arrives at the gateway or bridge, so a handshake whose response is still in flight when the wait expires leaves a real remote session — holding real backend resources — that mcp-tools can no longer terminate. It logs how many it abandoned, and the far side reclaims them through its own idle expiry. The alternative, waiting indefinitely, would let a wedged overlay hold shutdown open forever.
Inactive Streamable HTTP sessions expire after 30 minutes so a client that disappears without terminating its session cannot retain dedicated backend connections or bridge subprocesses indefinitely. Set session_idle_timeout in gateway YAML, or pass --session-idle-timeout <duration> to mcp-bridge or mcp-tools http, to tune that bound. An explicit 0 disables idle expiry and may retain those resources until the client terminates its session or the server shuts down.
Tool Filtering
Control which tools are exposed per backend:
backends:
- id: filesystem
transport: { type: stdio, command: mcp-server-filesystem }
tools:
mode: allow
list:
- "read_file"
- "list_directory"
# write operations not exposed
- id: github
tools:
mode: deny
list:
- "delete_*"
# everything except delete operationsArchitecture
MCP Gateway creates isolated sessions for each connecting client:
flowchart LR
subgraph Clients
A[Client A]
B[Client B]
end
A --> G[Gateway]
B --> G
subgraph Session A
G --> A1[Backend 1]
G --> A2[Backend 2]
end
subgraph Session B
G --> B1[Backend 1]
G --> B2[Backend 2]
endEach client gets dedicated backend connections—no shared state, no cross-talk.
Building from Source
git clone https://github.com/openziti/mcp-gateway.git
cd mcp-gateway
go build ./cmd/mcp-gateway
go build ./cmd/mcp-bridge
go build ./cmd/mcp-toolsEach binary has a version subcommand that prints build metadata:
mcp-gateway versionLocal builds report a developer build (e.g. v0.1.x [developer build]); release binaries are stamped with the version, commit, build date, branch, and builder.
Documentation
Example Configuration - Fully documented configuration file
Agora Integration - Agora serving, backend connects, mcp-tools dialing, and smoke scenarios
End-to-End Smoke Suite -
make e2e, the hand-run check across all three components
License
Apache 2.0 - see LICENSE
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
A MCP server built for developers enabling Git based project management with project and personal…
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Trust, freshness, policy, and discovery layer for public MCP servers.
Related MCP Servers
- AlicenseBqualityCmaintenanceSecurity gateway that wraps any MCP server with per-tool policies, approval gates, and optional Ed25519-signed decision receipts. Shadow mode logs every tool call without blocking; enforce mode applies block, rate-limit, and minimum-tier rules. Receipts are independently verifiable offline with no accounts needed.558710MIT
- AlicenseNot gradedqualityBmaintenanceSecurity gateway for MCP servers. Wraps any MCP server with per-tool policies (Cedar + JSON), Ed25519-signed decision receipts, human approval gates, and trust tiers. Shadow mode by default — logs everything, blocks nothing.5879MIT
- AlicenseNot gradedqualityDmaintenanceMCPGate aggregates multiple MCP servers into a single unified endpoint, enabling centralized tool management with granular filtering, automatic namespacing, and observability. Features a real-time web dashboard and optional PostgreSQL-backed audit trails for monitoring and controlling AI tool access across local and remote deployments.15Apache 2.0
- AlicenseNot gradedqualityBmaintenanceA least-privilege enforcement proxy for MCP servers. It sits between MCP clients and upstream servers, enforcing tool policies, hiding denied tools, requiring human approval for risky actions, and providing a structured audit trail.MIT