MCP-001 Unpinned npx / uvx registry install
An MCP server entry runs npx <pkg> or uvx <pkg> against a registry
package with no version pin. Both runners resolve the package fresh
on every host launch. A publisher account takeover, a maintainer
swap, or a typo on the publisher's next push lands directly inside
the process your LLM client has agreed to run, with whatever
filesystem and credential reach the host grants.
This is the simplest supply-chain trap in the MCP space and the easiest one to close: pin the version.
What Vulkro detects
Rule fires when an mcpServers[*] entry in any MCP host config has:
commandofnpxoruvx.- A first positional argument that is a registry package spec (not a local path, not a git URL).
- No version pin on the spec.
Pin detection is per-runner:
- npm-style (
[email protected],@scope/[email protected],pkg@^1.0.0, tags likepkg@beta) fornpx. Bare@scope/pkgis NOT treated as pinned (the leading@is the scope marker, not a version). - Python
name==version(or npm-style as a fallback) foruvx.
Skipped: local paths (./bin/server, /usr/local/..., ~/...),
git URLs (handled by MCP-002).
Severity: Medium by default. Escalates to High when the server
has filesystem reach (the package name contains server-filesystem,
filesystem-server, mcp-server-fs, or server-fs); a fresh
resolve of a typosquatted filesystem server can read everything the
mount points at.
Evidence signal: mcp-server-unpinned-npx, weight 0.8, source
Pattern. Confidence: High.
Non-compliant config
{
"mcpServers": {
"fs": {
"command": "npx",
"args": [
"@modelcontextprotocol/server-filesystem",
"/Users/me/work"
]
},
"github": {
"command": "uvx",
"args": ["mcp-server-github"]
}
}
}
Both entries re-resolve on every Claude Desktop / Cursor / Windsurf / VS Code launch.
Compliant config
{
"mcpServers": {
"fs": {
"command": "npx",
"args": [
"/Users/me/work"
]
},
"github": {
"command": "uvx",
"args": ["mcp-server-github==0.4.2"]
}
}
}
Remediation
- Add an explicit version to the package spec. For npm,
[email protected]or@scope/[email protected]. For uvx targeting PyPI,pkg==x.y.z. - Update the pin in a controlled review when you genuinely want the newer build. The supply-chain footgun is silent re-resolution at launch time, not the act of updating.
- If you need a moving label for development, prefer
pkg@<commit-sha>against a known release tag rather than a bare name.
See also
vulkro mcp-audit- parent CLI command.- MCP-002 - the sibling rule for git-source installs with a mutable ref.
- MCP-006 - exact-version catalog hit (known-compromised release).
- Confidence model - what
High,Medium,Lowmean for MCP findings.