meshmcp
Expose any Model Context Protocol server as a dark service — reachable only over a private WireGuard mesh, zero open ports, a cryptographic identity for every caller. Then enforce what each agent may do, prove what it did, learn a policy from how it behaves, and hand it credentials it never holds.
not a secret
Six layers, one library
Connectivity, identity, policy, proof, intelligence, and reach — each a Go package, each built on the layer below. Adopt as much of the stack as you need; every layer keys off the same cryptographic identity.
One control plane, many MCP servers
meshmcp isn't a server — it's the layer in front of your servers. A filesystem, a web-fetcher, a payments API, a customer database, a deploy pipeline: each a different MCP server, wrapped unmodified. Every call to any of them gets the same identity, policy, audit, and secret injection — and the router unions them into one namespaced endpoint.
Each server keeps its own nature — different tools, transports, risk. The plane gives them a shared spine: one WireGuard identity per caller, one policy language, one tamper-evident ledger, one credential broker.
Give your team a local tool — expose nothing
Run an MCP server behind zero open ports. Teammates reach it over the mesh; an internet scan finds nothing.
Stop prompt injection at the network
Untrusted content taints the session; the privileged tool simply won't route. No jailbreak can talk past a rule the mesh enforces.
Never let PII leave the mesh
Reading customer data labels the session pii; any egress tool refuses to run while that label is present.
Give an agent a key it never sees
The agent writes {{secret:stripe_key}}; the gateway injects the real value by identity. The agent, its logs, its context — none hold it.
Show an auditor exactly what happened
A tamper-evident, signed ledger of every tool call by identity. Provable complete-and-unedited with the public key alone.
Require a human to co-sign money movement
A privileged call is held until a human identity on the mesh approves it. Silence is not permission.
The agent firewall
Policy is declarative and keyed off the WireGuard identity the transport proves — not a header the caller sends. A denied call gets an inline JSON-RPC error; the backend never sees it.
default_allow: false # deny by default rules: # rate-limited reads for everyone - peers: ["*"] tools: ["read_*", "search"] allow: true rate: { max: 30, per: "1m" } # deploy only in business hours - peers: ["pubkey:<agent>"] tools: ["deploy"] allow: true when: { days: [mon,tue,wed,thu,fri], hours: "09:00-17:00", tz: "UTC" } # money movement needs a human co-sign - peers: ["*"] tools: ["transfer_funds"] allow: true require_cosign: true
Taint tracking is prompt-injection defense at the network layer. Mark data-fetching tools as a taint_source and privileged tools as a taint_guard. The moment untrusted content enters a session, the guarded tool stops routing — decided from connection state the model can't influence.
Data-flow labels generalize taint to a lattice: emit_labels: [pii] on a reader, block_labels: [pii] on an egress tool — and PII can never leave the mesh through an agent.
A ledger you can prove to a regulator
Every decision is a hash-chained record. Periodic Ed25519-signed Merkle checkpoints seal the chain, so the log is complete-and-unedited — provable with the public key alone, even against an insider who can write the file.
$ meshmcp audit verify audit.jsonl --checkpoints cps.jsonl --pubkey <key> OK 1240 records, 10 signed checkpoint(s), 1240 records committed non-repudiable: the log is complete and unedited, provable with the public key alone # after a single tampered record: $ meshmcp audit verify audit.jsonl --checkpoints cps.jsonl --pubkey <key> FAILED checkpoint 1 Merkle root mismatch: the records it covers were edited (signed root 0b78259d2e2c… , recomputed d11992f3ae84…) exit 1
Policy that writes itself
A deny-by-default firewall nobody can author is a Ferrari with no steering wheel. insight is the read side: it turns the audit stream into policy, then watches for drift. It's the function AUDIT-RECORD* → POLICY-DSL.
profile
per-identity behavior: tools, rate p99, hours, label flows.
synthesize
a least-privilege policy — exactly what was used, nothing more.
CI gate
replay real traffic through the change; exit ≠ 0 on any regression.
drift → co-sign
deviation from baseline opens a human gate — fail-to-human, not fail-closed.
$ meshmcp insight recommend hist.jsonl > policy.yaml # → pubkey:K granted read_dir, read_file · rate max:2/1m · when wed 09:00-17:00 $ meshmcp insight simulate hist.jsonl --policy policy.yaml simulated 25 decisions: 25 unchanged, coverage 96% exit 0 $ meshmcp insight simulate hist.jsonl --policy too-tight.yaml REGRESSIONS (were allowed, now blocked): agent.mesh read_dir allow→deny ×8 exit 1 $ meshmcp insight detect today.jsonl --baseline last-week.jsonl [1.00] unknown-identity stranger.mesh → require enrollment [0.80] new-tool exfiltrate → open co-sign [0.50] off-hours 03:00 UTC → open co-sign
The agent never holds the credential
Because the gateway already parses every call, it injects secrets by identity. The agent carries a reference; the real value is spliced in on the way to the backend — and reaches only the backend.
"auth":"Bearer {{secret:stripe_key}}"
}
✓ refused if session tainted
✓ audited: "stripe_key: injected"
"auth":"Bearer sk_live_…"
}
From one server to a federated network
The same identity and audit primitives compose outward — one endpoint over many servers, a control plane that onboards nodes, and boundaries that bridge organizations.
One endpoint over N servers
Namespaced union of many backends, with replica load-balancing, health-based failover, a discovery registry, and full bidirectional MCP.
Onboard nodes without hand-wiring
A managed control plane serves enrollment (real NetBird one-off key issuance), the service registry, and policy distribution — as one mesh peer.
Bridge two organizations
A boundary maps a remote org's identity, admits only its granted tools, stamps a local principal, and audits every crossing.
MCP protocol models, in Go
The plane speaks fluent MCP because the whole wire format is modelled as Go — faithfully translated from the official TypeScript sources, one package per protocol domain, each with round-trip and conformance tests. Polymorphic unions decode through Decode* discriminators; extends becomes struct embedding.
$ go test ./protocol/... ok meshmcp/protocol/tool structuredContent: object · array · error ok meshmcp/protocol/discover server/discover · capabilities ok meshmcp/protocol/samplingtools tool_use · tool_result ok meshmcp/protocol/authorization RFC 9728/8414/7591 · token-exchange # 23 packages · 92 tests · every union round-trips
19 commands, one static binary
Everything is one meshmcp binary. Mesh credentials come from $NB_SETUP_KEY or flags.
$ go build -o meshmcp . $ export NB_SETUP_KEY=<key from app.netbird.io> $ meshmcp serve --config examples/demo-backends.yaml # prints mesh IP 100.x.y.z # from any other machine on the mesh — nothing is exposed to the internet: $ meshmcp ls 100.x.y.z:9101 $ meshmcp call 100.x.y.z:9101 add --arg a=2 --arg b=40 → 42