Skip to content

Repository files navigation

pnx-mcp-audit

Security audit CLI for Model Context Protocol (MCP) servers.
Static and runtime checks for prompt-injection vectors, tool poisoning, hidden Unicode, permissive schemas, insecure transports, and capability drift.
Zero third-party Go dependencies. Cross-platform. Runs offline. No telemetry.

Release CI Go Report License Go Backed by Penaxtra

Checks · Install · Quick start · Usage · CI · FAQ · About Penaxtra


pnx-mcp-audit is a single-binary command-line auditor for Model Context Protocol (MCP) servers. It connects to a server over stdio or Streamable HTTP, performs the MCP initialize handshake, enumerates the server's tools, resources, and prompts, and runs a battery of static safety checks against the descriptors returned. The output is a deterministic finding list mapped to severity tiers and exit codes that fit straight into a CI pipeline.

The tool exists because MCP servers expose a wide and almost entirely text-based attack surface to any LLM that connects to them. A tool description is an instruction stream the model will read. A schema with additionalProperties: true is a free-form argument bag. An invisible Unicode tag inside a tool name is a tool-poisoning channel. Today most of these issues are caught, if at all, by manual review. pnx-mcp-audit lets you check them in a second, every commit.

The project is sponsored, but not gated, by Penaxtra, an enterprise AI Security Posture Management (AI-SPM) platform. Penaxtra runs this and a much wider catalogue of checks against production LLM deployments for regulated mid-market customers in Europe and Turkiye. The audit logic shipped here is the same logic Penaxtra uses against the MCP attack surface; the project is open-source so that the broader MCP ecosystem can adopt it.


Why MCP servers need an auditor

The Model Context Protocol is, by design, a way for a language model to discover and invoke external capabilities. A server publishes:

  • Tools -- callable functions with a JSON Schema for arguments.
  • Resources -- addressable read-only data the model can pull in.
  • Prompts -- reusable prompt templates with parameters.

Three properties of this design make MCP servers a high-leverage target:

  1. Tool descriptions are model-trusted prose. The text in tools/list -> description reaches the host model as part of its instruction context. A malicious or sloppy description is a direct prompt- injection vector against every model that connects.
  2. Schemas are advisory, not enforcing. A loose schema (additionalProperties: true, type: object with no properties, or a command: string-style argument) lets the model pass anything the server is willing to execute.
  3. Capability declaration is voluntary. Servers can ship tools without declaring the tools capability, exposing surface area that hosts may route to without expecting it.

pnx-mcp-audit codifies the resulting checklist into 13 production rules (MCP-001 ... MCP-013) plus an opt-in active-probe slot (MCP-100+). Each finding carries a severity, a stable rule ID, the exact descriptor that triggered it, and a remediation hint. The audit terminates with an exit code that encodes the highest severity found, so --fail-on high becomes a one-line CI gate.


What it checks

ID Severity What it catches
MCP-001 HIGH Tool description contains prompt-injection prose ("ignore previous", "system:", "you are now", role-override patterns).
MCP-002 MEDIUM Tool description carries inline HTML or Markdown that hosts may render verbatim into the model context.
MCP-003 HIGH Hidden Unicode in tool descriptions: zero-width, bidi override, tag-block characters used as a tool-poisoning channel.
MCP-004 MEDIUM Destructive verbs in tool names (delete, drop, wipe, rm, truncate) without an explicit confirmation argument.
MCP-005 LOW Tool ships with no description at all -- host cannot reason about safety.
MCP-006 MEDIUM Permissive input schema: missing schema, additionalProperties: true, or empty properties object.
MCP-007 HIGH Tool argument is a free-form shell-like string (command, cmd, shell, exec, script, bash).
MCP-008 HIGH Duplicate tool names returned by tools/list -- behaviour depends on host tie-break.
MCP-009 LOW Server exposes a very large tool catalogue (high cognitive load + context pollution risk).
MCP-010 HIGH Resource URI uses a sensitive scheme or absolute filesystem path (file:///etc/, file:///home/, etc.).
MCP-011 CRITICAL Transport is unencrypted: bare http:// or stdio over an untrusted boundary.
MCP-012 LOW Server exposes surface (tools / resources / prompts) without declaring the matching capability in initialize.
MCP-013 MEDIUM Server negotiated a protocol version this auditor does not recognise.

Full rule documentation: docs/checks.md.


Install

Prebuilt binaries (recommended)

Grab the archive for your platform from the releases page. Platforms in the v0.1.0 release matrix: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64. Each archive ships with SHA256SUMS and a detached signature; verify before running.

# Linux/macOS
curl -sSL https://github.com/projectboot/pnx-mcp-audit/releases/latest/download/pnx-mcp-audit_linux_amd64.tar.gz | tar xz
./pnx-mcp-audit version

Via go install

go install github.com/projectboot/pnx-mcp-audit/cmd/pnx-mcp-audit@latest

Requires Go 1.22 or newer. The binary lands in $(go env GOBIN) or $(go env GOPATH)/bin.

From source

git clone https://github.com/projectboot/pnx-mcp-audit
cd pnx-mcp-audit
make build      # produces ./bin/pnx-mcp-audit
make test       # runs the table-driven test suite with -race
make smoke      # builds the mock server + runs the audit against it

The build is stdlib-only. The single dependency declared in go.mod is the Go standard library itself, so the supply chain is auditable in a single go list -m all line.


Quick start

Audit a local stdio MCP server (its binary or npx invocation):

pnx-mcp-audit scan stdio --command "/usr/local/bin/my-mcp-server" --fail-on high

Audit a remote Streamable HTTP MCP server:

pnx-mcp-audit scan http --url "https://mcp.example.com/mcp" \
                       --header "Authorization: Bearer $MCP_TOKEN" \
                       --format json \
                       --fail-on critical

Exit codes:

Code Meaning
0 Audit completed; no finding at or above --fail-on.
1 Auditor itself failed (transport error, bad flags).
2 One or more LOW or MEDIUM findings (only if --fail-on low/medium).
3 One or more HIGH findings.
4 One or more CRITICAL findings.

Usage

The CLI exposes three subcommands:

pnx-mcp-audit scan stdio    --command <path>  [flags]
pnx-mcp-audit scan http     --url <url>       [flags]
pnx-mcp-audit checks
pnx-mcp-audit version

Common flags:

Flag Purpose
`--format text json
`--fail-on low medium
--no-color Force monochrome output (CI logs).
--timeout <duration> Per-request timeout (default 30s).
--max-pages <n> Pagination guard for tools/list, etc. (default 32).
--include-mcp <id,id> Run only the listed rules.
--exclude-mcp <id,id> Skip the listed rules.
--probe Enable active probes (MCP-100+). Off by default.
--header <k: v> Repeatable; adds an HTTP header (Streamable HTTP only).

Full flag reference: docs/usage.md.


Output examples

Terminal (default)

pnx-mcp-audit v0.1.0
target:    stdio:///usr/local/bin/my-mcp-server
duration:  812ms
findings:  5  (1 critical, 2 high, 1 medium, 1 low)

[CRITICAL] MCP-011  Insecure transport
  scope:    transport
  detail:   server reachable via plain http://; prompts and tool I/O are
            observable to any on-path attacker
  fix:      terminate TLS in front of the MCP server; require https://

[HIGH] MCP-001  Prompt-injection patterns in tool description
  scope:    tool "send_email"
  detail:   description contains "ignore previous instructions" (offset 142)
  fix:      remove instruction-like prose; describe behaviour, not directives

[HIGH] MCP-007  Free-form shell argument
  scope:    tool "shell_exec" -> input.command (type: string)
  detail:   argument name + type allows arbitrary command strings
  fix:      replace with structured arguments + an allow-list

[MEDIUM] MCP-006  Permissive input schema
  scope:    tool "query" -> input
  detail:   additionalProperties: true
  fix:      set additionalProperties: false and enumerate properties

[LOW] MCP-009  Large tool catalogue (47 tools)
  scope:    server
  detail:   beyond ~30 tools the host model context fills quickly with
            descriptions; consider splitting servers

JSON (machine-readable)

{
  "tool": "pnx-mcp-audit",
  "version": "0.1.0",
  "target": "stdio:///usr/local/bin/my-mcp-server",
  "duration_ms": 812,
  "summary": { "critical": 1, "high": 2, "medium": 1, "low": 1 },
  "findings": [
    {
      "id": "MCP-011",
      "severity": "critical",
      "title": "Insecure transport",
      "scope": "transport",
      "detail": "server reachable via plain http://",
      "remediation": "terminate TLS in front of the MCP server",
      "references": ["OWASP-LLM-02"]
    }
  ]
}

JSON output is stable; new fields are additive. It is intended to be the input to upstream tooling -- SIEM rules, dashboard ingest, evidence packs.

Markdown (for PR review)

--format markdown produces a single Markdown table suitable for posting into a GitHub PR comment via the gh pr comment command.


CI integration

GitHub Actions

name: mcp-audit
on:
  pull_request:
    paths: ['mcp-server/**']
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: '1.22' }
      - run: go install github.com/projectboot/pnx-mcp-audit/cmd/pnx-mcp-audit@latest
      - run: |
          go build -o ./bin/server ./mcp-server
          pnx-mcp-audit scan stdio \
            --command "./bin/server" \
            --format json \
            --fail-on high \
            > mcp-audit.json
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mcp-audit-report
          path: mcp-audit.json

GitLab CI

mcp-audit:
  image: golang:1.22
  script:
    - go install github.com/projectboot/pnx-mcp-audit/cmd/pnx-mcp-audit@latest
    - pnx-mcp-audit scan stdio --command "./bin/server" --fail-on high --format json > mcp-audit.json
  artifacts:
    when: always
    paths: [mcp-audit.json]

Generic shell

pnx-mcp-audit scan stdio --command "./server" --fail-on high
RC=$?
[ "$RC" -ge 3 ] && { echo "audit failed (rc=$RC)"; exit 1; }

More recipes: docs/usage.md.


Try it against the included mock server

The repository ships an intentionally vulnerable MCP server in examples/mock-server/. It exposes a tool with a prompt-injection-laden description, a free-form command argument, a duplicate tool name, and a file:///etc/ resource URI. Use it to verify your install end-to-end:

make mock                                      # build ./bin/mock-server
./bin/pnx-mcp-audit scan stdio \
  --command "./bin/mock-server" \
  --fail-on high
# expected exit code: 3

make smoke chains build + mock + audit + exit-code assertion into a single target, used by CI as the integration test.


Architecture

  pnx-mcp-audit (single static binary)
  -----------------------------------
              cmd/pnx-mcp-audit/main.go       (flag parsing, subcommands)
                          |
                          v
              internal/mcp/                   (transport + JSON-RPC client)
              internal/audit/                 (Severity, Finding, Registry)
              internal/checks/                (MCP-001 ... MCP-013)
              internal/report/                (text / JSON / Markdown)
              internal/version/               (build metadata)
                          |
                          v
                       stdout

  No network egress except to the MCP target.
  No telemetry. No phone-home. No analytics.

The transport layer is split per protocol. transport_stdio.go spawns the target as a child process and frames JSON-RPC messages over its stdin/stdout. transport_http.go speaks the MCP Streamable HTTP profile, including the SSE chunking that some servers emit on tool responses.

The check engine is a registry of pure functions over the captured server snapshot (tools, resources, prompts, serverInfo, negotiatedVersion). Adding a rule means writing one function plus a table-driven test. Active probes (--probe) run in a separate phase against a deliberately restricted client to avoid side effects on the target.


What this tool does NOT do

To set expectations correctly:

  • It does not test LLM safety. It tests the MCP server that an LLM might connect to. The LLM itself is out of scope.
  • It does not modify state on the server. All checks are read-only against initialize, tools/list, resources/list, prompts/list. Active probes are gated behind --probe and explicitly documented per rule.
  • It does not catch logic bugs in tool implementations. It catches descriptor-level safety issues. Runtime behaviour of a tool requires a different class of testing (which Penaxtra ships in its commercial platform).
  • It does not ship rules-as-data updates over the wire. New rules ship in new releases; the binary you run yesterday will produce the same report tomorrow. Reproducibility is a feature.

Roadmap

  • MCP-014 ... MCP-019: depth checks on prompt templates, resource MIME- type drift, recursive tool references, schema-vs-example contradictions.
  • MCP-100 ... MCP-110 (active probes): instruction-confusion echo, parameter-fuzzing for permissive schemas, large-response truncation behaviour, capability misreporting, partial-result handling.
  • WebSocket transport: a third transport once the MCP WebSocket profile is final.
  • SARIF output: optional --format sarif for native GitHub Code Scanning ingest.

The roadmap is intentionally narrow. Anything that requires shipping a service or accepting customer data lives in the commercial Penaxtra platform, not here.


FAQ

Does this replace a manual MCP server code review? No. It catches the descriptor-level issues a reviewer would otherwise spot manually, freeing the human reviewer to focus on logic, state-handling, and authn/authz boundaries.

Does it work without an internet connection? Yes. The audit logic is fully local. The only network traffic is to your MCP target when you scan a remote HTTP server.

Does it phone home? No. There is no telemetry, no usage analytics, no checksum upload. The only network calls are the ones you ask for via scan stdio or scan http.

Is the rule set extensible? Yes. Rules are plain Go functions registered into a Registry. See CONTRIBUTING.md for the process and internal/checks/static.go for examples.

Does it map to OWASP / NIST / MITRE? Yes. Findings carry references such as OWASP-LLM-02, OWASP-LLM-06, MITRE-ATLAS-TA0043, where applicable. The commercial Penaxtra platform extends this with EU AI Act, NIST AI 600-1, and ISO 42001 control mapping.

What is the licence? Apache-2.0. Use it in commercial products, modify it, redistribute it. See LICENSE and NOTICE.


Security

If you find a vulnerability in this tool itself, please disclose it privately. The process and timelines are documented in SECURITY.md.

If you find an MCP server in the wild that triggers a new class of issue this tool does not catch, open an issue with a minimal reproducible sample (redact secrets first). New rules ship with MCP-NNN IDs and stay backward compatible.


Contributing

Contributions are welcome, especially:

  1. New check rules backed by a real-world MCP server sample.
  2. False-positive / false-negative bug reports with a fixture.
  3. Active probes (MCP-100+) with a written safety argument.
  4. CI recipes for additional platforms.

Read CONTRIBUTING.md before opening a PR. Every PR must pass go vet, gofmt, and go test -race -count=1 ./....


About Penaxtra

Penaxtra is an enterprise AI Security Posture Management (AI-SPM) platform built by Seccops Siber Güvenlik Teknolojileri A.Ş., headquartered in Istanbul, Turkiye. Penaxtra runs continuous adversarial test suites against production LLM applications, maps findings to OWASP LLM Top 10, OWASP Agentic Top 10, NIST AI 600-1, MITRE ATLAS, EU AI Act, and ISO/IEC 42001, and produces audit-grade evidence packs for regulated mid-market customers in Europe and Turkiye.

pnx-mcp-audit is one open-source slice of Penaxtra's wider check catalogue. The commercial platform additionally provides:

  • A multi-judge adversarial scanning pipeline (three independent LLM judges plus a meta-judge for consensus and bias reduction).
  • A runtime AI gateway agent that enforces signed DLP policy at the request boundary, with zero prompt bytes leaving customer infrastructure.
  • Continuous compliance evidence collection across six regulatory frameworks, exportable as auditor-ready PDF or JSON.
  • Risk scoring across applications, models, datasets, prompts, and tools.

If your team is shipping production LLM workloads under a compliance obligation (EU AI Act, ISO 42001, banking / healthcare / insurance / public sector), start a conversation with Penaxtra.

Founder

pnx-mcp-audit is authored and maintained by Tolga SEZER, Founder of Penaxtra. Tolga has spent more than fifteen years on the offensive and defensive sides of enterprise security, leading penetration testing and red-team engagements for banking, public sector, and critical-infrastructure customers before turning to the LLM security problem space full-time.

"MCP servers are the new high-leverage attack surface for LLM applications. Treating them like any other untrusted service is the minimum bar. pnx-mcp-audit is the smallest possible tool that lets a security engineer enforce that bar in CI on day one." -- Tolga SEZER

Read more on penaxtra.com

Topic Link
What AI-SPM is and why regulated teams need it penaxtra.com/ai-security-posture-management
Runtime AI gateway architecture (zero prompt egress) penaxtra.com/platform/runtime-ai-gateway
MCP server security in production penaxtra.com/platform/mcp-security
MCP security checklist for engineering teams penaxtra.com/learn/mcp-security-checklist
Multi-judge consensus methodology penaxtra.com/methodology/judge-consensus
OWASP LLM Top 10 coverage map penaxtra.com/compliance/owasp-llm-top-10
OWASP Agentic Top 10 coverage map penaxtra.com/compliance/owasp-agentic-top-10
Case study: prompt-injection cross-customer data leak penaxtra.com/case-studies/chatbot-prompt-injection-cross-customer-leak

Citation

If you use pnx-mcp-audit in academic or industry research:

@software{pnx_mcp_audit_2026,
  title        = {pnx-mcp-audit: A static security auditor for Model Context Protocol servers},
  author       = {SEZER, Tolga},
  year         = {2026},
  publisher    = {Penaxtra},
  url          = {https://github.com/projectboot/pnx-mcp-audit},
  note         = {Apache-2.0 licensed}
}

Licence

Copyright 2026 Penaxtra

Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Full text: LICENSE. Attribution requirements: NOTICE.


Built and maintained by Penaxtra - AI Security Posture Management for regulated teams.
penaxtra.com · Contact · Founder on LinkedIn

About

Security audit CLI for Model Context Protocol (MCP) servers. Zero deps, offline, cross-platform.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages