Avoid install scripts in your MCP package
What we checked
We looked at your package metadata for install-time scripts: code the package manager runs automatically during installation, before you’ve run a single intentional line. The signal passes when no such scripts are declared. If your package declares one, it’s flagged as an un-assessed risk factor: a hook we could not review. If our scanning provider has since assessed that hook, we record the risk as assessed instead. And if we couldn’t tell either way whether scripts are declared, the signal is unverifiable and scores 0 until we can assess it.
Why it matters
An install script runs with your shell’s permissions the moment a user (or an
agent) types install, with no review, no opt-in, often no awareness it happened.
That makes it one of the most heavily abused vectors in supply-chain attacks:
- a hijacked or typosquatted package can exfiltrate environment variables,
tokens, or SSH keys from a
postinstallhook; - the malicious code never appears in the source a reviewer reads at runtime, so it hides in plain sight;
- because it fires on install, CI machines and developer laptops are exposed before any test or sandbox boundary applies.
A server that needs no install script is strictly safer to add, and consumers increasingly install with scripts disabled by default.
How to fix it
This is primarily an npm/Node concern. Move any work a script was doing into
an explicit runtime step or a documented CLI command the user runs deliberately,
and delete the install/postinstall/preinstall hooks from package.json:
{
"name": "your-mcp-server",
"scripts": {
"postinstall": "node ./scripts/setup.js",
"start": "node ./dist/index.js"
}
} {
"name": "your-mcp-server",
"scripts": {
"start": "node ./dist/index.js"
}
}
// Setup now runs on first launch (or via an explicit
// "your-mcp-server init" command), not on install. If a script is genuinely unavoidable (for example, compiling a native addon), keep it minimal, do only what’s strictly necessary, and document in your README exactly what it does and why.
How we re-check
We re-read your package metadata on our next crawl. Once the published version no longer declares install-time scripts, the signal flips to a pass on the following score refresh.
Give this to your AI
Paste this into Claude Code (or any coding agent) from inside your server's repository. It states the failing signal, the outcome we re-check for, and the format the fix has to take.
Context: this repository publishes an MCP server. VerifyMCP's install scripts check failed, which means the published package declares an install-time script (`install`, `preinstall`, or `postinstall`) that runs automatically, with your shell's permissions, before anyone has run a single intentional command. Goal: the published package declares no install-time script, so the next crawl records it as clean. Do this: 1. This check is npm/Node-specific; confirm the package is npm before doing anything else. 2. Read `package.json` `scripts` for `install`, `preinstall`, and `postinstall` entries and work out what each one actually does. 3. Move any setup work into an explicit runtime step, or a documented CLI command the user runs deliberately (for example a `your-mcp-server init` command), instead of running it at install time. 4. Delete the `install`/`preinstall`/`postinstall` hooks from `package.json` once nothing depends on them running automatically. 5. Verify the package still installs and works with `npm install --ignore-scripts`; if it breaks, more logic still needs moving to runtime. 6. If a script is genuinely unavoidable (for example compiling a native addon), ask me before keeping it. If we keep it, make it as minimal as possible and document in the README exactly what it does and why. 7. Prepare the release, then stop and let me run the publish myself, since the check reads the published metadata. Rules: - Never run the publish command (`npm publish`, `twine upload`, `dotnet nuget push`, `cargo publish`, a release tag that triggers one) yourself. A published version is permanent and notifies every downstream consumer: prepare the change, show me the diff, and let me publish it. - Never rename or obfuscate a hook to dodge detection; the fix is removing the behaviour, not hiding it. - Never silently keep a script that does something other than what the README says it does. - Ask me before deciding a script is "unavoidable" rather than assuming it. Report back: which hooks you removed or kept, where the logic moved to, and confirmation that `npm install --ignore-scripts` still works. Reference: https://verifymcp.io/docs/packages/install-scripts