Skip to content

AP011: MCP server runs an unpinned package

Severity medium
Runs in scan
One-line fix Pin the package to an exact version (npx -y pkg@1.2.3, uvx pkg==1.2.3, image:1.2.3 or image@sha256:…) so the server only changes when you change the config.

An MCP server whose command installs a package each time it starts, with no exact version:

  • npx, npm exec, pnpm dlx, yarn dlx or bunx with a package that has no @<version>, or only @latest, another tag or a range such as @^1.2.0;
  • uvx, uv tool run or pipx run with a package that has no ==<version> (also through --from or --spec);
  • docker run or podman run with an image that has no tag, or the latest tag, and no @sha256: digest, or a tag pulled again on every start with --pull=always;
  • deno run with an npm: package that has no version;
  • a git source with no commit, or a remote archive whose address names no version.

It reads every agent’s MCP config, and the MCP config that installed plugins and extensions carry. The print names the server and shows the pinned form to write instead.

An unpinned server runs whatever version was published last, with your permissions, every time the agent starts. A hijacked maintainer account or a new malicious release reaches you without anything changing on your disk.

Pin an exact version: npx -y pkg@1.2.3, uvx pkg==1.2.3 (or uvx --from pkg==1.2.3 cmd), image:1.2.3 or image@sha256:<digest>. Choose the version you use now, after reading its release notes. agentprint fix never changes the config: the version is your call.

  • Local programs and scripts are not packages: node server.js, ./bin/server, uv run, and a runner such as npx tsx ./server.ts that starts a local file are never printed.
  • A command built from variables (${IMAGE}) is not read.
  • A pinned version can still be a bad one: pinning makes a change visible, it does not vouch for the code.