P4ST4S/mcp-audit

GitHub: P4ST4S/mcp-audit

mcp-audit 是一个透明的 Go 代理,用于在不修改客户端和服务器的前提下拦截、签名记录和审计 MCP 工具调用流量。

Stars: 7 | Forks: 8

# mcp-audit ![Go](https://img.shields.io/badge/Go-1.22%2B-00ADD8) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/P4ST4S/mcp-audit/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/P4ST4S/mcp-audit/graph/badge.svg)](https://codecov.io/gh/P4ST4S/mcp-audit) [![mcp-audit MCP server](https://glama.ai/mcp/servers/P4ST4S/mcp-audit/badges/score.svg)](https://glama.ai/mcp/servers/P4ST4S/mcp-audit) ![License](https://img.shields.io/badge/License-Apache--2.0-blue) ![Status](https://img.shields.io/badge/Status-stable-brightgreen) [![GitHub Discussions](https://img.shields.io/badge/discussions-join-blue?logo=github)](https://github.com/P4ST4S/mcp-audit/discussions) 一个用于 MCP server 的即插即用型安全与可观测性代理。`mcp-audit` 位于 MCP client 和任何上游 MCP server 之间,用于生成签名的审计跟踪、脱敏敏感 payload、执行允许/拒绝策略和基于工具的速率限制,并公开本地只读 dashboard。 有关面向贡献者的 runtime、package 边界、并发模型和设计不变量说明,请参阅 [ARCHITECTURE.md](ARCHITECTURE.md)。 ## 为什么选择 mcp-audit? [MCP 2026 路线图](https://modelcontextprotocol.io/development/roadmap) 提出了在审计跟踪、网关模式和运维可见性方面的企业需求。`mcp-audit` 作为可部署的 sidecar 或本地封装填补了这一空白:它位于任何 MCP client 和 server 之间,保留协议流量,并为 tool 调用、资源读取、prompt 请求以及所有其他 JSON-RPC 方法记录签名的审计条目。 ``` +-------------+ JSON-RPC / MCP +-----------+ JSON-RPC / MCP +-------------+ | MCP client | <-------------------> | mcp-audit | <---------------------> | MCP server | +-------------+ +-----------+ +-------------+ | v JSONL or SQLite audit log | v Read-only dashboard ``` ## 它是什么 / 不是什么 `mcp-audit` 不是特定领域的 MCP server。它是一个透明的安全与可观测性代理,封装了任何 MCP server 并审计流经它的 JSON-RPC 流量。 目录可能会显示上游 server 暴露的工具,而不是 `mcp-audit` 自身实现的工具。 ## 支持的 Transport - `stdio` 用于本地 MCP client(例如 Claude Desktop) - `http` 用于通过 HTTP 暴露的 MCP server HTTP 上游可以使用自定义 CA bundle、TLS server 名称覆盖和可选的 mTLS client 证书。上游重试默认关闭,仅在启用时应用于保守的、幂等的 JSON-RPC 方法;不会对 `tools/call` 进行重试。 ## 使用场景 - 审计受监管环境中 AI agent 进行的 tool 调用 - 检测意外或危险的 MCP tool 使用情况 - 保留签名的 JSONL 或 SQLite 日志以供事件审查 - 在存储请求和响应之前脱敏敏感字段 - 阻止不允许的工具并应用基于工具的速率限制,而无需修改上游 MCP server ## 演示 ![mcp-audit 演示](https://raw.githubusercontent.com/P4ST4S/mcp-audit/main/demo/mcp-audit-demo.gif) ## 安装 有关详细的特定平台说明和故障排除,请参阅 [INSTALL.md](INSTALL.md)。 下载最新的预编译二进制文件并运行: ``` # Linux/macOS:解析最新版本、下载、验证其启动 version=$(curl -fsSL https://api.github.com/repos/P4ST4S/mcp-audit/releases/latest \ | grep '"tag_name"' | head -n1 | cut -d'"' -f4 | sed 's/^v//') os=$(uname | tr '[:upper:]' '[:lower:]') arch=$(uname -m); [ "$arch" = "x86_64" ] && arch=amd64 || arch=arm64 base="https://github.com/P4ST4S/mcp-audit/releases/download/v${version}" archive="mcp-audit_${version}_${os}_${arch}.tar.gz" curl -L -o "${archive}" "${base}/${archive}" tar -xzf "${archive}" ./mcp-audit --version ``` 使用 Docker 运行: ``` docker run --rm ghcr.io/p4st4s/mcp-audit:latest --version ``` 使用 Go 从源码安装: ``` go install github.com/P4ST4S/mcp-audit/cmd/mcp-audit@latest ``` 要固定特定的发布版本以实现可重现安装,请参阅 [INSTALL.md](INSTALL.md#choosing-a-version)。 ## 快速开始 在 stdio 模式下运行: ``` AUDIT_SECRET="$(openssl rand -hex 32)" \ mcp-audit --transport stdio --upstream "npx @modelcontextprotocol/server-filesystem /tmp" ``` 在 Windows PowerShell 上,生成密钥并将其设置为环境变量: ``` $env:AUDIT_SECRET = -join ((1..32) | ForEach-Object { '{0:x2}' -f (Get-Random -Max 256) }) .\mcp-audit.exe --transport stdio --upstream "npx @modelcontextprotocol/server-filesystem C:\Temp" ``` 在 HTTP 模式下运行: ``` mcp-audit --transport http --upstream http://localhost:8080 --port 4422 ``` 使用 Docker Compose 运行: ``` docker compose up --build ``` 默认情况下,dashboard 可在 `http://127.0.0.1:9090` 访问。 默认情况下,Prometheus 指标可在 `http://localhost:9091/metrics` 访问。 ## 示例 - [Cursor stdio 配置](examples/cursor/README.md) - [Continue stdio 配置](examples/continue/README.md) - [VS Code stdio 配置](examples/vscode/README.md) - [Claude Desktop stdio 配置](examples/claude-desktop/README.md) ## 配置 `mcp-audit` 默认从当前目录加载 `config.yaml`。CLI 标志会覆盖配置值,而 `AUDIT_SECRET` 会覆盖 `audit.secret`。 | Key | Default | Description | | --- | --- | --- | | `proxy.transport` | `stdio` | 代理 transport:`stdio` 或 `http`。 | | `proxy.upstream` | 必需 | Stdio 命令或 HTTP 上游 URL。 | | `proxy.port` | `4422` | HTTP 监听端口。 | | `proxy.upstream_timeout_ms` | `30000` | HTTP 上游请求超时时间(以毫秒为单位)。 | | `proxy.forward_headers` | 空 | 允许绕过默认上游剥离列表的请求 header。仅在上游 MCP HTTP server 需要 bearer-token 认证时使用 `["Authorization"]`。 | | `proxy.tls.ca_file` | 空 | 可选的 CA bundle,用于验证 HTTPS 上游 MCP server。 | | `proxy.tls.server_name` | 空 | 上游 MCP server 的可选 TLS server 名称覆盖。 | | `proxy.tls.insecure_skip_verify` | `false` | 跳过上游 TLS 证书验证。仅用于本地测试。 | | `proxy.tls.client_cert_file` | 空 | 用于上游 mTLS 的可选 client 证书。必须与 `proxy.tls.client_key_file` 一起配置。 | | `proxy.tls.client_key_file` | 空 | 用于上游 mTLS 的可选 client 密钥。必须与 `proxy.tls.client_cert_file` 一起配置。 | | `proxy.retry.max_retries` | `0` | 安全 HTTP 上游请求的最大保守重试次数。默认关闭。 | | `proxy.retry.initial_interval_ms` | `200` | 初始上游重试退避时间。 | | `proxy.retry.max_interval_ms` | `2000` | 最大上游重试退避时间。 | | `proxy.client_id` | `claude-desktop` | 写入审计条目的 client 标识符。 | | `proxy.server_id` | `filesystem` | 写入审计条目的 server 标识符。 | | `audit.storage` | `jsonl` | 存储后端:`jsonl` 或 `sqlite`。 | | `audit.path` | `./audit.jsonl` | JSONL 审计日志路径。 | | `audit.sqlite_path` | `./audit.db` | SQLite 数据库路径。 | | `audit.sign` | `true` | 设置密钥时启用 HMAC-SHA256 签名。 | | `audit.secret` | 空 | HMAC 密钥。首选 `AUDIT_SECRET`。 | | `audit.async.enabled` | `false` | 通过有界环形缓冲区启用异步批处理审计写入。 | | `audit.async.queue_size` | `4096` | 在反压阻塞写入器之前排队的最大审计条目数。 | | `audit.async.batch_size` | `128` | 每个存储批次写入的最大条目数。 | | `audit.async.flush_interval_ms` | `1000` | 刷新部分批次前的最长时间。 | | `audit.rotation.max_size_bytes` | `0` | 归档轮转前的最大活动 JSONL 文件大小。`0` 禁用内置轮转。 | | `audit.rotation.max_files` | `0` | 要保留的最大已轮转 JSONL 归档数量。`0` 禁用保留。 | | `audit.rotation.interval` | 空 | 可选的基于时间的 JSONL 轮转间隔:`hourly` 或 `daily`。为空则禁用基于时间的轮转。 | | `audit.rotation.max_age_days` | `0` | 删除文件名轮转时间戳早于指定天数的 JSONL 归档。`0` 禁用年龄保留。 | | `middleware.rate_limit.enabled` | `true` | 启用基于 client、基于 tool 的 token bucket。 | | `middleware.rate_limit.requests_per_minute` | `60` | 每个 `(client_id, tool_name)` 每分钟允许的请求数。 | | `middleware.redact.enabled` | `true` | 启用基于 JSON 键的 PII 脱敏。 | | `middleware.redact.patterns` | 敏感键 | 用于脱敏的区分大小写的键片段。 | | `policy.enabled` | `false` | 对 `tools/call` 启用同步允许/拒绝策略检查。 | | `policy.default_action` | `allow` | 没有策略规则匹配时的后备操作:`allow` 或 `deny`。 | | `policy.rules` | 空 | 用于 tool 调用的有序首个匹配允许/拒绝规则。 | | `dashboard.enabled` | `true` | 提供 dashboard 服务。 | | `dashboard.bind_address` | `127.0.0.1` | Dashboard 监听地址。仅在 dashboard 受网络控制或 auth 保护时,才显式设置(例如设置为 `0.0.0.0`)。 | | `dashboard.port` | `9090` | Dashboard 监听端口。 | | `dashboard.auth.token` | 空 | 访问 dashboard HTML 和 API 请求所需的可选 bearer token,格式为 `Authorization: Bearer `。 | | `metrics.enabled` | `true` | 在单独的 HTTP endpoint 上提供 Prometheus 指标服务。 | | `metrics.port` | `9091` | 指标监听端口。 | | `metrics.path` | `/metrics` | 指标 HTTP 路径。 | | `metrics.include_go_metrics` | `true` | 包含 Go runtime 指标。 | | `metrics.include_process_metrics` | `true` | 包含进程指标。 | | `metrics.tool_labels` | `true` | 包含用于工具级别指标的 `tool_name` 和 `client_id` 标签。禁用以最小化标签基数。 | | `otel.enabled` | `false` | 将 `tools/call` 审计条目导出为 OTLP/HTTP JSON span。 | | `otel.endpoint` | `http://localhost:4318` | OTLP HTTP endpoint 基础 URL。自动追加 `/v1/traces`。 | | `otel.service_name` | `mcp-audit` | OpenTelemetry `service.name` 资源属性。 | | `otel.headers` | 空 | 附加的 OTLP HTTP header,例如 `Authorization` 或 API 密钥 header。 | | `otel.tls.ca_file` | 空 | 用于验证 OTLP endpoint 的可选 CA bundle。 | | `otel.tls.server_name` | 空 | 可选的 TLS server 名称覆盖。 | | `otel.tls.insecure_skip_verify` | `false` | 跳过 OTLP TLS 证书验证。仅用于本地测试。 | | `otel.retry.max_retries` | `3` | 导出请求失败后的最大 OTLP 重试次数。 | | `otel.retry.initial_interval_ms` | `200` | 初始 OTLP 重试退避时间。 | | `otel.retry.max_interval_ms` | `2000` | 最大 OTLP 重试退避时间。 | | `otel.queue_size` | `1024` | 丢弃 trace 导出前排队的最大审计条目数。 | | `otel.batch_size` | `64` | 每个 OTLP 导出请求的最大 span 数。 | | `otel.flush_interval_ms` | `1000` | 导出部分 OTLP 批次前的最长时间。 | | `otel.timeout_ms` | `5000` | OTLP HTTP 请求超时时间。 | 默认情况下,`mcp-audit` 在将 HTTP 请求转发给上游之前,会剥离逐跳(hop-by-hop)请求 header 和 `Authorization`。要将 bearer token 传递给受信任的已认证上游,请显式选择启用: ``` proxy: forward_headers: - Authorization ``` 安全提示:转发的 header(包括 bearer token 等机密信息)将原封不动地传输到上游 server。仅在您控制或信任上游 MCP server 时才启用此功能。`Authorization` 是唯一可以选择启用转发的敏感 header,因为某些 MCP HTTP server 需要它进行上游认证。`Cookie`、`Set-Cookie` 和 `Proxy-Authorization` 始终会被拒绝:它们代表 destined for 其他组件(例如浏览器会话或代理链)的状态,在 MCP 请求转发中没有合法用途。如果现有部署依赖于隐式的 `Authorization` 转发,请添加上述配置。 JSONL 轮转默认禁用,并支持基于大小和基于 UTC 时间的触发器。轮转后的归档文件使用 UTC 时间戳,例如 `audit.jsonl.20260610T214605Z`;如果同一秒内发生多次轮转,则会添加数字后缀。归档时间戳反映的是轮转事件的挂钟时间,而不是跨越的截止时间。基于时间的轮转是由 append 驱动的:`mcp-audit` 不会启动后台计时器,因此如果几天都没有发生写入,则活动文件在越过截止后的下一次 append 之前不会轮转。错过的截止时间不会被追赶;下一次 append 最多创建一个归档。 ``` audit: storage: jsonl rotation: max_size_bytes: 104857600 interval: daily max_files: 10 max_age_days: 30 ``` `max_age_days` 使用归档文件名中编码的轮转时间戳。这意味着自轮转以来的年龄,而不是归档内最旧条目的年龄。年龄保留在 `max_files` 保留之前运行。压缩和 SQLite 归档不属于此版本的一部分。 CLI 标志: ``` --transport stdio | http --upstream upstream server command or URL --port proxy port for http mode --upstream-timeout upstream HTTP request timeout in milliseconds --config path to config.yaml --storage jsonl | sqlite --no-dashboard disable the web dashboard --no-metrics disable Prometheus metrics --version print version and exit --log-level debug | info | warn | error ``` ## Claude Desktop 将 Claude Desktop 配置为启动 `mcp-audit` 而不是上游 MCP server: ``` { "mcpServers": { "filesystem-audited": { "command": "mcp-audit", "args": [ "--transport", "stdio", "--upstream", "npx @modelcontextprotocol/server-filesystem /tmp" ], "env": { "AUDIT_SECRET": "replace-with-a-long-random-secret" } } } } ``` ## 仪表板 Dashboard 显示最近的条目、过滤器、可展开的请求/结果 JSON、热门工具、今日调用和错误率。它每五秒刷新一次。 默认情况下,dashboard 仅在 `127.0.0.1:9090` 上监听。要在其他接口上公开它,请显式配置 `dashboard.bind_address`,并启用身份验证或将其置于受信任的访问代理之后。 ``` dashboard: enabled: true bind_address: 127.0.0.1 port: 9090 auth: token: "replace-with-a-long-random-token" ``` 配置 `dashboard.auth.token` 后,对 `/`、`/api/entries` 和 `/api/stats` 的请求必须包含: ``` Authorization: Bearer replace-with-a-long-random-token ``` 缺失或无效的凭证将返回 `401 Unauthorized` 以及 `WWW-Authenticate: Bearer realm="mcp-audit-dashboard"`。来自同一远程地址的重复失败身份验证尝试将受到 `429 Too Many Requests` 的限制。 Dashboard JSON API 响应包含 `Cache-Control: no-store`,因此中介和浏览器不会保留审计 payload。 ## Prometheus 指标 `mcp-audit` 在单独的 endpoint 上公开 Prometheus 指标,以便平台团队可以在不公开 dashboard 的情况下抓取操作数据。 ``` scrape_configs: - job_name: mcp-audit static_configs: - targets: ["localhost:9091"] ``` 应用程序指标使用 `mcp_audit_` 前缀,并避免无界的标签。可以通过 `metrics.tool_labels: false` 禁用工具级别标签,以进行更严格的基数控制。策略决策以 `mcp_audit_policy_decisions_total{action="allow|deny"}` 的形式公开。 有关现成的 Prometheus + Grafana 技术栈,请参阅 [examples/docker-compose-observability](examples/docker-compose-observability/README.md)。 ## 策略引擎 `mcp-audit` 可以在 `tools/call` 到达上游 MCP server 之前强制执行同步允许/拒绝规则。被拒绝的调用将返回 JSON-RPC 错误,并且仍会写入审计日志。 ``` policy: enabled: true default_action: allow rules: - action: deny client_id: claude-desktop server_id: filesystem tool_name: delete_file reason: "Destructive filesystem operations are blocked" ``` 规则按顺序评估。空字段和 `*` 匹配任何值,因此 `default_action: deny` 可以与显式的允许规则结合使用,以实现更严格的部署。 ## OpenTelemetry `mcp-audit` 可以将 `tools/call` 审计条目作为 OTLP/HTTP JSON span 导出到 Jaeger、Tempo、Honeycomb 或任何兼容 OTLP 的 collector。 ``` otel: enabled: true endpoint: "http://localhost:4318" service_name: "mcp-audit" headers: Authorization: "Bearer your-token" timeout_ms: 5000 retry: max_retries: 3 initial_interval_ms: 200 max_interval_ms: 2000 ``` 导出器在可能的情况下使用当前的 OpenTelemetry MCP 和 GenAI 语义约定,包括 `mcp.method.name`、`jsonrpc.request.id`、`gen_ai.operation.name`、`gen_ai.tool.name`、`network.transport`、`network.protocol.name`、`rpc.response.status_code` 和 `error.type`。项目特定的属性保持面向链接,例如 `mcp_audit.entry_id`、`mcp_audit.direction`、`mcp_audit.client_id`、`mcp_audit.server_id`、`mcp_audit.storage` 和 `mcp_audit.signature.present`。 默认情况下,请求参数和工具结果不会导出到 span。签名的 JSONL 或 SQLite 审计行保留为证据工件;OTLP 提供相关性、延迟和运维可见性。 导出器健康状况可以通过 `mcp_audit_otel_` 前缀下的 Prometheus 指标查看,包括导出请求、span 结果、丢弃的 span、队列深度和队列容量。临时 OTLP 故障将通过有限的指数退避进行重试;对于可重试的响应,最多可遵从 `Retry-After`,直至达到 `otel.retry.max_interval_ms`。 ## 审计条目 每个存储的条目都包含一个 ULID、时间戳、方向、transport、JSON-RPC 方法(如果存在)、脱敏的参数/结果、JSON-RPC 错误(如果存在)、持续时间、client/server 标识符以及可选的 HMAC-SHA256 签名。 JSONL 条目示例: ``` { "id": "01HY8G6Y8S6W9K6ZD7VJ4Q8X4R", "timestamp": "2026-05-25T12:34:56Z", "direction": "client_to_server", "transport": "stdio", "method": "tools/call", "tool_name": "read_file", "params": { "name": "read_file", "arguments": { "path": "/tmp/example.txt", "token": "[REDACTED]" } }, "duration_ms": 18, "client_id": "claude-desktop", "server_id": "filesystem", "signature": "hmac-sha256:..." } ``` 签名涵盖: ``` id + timestamp + method + tool_name + raw_params ``` ## 路线图 - SIEM 友好的导出 - OTLP 压缩和 trace context 传播 ## 社区 - [讨论](https://github.com/P4ST4S/mcp-audit/discussions):问题、想法和设计对话 - [问题](https://github.com/P4ST4S/mcp-audit/issues):bug 报告和具体的功能请求 - 安全:有关私有漏洞报告流程,请参阅 [SECURITY.md](SECURITY.md) ## 许可证 Apache-2.0。请参阅 [LICENSE](LICENSE)。
标签:EVTX分析, 日志审计, 用户代理, 自定义请求头, 请求拦截