mcpField
M
mcp-bastion

mcp-bastion

本地服务
github.com/Gowthaman90安全71 天前更新

关于 mcp-bastion

mcp-bastion 是位于 MCP 客户端与上游服务之间的代理,面向需要连接恢复、工具安全控制和审计的 MCP 部署。它聚合多个服务,并通过标准协议提供状态检查与重新连接能力。

主要功能

  1. 01健康检查与自动重连
  2. 02聚合并命名空间化工具
  3. 03检查工具定义与安全风险
  4. 04按调用者和工具授权
  5. 05生成带哈希链的审计记录

适用场景

  1. 01降低长会话中的服务断连影响
  2. 02检查工具定义是否发生变化
  3. 03按调用者限制工具访问
  4. 04为 MCP 活动保留审计证据

MCP 相关

它对客户端表现为 MCP 服务,同时作为 MCP 客户端连接上游服务。它将多个上游工具聚合并向客户端提供状态、重连、安全和合规控制工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"gowthaman90-mcp-bastion": {
"command": "npx",
"args": [
"-y",
"mcp-bastion"
]
}
}
}
启动命令npx -y mcp-bastion

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmmcp-bastion1.4.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

🛡️ mcp-bastion

面向模型上下文协议(MCP)的可靠性与安全代理。

自愈连接、运行时工具安全,以及为 MCP 服务提供的合规映射审计跟踪。

npm version npm downloads MCP Registry Measured coverage mcp-bastion MCP server – quality and maintenance score on Glama CI License Node TypeScript PRs welcome


mcp-bastion:MCP 服务在会话中途崩溃,Agent 自动将其恢复

mcp-bastion 位于你的 MCP 客户端(Claude Code、Cursor、Cline、Windsurf、Zed、Claude Desktop,或任何符合 MCP 标准的 Agent)与 MCP 服务之间。它与客户端无关——仅通过配置即可与任何兼容客户端配合使用,无需编写任何客户端专属代码;同时无侵入性: 你的服务无需改动即可运行,移除 Bastion 只需恢复一行配置。

📦 软件包: npm 上的 mcp-bastion · 🗂️ 官方 MCP Registry: io.github.Gowthaman90/mcp-bastion

🔒 **以实测数据衡量的安全性:**在开放、供应商中立的 mcp-defense-bench v0.9.0 基准测试中,Bastion 在开发测试样本上覆盖 66% 的 MCP 攻击面(24.6/37 个向量;23 个得到强制执行),在预先注册的留出集上覆盖 53%,且针对匹配对照组的误报率为零——在受测代理中覆盖范围最广。

📖 发布介绍: Medium · dev.to

👤 由 Gowthaman Arumugam 创建并维护——独立研究员。配套基准测试:mcp-defense-bench。

目录

原因

当 MCP 服务在会话中途断开时,Agent 只会看到通用的 “没有此工具” 错误——无法与从未存在过的工具区分开来——而且无法重新连接;只有人工才能做到。漫长的 Agent 会话会在不知不觉中失去能力,并以令人困惑的方式失败。

Bastion 弥补了这一缺口。它会对每个服务进行健康检查,采用退避策略自动重新连接;更重要的是,它会提供控制工具,让 Agent 自身 检查连接健康状况,并在无需人工干预的情况下恢复已断开的服务。

Bastion 现已覆盖三个层面:可靠性(v0.1)、运行时安全(v0.2 — 工具固定 / 拉地毯与投毒检测)以及审计与合规(v0.3 — 可插拔的接收端,映射至 NIST AI RMF / OWASP LLM Top 10)。请参阅路线图。

工作原理

如今,客户端会直接连接到每个服务。使用 Bastion 后,客户端会连接到 Bastion,再由 Bastion 代表你连接到这些相同的服务——因此它处于工具调用路径中, 可以透明地增加可靠性(以及未来的安全性)。

Before:   Client ─▶ server A / server B / server C

After:    Client ─▶ mcp-bastion ─▶ server A
                                  ─▶ server B
                                  ─▶ server C

对于客户端而言,Bastion 是一个标准的 MCP 服务;对于每个上游服务而言,它是一个标准的 MCP 客户端。 由于它忠实地遵循协议,因此会自动兼容所有符合规范的客户端——不同客户端之间唯一的差别是配置几行内容的位置。

安全模型

Bastion 的安全性基于 三种相互独立的机制。启发式方法为这三种机制提供信息,但不能替代其中任何一种:启发式发现只是风险信号,绝不能证明某个工具是安全的。

机制提供的保障检查内容
定义完整性调用的工具、服务和配置与获批内容一致工具固定(拉地毯)、服务身份、配置漂移、参数架构
运行时授权每个调用方只能访问其策略授权的工具和参数值,并且会在联系任何上游之前作出决定authorization 规则、委派检查(谁代表谁行事)、MRTR 延续托管
可信审计证据记录每项决策且不泄露机密,并且可以检测日志是否被更改或截短审计事件、哈希链、签名检查点、接收端健康状况、脱敏

每项发现都带有一个 kind:确定性(针对固定值、架构、策略或令牌声明进行精确比较)或启发式(模式匹配,可能会对无害文本误报,也可能被新型载荷绕过)。各执行配置文件对这些发现的处理方式如下:

检查项类型observebalanced(默认)strict
工具固定(恶意替换),服务身份确定性警告阻止阻止
运行时授权确定性警告阻止阻止
委派令牌检查确定性警告阻止阻止
委派链深度 / 循环确定性警告警告阻止
跨服务数据流确定性警告阻止阻止
参数检查:架构,以及命令注入和注入指令模式混合警告阻止阻止
MRTR(input_required)门控混合警告阻止阻止
描述投毒启发式警告警告阻止
响应启发式(注入、引导)启发式警告警告阻止

尽管参数检查中有一部分属于启发式,balanced 仍会阻止它:误报会让 Agent 重试,而漏报则可能导致命令运行。所有配置档案都会启用结果中的内联密钥脱敏(redactResponseSecrets)。在 observe 模式下不会阻止任何操作,并且每条审计事件都会记录 wouldBlock:balanced 本会采取的操作。

已知限制。 投毒和响应启发式可能被此前未见过的载荷绕过。脱敏仅涵盖已知的密钥格式,并非所有密钥。Bastion 不会对上游服务本身进行沙箱隔离。授权会信任提供给它的身份:它会自行验证 bearer 客户端,而委派声明仅在签名模式下验证(参见 trustStructuralClaims)。

功能

  • 🔌 与客户端无关 — 一个二进制文件,仅需配置即可集成;无需为每个客户端安装插件。
  • ♻️ 自我修复 — 健康检查 + 有上限的指数退避自动重连,适用于 stdio 服务。
  • 🧭 可由 Agent 恢复 — bastion__status 和 bastion__reconnect 可让 Agent 自行检测并修复连接中断, 无需面对不透明的“没有此工具”障碍。
  • 🧩 透明聚合 — 将多个服务合并为一个,并为每个服务的工具单独命名空间,以防止名称冲突和工具遮蔽。
  • 💬 清晰的故障提示 — 服务断开时会提供可执行的消息,而不是导致崩溃。
  • 🛡️ 运行时安全 (v0.2 新增) — 固定每个工具的定义并阻止“暗度陈仓”(服务在获批后更改工具);通过启发式方法检查描述是否存在投毒;检测跨服务遮蔽。参见运行时安全。
  • 🔐 运行时授权 (v1.4 新增,可选启用) — 在联系任何上游服务前,按调用方和工具制定策略:哪些客户端、用户和 Agent 可以使用哪些参数值调用哪些工具;拒绝优先、默认拒绝,并提供可预览策略的观察模式。参见 运行时授权。
  • 📝 审计与合规 (可选启用) — 将结构化、哈希链式审计事件发送到可插拔的目标(控制台 / 文件 / webhook / OTLP),并映射到 NIST AI RMF 和 OWASP LLM Top 10;从 v1.4 起支持仅限 HTTPS 的远程目标、重试投递、按目标脱敏,以及可检测日志是否被截短的 Ed25519 签名检查点。参见审计与合规。
  • 🪶 无侵入且可逆 — 你的服务无需修改即可运行;卸载时只需还原配置。
  • 🧱 企业级代码库 — 严格的 TypeScript、分层架构、ESLint + Prettier,以及单元测试和端到端测试。

快速开始

Bastion 已发布到 npm,包名为 mcp-bastion — 下方的 npx 命令会自动获取它,因此无需预先安装任何内容。

1. 将 Bastion 添加到你的客户端,并指定配置文件:

// your client's mcpServers config
{
  "mcpServers": {
    "bastion": {
      "command": "npx",
      "args": ["-y", "mcp-bastion", "--config", "bastion.config.json"],
    },
  },
}

2. 在 bastion.config.json 中列出实际使用的服务(从客户端原样移入):

{
  "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. 重启客户端。 你的工具现在会以命名空间形式显示(例如 github__create_issue),同时还会显示 Bastion 的控制工具。完整选项请参阅 bastion.config.example.json。

🔒 默认启用安全防护。 开箱即用时,Bastion 会采用 balanced 强制执行配置: 它会阻止高置信度攻击(暗度陈仓、参数 / 命令注入、跨服务数据泄露),并对启发式检测到的攻击 (描述 / 响应投毒)发出警告,同时会从工具结果中删去泄露的机密信息。将 security.enforcementProfile 设为 observe(仅警告)或 strict(全部阻止),也可以调整任何单项控制措施 — 参见 运行时安全。

演示

观看完整演示 — 服务在会话中途崩溃并自行恢复:

npm run demo

它会在一个收到命令便会崩溃的服务前启动 Bastion,展示 Agent 收到可执行的“重新连接”消息,而不是晦涩的错误,随后连接会在没有人工介入的情况下自动恢复。要将演示录制为 GIF:asciinema rec demo.cast -c "npm run demo" && agg demo.cast assets/demo.gif。

控制工具

Bastion 注入控制工具,让 Agent 仅使用标准 MCP 调用即可自行管理连接并检查安全性:

工具用途
bastion__status每个代理服务器的健康状态:已连接/已断开/正在重新连接/失败、工具数量、上次错误。
bastion__reconnect重新连接指定服务器(参数:{ "server": "<name>" }),无需人工干预。
bastion__security按工具生成的安全报告:固定状态(已批准还是已更改)、投毒发现和遮蔽情况。
bastion__compliance将近期活动映射到 NIST AI RMF/OWASP LLM Top 10 的审计摘要(需要 audit.enabled)。

只有操作员可以重新批准。 清除供应链攻击拦截属于安全权限,因此这不是可由 Agent 调用的工具——遭到提示词注入的 Agent 不得重新批准自己被拦截的工具。更改后的工具会一直处于拦截状态,直到操作员通过带外方式解除拦截;系统不会公布 bastion__approve,客户端对它的调用也会被拒绝。

配置

键类型默认值描述
serversmap—要代理的上游服务(必填,至少一个)。
servers.<name>.commandstring—要启动的可执行文件(例如 npx、node)。
servers.<name>.argsstring[][]command 的参数。
servers.<name>.envmap—与进程环境变量合并的环境变量覆盖项。
servers.<name>.cwdstring—启动进程的工作目录。
reconnect.autobooleantrue意外断开连接后自动重连。
reconnect.maxRetriesnumber10放弃前的最大尝试次数(-1 = 不限次数)。
reconnect.initialBackoffMsnumber500初始退避时间,每次尝试都会翻倍。
reconnect.maxBackoffMsnumber30000退避时间上限。
healthCheck.enabledbooleantrue启用定期存活探测。
healthCheck.intervalMsnumber30000探测间隔。
healthCheck.timeoutMsnumber5000单次探测超时时间。
namespace.strategyprefix | passthroughprefix上游工具名称的公开方式。
namespace.separatorstring__prefix 策略使用的分隔符。
security.pinToolsbooleantrue固定工具定义并检测后续变更。
security.onRugPullblock | warnblock固定的工具定义发生变更时采取的操作。
security.inspectDescriptionsbooleantrue对工具描述运行投毒启发式检测。
security.onPoisoningblock | warnwarn发现高严重性投毒问题时采取的操作。
audit.enabledbooleanfalse为每次工具调用记录审计事件。
audit.includeArgsnone|redacted|fullnone工具参数的记录方式。
audit.tamperEvidentbooleanfalse对事件进行哈希链处理,以便检测篡改。
audit.sinksarrayconsole目标:console、file、webhook、otlp。
servers.<name>.transportstdio | httpstdio本地子进程或远程端点。
servers.<name>.urlstring—远程 MCP URL(http 必填)。
servers.<name>.headersmap—http 上游的请求头(例如 Authorization)。
servers.<name>.forwardHeadersmap—仅适用于 http。上游请求头 ← 传入请求头,按请求复制(网关模式)。需要 listen.mode: "http"。
servers.<name>.onMissingForwardedHeadererror | omiterror请求缺少转发请求头时:返回工具错误且不调用上游,或不带该请求头调用上游。
servers.<name>.connectstartup | onDemandstartuponDemand:在首个需要此上游的请求到来时连接,并使用该请求的转发请求头。
servers.<name>.authHeadersstring[]—在 bastion__status 中视为身份验证信息的额外请求头名称(forwardHeaders 中的键始终视为身份验证信息)。
servers.<name>.delegationobject—仅适用于 http,需要 forwardHeaders。对转发请求头中的委托 JWT 执行无密钥检查(见下文)。
servers.<name>.delegation.verifystructural | signaturestructuralsignature 还会使用公钥(jwksUrl 或 jwks)验证 JWT,并检查 issuer 和 algorithms 允许列表。
delegation.gatewayAudiencesstring[][]用于标识 Bastion 本身的受众;面向其中之一的令牌(或没有 aud 的令牌)会透传。
delegation.oneUserPerRequestbooleantrue单个请求中的所有委托令牌必须指向同一用户(iss + sub)。
delegation.recordSubjecthash | plainhash审计主体:iss|sub 的 HMAC 假名,或原样记录 sub。
delegation.subjectKeystring (≥16)见下文假名密钥;否则依次使用 MCP_BASTION_SUBJECT_KEY、审计完整性密钥,然后使用每个进程随机生成的密钥。
security.onDelegationblock | warnblock发生委托问题(链规则除外)时采取的操作。
security.onDelegationChainblock | warnwarn对 delegation-chain-depth / -chain-cycle 采取的操作(strict 下为 block)。
listen.modestdio | httpstdio通过 stdio 或 Streamable HTTP 提供 Bastion 服务。
listen.host / listen.portstring / number127.0.0.1 / 3000http 模式的绑定地址。
listen.validateRoutingHeadersbooleantrue拒绝与正文不一致的 Mcp-* 路由请求头(-32020,MCP 2026-07-28)。
security.maxCacheTtlMsnumber (ms)3600000限制转发到下游的上游 ttlMs 缓存提示上限(MCP 2026-07-28)。
servers.<name>.protocolauto|legacy|2026-07-28auto按上游协商协议版本(探测 2026-07-28,协商失败时回退;或固定版本)。
listen.legacystateless | rejectstateless以无状态方式服务 2026-07-28 之前的客户端,或拒绝它们(-32022)。
security.inspectInputRequestsbooleantrue对 input_required 轮次进行审查(凭据钓鱼、模型诱导)。
security.onInputRequiredblock | warnblock发现高严重性的 MRTR 问题时采取的操作(warn 会移除该请求)。
security.requestStateKeystring (≥16)每个进程随机生成用于封装 requestState 信封的 HMAC 密钥;也可使用 MCP_BASTION_REQUEST_STATE_KEY。
security.requestStateTtlSecondsnumber300封装后的 requestState 信封有效期。
security.enforcementProfileobserve | balanced | strictbalanced将所有 on* 操作设为一组;见安全模型。显式设置的 on* 优先。
security.onAuthorizationblock | warnblockauthorization 拒绝调用时采取的操作(observe 下为 warn)。
security.redactResponseSecretsbooleantrue在 Agent 看到工具结果前,隐去其中的机密值。
authorization.enabledbooleanfalse启用运行时授权;见运行时授权。
authorization.defaultdeny | allowdeny对没有任何规则允许的身份采取的决策。
authorization.rulesarray[]{ id, effect, principal, tools, args? } 规则;拒绝优先于允许。
authorization.trustStructuralClaimsbooleanfalse接受结构模式委托令牌中的 user/agent。
listen.authTokenstring—客户端 default 的 Bearer 令牌。除非设置了 listen.clients,否则非回环绑定时必填。
listen.clientsmap—命名客户端:{ "<name>": { "tokenEnv": "VAR" } },每个客户端一个 Bearer 令牌。
audit.integrityKeystring—用于哈希链的 HMAC 密钥;也可使用 MCP_BASTION_AUDIT_KEY。
audit.onSinkFailurecontinue | blockcontinue没有可用目标时,block 会拒绝调用。
audit.sinks[].includeArgsnone|redacted|fullaudit.includeArgs按目标设置参数记录方式。
audit.sinks[].headersEnvmap—webhook/otlp:请求头名称 → 环境变量。
audit.sinks[].allowInsecureTransportbooleanfalsewebhook/otlp:允许通过普通 HTTP 连接到非回环主机。
audit.sinks[].maxQueuenumber10000接收端不可用时暂存的事件数(webhook/otlp)。
audit.checkpointobject—已签名检查点:privateKeyFile、everyEvents(100)、everySeconds(60)、stateFile、sinks。

传输方式

Bastion 基于 MCP TypeScript SDK 2.0 运行,并在两端都支持两个协议时代——无状态的 2026-07-28 修订版和 2026 年之前的 initialize 握手;对于上游服务和客户端,两端均如此。当协议为无状态时,网关是唯一仍可持有安全状态的组件,因此 v1.0 增加了 requestState 托管和 input_required 同意门控(请参阅 CHANGELOG.md)。

Bastion 在两端都支持两种传输方式:

  • stdio(默认)——客户端启动 Bastion,Bastion 启动本地服务。
  • Streamable HTTP——连接到远程 MCP 服务(servers.<name> 配置 transport: "http"、 url 和可选的身份验证 headers),和/或通过 HTTP 向多个/远程客户端提供 Bastion 服务 (listen.mode: "http" 或 --http <port>)。

未配置身份验证标头的 HTTP 上游服务会在 bastion__status 中标记为(authenticated: false),并在连接时发出警告。自定义凭据标头在 authHeaders 中命名后即视为已配置;转发的标头始终视为已配置。

按用户身份验证(网关模式)

当 Bastion 位于 Agent 与必须以已登录用户身份执行操作的子 Agent 之间时,每次上游调用都必须携带该用户的身份,而不是使用一个固定令牌。forwardHeaders 可实现此功能:

{
  "servers": {
    "pfqa": {
      "transport": "http",
      "url": "http://localhost:8011/mcp",
      "protocol": "legacy",
      "connect": "onDemand",
      "forwardHeaders": { "x-cso-identity": "x-cso-delegate-pfqa" },
    },
  },
  "listen": { "mode": "http" },
}

此映射的含义是 上游标头 ← 传入标头。处理下游 HTTP 请求期间发出的每个上游请求(initialize、tools/list、tools/call、ping),Bastion 都会将传入的 x-cso-delegate-pfqa 标头值复制到上游请求的 x-cso-identity 标头中。

在此模式下,Bastion 不持有任何凭据,只会转发每个请求所携带的内容。

  • 与请求绑定。 传入标头会存放在 AsyncLocalStorage 中,生命周期恰好与一个下游请求相同。上游传输上的自定义 fetch 会在发送时读取这些标头。不同用户的并发请求可以共用一个上游连接,但彼此不会看到对方的标头。
  • 仅转发列出的标头。 不会转发其他传入标头。在加载配置时,逐跳标头和传输层自有标头(connection、mcp-session-id、content-type、……)会被拒绝作为转发目标。如果设置了 listen.authToken,则无法转发传入的 authorization 标头(Bastion 自身的 bearer 令牌)。
  • 转发的标头会覆盖同名的静态 headers 条目。
  • 缺少标头。 默认情况下(onMissingForwardedHeader: "error"),不会调用上游。调用方会收到类似 pfqa: the request carried no x-cso-delegate-pfqa header, so the call was not forwarded. 的工具错误。使用 "omit" 可在缺少标头时仍调用上游,由上游自行决定如何处理。
  • 按需连接。 使用 connect: "onDemand" 时,启动时不会发送任何请求。首次需要上游的请求(tools/list,或直接调用 pfqa__… 的 tools/call)会使用该请求的标头连接上游。随后会像往常一样缓存工具列表。健康检查会跳过按需连接的上游;在首次使用前,其状态显示为 idle。连接失败后会恢复为 idle,且不会设置重连计时器。使用 forwardHeaders 时,connect: "startup" 会在启动时仅使用静态 headers 建立连接,因此,如果上游也会在 initialize 时进行身份验证,请使用 onDemand。
  • 401 表示身份验证失败。 工具结果和 bastion__status 的 lastError 都会如实报告此问题,不会将其显示为崩溃或未知工具。一个用户收到的 401 不会拆除其他用户共用的连接。
  • 不会记录标头值。 日志、审计接收器和 bastion__status 均不包含转发的值。审计事件只会记录转发了哪些标头("forwardedHeaders": ["x-cso-identity"])。
  • 固定规则由所有用户共享。 首次发现工具定义时就会将其固定;无论是哪个用户的请求发现定义发生变化,都会阻止该变化。只有身份信息是按请求区分的。

forwardHeaders、onMissingForwardedHeader、connect 和 authHeaders 仅适用于 transport: "http" 上游。forwardHeaders 还要求设置 listen.mode: "http"。加载配置时会拒绝其他组合。要了解一个 Bastion 后配置三个子 Agent 的示例,请参阅 examples/identity-forwarding.config.json。

委派检查

自 v1.2.0 起。 在网关模式下,调用方(例如代表已登录用户工作的主 Agent)会为每个子 Agent 发送一个短时有效的委派 JWT,每个 JWT 放在各自的标头中。每个子 Agent 只能看到自己的令牌。Bastion 能看到整个请求,因此可以检查子 Agent 无法检查的内容,而且无需持有任何密钥或密钥材料:

"servers": {
  "pfqa": {
    "transport": "http", "url": "http://localhost:8011/mcp", "protocol": "legacy", "connect": "onDemand",
    "forwardHeaders": { "x-cso-identity": "x-cso-delegate-pfqa" },
    "delegation": {
      "audience": "pfqa",                 // required: what this upstream's tokens must carry in `aud`
      "header": "x-cso-identity",         // forwardHeaders key carrying the JWT (default: the only one)
      "verify": "structural",             // or "signature" (below)
      "maxLifetimeSeconds": 120,          // default 3600
      "clockSkewSeconds": 30,             // default 30
      "allowedActors": ["master-quote-agent"],
      "requireActor": false,
      "maxChainDepth": 2
    }
  }
},
"delegation": { "gatewayAudiences": ["mcp-bastion"], "oneUserPerRequest": true, "recordSubject": "hash" }
规则触发条件默认值(balanced)
delegation-malformed不是严格的 JWS 紧凑令牌(见下文),或声明的类型错误block
delegation-audience-mismatch没有任何 aud 值与此上游的 audience 相等(即来自同级服务的令牌)block
delegation-passthrough缺少 aud,或任意 aud 值属于 gatewayAudiencesblock
delegation-subject-mixing请求中的委派令牌包含多个不同的(iss,sub)block
delegation-expired / -not-yet-validnow > exp + skew / nbf > now + skewblock
delegation-lifetime-excessexp − iat > maxLifetimeSeconds,或缺少 expblock
delegation-actor-not-allowed / -actor-required当前 actor(act.sub,否则为 act.client_id)不在 allowedActors 中 / 缺少 actblock
delegation-chain-depth / -chain-cycleact 链超过 maxChainDepth / 同一 actor 出现两次warn
delegation-alg-disallowed签名模式:alg 不在 algorithms 中(区分大小写;none 和 HMAC 始终不允许)block
delegation-key-unknown签名模式:没有可信密钥与 kid 匹配,或无法获取密钥集block
delegation-signature-invalid签名模式:签名无法通过可信密钥验证block
delegation-issuer-mismatch签名模式:iss 与配置的 issuer 不匹配block

链深度和循环涉及先前的 actor。RFC 8693 §4.1 将其称为信息性内容,因此严重性为中等,除非 security.onDelegationChain(或 strict 配置)设为 block,否则只发出警告。其他所有情况均遵循 security.onDelegation(在 observe 配置下为 warn)。

  • 严格解码。 必须恰好有三个 base64url 段、无填充、采用规范编码,且 JSON 对象的成员名(转义解码后)唯一,并包含字符串 alg;允许使用可选的 Bearer 前缀。其他任何情况都会安全失败,并返回 delegation-malformed。
  • 每个请求只判定一次。 首次需要时,Bastion 会解码请求携带的每个已配置委托的令牌,执行针对每个上游的检查和跨令牌主体检查,并在请求剩余时间内缓存判定结果。检查器导出为 checkDelegation(policy, carriedHeaders, now)(结构检查,同步)和 checkDelegationAsync(policy, carriedHeaders, now)(增加签名验证)。
  • 联系任何上游之前。 如果请求被阻止,Bastion 会在连接前拒绝 tools/call(包括按需连接),返回指出规则名称的工具错误,但绝不包含令牌或声明值,并记录决策为 blocked_delegation 的审计事件。tools/list 会排除请求无权访问的上游。作为兜底措施,上游传输层会拒绝发送判定结果为阻止的令牌(包括请求期间发生的重连和其他发送)。
  • 谁代表谁执行操作。 对已配置委托的上游发起调用时,审计记录会包含 delegation 字段,记录 subject、issuer、audience、tokenId(jti)、expiresAt、actors(当前项在前)、chainDepth、verified(structural、签名验证通过时为 signature,或 failed)以及检查结果。绝不记录令牌。在 hash 模式下,主体为 "hmac:" + HMAC-SHA256(key, iss|sub) 的前 16 个十六进制字符:持有密钥的操作员可以重新计算它,以查找某位用户的记录。密钥依次取自 delegation.subjectKey、MCP_BASTION_SUBJECT_KEY、审计完整性密钥;若均不存在,则使用每个进程随机生成的密钥,其假名在进程重启后无法关联。
  • 状态。 bastion__status 会按上游显示 delegation: { mode, audience, lastViolation },其中最近一次违规信息仅包含规则 ID 和时间。
  • 配置检查。 delegation 需要 forwardHeaders;header 必须是其键之一;两个上游不能共用同一个 audience,且上游的 audience 不能与网关的 audience 相同;未知键会被拒绝。签名模式需要 issuer、非空的 algorithms 列表以及且仅需一种密钥来源。
签名模式

当签发方使用非对称密钥(ES256、EdDSA、RS256、PS256,……)签署委托令牌时,Bastion 可以根据签发方的公钥验证这些令牌:

"delegation": {
  "audience": "pfqa",
  "verify": "signature",
  "issuer": "https://spire.example/identity",               // exact match on `iss`
  "algorithms": ["ES256", "EdDSA"],                          // allow-list; HS256/384/512 and none are rejected
  "jwksUrl": "https://spire.example/.well-known/jwks.json"   // or "jwks": { "keys": [ … public JWKs … ] }
}
  • 密钥仅来自已配置的来源。 不会跟随 jku、x5u、x5c 和内嵌的 jwk 标头。配置加载时,如果内联 jwks 包含私有或机密成员(d、p、q、dp、dq、qi、oth、k),则会被拒绝。除环回主机外,jwksUrl 必须使用 HTTPS。
  • 算法允许列表会先运行,并区分大小写,之后才会查找密钥:alg: none(无论大小写如何)以及使用公钥签名的 HS256 都会被拒绝,且不会触发获取操作。关键标头扩展(crit,例如未编码的负载)也会被拒绝。
  • 远程密钥集会在首次使用时获取并缓存。kid 未知的令牌每 30 秒冷却期内最多触发一次重新获取。获取失败或超时 5 秒时会默认拒绝:调用会被阻止,并返回 delegation-key-unknown;上游的 lastError 会说明密钥集不可用,后续获取操作会等待冷却期结束。
  • 结构规则仍会运行,并应用于已验证的令牌,规则名称保持不变;审计记录会显示 verified: "signature"。

坦诚说明其局限。

  • 结构模式不进行任何身份验证:能够伪造令牌的一方也能伪造一致的声明。它能阻止诚实但有误或受到提示词注入的调用方造成的路由错误、透传、用户混淆、过期中继和链式滥用。上游仍是验证方。若要在网关进行身份验证,请使用签名模式(非对称令牌)。
  • 签名模式仅持有公钥,因此 Bastion 无需持有密钥:它可以验证令牌,但绝不会签发令牌。它需要签发方使用非对称算法签名;HS256 令牌只能进行结构验证,因为验证它们需要共享密钥。它信任配置的密钥集所声明的内容,因此 jwksUrl 的安全性(HTTPS、签发方自己的主机)决定了验证的安全性。
  • 每个请求一个用户的检查仅适用于配置了 delegation 的令牌。
  • 不会阻止令牌有效期内的重放(尚无 jti 缓存;未来可能提供可选且有界的实现)。

有关三个子 Agent 的示例,请参阅 examples/delegation.config.json。

运行时授权

v1.4 新增,需选择启用。 在每次调用时、联系任何上游服务之前,决定谁可以调用哪个工具,以及可以使用哪些参数。除非启用 authorization.enabled,否则此功能处于非活动状态。

"listen": {
  "mode": "http",
  "clients": {                                   // one bearer token per client, read from the environment
    "docs-assistant":   { "tokenEnv": "BASTION_TOKEN_DOCS" },
    "release-pipeline": { "tokenEnv": "BASTION_TOKEN_RELEASE" },
    "ops-console":      { "tokenEnv": "BASTION_TOKEN_OPS" }
  }
},
"security": { "enforcementProfile": "observe" },   // preview first: log, never block
"authorization": {
  "enabled": true,
  "default": "deny",
  "rules": [
    { "id": "docs-read", "effect": "allow", "principal": { "client": "docs-assistant" },
      "tools": ["files__read_file", "files__list_dir"],
      "args": { "path": { "pathWithin": "/srv/docs" } } },
    { "id": "release-staging", "effect": "allow", "principal": { "client": "release-pipeline" },
      "tools": ["deploy__*"], "args": { "env": { "in": ["staging"] } } },
    { "id": "no-prod-rollback", "effect": "deny", "principal": "*",
      "tools": ["deploy__rollback"], "args": { "env": { "equals": "prod" } } },
    { "id": "ops-control", "effect": "allow", "principal": { "client": "ops-console" },
      "tools": ["bastion__*"] }
  ]
}

调用者是谁。 一次调用会携带 Bastion 已验证的所有身份:客户端(具名的 listen.clients bearer token、listen.authToken 对应的 default,或 stdio),以及在存在委托令牌时,来自该令牌的用户和执行操作的Agent。忽略参数、标头、_meta 或输入响应中声明的身份:请求不能通过自称为其他人来提升自身权限。

如何判定一次调用。

  • 每个身份都必须获得许可。 代表用户行事的 Agent 需要同时拥有用户和自身的授权,因此 Agent 的宽泛服务授权绝不会用于没有相应权限的用户(即困惑代理问题),而用户的权限也不会被未获授权的 Agent 使用。委托链中的较早 Agent 不授予任何权限;只有当前 Agent 有效。
  • 拒绝优先于允许。 对于每个身份,任何匹配的 deny 规则都会拒绝调用;否则,必须有一条 allow 规则,且其所有参数约束都成立。如果 default: "deny",则没有匹配的允许规则就意味着无法访问。
  • 规则。 principal 可以是 "*",也可以是 client、user、agent 之一(名称或列表)。tools 是带命名空间的名称,可以是精确名称,也可以以一个 * 结尾。args 使用 equals、in、max、min 和 pathWithin 约束顶层参数。匹配是精确的:不进行大小写折叠、Unicode 规范化或类型强制转换;max/min 仅接受 JSON 数字;pathWithin 会解析 . 和 ..,并在 / 处检查边界(因此 /srv/docs-archive 不属于 /srv/docs)。
  • 默认拒绝。 缺少受约束的参数会导致 allow 规则不匹配。带有 args 的 deny 规则会生效,除非其约束能够被评估且不成立,因此缺失或类型错误的参数无法绕过拒绝规则。
  • 控制工具(bastion__*)与任何工具一样接受授权,服务名称为 bastion。使用 default: "deny" 时,应像上面的 ops-console 一样,向运维客户端授予这些工具的权限。

执行位置。 在 tools/call 中,于委托检查之后、联系任何上游服务之前执行,因此被拒绝的调用绝不会建立携带调用者转发标头的连接。继续调用(input_required 重试)会重新进行授权。tools/list 会隐藏调用者永远无法获准使用的工具。

逐步启用。 先使用 enforcementProfile: "observe":此模式不会拒绝任何调用,每条审计事件都会带有 authorization 记录(包括 effect、用于判定的 rule、agent:argument 等 violations),并在强制执行时会拒绝调用的情况下附带 wouldBlock: "blocked_authorization"。当日志只显示你有意拒绝的调用后,切换到 balanced。在 balanced 模式下,被拒绝的调用会收到答复 Blocked by mcp-bastion: this caller is not authorized to call "<tool>" (rule <id>).;不会泄露其他策略内容。

信任用户和 Agent 身份。 这些身份只能来自受信任的委托令牌:在签名模式下经过验证的令牌,或在结构模式下启用了 trustStructuralClaims: true 的令牌。只有在上游也会验证同一个令牌时才设置此项,这样即使伪造的声明通过了 Bastion,上游仍会拒绝它。对于结构模式的上游,如果规则指定了 user 或 agent,则会导致配置错误;如果令牌存在但不受信任,则会拒绝调用(delegation:unverified)。

已有评测。 这组检查已在编写此代码之前,预先注册为 mcp-defense-bench 的测试用例(标签 authorization-prereg-2026-10-26)。其他网关也提供按调用者进行的 MCP 授权(例如 agentgateway 的 CEL 规则和 ToolHive 的 Cedar 策略),而该基准测试的预注册承诺将对它们进行并列评分。

运行时安全

在 v0.2 中新增。 Bastion 在工具调用路径中添加了一个安全层(拦截器管道),默认启用:

  • 诱饵检测(工具固定)。 每个工具的定义都会在首次使用时固定。如果服务之后更改了该定义,工具就会被阻止(onRugPull: "block"),直到操作员进行审核并通过带外方式重新批准(仅限操作员,不能由 Agent 调用)。这可以发现安装时看似无害、之后却变得恶意的服务。
  • 投毒检查。 系统会扫描工具名称和描述,查找操纵行为特征(覆盖指令、访问机密、外泄数据、隐蔽指令、嵌入式指示、隐藏字符/零宽字符)。由于启发式检查可能产生误报,默认设置为 warn(记录并报告,但不阻止);设置 onPoisoning: "block" 可强制阻止。
  • 名称冲突。 当两个服务公开同名工具时,报告会将其列出。

使用 bastion__security 工具查看所有检查结果。每项发现都会说明其属于 heuristic 还是 deterministic;请参阅安全模型。

审计与合规

可选启用。 启用 audit 后,系统会为每次工具调用记录结构化、带版本的事件,包括被安全层或授权拒绝的调用:

"audit": {
  "enabled": true,
  "includeArgs": "redacted",            // none | redacted | full (default for every sink)
  "tamperEvident": true,                // hash chain; keyed with integrityKey / MCP_BASTION_AUDIT_KEY
  "onSinkFailure": "continue",          // or "block": refuse calls while no sink can deliver
  "sinks": [
    { "type": "file", "path": "./bastion-audit.jsonl", "includeArgs": "full" },
    { "type": "otlp", "endpoint": "https://collector.example:4318",
      "headersEnv": { "authorization": "OTEL_AUTH_HEADER" }, "includeArgs": "none" }
  ],
  "checkpoint": {                       // signed checkpoints (needs tamperEvident)
    "privateKeyFile": "/etc/bastion/checkpoint-private.pem",
    "stateFile": "/var/lib/bastion/audit-state.json",
    "sinks": [{ "type": "webhook", "url": "https://witness.example/checkpoints" }]
  }
}

事件。 每个事件都会列出服务、工具、决策(allowed、blocked_rug_pull、blocked_authorization 等)、结果、带有 kind 的发现、存在时的 authorization 和委派记录、runId,以及与 NIST AI RMF 职能和 OWASP LLM Top 10 类别的映射。bastion__compliance 会返回汇总数据。

每个接收端都是独立的安全边界。

  • console(stderr)、file(JSON 行格式,以 0600 权限创建)、webhook 和 otlp(OpenTelemetry 日志)。
  • 除回环地址外,远程接收端必须使用 HTTPS。除非接收端设置了 allowInsecureTransport: true(此时会记录警告),否则向远程主机发送普通 HTTP 会导致配置错误。
  • 凭据从环境变量(headersEnv)获取,而不是从配置文件读取;缺少变量会导致启动失败。
  • 投递失败时会采用指数退避重试,而不是直接丢弃。事件会在有界队列中等待(maxQueue,默认值为 10 000);超过上限后,最旧的事件会被丢弃并计数。接收端还可以通过每个事件的 seq 检测缺失。
  • bastion__status 会显示每个接收端的运行状况:healthy、delivered、dropped、queued、lastError。
  • 设置 onSinkFailure: "block" 后,当没有任何接收端能够投递时,调用会被拒绝(blocked_audit_unavailable),适用于不能接受未审计调用的部署。Bastion 自身的控制工具仍可使用,以便检查并恢复接收端。

将脱敏作为核心功能。

  • 默认不记录参数。每个接收端都可以单独设置记录级别(每个接收端的 includeArgs):例如,将完整参数写入本地文件,而不写入共享 SIEM。
  • redacted 会保留结构,并对凭据键名下的值以及看起来像机密的值进行掩码:云服务和提供商密钥、GitHub、Slack、Stripe、Google 和 npm 令牌、JWT、Bearer 凭据、URL 中的密码、私钥块和 NAME=secret 赋值。同样的模式也会在 Agent 看到工具结果之前对其中的机密进行脱敏(redactResponseSecrets,默认启用)。在一个包含 45 个案例的语料库(test/redaction-corpus.test.ts)中进行测试:29/29 个机密被捕获,0/16 个相似但无害的内容被更改。
  • 永远不会写入任何接收端的内容:工具结果、委派令牌值、转发的标头值(仅记录名称),以及当 delegation.recordSubject 为 hash 时的原始用户 sub。参数仅按 includeArgs 的设置记录,因此在 full 模式下,作为参数传入的机密仍会被保留。
  • 脱敏基于模式。形态无法识别、且位于无法识别键名下的机密仍可能被记录,因此对于高敏感度接收端,建议使用 none。

防篡改能力及其局限。

  • 使用 tamperEvident 时,事件会构成哈希链。使用 integrityKey 时,哈希链采用 HMAC-SHA256: 没有密钥,篡改日志的一方就无法重新计算哈希链。未使用密钥时,它只能检测简单的 编辑。
  • 当各个接收器以不同方式记录参数时,哈希链(v2)会覆盖每个事件(不含其 args 视图), 并包含一个 argsDigest(完整参数的 HMAC),这样每个接收器验证的都是同一条哈希链。
  • 哈希链无法证明最新记录是否被截断。签名检查点可以做到这一点: audit.checkpoint 会每隔 everyEvents(100)个事件、每隔 everySeconds(60)秒以及关闭时, 写入一条使用 Ed25519 签名的链头声明;每次运行都会以 run-start 记录开头,并通过 stateFile 链接到上一次运行。将检查点发送到日志所在主机无法篡改的见证接收器。
  • mcp-bastion audit verify <log> --checkpoints <witness.jsonl> --public-key <pem> 会检查哈希链、 seq 连续性、检查点签名、运行链接、日志是否被截断以及运行记录是否被删除。完整时退出码为 0,遭到篡改时为 1,无法验证时为 2。mcp-bastion audit keygen --out <dir> 会生成密钥对。
  • **限制:**只有当攻击者无法篡改的见证接收器收到检查点时,截断检测才能追溯到该检查点;如果没有 见证接收器,同时截断日志和检查点就无法被检测到。持有私钥的攻击者可以伪造检查点,因此请勿将 私钥存放在日志所在主机上。

影子模式。 在 security.enforcementProfile: "observe" 下,不会阻止任何操作;每个事件都会说明 执行强制策略时会采取什么措施:wouldBlock 保存 balanced 原本会记录的决策,findings 会将 rug pull 标记为 tool-definition-changed,而 responseFindings / redactedSecrets 会记录结果扫描 和内联 DLP 的发现(如果关闭了脱敏,则记录为 secret-in-result)。

标准对齐

每项安全检查都对应公认的框架——NIST AI 风险管理框架(美国联邦标准)、OWASP LLM (2025)与 Agentic(2026)应用十大风险以及 STRIDE。各项检查的完整映射(含检查所依据的 MCP 安全文献参考链接)见 docs/CHECKS-MAPPING.md。

覆盖度独立衡量——bastion 使用不受厂商限制的 mcp-defense-bench 基准测试,针对 22 项攻击面向量进行评分,并提供公开的 排行榜和框架映射。

客户端设置

所有客户端的步骤都相同——只有配置文件位置不同:

客户端添加 bastion 条目的位置
Claude Code项目 .mcp.json(或 claude mcp add)
Cursor~/.cursor/mcp.json 或项目 .cursor/mcp.json
Claude Desktopclaude_desktop_config.json
Clinecline_mcp_settings.json
Windsurf~/.codeium/windsurf/mcp_config.json

**逐步采用:**你不必将所有服务都通过 Bastion 路由——只需将不稳定或不受信任的服务置于其后, 其余服务仍可直接连接。

架构

Bastion 按清晰的层次组织,依赖关系单向流动,因此每项关注点都可独立测试,也易于演进:

src/
├── cli.ts              # thin CLI entrypoint (parse → wire → serve)
├── index.ts            # public library API
├── errors.ts           # error hierarchy (BastionError, …)
├── config/             # schema (Zod) + loader
├── core/               # domain: upstream connection lifecycle, aggregation & routing
├── proxy/              # client-facing MCP server + control tools
├── observability/      # logging (audit sinks in v0.3)
└── internal/           # small cross-cutting utilities

设计细节——包括与客户端无关的设计理由、拦截器流水线和审计接收器策略——请参阅项目的设计文档。

开发

npm install
npm run check      # format:check + lint + typecheck + test (the full gate)
npm test           # unit + end-to-end (in-memory transport) tests
npm run build      # bundle to dist/ (CLI + library)
npm run dev -- --config bastion.config.json
脚本功能
build使用 tsup 打包 CLI 和库。
dev使用 tsx 从源代码运行 CLI。
typechecktsc --noEmit(严格模式)。
lint / lint:fixESLint(扁平配置)。
format / format:checkPrettier。
test / test:watchVitest。
check执行以上所有检查,作为统一门禁。

路线图

版本主题重点
v0.1 ✅可靠性聚合代理、自动重连、bastion__status / __reconnect。
v0.2 ✅运行时安全工具定义固定(检测 rug pull)、投毒检查、影子检测。
v0.3 ✅审计与合规可插拔审计接收器(控制台 / 文件 / webhook)、NIST AI RMF / OWASP LLM Top 10 映射。
v1.2 ✅委派对委派令牌进行无密钥检查:受众、主体混淆、过期时间、Actor 链、签名。
v1.4授权与证据按调用方进行运行时授权;仅限 HTTPS、自动重试、按接收器脱敏的审计接收器;签名检查点和 audit verify。

支持 stdio 和 Streamable HTTP 传输方式(参见传输方式)。

参与贡献

非常欢迎大家贡献——本项目旨在由社区共同维护。请阅读 CONTRIBUTING.md,了解开发环境设置、项目结构和 PR 工作流,以及我们的 行为准则。

简而言之:对于重要变更,请先提出 issue;让 PR 聚焦并附带测试;并确保 npm run check 通过(CI 在 Node 18/20/22 上运行)。适合新手的领域包括:补充客户端配置示例、 增加上游测试样例,以及支持 Streamable HTTP 传输方式。

安全

mcp-bastion 属于安全相关软件,因此我们采用高标准。请私下报告 漏洞——不要公开创建 issue。披露流程请参阅 SECURITY.md。

许可证

Apache-2.0 © Gowthaman Arumugam 和 mcp-bastion 贡献者

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。