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: 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   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, 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_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.

  • 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.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.

Inputs: rule (required).

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.

Inputs: repository_url (required), min_severity.

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.

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

    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

    ActivitySlowing
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables 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 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      A 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 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables 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 PyPI
      1
      Apache 2.0
    • A
      license
      Not graded
      quality
      B
      maintenance
      Runs 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 npm
      MIT