Checks

August 22, 2026 · View on GitHub

Four checks run on every scan. Each produces Findings with a severity; the highest severity decides the exit code via --fail-on (default high).

Check idWhat it comparesNeeds a lockfile?
rug-pullCurrent definitions against toolprint.lockYes
tool-poisoningAgent-readable text against 11 injection signalsNo
secret-leakConfig values and capability text against 18 credential formatsNo
tool-outputWhat a tool returns, under --probeNo

Precision over recall

One false "this server is malicious" accusation is worse than a missed low-signal case, because a gate that cries wolf gets turned off. Every heuristic pattern is anchored to concrete, security-relevant nouns rather than generic verbs, and each one ships with a near-miss test that must not fire.

The one check with no heuristic at all is rug-pull: it is a deterministic hash comparison, so gating on it can never cost you a false positive. That is why it is high by default.

rug-pull

Everything you pinned is hashed with SHA-256 over its complete definition — name, description, input and output schema, title, annotations — minus volatile _meta. Any difference is drift.

SituationSeverity
A pinned capability's description changedhigh
A pinned capability's schema or metadata changedhigh
A pinned capability disappearedhigh
A new, never-pinned capability appearedlow
The whole server has no lockfile entry yetinfo

A changed description is the classic tool-poisoning vector, but schema drift matters just as much: a new parameter can widen what a tool receives without a word of the description changing.

Applies to every capability kind — tools, prompts, resources, resource templates, and skills.

tool-poisoning

Instruction injection hidden anywhere the model reads. toolprint walks the entire capability object and inspects every description, title, and instructions string it finds — at the top level, in tool input/output schemas, in prompt arguments, in resource metadata, in a skill's body — tagging each hit with the JSON path where it was found.

SignalWhat it models
ignore-instructions"Ignore all previous instructions" and variants
hide-from-user"Do not tell the user", "never mention this"
exfiltrationSending .ssh, .aws, .env, keys, or tokens somewhere
read-sensitive-filesReading id_rsa, .ssh, .aws, .netrc, /etc/passwd
before-other-tools"Before using any other tool, first…" — a covert precondition
model-scaffoldingChat-template tokens: <<SYS>>, [INST], <|system|>
no-confirmation"Without asking the user", "no need to confirm"
decode-execute"Base64-decode this and run it"
hidden-directive-tag<IMPORTANT>, <SECRET>, <SYSTEM> pseudo-tags
hidden-unicodeZero-width and bidirectional-control characters
encoded-blobA long base64-like run where prose belongs

The critical tier

When two or more independent high-severity signals land on the same capability — say an instruction-override and hidden unicode — toolprint collapses them into one critical "combined attack" finding. Any one signal can be a coincidence; that combination essentially never is.

secret-leak

Live-looking credentials in two places:

  1. Your MCP configenv values, headers, and the url of each server entry. These are usually committed, which is what makes them worth catching.
  2. Capability text — a key accidentally pasted into a description.

18 formats are recognized, including OpenAI (sk-…), Anthropic (sk-ant-…), AWS access keys, GCP service-account keys, GitHub tokens (ghp_, gho_, …), Hugging Face (hf_), Stripe, Slack, npm (npm_), SendGrid (SG.), Google OAuth (GOCSPX-), Azure connection-string keys, and database URIs with embedded credentials (postgres://user:pass@…).

Matches are always redacted before they reach stdout, JSON, SARIF, or a PR comment.

Credentials you pass at runtime — --bearer, --header, TOOLPRINT_BEARER, TOOLPRINT_HEADER_* — are intentional and are never flagged, and never written to the lockfile. See Authentication.

tool-output

Only produces findings when you opt in with --probe. It runs the same poisoning and secret engines over what a tool actually returns, catching an attack staged in output rather than in a description. Output that combines injected instructions with a leaked secret escalates to critical.

Tuning what fails

npx toolprint scan --fail-on critical   # only the combined-attack tier fails
npx toolprint scan --fail-on low        # gate new, unpinned capabilities too

For a specific finding you have reviewed and accepted, use Suppressions rather than lowering the gate for everything.