{
  "markdown": "# mcp-audit\n\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n[![Go Reference](https://pkg.go.dev/badge/github.com/firatmio/mcp-audit-proxy.svg)](https://pkg.go.dev/github.com/firatmio/mcp-audit-proxy)\n\n**Wireshark + auditd, but for MCP.**\n\n`mcp-audit` sits transparently in front of any MCP server and records every\ntool call that passes through it — what was called, by whom, with which\narguments, and what came back. One Go binary, no daemon, no Docker, no\nKubernetes.\n\nIt answers the question every team adopting agents eventually has to answer:\n*which tool did our agent call, when, and with what?*\n\n```console\n$ mcp-audit run -- npx -y @modelcontextprotocol/server-filesystem /tmp\nmcp-audit dev | mode: stdio, wrapping \"npx -y @modelcontextprotocol/server-filesystem /tmp\"\nmcp-audit config: built-in defaults (no config file found)\nmcp-audit policy: shadow (recording only, nothing is blocked)\nmcp-audit detectors: rug-pull, tool-poisoning\nmcp-audit audit log: /home/you/.mcp-audit/logs/events.jsonl\n```\n\nThat is the whole setup. No config file, nothing blocked, everything recorded.\n\n## Install\n\nDownload a binary for your platform from the\n[latest release](https://github.com/firatmio/mcp-audit-proxy/releases/latest),\nor use npm:\n\n```bash\nnpx mcp-audit-proxy run -- npx -y @modelcontextprotocol/server-filesystem /tmp\n```\n\nBuild from source — Go 1.24 or newer, no other dependencies:\n\n```bash\ngo build -o mcp-audit ./cmd/mcp-audit\n```\n\nOr install straight into your `GOBIN`:\n\n```bash\ngo install github.com/firatmio/mcp-audit-proxy/cmd/mcp-audit@latest\n```\n\n## Quick start\n\n### Local (stdio) MCP servers\n\nPut `mcp-audit run --` in front of the command you already run:\n\n```bash\nmcp-audit run -- npx -y @modelcontextprotocol/server-filesystem /tmp\n```\n\n### Remote (Streamable HTTP) MCP servers\n\nPoint the proxy at the upstream server and your client at the proxy:\n\n```bash\nmcp-audit serve --target https://example.com/mcp --listen :9000\n```\n\nAuthentication is not touched: `Authorization` headers, OAuth flows and\n`Mcp-Session-Id` all pass through exactly as they arrive.\n\n### Read the log\n\nEvery message is one JSON line:\n\n```console\n$ tail -1 ~/.mcp-audit/logs/events.jsonl | jq\n{\n  \"timestamp\": \"2026-08-12T13:05:08.466Z\",\n  \"event_id\": \"236d1568-e1f0-4ab4-ba75-f10f74f7b2c9\",\n  \"client_id\": \"\",\n  \"server_name\": \"server-filesystem\",\n  \"direction\": \"request\",\n  \"method\": \"tools/call\",\n  \"tool_name\": \"read_file\",\n  \"arguments\": { \"path\": \"/etc/hosts\" }\n}\n```\n\nSome things you can do with it straight away:\n\n```bash\n# Which tools has this agent called, and how often?\njq -r 'select(.direction==\"request\" and .tool_name) | .tool_name' \\\n  ~/.mcp-audit/logs/events.jsonl | sort | uniq -c | sort -rn\n\n# Show everything the policy engine flagged.\njq -c 'select(.policy_flags)' ~/.mcp-audit/logs/events.jsonl\n\n# What arguments has a particular tool been called with?\njq -c 'select(.tool_name==\"read_file\") | {timestamp, arguments}' \\\n  ~/.mcp-audit/logs/events.jsonl\n```\n\n## Connecting your MCP client\n\nSee [`examples/clients/`](examples/clients/) for drop-in config snippets for\nClaude Desktop, Cursor and Windsurf. The pattern is always the same: keep the\ncommand you had, and put `mcp-audit run --` in front of it.\n\n## What it detects\n\nRecording is the default. These checks run on top of it and, apart from RBAC,\nnever block anything — they flag the event and print an alarm to stderr.\n\n### Tool poisoning\n\nA poisoned MCP server hides instructions in a tool *description*. The user only\nsees a tool called `echo`; the model reads the rest. `mcp-audit` scans every\nadvertised description and schema field for seven patterns:\n\n| Rule | What it looks for |\n|---|---|\n| `instruction_override` | \"ignore all previous instructions\" and variants |\n| `hidden_instruction` | markup aimed at the model: `<IMPORTANT>`, `<system>`, `<secret>` |\n| `concealment` | \"do not tell the user\", \"without informing the user\" |\n| `credential_bait` | `~/.ssh`, `id_rsa`, `.env`, `~/.aws/credentials`, `/etc/shadow` |\n| `exfiltration` | \"send/upload/post …\" with a URL nearby |\n| `cross_tool_instruction` | orders about *other* tools — the tool-shadowing attack |\n| `invisible_characters` | zero-width and bidi-override characters a human cannot see |\n\n```console\nmcp-audit: ALERT possible tool poisoning on server \"demo\": tool \"echo\" description matched hidden_instruction: \"<IMPORTANT>\"\nmcp-audit: ALERT possible tool poisoning on server \"demo\": tool \"echo\" description matched concealment: \"do not mention this to the user\"\n```\n\n### Rug pulls\n\nA rug pull is a server that advertises a harmless tool, waits for you to approve\nit, and changes the description days later. `mcp-audit` fingerprints every tool\n(SHA-256 over description + input schema) and remembers it in\n`~/.mcp-audit/state/tools.json`, so the check survives restarts — which is the\nonly way it could ever catch the attack.\n\n```console\nmcp-audit: ALERT rug pull on server \"demo\": tool \"read_file\" changed its description or schema (first seen 2026-08-05T20:30:04Z, hash c203dda7a9ea -> 3f6e61538bfc)\n```\n\n### RBAC\n\nThe one check that can block. With no rules it allows everything; add a rule and\na refused call never reaches the server — the client gets a JSON-RPC error\ninstead.\n\n```yaml\npolicy:\n  rbac:\n    default: allow\n    rules:\n      - client: \"*\"\n        deny: [\"shell_exec\", \"delete_*\"]\n```\n\n```console\nmcp-audit: blocked: tool \"shell_exec\" is denied by rule for client \"*\" (deny: \"shell_exec\")\n```\n\n## Configuration\n\nEntirely optional. See [`config.example.yaml`](config.example.yaml) for the\nannotated version. `mcp-audit` looks for a config file in this order:\n\n1. `--config <path>`\n2. `$MCP_AUDIT_CONFIG`\n3. `./mcp-audit.yaml`\n4. `~/.mcp-audit/config.yaml`\n\nIf it finds none, it uses built-in defaults and says so.\n\n> **Windows paths in YAML:** write them with forward slashes\n> (`\"C:/Users/you/logs.jsonl\"`) or in single quotes\n> (`'C:\\Users\\you\\logs.jsonl'`). Inside double quotes a backslash is a YAML\n> escape character.\n\n### Sending events elsewhere\n\nThe local JSONL log is always on. A webhook is optional and best-effort — if it\nis down, delivery is retried four times over about three seconds and then that\nevent is dropped from that sink only. **The local log is never affected.**\n\n```yaml\nsinks:\n  webhook:\n    enabled: true\n    url: \"https://hooks.slack.com/services/T000/B000/xxx\"\n    # format and send are detected from the URL:\n    # a Slack or Discord URL gets a chat-formatted message and, by default,\n    # only flagged events. Anything else gets the raw JSON event and all of them.\n```\n\n## CLI reference\n\n```\nmcp-audit run [flags] -- <command> [args...]   wrap a local (stdio) MCP server\nmcp-audit serve --target <url> [flags]         proxy a remote (HTTP) MCP server\nmcp-audit version                              print the version\n\n--config <path>       config file to use\n--log <path>          audit log path, overriding the config\n--server-name <name>  name recorded in every audit event\n--client-id <id>      client identity recorded in every audit event\n--quiet               suppress the startup banner\n\nserve only:\n--target <url>        upstream MCP server URL (required)\n--listen <addr>       address to listen on (default \":9000\")\n```\n\n## Design guarantees\n\n- **Transparent.** Every byte the client sends reaches the server unchanged, and\n  vice versa. The only exception is a call RBAC refuses.\n- **The local log never loses an event.** It applies backpressure rather than\n  dropping. Every other sink is best-effort and drops instead of slowing the\n  proxy down.\n- **Cheap.** Parsing costs about 8µs per tool-call round trip on a modern\n  laptop — roughly 1/600th of the 5ms latency budget. See\n  [`ARCHITECTURE.md`](ARCHITECTURE.md#performans-notu) for the measurements.\n- **A message it cannot parse is still forwarded.** Auditing must never break a\n  working MCP session.\n\n## Demo\n\n[`scripts/demo.sh`](scripts/demo.sh) runs the whole story end to end — a normal\nsession, a blocked call, a poisoned tool description and a rug pull — against\nthe stub server, in a temp directory that leaves your real state alone.\n\n```bash\n./scripts/demo.sh                                  # watch it\nasciinema rec -c ./scripts/demo.sh mcp-audit.cast  # record it\n```\n\n`TYPING_SPEED=0 PAUSE=0 ./scripts/demo.sh` runs it instantly, which is handy as\na smoke test.\n\n## Development\n\n```bash\ngo test ./...                                       # everything\ngo test -race ./...                                 # concurrency\ngo test ./internal/interceptor/ -bench=. -benchmem  # performance\ngo build -o bin/dummy-mcp-server ./cmd/dummy-mcp-server\n```\n\nThe race detector needs a C toolchain. On Windows, `scoop install mingw` (or\nMSYS2) provides one; the performance assertion skips itself under `-race`,\nsince instrumented memory accesses measure the detector rather than the code.\n\nCI runs on a self-hosted runner and therefore only on pushes to `main` — see\n[`docs/ci.md`](docs/ci.md) for why, and for what would have to change before a\n`pull_request` trigger would be safe. Pull requests are tested by pulling the\nbranch and running the suite locally.\n\n`cmd/dummy-mcp-server` is a stub MCP server for testing the proxy against. It\nspeaks both stdio and Streamable HTTP and has flags for staging the attacks the\ndetectors look for:\n\n```bash\nmcp-audit run -- ./bin/dummy-mcp-server --poison     # poisoned tool description\nmcp-audit run -- ./bin/dummy-mcp-server --rug-pull   # description changes after the first tools/list\n./bin/dummy-mcp-server --http :8765                  # Streamable HTTP, for testing `serve`\n```\n\nThe module layout, the data model and the design decisions behind them are in\n[`ARCHITECTURE.md`](ARCHITECTURE.md).\n\n## License\n\n[Apache License 2.0](LICENSE) — free to use, modify and redistribute,\ncommercially included, with an explicit patent grant. See [`NOTICE`](NOTICE).\n",
  "bytes": 9905,
  "sha": "13b4a98c7ade94b3aef24519944a285af7844b0616844125768936885726a1f5",
  "repo_slug": "firatmio/mcp-audit-proxy",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_firatmio_mcp_audit_proxy_266d6b21/readme"
}