Skip to main content
Glama

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 only

Or in CI, where it runs on every pull request:

- uses: dhb520cat/toolfence@v0
  with:
    fail-on: critical

Findings 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

DESTRUCTIVE_NO_CONFIRM

Mermail gates destructive tools behind a single-use confirmationToken. Most servers gate nothing.

ANNOTATION_MISMATCH

MCP ships readOnlyHint / destructiveHint. A wrong hint is a false safety guarantee — callers auto-approve on it.

INJECTION_SURFACE

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.

HONEYPOT

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.

PII_EXPOSURE

Ported from a TEE contract's PII guard — the check that stops personal data reaching an enclave that shouldn't hold it.

CREDENTIAL_IN_PARAM

Tool arguments end up in conversation history, logs and telemetry. Secrets belong server-side.

UNBOUNDED_SCOPE

Arbitrary path / URL / command / SQL — traversal, SSRF, injection.

COMMAND_INJECTION

A tool argument reaching a shell. Declarations are design choices; this is a bug.

PATH_TRAVERSAL

A tool argument reaching a filesystem call without a resolved-root check.

SSRF

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   20

7 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_service

Two 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_emails is not "send an email" — nouns that double as verbs only count in the verb slot.

  • get_create_fields is a read — a read verb in the head position settles the whole name.

  • search_files says "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.go had a tool literally named delete.

  • Python signatures are parsed for parameter names. Without them every Python tool looked unguarded; awslabs/mcp's delete_resource actually takes a confirmed parameter, and the scanner was calling it unprotected.

  • credentials_token described 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_blur is not delete_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.shape hides 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 --help

No 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-audit

MIT.

Available Tools

3 tools
explain_ruleA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
ruleYesThe rule identifier, as it appears in findings.

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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_repositoryA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
min_severityNoOmit findings below this severity. Defaults to low.
repository_urlYesA github.com repository URL, e.g. https://github.com/owner/name

TDQS

A4.2/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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_manifestA
Read-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.

ParametersJSON Schema
NameRequiredDescriptionDefault
manifestYesA tools/list result, or {"tools": [...]}
min_severityNoOmit findings below this severity. Defaults to low.

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 3 tool updatesv0.3.0
    • First observedexplain_rule
    • First observedscan_repository
    • First observedscan_tool_manifest

TDQS

A4/5.0

Scored across 3 tools

Disambiguation4/5

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.

Naming Consistency5/5

All three names follow a consistent verb_noun snake_case pattern (scan_tool_manifest, explain_rule, scan_repository). No mixed conventions or casing irregularities.

Tool Count4/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Agent-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.
    1
    6
    2
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Security 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.
    55
    87
    5
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Audits MCP server configurations and packages for security risks such as typosquats, credential exposure, and malicious code, with zero dependencies and no execution.
    2
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    MCP 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.
    4
    13
    MIT