Skip to content

Methodology

anlyon.dev reads what a server says its tools are (names, descriptions, input schemas and annotations) and runs fixed rules over that text. The same input always gives the same result. No model writes a score, and no finding is not a safety guarantee.

On this pageShow

The four groupsLink to this section

Every tool lands in exactly one group, the most consequential one its definition supports. The permissions block follows the group.

  • Deletes or cancelsdestructive

    Removes, cancels or revokes something.

    Claude Code: deny

  • Can't be undoneirreversible

    Sends, pays, publishes or merges: once done, it cannot be taken back.

    Claude Code: ask

  • Changes thingswrite

    Creates or changes something that can be changed back.

    Claude Code: ask

  • Only readsread

    Looks things up and changes nothing.

    Claude Code: allow

Tools come from the server itself, through an anonymous initialize and tools/list. anlyon.dev never signs in, never calls a tool, and never runs a third-party package.

How a tool is sorted into a groupLink to this section

The verb in the tool name#class-name-verb
The first known verb in the tool name sets its class: read words such as "get", "list" and "search" give read; change words such as "create", "update" and "add" give write; money words ("refund", "charge", "payout"), send words ("send", "notify", "reply") and final words ("merge", "publish", "transfer") give irreversible; removal words ("delete", "remove", "cancel", "archive", "revoke") give destructive. "post" followed by "message" or "email" is a send. In a name that is not a read, a money or send word right after a plain verb ("create_refund") and a second verb after "and" also count. A money word followed by "method", "link" or "setting" is configuration, not a payment.
The first verb of the description#class-description-verb
The first verb of the description's first sentence, after words like "Use this tool to", is read with the same word lists. It sets the class when the name has no known verb, and otherwise can only raise it.
Wording that says a change is final#class-final-wording
A description that says "cannot be undone", "irreversible" or "permanently delete" raises the class to at least irreversible.
An amount and a currency in the input#class-money-input
An input field named amount with a currency field beside it, at the top level or one object down (amount_money.amount), raises a tool that is not a read to irreversible, because it moves money. Fields such as unit_amount on a price or amount_off on a coupon do not count: they define a number for later.
Recipients in the input#class-recipient-input
An input field named "to" or "recipients", at the top level or one object down, raises a tool that is not a read to irreversible, because what it sends cannot be taken back.
A statement or command in the input#class-code-input
A top-level string input named "sql", "statement", "command", "cmd", "script", "code", "javascript" or "shell" raises a read to write, because what it runs can change anything it can reach.
No known verb#class-default-write
A tool whose name and description name no known verb is counted as a write. A read has to be named, because a read is allowed by default in the permissions block.
Annotations can only raise a class#class-annotations
MCP annotations are the server's own hints. destructiveHint: true raises the class to destructive, and readOnlyHint: false raises a read to a write. readOnlyHint: true and destructiveHint: false never lower a class: where they disagree with the tool's own words, the scan reports an annotation conflict instead.

What the findings look forLink to this section

Hidden Unicode#hidden-unicode
Characters a person cannot see but a model reads: zero-width characters (U+200B to U+200D, U+2060 to U+2064, U+FEFF, U+180E), bidirectional controls (U+200E, U+200F, U+061C, U+202A to U+202E, U+2066 to U+2069), and tag characters (U+E0000 to U+E007F), which can spell out ASCII text. Checked in the name, titles, description and every string and property name in the input schema. Evidence shows each character as U+XXXX and, for tag characters, the text they spell.
Text addressed to the model#model-directed-text
Phrases that address the model rather than describe the tool: tags such as "<IMPORTANT>" or "<SYSTEM>", "ignore previous instructions", "do not tell the user" and similar instructions to keep something from the user, and "before using this tool, read". These are the patterns of published tool-poisoning examples. Checked in the titles, description and input schema strings.
References to secrets on disk#sensitive-path
Paths where keys and secrets usually live: ~/.ssh, id_rsa and other SSH key names, .env files, ~/.aws and AWS key names, credentials files and .git-credentials, MCP client configs (mcp.json, claude_desktop_config.json, ~/.cursor/), .netrc, .npmrc, .pypirc, .pgpass, .gnupg, Docker and Kubernetes configs, and /etc/passwd or /etc/shadow.
The word "credentials"#credentials-mention
The word "credential" or "credentials" outside a path. Many tools mention credentials for ordinary reasons, so this is information only.
Mentions of other tools or servers#tool-shadowing
A tool that names a tool this server does not list (for example "the send_email tool", "mcp_tool_send_email" or "mcp__other__send_email"), or uses wording such as "other servers", "all other tools" or "when this tool is available". A description that steers how the model uses another server's tools is how tool shadowing works. A name counts when it is written as an identifier (send_email, sendEmail) or in quotes, next to the word "tool". Mentions of the server's own tools are not reported.
Base64 strings#base64-blob
A run of 40 or more base64 characters that mixes upper case, lower case and digits, or ends in = padding, outside a URL. When it decodes to readable text, the text is shown, and the finding is high when that text contains model-directed phrases.
Long descriptions#long-description
A description over 2,000 characters. Long descriptions are not wrong in themselves, but they are where extra instructions are easiest to miss, so the length is shown.
Annotations that disagree with the tool#annotation-conflict
Where the server's annotations disagree with its own definition: readOnlyHint: true on a tool whose name, description or input give a higher class (a tool named "delete_*", for example), destructiveHint: false on a tool whose words say it removes something, destructiveHint: true on a tool named as a read, readOnlyHint: false on a tool named as a read, and readOnlyHint and destructiveHint both true.
Install scripts that reach the network#install-script-network
A "preinstall", "install" or "postinstall" script in the package.json that contains curl, wget, fetch or http. npm runs these scripts on install, before the server is started. Read from the published package manifest; the package is never run.
Install scripts#install-script
A "preinstall", "install" or "postinstall" script in the package.json that does not reach the network by these patterns. Information only: npm runs it on install.
Duplicate tool names#duplicate-tool-name
Two or more tools with the same name in one tools/list. A client keeps one of them, and which one depends on the client.

Hashes, permissions and what a scan isLink to this section

The definition hash#definition-hash
Each scan records the sha256 of the server's normalised tool definitions (name, title, description, input schema and annotations, with keys sorted and tools sorted by name). A different hash means a tool definition changed, and the change is shown as a diff.
The Claude Code permissions block#claude-code-permissions
A block for .claude/settings.json with one mcp__<server>__<tool> rule per tool: read tools in allow, write and irreversible tools in ask, destructive tools in deny. It is built from the same classes as the scan, as a starting point to review.
What a scan is and is not#scope
A scan reads tool definitions only: the tools/list a server publishes and its package manifest. It never runs the package, calls a tool or signs in. Every finding is evidence with a place and an excerpt, found by fixed rules with no model in the loop, and the same definitions always give the same result. No finding is not a safety guarantee: a check only sees the patterns it looks for, and a scan is a point in time.

How Agent Skills are checkedLink to this section

A skill is a folder with a SKILL.md and, often, scripts. Its files are read from GitHub as text and never run. A skill gets every label that applies; the first one below is the most consequential.

Destructive commands
Ships or asks for commands that delete or overwrite: rm -rf, force pushes, DROP TABLE and the like.
Reaches the network
Its scripts or instructions fetch from or send to the network.
Runs commands
Allows a shell or ships scripts the agent runs.
Edits files
Allows the agent to write or edit files.
Instructions only
Text only: no shell, no file writes, no scripts.
Tools the skill allows#skill-allowed-tools
The allowed-tools line in SKILL.md's frontmatter. Bash allows commands; Write, Edit, MultiEdit and NotebookEdit allow file changes; WebFetch and WebSearch reach the network. A narrowed form such as Bash(git status:*) still counts as a shell.
Scripts it ships#skill-scripts
Python, shell, JavaScript, TypeScript, Ruby and PowerShell files in the skill's folder are scripts the agent can run. Their text is read for network calls, destructive commands and secret paths. They are never run.
Network calls#skill-network
curl, wget, requests, urllib, httpx, fetch(, axios, Invoke-WebRequest, or an http(s) URL in a script. A URL the instructions tell the agent to open counts too.
Destructive commands#skill-destructive
rm -rf, shutil.rmtree, git push --force, git reset --hard, git clean -fd, DROP TABLE or DATABASE, TRUNCATE, kubectl delete, terraform destroy and similar, in a script or in an instruction.
Code fetched and run#skill-remote-code
A download piped straight into a shell, such as curl ... | sh, in a script or an instruction.
Commands built at run time#skill-dynamic-exec
A script that passes a command string to a shell (shell=True, os.system, child_process, execSync) or evaluates code (eval, exec). Common and often fine; it means what runs depends on input.
Secrets on disk#skill-secret-paths
~/.ssh, id_rsa, .env files, ~/.aws/credentials, .netrc, .npmrc tokens, mentioned in a script or the instructions.
Hidden characters#skill-hidden-unicode
Zero-width, bidi-control and tag characters in SKILL.md or a script, which a reader cannot see and a model can.
Instructions to hide things or skip checks#skill-model-directed
Phrases such as "do not tell the user", "ignore previous instructions", "without asking", "skip confirmation" or <IMPORTANT> tags in SKILL.md.
Bundled binaries#skill-binaries
Executables or libraries (.exe, .dll, .so, .dylib, .bin) in the skill's folder, which no text check can read.

Found a wrong group or a missed pattern? Tell us.