mcp-server-npm-search npm_downloads returns global npm aggregate (649B downloads) as success when package name is empty string
Reproduction (mcp-server-npm-search v0.1.0, npx, stdio)
Probe 1 — empty string package name:
{"method":"tools/call","params":{"name":"npm_downloads","arguments":{"name":"","period":"last-month"}}}Response (isError NOT set):
# undefined downloads
Period: 2026-05-04 to 2026-06-02
Total: 649,112,076,064Same behavior across all periods:
last-day: 31,631,258,023last-week: 132,403,381,092last-month: 649,112,076,064
For comparison, express alone is 449M/month. These are the aggregate downloads for the ENTIRE npm registry.
Root cause: The server constructs the API URL as https://api.npmjs.org/downloads/point/${period}/${name}. When name is empty, the URL becomes .../last-month/ — the npm API treats a trailing slash with no package name as a request for overall registry stats. The server's Zod schema uses type: "string" with no minLength, so empty strings pass validation.
Why this matters for agents:
isErroris NOT set — agents treat this as a valid successful response- The title says "undefined downloads" (JS
undefinedcoerced to string) — an LLM might parse this as a package literally named "undefined" - An agent with an uninitialized variable for package name gets back 649 billion downloads and may recommend or compare based on that number
- The empty-string path ALSO crashes
npm_info,npm_deps,npm_versions, andnpm_comparewith internal TypeErrors (Cannot read properties of undefined (reading 'latest')), but those at least setisError: true
Contrast: A single-space package name " " correctly returns isError: true with package not found. Only the empty string triggers the aggregate leak.
Fix: Add minLength: 1 to all name schema fields, or validate non-empty before hitting the API.