Skip to content

fix(mcp): only derive a package from the manager's executor subcommand - #34

Open
alexviktorov wants to merge 1 commit into
perplexityai:mainfrom
alexviktorov:fix/mcp-run-script-detection
Open

alexviktorov wants to merge 1 commit into
perplexityai:mainfrom
alexviktorov:fix/mcp-run-script-detection

Conversation

@alexviktorov

@alexviktorov alexviktorov commented May 27, 2026

Copy link
Copy Markdown

What

For npm/pnpm/yarn/bun, derive a package identity only from the manager's
package executor: npm exec/npm x, pnpm dlx, yarn dlx, bun x (or the separate
npx/bunx). Any other first token runs a local script, file, or binary, so it yields
no spec and the record falls back to the server id at confidence=low — the same rule the
parser already applies to uv run <script>.

Bug

MCP servers launched through a script runner had the script or file name recorded as the
package. The claude-plugins-official messaging plugins launch their bundled server with

{ "command": "bun", "args": ["run", "--cwd", "${CLAUDE_PLUGIN_ROOT}", "--shell=bun", "--silent", "start"] }

and a baseline scan of a machine with them installed records package_name="start" for
discord, imessage, telegram and fakechat. start is a real npm package, so a catalog
entry for it would spuriously match. The same leak shows up as:

launcher main records expected
bun run … start start server id
bun server.ts / bun run src/index.ts server.ts / src/index.ts server id
bare yarn dev dev server id
yarn exec node dist/index.js, pnpm exec node … node (a real npm package) server id

exec fetches a published package only under npm. pnpm exec runs a locally installed bin,
and yarn/bun exec run a shell command, so it is not an executor there — hence a
per-manager packageExecutors table rather than one shared subcommand set.

Evidence

The new unit rows and corpus entries fail on main with the leaked values (13 rows, 5 entries):

inferPackageFromArgs("bun",[run --cwd /x --shell=bun --silent start]) = ("start",""), want ("","")
inferPackageFromArgs("bun",[run src/index.ts]) = ("src/index.ts",""), want ("","")
inferPackageFromArgs("yarn",[exec node dist/index.js]) = ("node",""), want ("","")
discord: got package_name="start" package_manager="mcp", want "discord"/"mcp"

Package executors are unchanged: npx/bunx, --package, the -- terminator and
credential-bearing value flags keep their identity (existing tests, plus a new
--registry … exec @scope/pkg row).

Trade-off, pinned in a test: a bare yarn <installed-bin> also falls back to the server id.
The scanner cannot tell a script from a bin without reading package.json, and a
low-confidence server id beats a wrong package guess.

Files

internal/ecosystem/mcp/mcp.go (per-manager packageExecutors, doc comment),
internal/ecosystem/mcp/mcp_test.go, docs/inventory-sources.md. Rebased on current
main; go build, go vet, gofmt -l, go test -race ./... and bumblebee selftest pass.

@alexviktorov
alexviktorov force-pushed the fix/mcp-run-script-detection branch from a600a96 to b05327c Compare May 27, 2026 19:18
@alexviktorov

alexviktorov commented May 29, 2026

Copy link
Copy Markdown
Author

@adel-pplx Let me know if you need any context on this. Basically after scanning my Mac I saw several records listing "start" as the package, where it's evidently a command. Specifically it was for some Claude official plugins. This PR fixes the discrepancy and instead you get the actual names, similar to other places in code. Tested on my local config and generated synthetic unit tests from real world examples. Thank you!

@alexviktorov
alexviktorov force-pushed the fix/mcp-run-script-detection branch from b05327c to 189cd77 Compare July 5, 2026 06:52
MCP servers launched through a package-manager script runner leaked the
script or file name as the package. The official Claude messaging plugins
use `bun run … start` and were recorded as package_name "start" — a real
npm package, so an advisory for it would spuriously match. `bun server.ts`
/ `bun run src/index.ts` leaked the file path, and bare `yarn dev` /
`pnpm build` leaked the script name the same way.

The parser already handles this for uv: `uv run <script>` without --from
yields no package identity and the record falls back to the server id at
low confidence. Apply the same rule to the npm family, per manager: only
`npm exec`/`npm x`, `pnpm dlx`, `yarn dlx` and `bun x` (or the separate
npx/bunx) fetch and run a published package. `exec` under pnpm, yarn and
bun runs a local bin or a shell command, so it is not an executor there
(`yarn exec node dist/index.js` must not become the package "node").
Every other first token — `run <script>`, lifecycle aliases, bare
scripts, files, create/init — returns no spec.

Executor launches are unchanged: --package, the -- terminator and
credential-bearing value flags keep their identity. Trade-off: a bare
`yarn <installed-bin>` also falls back to the server id, the same
conservative choice as `uv run`.

Updates the inferPackageFromArgs doc comment and docs/inventory-sources.md;
adds unit rows and corpus entries that fail on main with the leaked values.
@alexviktorov
alexviktorov force-pushed the fix/mcp-run-script-detection branch from 189cd77 to cea5055 Compare September 15, 2026 20:32
@alexviktorov alexviktorov changed the title fix(mcp): only derive a package from executor subcommands fix(mcp): only derive a package from the manager's executor subcommand Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant