# ๐ก๏ธ mcp-bastion
**A reliability & security proxy for the Model Context Protocol (MCP).**
_Self-healing connections, runtime tool-security, and a compliance-mapped audit trail for your MCP servers._
[](https://www.npmjs.com/package/mcp-bastion)
[](https://www.npmjs.com/package/mcp-bastion)
[](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.Gowthaman90/mcp-bastion)
[](https://github.com/Gowthaman90/mcp-defense-bench)
[](https://glama.ai/mcp/servers/Gowthaman90/mcp-bastion)
[](https://m8ven.ai/mcp/gowthaman90-mcp-bastion-1l5oda?s=readme)
[](https://github.com/Gowthaman90/mcp-bastion/actions/workflows/ci.yml)
[](https://scorecard.dev/viewer/?uri=github.com/Gowthaman90/mcp-bastion)
[](./LICENSE)
[](https://nodejs.org)
[](https://www.typescriptlang.org/)
[](#contributing)
---
`mcp-bastion` sits between your MCP client (Claude Code, Cursor, Cline, Windsurf, Zed, Claude
Desktop, or any MCP-compliant agent) and your MCP servers. It is **client-agnostic** โ it works with
any compliant client through configuration alone, with zero client-specific code โ and **non-invasive**:
your servers run unchanged, and removing Bastion is a one-line config revert.
๐ฆ **Package:** [`mcp-bastion` on npm](https://www.npmjs.com/package/mcp-bastion) ยท ๐๏ธ **Official MCP Registry:** `io.github.Gowthaman90/mcp-bastion`
๐ **Security, measured:** on the open, vendor-neutral [mcp-defense-bench](https://github.com/Gowthaman90/mcp-defense-bench) v0.9.0, Bastion covers **66% of the MCP attack surface (24.6/37 vectors; 23 enforced)** on development fixtures and **53%** on the pre-registered held-out set, at zero false positives on matched controls โ the broadest of the proxies measured.
๐ **Launch story:** [Medium](https://medium.com/p/e74f638e9e15) ยท [dev.to](https://dev.to/gowthaman90/the-mcp-reliability-security-gap-and-an-open-source-proxy-that-fills-it-3ppo)
๐ค **Created & maintained by [Gowthaman Arumugam](https://github.com/Gowthaman90)** โ Independent Researcher. Companion benchmark: [mcp-defense-bench](https://github.com/Gowthaman90/mcp-defense-bench).
## Contents
- [Why](#why)
- [How it works](#how-it-works)
- [Security model](#security-model)
- [Features](#features)
- [Quick start](#quick-start)
- [Demo](#demo)
- [Control tools](#control-tools)
- [Configuration](#configuration)
- [Transports](#transports)
- [Per-user identity (gateway mode)](#per-user-identity-gateway-mode)
- [Delegation checks](#delegation-checks)
- [Runtime authorization](#runtime-authorization)
- [Runtime security](#runtime-security)
- [Audit & compliance](#audit--compliance)
- [Client setup](#client-setup)
- [Architecture](#architecture)
- [Development](#development)
- [Roadmap](#roadmap)
- [Contributing](#contributing)
- [Security](#security)
- [License](#license)
## Why
When an MCP server disconnects mid-session, the agent only sees a generic _"No such tool available"_
error โ **indistinguishable from a tool that never existed** โ and it cannot reconnect; only a human
can. Long agent sessions silently lose capabilities and fail in confusing ways.
Bastion closes that gap. It health-checks every server, auto-reconnects with backoff, and โ crucially โ
exposes control tools so the **agent itself** can inspect connection health and recover a dropped
server without human intervention.
> Bastion now spans three layers: **reliability** (v0.1), **runtime security** (v0.2 โ tool pinning /
> rug-pull & poisoning detection), and **audit & compliance** (v0.3 โ pluggable sinks mapped to NIST
> AI RMF / OWASP LLM Top 10). See the [roadmap](#roadmap).
## How it works
Today your client connects **directly** to each server. With Bastion, your client connects to
**Bastion**, which connects to those same servers on your behalf โ so it sits in the tool-call path
and can add reliability (and, later, security) transparently.
```
Before: Client โโถ server A / server B / server C
After: Client โโถ mcp-bastion โโถ server A
โโถ server B
โโถ server C
```
Bastion is a standard MCP **server** to your client and a standard MCP **client** to each upstream.
Because it speaks the protocol faithfully, it works with every compliant client automatically โ the
only per-client difference is where you put a few lines of config.
## Security model
Bastion's security rests on **three independent mechanisms**. Heuristics feed all three and replace
none of them: a heuristic finding is a risk signal, never a proof that a tool is safe.
| Mechanism | What it gives you | Checks |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Definition integrity** | The tool, server and configuration you approved are the ones being called | tool pinning (rug pull), server identity, config drift, argument schema |
| **Runtime authorization** | Each caller reaches only the tools and argument values its policy grants, decided before any upstream contact | [`authorization`](#runtime-authorization) rules, [delegation checks](#delegation-checks) (who acts for whom), MRTR continuation custody |
| **Trustworthy audit evidence** | Every decision is recorded without leaking secrets, and a changed or shortened log can be detected | [audit events](#audit--compliance), hash chain, signed checkpoints, sink health, redaction |
Every finding carries a `kind`: **deterministic** (an exact comparison against a pin, a schema, a policy
or a token's claims) or **heuristic** (pattern matching that can misfire on benign text and can be
evaded by a novel payload). What each enforcement profile does with them:
| Check | Kind | `observe` | `balanced` (default) | `strict` |
| ------------------------------------------------------------------------------------- | ------------- | --------- | -------------------- | -------- |
| Tool pinning (rug pull), server identity | deterministic | warn | block | block |
| Runtime authorization | deterministic | warn | block | block |
| Delegation token checks | deterministic | warn | block | block |
| Delegation chain depth / cycles | deterministic | warn | warn | block |
| Cross-server data flow | deterministic | warn | block | block |
| Argument inspection: schema, plus command-injection and injected-instruction patterns | mixed | warn | block | block |
| MRTR (`input_required`) gate | mixed | warn | block | block |
| Description poisoning | heuristic | warn | warn | block |
| Response heuristics (injection, steering) | heuristic | warn | warn | block |
Argument inspection blocks under `balanced` even though part of it is heuristic: a false positive costs
the agent a retry, while a miss can run a command. Inline secret redaction in results
(`redactResponseSecrets`) is on in every profile. Under `observe` nothing is blocked and each audit event
records `wouldBlock`: what `balanced` would have done.
**Known limits.** Poisoning and response heuristics can be evaded by payloads they have not seen.
Redaction covers known secret shapes, not every secret. Bastion does not sandbox the upstream server
itself. Authorization trusts the identities it is given: bearer clients it authenticates itself, and
delegation claims it verifies only in signature mode (see `trustStructuralClaims`).
## Features
- ๐ **Client-agnostic** โ one binary, config-only integration; no per-client plugins.
- โป๏ธ **Self-healing** โ health checks + capped exponential-backoff auto-reconnect for stdio servers.
- ๐งญ **Agent-recoverable** โ `bastion__status` and `bastion__reconnect` let the agent detect and fix
drops itself, instead of hitting an opaque "no such tool" wall.
- ๐งฉ **Transparent aggregation** โ merges many servers into one, with per-server tool namespacing to
prevent collisions and tool-shadowing.
- ๐ฌ **Legible failures** โ a dropped server yields an actionable message, not a crash.
- ๐ก๏ธ **Runtime security** _(new in v0.2)_ โ pins each tool's definition and blocks "rug pulls" (a
server changing a tool after approval); heuristically inspects descriptions for poisoning; detects
cross-server shadowing. See [Runtime security](#runtime-security).
- ๐ **Runtime authorization** _(new in v1.4, opt-in)_ โ per-caller, per-tool policy decided before any
upstream contact: which client, user and agent may call which tool with which argument values,
deny-overrides, default-deny, and an observe mode to preview a policy. See
[Runtime authorization](#runtime-authorization).
- ๐ **Audit & compliance** _(opt-in)_ โ structured, hash-chained audit events to pluggable sinks
(console / file / webhook / OTLP), mapped to NIST AI RMF & OWASP LLM Top 10; since v1.4 with HTTPS-only
remote sinks, retrying delivery, per-sink redaction and Ed25519-signed checkpoints that make a shortened
log detectable. See [Audit & compliance](#audit--compliance).
- ๐ชถ **Non-invasive & reversible** โ your servers run unchanged; uninstall is a config revert.
- ๐งฑ **Enterprise-grade codebase** โ strict TypeScript, layered architecture, ESLint + Prettier, and
unit + end-to-end tests.
## Quick start
Bastion is published on npm as [`mcp-bastion`](https://www.npmjs.com/package/mcp-bastion) โ the
`npx` command below fetches it automatically, so there's nothing to install first.
**1. Add Bastion to your client**, pointing it at a config file:
```jsonc
// your client's mcpServers config
{
"mcpServers": {
"bastion": {
"command": "npx",
"args": ["-y", "mcp-bastion", "--config", "bastion.config.json"],
},
},
}
```
**2. List your real servers in `bastion.config.json`** (moved verbatim from the client):
```jsonc
{
"servers": {
"github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"] },
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"],
},
},
"reconnect": { "auto": true },
"healthCheck": { "enabled": true },
}
```
**3. Restart your client.** Your tools now appear namespaced (e.g. `github__create_issue`) alongside
Bastion's control tools. See [`bastion.config.example.json`](./bastion.config.example.json) for the
full set of options.
> ๐ **Security is on by default.** Out of the box, Bastion runs the `balanced` enforcement profile:
> it **blocks** high-confidence attacks (rug-pulls, argument/command injection, cross-server
> exfiltration) and **warns** on heuristic ones (description/response
> poisoning), while redacting leaked secrets from tool results. Set `security.enforcementProfile` to
> `observe` (warn-only) or `strict` (block-all), or tune any individual control โ see
> [Runtime security](#runtime-security).
## Demo
See the whole thing in action โ a server crashing mid-session and healing itself:
```bash
npm run demo
```
It boots Bastion in front of a server that crashes on command, shows the agent getting an actionable
"reconnect" message instead of a cryptic error, and then the connection auto-recovering with no human
involved. To record it as a GIF: `asciinema rec demo.cast -c "npm run demo" && agg demo.cast assets/demo.gif`.
## Control tools
Bastion injects control tools so the agent can manage connections and review security itself, using
only standard MCP calls:
| Tool | Purpose |
| --------------------- | ---------------------------------------------------------------------------------------------------------- |
| `bastion__status` | Health of every proxied server: connected / disconnected / reconnecting / failed, tool counts, last error. |
| `bastion__reconnect` | Reconnect a named server (argument: `{ "server": "