
mcp-audit
io.github.firatmioPublished by GitHub account firatmiov0.1.0Updated Oct 24, 2026
Transparent audit proxy for MCP servers: logs every tool call, flags poisoning and rug pulls.
The publisher's own server: audit.domains - Domain Valuation
Overview
A transparent proxy that sits in front of MCP servers, recording every tool call and flagging tool poisoning, rug pulls, and denied tools.
- What you can do
- Wraps a local stdio MCP server or proxies a remote HTTP one, logging each request and response as JSON lines: timestamp, server, method, tool name, and arguments. It scans advertised tool descriptions and schemas for seven poisoning patterns, fingerprints tools to detect later description or schema changes, and can enforce RBAC rules that block denied calls with a JSON-RPC error. Optional webhook sinks forward events, and the local log applies backpressure rather than dropping events.
- When to use it
- Use it when you want visibility into which tools an agent calls and with what arguments, or when you need to catch a server that hides instructions in tool descriptions or changes them after approval. It is also useful for enforcing a deny list of tools before calls reach the upstream server.
- Requirements
- Runs as a local process on the user's machine, installed via npm (mcp-audit-proxy) or a downloaded binary; building from source needs Go 1.24 or newer. No accounts, API keys, or environment variables are declared. Configuration is optional and read from a flag, MCP_AUDIT_CONFIG, ./mcp-audit.yaml, or ~/.mcp-audit/config.yaml.
Installation
In SourceWeft
- Open mcp-audit in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
mcp-audit
[License] [Go Reference] [Audit MCP on mcprush]
A package-lock and flight recorder for MCP tool descriptions.
An MCP server's tool descriptions go straight into your model's context, and a
server can change them at any time. mcp-audit sits transparently in front of
any MCP server and:
- pins every tool the first time it is seen, and raises an alarm on every
session after a description or schema changes, until a person reviews the
change with
mcp-audit lock diffand approves it withmcp-audit lock approve; - records every message — which tool was called, with which arguments, and what came back — to a local JSONL file;
- flags prompt injection in tool descriptions and in tool results;
- blocks only what you tell it to, with optional allow/deny rules.
Everything runs locally. No account, no cloud, no telemetry: nothing leaves your machine unless you configure a webhook. One Go binary, Apache 2.0.
That is the whole setup. No config file, nothing blocked, everything recorded.
The recording is scripts/demo.sh, which runs the whole
story end to end against a stub server, in a temp directory that leaves your
real state alone:
Install
No install needed — run it through npm, in front of the server you already use:
Or download a binary for your platform from the latest release.
Note: the npm package and the release binaries are still 0.1.0, which predates
mcp-audit lockand result scanning. Until 0.2.0 is out, build from source to get them.
Build from source — Go 1.24 or newer, no other dependencies:
Or install straight into your GOBIN:
Quick start
Local (stdio) MCP servers
Put mcp-audit run -- in front of the command you already run:
Remote (Streamable HTTP) MCP servers
Point the proxy at the upstream server and your client at the proxy:
Authentication is not touched: Authorization headers, OAuth flows and
Mcp-Session-Id all pass through exactly as they arrive.
Read the log
Every message is one JSON line:
Some things you can do with it straight away:
Connecting your MCP client
See examples/clients/ for drop-in config snippets for
Claude Desktop, Cursor and Windsurf. The pattern is always the same: keep the
command you had, and put mcp-audit run -- in front of it.
What it detects
Recording is the default. These checks run on top of it and, apart from RBAC, never block anything — they flag the event and print an alarm to stderr.
Tool poisoning
A poisoned MCP server hides instructions in a tool description. The user only
sees a tool called echo; the model reads the rest. mcp-audit scans every
advertised description and schema field for seven patterns:
How often it cries wolf
A heuristic that flags every tool gets ignored, so the false-positive rate is
measured, not guessed. testdata/corpus/benign holds the tool lists of real MCP
servers — the official reference servers, and servers published by Microsoft
(Playwright), the Chrome DevTools team, Notion, Supabase and others — captured
with scripts/corpus-capture and pinned to exact package versions.
testdata/corpus/benign-results holds real tool results: the filesystem server
reading the MCP specification. Everything is run through the same policy engine
the proxy uses, with the default configuration:
The table is generated by go test ./internal/corpus, which fails if it no
longer matches what the detectors do. Every flagged item, and the heuristic
behind it, is listed in testdata/corpus/RESULTS.md.
The result set is small and comes from a single server, so treat its rate as a
first reading rather than a settled number.
This measures false alarms only. It says nothing about how many attacks the heuristics catch: a detector that flags nothing would score 0% here too. A detection rate needs a corpus of real attacks, which does not exist here yet.
Prompt injection in tool results
A tool's description is written by the server's author. Its result is often written by nobody you trust: the issue someone filed, the page a search returned, the ticket a customer opened. Instructions in that content reach the model the same way the real data does, with no malicious server involved.
mcp-audit scans every tools/call result — text blocks, embedded resources,
resource links, structuredContent, and JSON-RPC error messages — for language
addressed to the model, and flags the event result_injection_suspect:
It uses only three of the description rules: instruction_override,
hidden_instruction and concealment. The others misfire on ordinary content:
measured on the benign results, exfiltration matched OAuth documentation
sending the user to an https:// URL and cross_tool_instruction matched
"before invoking any tool". credential_bait and invisible_characters are
left out without having misfired there, because file reads in a coding session
mention .env and ~/.ssh all the time, and zero-width joiners and tag
characters are ordinary parts of emoji.
The cost of a scan is bounded, because the stdio pump reads the next message only once it is done:
- at most the first 1 MiB of a result's JSON is walked, and at most the first 64 KiB of its text is scanned — text placed beyond that is not seen, which is the trade for a predictable cost;
- base64 payloads (images, audio, blobs) are skipped;
- results are forwarded before they are scanned, so the client never waits.
go test ./internal/policy -bench ResultScan measures it on your machine. A
result is matched to its request by id, so a response whose request the proxy
did not see is not scanned. Turn it off with policy.result_scanning: false.
Rug pulls
A rug pull is a server that advertises a harmless tool, waits for you to approve
it, and changes the description days later. mcp-audit fingerprints every tool
(SHA-256 over description + input schema) and remembers it in
~/.mcp-audit/state/tools.json, so the check survives restarts — which is the
only way it could ever catch the attack.
The approved version stays the baseline, so the alarm repeats every session
until a person looks at the change and decides. Think package-lock.json, for
tool descriptions:
lock status exits with status 1 while anything is pending, so it also works
as a CI or pre-launch check.
RBAC
The one check that can block. With no rules it allows everything; add a rule and a refused call never reaches the server — the client gets a JSON-RPC error instead.
How it compares
Other tools cover some of the same ground, and some of them cover more. What each one documents:
Read from each project's README on 2026-10-21: agent-scan v0.6,
mcp-context-protector main, P4ST4S/mcp-audit v1.3.0. A "—" means that
documentation does not describe the feature, not that the project could never
have it. Corrections are welcome as issues.
In short: agent-scan inventories everything agent-related on a machine, using
Snyk's service; mcp-context-protector enforces, blocking a server after a
change instead of only raising an alarm; P4ST4S/mcp-audit is an audit and
observability gateway. mcp-audit is the lockfile and flight recorder, and
nothing but a rule you wrote ever blocks.
Configuration
Entirely optional. See config.example.yaml for the
annotated version. mcp-audit looks for a config file in this order:
--config <path>$MCP_AUDIT_CONFIG./mcp-audit.yaml~/.mcp-audit/config.yaml
If it finds none, it uses built-in defaults and says so.
Windows paths in YAML: write them with forward slashes (
"C:/Users/you/logs.jsonl") or in single quotes ('C:\Users\you\logs.jsonl'). Inside double quotes a backslash is a YAML escape character.
Sending events elsewhere
The local JSONL log is always on. A webhook is optional and best-effort — if it is down, delivery is retried four times over about three seconds and then that event is dropped from that sink only. The local log is never affected.
CLI reference
Without --server-name, a stdio server is named after what it runs, not after
the launcher: npx -y @modelcontextprotocol/server-filesystem is
server-filesystem, uvx mcp-server-fetch is mcp-server-fetch, docker run … ghcr.io/github/github-mcp-server is github-mcp-server, and
node ./weather-server/build/index.js is weather-server. The name matters: it
is the key tool fingerprints are pinned under, so two servers must not share
one. An HTTP server is named after its --target URL.
Design guarantees
- Transparent. Every byte the client sends reaches the server unchanged, and vice versa. The only exception is a call RBAC refuses.
- The local log never loses an event. It applies backpressure rather than dropping. Every other sink is best-effort and drops instead of slowing the proxy down.
- Cheap. Parsing costs about 8µs per tool-call round trip on a modern
laptop — roughly 1/600th of the 5ms latency budget. See
ARCHITECTURE.mdfor the measurements. - A message it cannot parse is still forwarded. Auditing must never break a working MCP session.
- Detectors flag; only rules block. Rug-pull, tool-poisoning and result-injection checks raise alarms and mark events. The only thing that refuses a call is an RBAC rule you wrote.
- Nothing leaves the machine by default. The log, the tool fingerprints and every check are local. Events go elsewhere only if you configure a webhook.
Development
The race detector needs a C toolchain. On Windows, scoop install mingw (or
MSYS2) provides one; the performance assertion skips itself under -race,
since instrumented memory accesses measure the detector rather than the code.
CI runs on a self-hosted runner and therefore only on pushes to main — see
docs/ci.md for why, and for what would have to change before a
pull_request trigger would be safe. Pull requests are tested by pulling the
branch and running the suite locally.
cmd/dummy-mcp-server is a stub MCP server for testing the proxy against. It
speaks both stdio and Streamable HTTP and has flags for staging the attacks the
detectors look for:
To add a server to the false-positive corpus, capture its tool list at a pinned version, then regenerate the results and review the diff:
To capture tool results instead, add -calls calls.json (a JSON array of
{"tool": ..., "arguments": {...}}) and write to testdata/corpus/benign-results;
-redact <path> replaces a local path with <root>, and the capture refuses to
write a file that still contains your home directory. Record where the content
the calls read comes from with -content-source and -content-license.
Check the captured file for anything machine-specific before committing it, and skip servers that phone home on startup.
The module layout, the data model and the design decisions behind them are in
ARCHITECTURE.md.
License
Apache License 2.0 — free to use, modify and redistribute,
commercially included, with an explicit patent grant. See NOTICE.
Source: README.md at commit 3014778
Tools
0Version history
1- v0.1.0LatestSep 16, 2026
Score
50% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts
Other Audit integrations
audit.domains - Domain Valuation
domains.auditOfficial publisher
Deterministic domain-name valuation: fair-value intervals, comparable recorded sales, availability checks, and a verified for-sale listing market.
Revenue Agent — OpenAPI Audit & Change Detection
news.prhOfficial publisher
A remote MCP endpoint that audits OpenAPI specifications and detects API changes, charging $0.10 USDC per analysis via x402 on Base.

SEO Audit & Broken Link Checker
Published by GitHub account prometheus-maker
An Apify-hosted MCP server that audits a website's pages for SEO metadata and finds broken links and images, returning per-page scores.

FinePrint AI Legal Risk & Contract Audit MCP Server
Published by GitHub account RitualDev-Lab
Local in-memory scanner that grades legal contracts, NDAs and leases for risk and drafts redline amendments for coding agents.

Mcp Supply Audit
Published by GitHub account kimmawla
Audits MCP servers as supply-chain artifacts — registry entries, PyPI/npm packages, client configs and tool definitions — using read-only static checks.