Fix mCP server declaration resolves code at run time in Claude Code

An MCP server is launched with `npx -y`, `uvx`, `bunx`, or a container image with no digest, or is reached over a remote transport. The code that gets a tool call today is not necessarily the code that got one yesterday, and there is no version in the repository to review.

medium likely Claude Code CWE-1357 / ASI ASI04

The vulnerable pattern in Claude Code

MEDIUM likely active MCP server declaration resolves code at run time ASI04 .mcp.json:4:18 2 │ "mcpServers": { 3 │ "db": { 4 │ "command": "npx", │ ~~~~~ resolves the package when the server starts 5 │ "args": [ 6 │ "-y",

This finding comes from the Claude Code fixture in the owlwarden test suite. `db` resolves its package at run time with no version pinned, so today's code is whatever the registry serves. Whoever controls what that name resolves to controls a process holding tool access to this workspace.

The corrected configuration

Pin the exact version in `.mcp.json` or `.claude/settings.json`. Better: add the server to `devDependencies` and launch it with `pnpm exec`, so the lockfile is the record of what runs.

// .mcp.json
{
  "mcpServers": {
    "db": { "command": "pnpm", "args": ["exec", "mcp-db"] }
  }
}

If you are not using Claude Code

Pin the version or the digest, and prefer a dependency in `devDependencies` over a run-time resolve. For a remote transport, state the trust decision in the config - a comment naming who runs the endpoint - rather than leaving it implicit.

Check your own repository

npx owlwarden scan
npx owlwarden explain agent-mcp-unpinned-remote

Runs on your machine. No account, no telemetry, no network unless you ask. In CI, SARIF uploads to code scanning and the exit code is the gate.

Other Claude Code checks

Rules with a tested Claude Code example.

agent-mcp-unpinned-remote for every agent host / All rules / owlwarden