ax-check rule
AXC-F003: Claimed npm package does not exist
An install command names an npm package that the npm registry does not know.
ax-check is a checker being prepared for release. This page documents the rule ahead of that release; see all 50 rules.
| Severity | error |
| Kind | Observed. Depends on the program and environment at run time. |
| Mode | ax-check drift |
| Applies to | Instruction files and manifests |
| Pattern tags | drift, prompt-injection |
| Fix in one line | Correct the package name, or publish the package before telling agents to install it. |
The file contains an install or run command for an npm package, and the npm registry has no package with that name. The instructions cannot work. They are also a security risk, because anyone can register a name that is not yet taken.
What it checks
ax-check reads install and run commands from code spans and fenced code blocks. It recognises npm install, npm i, npm add, pnpm add, yarn add, bun add, npx, bunx, pnpm dlx, yarn dlx and npm exec. In JSON files it also reads command and args pairs (as in .mcp.json) and server.json entries whose registryType is npm. For each package name it asks the npm registry for the package’s metadata. The rule fires when the registry answers 404.
Why it matters
The first reason is simple: the command fails, and the agent has to guess a replacement.
The second reason is security. An npm package name that nobody has registered is open to everyone. If your instructions name a package that does not exist, someone else can publish a package under that name. An agent that follows the instructions will run npm install or npx on whatever now lives at that name, usually without a person reading it first. The agent trusts the file, and the file now points at code written by a stranger. A typo in the name has the same effect, as does a package you meant to publish and never did.
Check names against the registry before you tell agents to install them.
How to fix
- Correct the package name if it is a typo or an old name.
- If the package is yours and unpublished, publish it before you tell anyone to install it. Publishing also stops others from taking the name.
- If the package was removed on purpose, delete the command from the file.
Example
Find it:
ax-check drift --online AGENTS.md
Before
## Setup
Install the helper first:
npm install -g invoice-kit-cli
Then run `npx invoicekit-lint` before committing.
The registry has invoicekit-cli but not invoice-kit-cli.
After
## Setup
Install the helper first:
npm install -g invoicekit-cli
Then run `npx invoicekit-lint` before committing.
How ax-check detects it
The extraction and the rule logic are deterministic, but the result depends on what the npm registry answers at the time of the run, so the same file can give different findings on another day. JSON and SARIF reports record when and where the check ran. It tokenises each command, finds the install verb, and takes the package names that follow. For npx, bunx, dlx and exec it takes only the first package. It asks https://registry.npmjs.org/<name> for each distinct name (the / in a scoped name is encoded). Only a 404 triggers this rule. If the registry answers with another error or cannot be reached, the package could not be checked, and the result is reported as AXC-F002 instead.
It deliberately ignores flags such as -g, relative and absolute paths, git:, https: and file: specifiers, .tgz files, and placeholders such as <package>, your-package, my-tool, example-pkg and .... It reads a version suffix such as name@1.2.3; a missing version is reported by AXC-F005.
Known limits:
- A private package that lives in a private registry looks missing to the public registry. Silence the rule for such files.
- Commands written in prose rather than in a code span or fenced block are not read.
- A package that exists but is not the one you intended (for example a look-alike name) is not detected. This rule only proves existence.
Run ax-check drift --online --dry-run AGENTS.md to list each package that would be looked up, without making a request. Offline runs do not check packages. AXC-F011 reports how many were left unchecked. To silence this rule, use --disable AXC-F003.
Sources
- Specification: npm registry package metadata. https://registry.npmjs.org/
. A request for a package that does not exist returns 404, which is the signal this rule uses. - Site guide: Agent tool catalogs, agentexperience.tech. https://agentexperience.tech/insights/agent-tool-catalogs/ . Background on catalogues of tools and servers that agents are pointed at.
Related evidence
Records in the AX evidence register that share a pattern tag with this rule. A shared tag means the record is about the same pattern, not that it tests this rule. Read the evidence class before the number.
- EV-0015: Approvals that outlive their task raise attack success (Preprint). A preprint reports that approvals persisted beyond the context that justified them raised prompt-injection attack success by up to 35.1 percentage points on 508 AgentDojo cases, and by 24.9 points on average in live tests on three production coding agents.
- EV-0026: Prompt injection split across tool channels evades defences (Preprint). Across 12 frontier models and over 15,000 trials (preprint), models that resisted single-channel prompt injection exfiltrated data at up to 100% when the payload was split across two channels, such as a tool description and a tool result, and seven third-party MCP security tools failed to detect it.
- EV-0039: Launch post and documentation disagree on agent output format (Independent measurement). Cloudflare's cf launch post says JSON output is 'condensed for agents', but the cf documentation says JSON output is indented whether or not output is a terminal, and a public issue reports byte-identical output with an agent detected.
- EV-0040: Agent skills recommended a package that does not exist (Vendor measurement). Merged pull requests in Vercel's agent plugin repository corrected skill instructions that recommended an npm package that is not published, and plugin guidance that advertised deployment cards the production MCP server does not expose.
