toolfence
This server lets you audit MCP servers and tool definitions for security issues, entirely read-only.
scan_repository— audit a public GitHub repository's source (TS/JS/Python/Go) for MCP tools and apply seven security rules; optionally filter by minimum severity.scan_tool_manifest— audit an existing tools/list response or tool definition list offline, with no network access.explain_rule— get the reasoning, detection logic, origin, and non-goals for any of the seven rules.
Provides security auditing for MCP server repositories hosted on GitHub, extracting tool definitions from TypeScript, JavaScript, Python and Go source and applying rules for unguarded destructive operations, annotation mismatches, injection surface, and other risky patterns without executing the code.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@toolfence scan https://github.com/awslabs/mcp for critical findings"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
toolfence
Security audit for MCP servers and agent tool-chains. Read-only: it never
executes the code it scans and never calls tools/call.
toolfence https://github.com/owner/repo # scan a repo (extracts tools from source)
toolfence ./path/to/server # scan a local checkout
toolfence tools.json # scan a tools/list export
toolfence --endpoint https://host/mcp # live server — calls tools/list onlyOr in CI, where it runs on every pull request:
- uses: dhb520cat/toolfence@v0
with:
fail-on: criticalFindings land in the job summary, and findings / critical / tools are
exposed as step outputs.
It is also an MCP server, so an agent can audit a server before trusting it:
{ "mcpServers": { "toolfence": {
"command": "python3", "args": ["-m", "toolfence.server"] } } }Three tools — scan_repository, scan_tool_manifest, explain_rule. All three
are read-only, declare readOnlyHint: true and destructiveHint: false, take no
credentials, and pass toolfence's own audit with zero findings. A test asserts
that they keep doing so.
Why
An agent with an inbox can be written to by anyone. An agent with a wallet can move money. The tools in between decide how bad that gets, and right now almost nobody audits them.
Every rule here came out of an actual audit, not a threat-modelling session:
Rule | Where it came from |
| Mermail gates destructive tools behind a single-use |
| MCP ships |
| Tool descriptions land verbatim in the model's context. An imperative in a description is an instruction anyone who can edit it gets to give your agent. |
| 73.2% of agent-bounty repos hide instructions from humans (HTML comments) that tell automated systems to paste their system prompt into a public PR. |
| Ported from a TEE contract's PII guard — the check that stops personal data reaching an enclave that shouldn't hold it. |
| Tool arguments end up in conversation history, logs and telemetry. Secrets belong server-side. |
| Arbitrary path / URL / command / SQL — traversal, SSRF, injection. |
| A tool argument reaching a shell. Declarations are design choices; this is a bug. |
| A tool argument reaching a filesystem call without a resolved-root check. |
| A tool argument reaching an HTTP client without a host allowlist. |
The last three read implementations rather than declarations, and only fire when
a model-controlled value reaches the sink with no validation nearby. Validation
in the neighbourhood downgrades the finding to low instead of silencing it, so
you can check whether the guard actually covers that path.
Related MCP server: mcpscan
What it found
A survey of 30 repositories drawn from punkpeye/awesome-mcp-servers
(3,877 entries, filtered to non-archived TS/JS/Python/Go projects by stars).
21 of them yielded extractable tools — 1,666 tools in total.
critical 17 high 96 medium 135 low 207 of 21 repositories come back completely clean.
Every remaining critical is an irreversible delete against something that matters, with no confirmation affordance in the tool itself:
awslabs/mcp delete_db_cluster, delete_db_instance
delete_fhir_resource (health records)
delete_instance_in_study (medical imaging)
mcp_delete_ecs_infrastructure
containers/kubernetes-mcp resources_delete
cloudflare/mcp-server container_file_delete
txn2/kubefwd remove_namespace, remove_serviceTwo INJECTION_SURFACE findings are real text shipped by github-mcp-server:
"always call this tool when the user asks for details about…". Not a
vulnerability — a pattern worth naming, since a description that issues orders
goes verbatim into the model's context.
This is a count of patterns, not a list of vulnerabilities. Most of these projects delegate confirmation to the client, which is where MCP's design puts it. What the number says is that the tool layer itself carries no guardrail — swap in a client that doesn't prompt, and the delete goes through.
Precision over recall
A scanner that flags everything is a scanner nobody reads, and the cost of a false positive is asymmetric: one wrong accusation against a careful team and the tool is never trusted again.
This one was wrong seven times against real code. I came within one commit of
publishing that AWS ships unguarded deletion tools. awslabs/mcp returned
criticals on delete_resource, delete_fhir_resource and
mcp_delete_ecs_infrastructure. Reading their source first: delete_resource
takes a confirmed parameter, create_resource takes security_scan_token and
skip_security_check. Their design is careful. The scanner had never parsed
Python function signatures at all, so every Python tool looked unguarded — a
systematic false positive across an entire language, exposed only by a real
codebase.
Each of the seven is now a regression test:
list_emailsis not "send an email" — nouns that double as verbs only count in the verb slot.get_create_fieldsis a read — a read verb in the head position settles the whole name.search_filessays "Only searches within allowed directories" — a declared boundary is not an unbounded scope."paste this in your configuration file"in an install guide is not a honeypot. Only demands for the agent's own system prompt count.Test files, fixtures and examples are skipped entirely —
main_test.gohad a tool literally nameddelete.Python signatures are parsed for parameter names. Without them every Python tool looked unguarded;
awslabs/mcp'sdelete_resourceactually takes aconfirmedparameter, and the scanner was calling it unprotected.credentials_tokendescribed as coming "from get_aws_session_info()" is a handle, not a secret in transit.ZWNJ (U+200C) is Persian and Arabic orthography, not a hidden-instruction marker. Flagging it fired on every project shipping RTL translations.
Severity follows the object, not the verb.
folder_remove_motion_bluris notdelete_db_cluster. Grading destructive tools by what they act on cut false criticals by 76% (72 → 17) across the survey.It could not see itself. This README once claimed the server passed its own audit with zero findings. It had not: the tools are declared as dict literals, which the Python extractor did not understand, so it extracted none of them. Fixed, it now finds five tools here and still reports nothing — and a test asserts that the server's own manifest stays clean.
Explicit annotations beat name inference: create_directory declares
destructiveHint: false and is believed.
Limits
Lexical, not semantic. It reads manifests and source text; it does not execute, type-check, or trace data flow.
TypeScript, JavaScript, Python and Go. Other languages extract nothing — a clean report on a Rust server means the scanner found no tools, not that the server is clean. Tool count is printed so you can tell the difference.
inputSchema: SomeZodSchema.shapehides parameter names; those tools are scanned on name and description alone.A clean report means these seven rules did not fire. It is not a safety certificate.
Install
pip install git+https://github.com/dhb520cat/toolfence
toolfence --helpNo dependencies outside the standard library. Python 3.10+.
--json for machine output. --fail-on {critical,high,medium,low,never} sets the
exit code, so it drops into CI as-is.
Tests
python3 test_toolfence.py # 80 assertions, 30 true negatives, plus a self-auditMIT.
Available Tools
3 toolsexplain_ruleARead-onlyIdempotent
Return the reasoning behind one of the seven rules: what it detects, why it matters, the audit it came from, and what it deliberately does not fire on. Offline and read-only.
Inputs: rule (required).
scan_repositoryARead-onlyIdempotent
Audit a public GitHub repository's MCP tool definitions and return structured findings. Extracts tools from TypeScript, JavaScript, Python and Go source, then applies seven rules covering unguarded destructive operations, annotation mismatches, injection surface in tool descriptions, hidden instructions aimed at automated systems, personal data in parameters, credentials in parameters, and unbounded scope. Read-only: clones nothing, executes nothing, and never calls tools/call on the audited server. Operates only on the repository named in the argument.
Inputs: repository_url (required), min_severity.
scan_tool_manifestARead-onlyIdempotent
Audit a tools/list response that you already have, without any network access. Accepts the JSON-RPC result object, a bare list of tool definitions, or the tools array itself. Use this to check a server you are already connected to. Read-only and offline.
Inputs: manifest (required), min_severity.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 3 tools
Each tool has a distinct input source: scan_tool_manifest works on a manifest you already hold offline, scan_repository pulls tool definitions from a public repo, and explain_rule documents a rule. The two 'scan' tools share the auditing verb and could momentarily be confused, but the descriptions clearly delimit offline-manifest vs repository input.
All three names follow a consistent verb_noun snake_case pattern (scan_tool_manifest, explain_rule, scan_repository). No mixed conventions or casing irregularities.
Three tools is focused and each earns its place within a small auditing surface. It sits at the low end of the well-scoped range but is not thin given the narrow domain.
Coverage spans both entry points for auditing (offline manifest and repository) plus rule explanation, which is a coherent lifecycle for a linter. A list-rules operation or batch/fleet scanning would round it out, but agents can work around these minor gaps.
Maintenance
Related MCP Connectors
Audit your MCP servers for poisoned tools and leaked secrets, and check AI-generated code for flaws.
Scan any MCP server for tool-poisoning, security, auth & license. Trust score before install.
Audit MCP servers for tool poisoning and shadowing. Scan a remote server by URL or paste its tools.
Scans remote MCP servers for protocol, security, and TLS issues; exposes scan tools via MCP.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients to run deterministic, read-only local repository audits for risky GitHub workflows, undocumented environment variables, possible credentials, and conflicting dependency configuration, returning structured findings without sending source code to a service.50 npmMIT
- AlicenseNot gradedqualityBmaintenanceA read-only MCP server that lets an agent audit its own environment by discovering local MCP configurations and skill folders on Windows, macOS, and Linux and running offline static checks for prompt injection, hidden instructions, hardcoded secrets, dangerous commands, unpinned packages, and excessive filesystem or transport permissions. It exposes tools to scan everything discovered, list configurations, vet a single skill, or analyze supplied text, returning text, JSON, SARIF, or HTML findings with CI-friendly severity levels.8 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables an agent or MCP client to scan MCP server configs and AI-agent tool manifests on demand, exposing tools that list detection rules and report hidden prompt injections, data-exfiltration attempts, embedded secrets, deceptive Unicode, unpinned packages and shell-launching tools. Each finding comes with a severity and location so risky tool descriptions can be caught before an attacker exploits them.28 PyPI1Apache 2.0
- AlicenseNot gradedqualityBmaintenanceRuns read-only, offline static scans of the local machine's MCP server configs and agent skill folders, surfacing prompt injection, hidden Unicode, dangerous commands, credential paths, hardcoded secrets, unpinned packages, and over-broad filesystem or transport scope as text, JSON, SARIF, or HTML reports an agent can act on. Also allows scanning a single downloaded skill folder or a saved tools/list JSON so an agent can vet third-party components before installing them.55 npmMIT