toolfence
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., "@toolfencescan 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: mcp-security-scanner
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. Three false positives found during development are now regression tests:
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| rule | Yes | The rule identifier, as it appears in findings. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, openWorldHint=false, and destructiveHint=false, so 'Offline and read-only' is largely redundant. The genuinely additive behavior detail is that the output includes what the rule 'deliberately does not fire on', disclosing a false-positive scope, but no other behavioral context (no traceability to the findings schema, no guarantee of stability) is offered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the content of the return value front-loaded. The trailing 'Offline and read-only' clause is minor padding since annotations already carry that information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description correctly compensates by enumerating the four components of the response. Combined with a fully documented single enum parameter, an agent has enough to invoke it correctly; only explicit usage/selection guidance is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum itself lists all seven rule identifiers, so the schema does the heavy lifting. The description only echoes 'one of the seven rules', adding no syntax, casing, or selection guidance beyond the enum.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (return the reasoning) and resource (one of the seven rules), and enumerates the exact content returned: detection target, rationale, originating audit, and non-firing cases. This clearly distinguishes it from the scan_* siblings, which produce findings rather than explain them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: an agent should infer this is used to interpret a rule id appearing in findings. There is no explicit when-to-use framing, no mention of what alternative to use if the rule id is unknown or if raw rule definitions are wanted.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| min_severity | No | Omit findings below this severity. Defaults to low. | |
| repository_url | Yes | A github.com repository URL, e.g. https://github.com/owner/name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by enumerating the seven audit rules, the source languages parsed, and explicit non-execution guarantees ('clones nothing, executes nothing, and never calls tools/call'), which is exactly the behavioral context an agent needs for a read-only scanner whose annotations already cover the safety profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the purpose, then the rule set, then the safety guarantees in three sentences with zero filler; the length is justified by the breadth of what the audit covers.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, 'return structured findings' is slightly thin on the return shape, but the enumeration of the seven rule categories gives the agent a good sense of what findings will contain. Complete enough for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already documented in the schema, including the enum for min_severity. The description reinforces the repository_url scope but adds no format or filtering detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Audit a public GitHub repository's MCP tool definitions') and immediately narrows scope to the repo in the argument, which cleanly separates it from siblings like scan_tool_manifest and explain_rule.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the scoping sentence ('Operates only on the repository named in the argument'), but there is no explicit when-to-use guidance and no mention of the alternatives scan_tool_manifest or explain_rule, leaving the routing decision to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| manifest | Yes | A tools/list result, or {"tools": [...]} | |
| min_severity | No | Omit findings below this severity. Defaults to low. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, openWorld=false, idempotent, non-destructive, so the safety profile is covered. The description still adds real value by disclosing the offline, no-network behavior and the accepted input shapes, which annotations do not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the core action and the input-format flexibility. 'Read-only and offline' slightly echoes the annotations, but it is short and earns its place as a scoping cue.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an offline audit tool with no output schema, the description adequately covers input flexibility and the severity-filtering concept implied by min_severity. It could say a bit more about the nature of the findings returned, but nothing essential to correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents both params and the min_severity enum. The description goes beyond it by enumerating the flexible input forms (JSON-RPC result object, bare list, tools array), clarifying what 'manifest' actually accepts.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Audit a tools/list response that you already have') and scopes it with 'without any network access', which cleanly distinguishes it from the sibling scan_repository that presumably reaches out over the network. An agent can select this without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use this to check a server you are already connected to' gives a clear conditional for when this tool applies versus a live scan, and 'offline/no network access' reinforces the boundary. It stops short of naming scan_repository as the explicit alternative, so not a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.3.0- First observed
explain_rule - First observed
scan_repository - First observed
scan_tool_manifest
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
Scan any MCP server for tool-poisoning, security, auth & license. Trust score before install.
Scans remote MCP servers for protocol, security, and TLS issues; exposes scan tools via MCP.
Security scanner for MCP servers. Detect vulnerabilities, prompt injection, and tool poisoning.
Scan any public GitHub MCP-server repo for security issues. 37 MCP-specific L1 rules, 8 languages.
Related MCP Servers
- AlicenseAqualityDmaintenanceAgent-native "safe to ship?" security gate for AI-generated code. Uses real parsers and inter-rocedural taint analysis (JS/TS, Python, Go) to flag the classes AI coding agents get wrong — secrets, SQL injection, SS, SSRF, path traversal, command injection, weak JWT/CORS — and ranks findings by confidence. Exposes a scan tool over MCP.162MIT
- AlicenseAqualityAmaintenanceSecurity scanning for MCP servers from the inside out. Provides runtime inspection, AST-based static analysis, config audit, dependency analysis, and OWASP MCP Top 10 compliance in a single MCP server.55875MIT
- AlicenseNot gradedqualityCmaintenanceAudits MCP server configurations and packages for security risks such as typosquats, credential exposure, and malicious code, with zero dependencies and no execution.2MIT
- AlicenseAqualityAmaintenanceMCP server that provides audit and safety-check tools for enterprise SDLC code integrity, enabling AI agents to scan workspaces for lifecycle gaps, mock-theater tests, DRY violations, and language-specific issues in shell, JavaScript/HTML, and Python.413MIT