npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcp-down-wrapper

v1.0.1

Published

Wraps an MCP server command so that a failed/missing server reports zero tools instead of a load failure.

Readme

mcp-down-wrapper

A transparent wrapper for MCP server commands. When the wrapped server fails to start — bad binary, missing dependency, crash during boot, whatever — Claude sees a server that loaded successfully with zero tools, instead of a "failed to load" warning.

The problem

Claude's MCP client shows a hard failure warning any time a configured server process exits or errors before completing its handshake. That's the right default, but it means a single flaky/optional MCP server (e.g. one that depends on a local service like ArangoDB being up) throws a scary warning every time that dependency isn't running — even if you don't care about that server's tools right now.

mcp-down-wrapper sits between Claude and the real server command. If the real command starts fine, the wrapper is invisible — it proxies stdio byte-for-byte. If the real command fails before producing any output, the wrapper answers the MCP handshake itself and reports an empty tool list, so Claude treats it as a normal, healthy, toolless server.

How it works

The wrapper is invoked as:

mcp-down-wrapper <command> [args...]

<command> [args...] is exactly what used to be your server's own command/args — the wrapper spawns it verbatim, inheriting env from its own process (which Claude already populated from your MCP config).

  • Child starts and talks normally: stdin/stdout are proxied directly between Claude and the child process. The wrapper does not parse or alter any protocol messages in this path — it's a transparent pipe.
  • Child fails to spawn (e.g. ENOENT) or exits before writing a single byte to stdout: the wrapper switches into a minimal built-in MCP responder. It replays whatever Claude had already sent (so the initialize request isn't lost) and answers:
    • initialize → normal handshake response, no tools capability advertised
    • tools/list{ "tools": [] }
    • ping{}
    • anything else → JSON-RPC "method not found" (-32601)
  • Child crashes after already producing real output: this is treated as a genuine runtime crash, not a failed start, and the wrapper just exits with the child's exit code. Masking only applies to startup failures.

The child's stderr is always inherited straight through, so real crash logs and error messages are still visible for debugging — only the protocol-level "did the server fail to load" signal is smoothed over.

Usage

Wrap any existing MCP server config by moving its command/args inside a mcp-down-wrapper invocation. env stays exactly as it was.

Before:

{
  "command": "npx",
  "args": ["arango-server"],
  "env": {
    "ARANGO_URL": "http://localhost:8529",
    "ARANGO_USERNAME": "root",
    "ARANGO_PASSWORD": "root"
  }
}

After (published to npm):

{
  "command": "npx",
  "args": ["mcp-down-wrapper", "npx", "arango-server"],
  "env": {
    "ARANGO_URL": "http://localhost:8529",
    "ARANGO_USERNAME": "root",
    "ARANGO_PASSWORD": "root"
  }
}

Note that args is a real array — "mcp-down-wrapper" and "npx" are separate elements, not one string. The wrapper is intentionally generic: the first argument is whatever command needs to run (npx, uvx, node, python, ...), and everything after it is that command's own arguments.

Running it locally (not published)

Two options, since this package isn't on the npm registry:

Option A — point npx at the project folder directly:

{
  "command": "npx",
  "args": [
    "/absolute/path/to/mcp-down-wrapper",
    "npx",
    "arango-server"
  ],
  "env": {
    "ARANGO_URL": "http://localhost:8529",
    "ARANGO_USERNAME": "root",
    "ARANGO_PASSWORD": "root"
  }
}

npm 7+ resolves a local folder path directly to its bin, so this works without installing or publishing anything.

Option B — call node on the built file directly (most deterministic):

{
  "command": "node",
  "args": [
    "/absolute/path/to/mcp-down-wrapper/dist/index.js",
    "npx",
    "arango-server"
  ],
  "env": {
    "ARANGO_URL": "http://localhost:8529",
    "ARANGO_USERNAME": "root",
    "ARANGO_PASSWORD": "root"
  }
}

Option C — npm link for a plain npx mcp-down-wrapper ... invocation:

cd /absolute/path/to/mcp-down-wrapper
npm link

This registers mcp-down-wrapper globally, so you can use the exact same config shape as the "published to npm" example above, with no path or version to keep track of.

How to build

Prerequisites: Node.js 20+ and npm.

git clone <this-repo>
cd mcp-down-wrapper
npm install    # installs devDependencies and builds via the `prepare` script
npm run build  # compiles src/index.ts -> dist/index.js

dist/ is a build artifact and is gitignored — rebuild it after pulling changes or editing src/index.ts. There's no test suite, so validate a build by pointing one of the local usage options at your freshly built dist/index.js.

Development

There's no test suite; the behavior was verified manually against three scenarios: a well-behaved child (pure passthrough), a nonexistent command (ENOENT), and a child that exits immediately without writing to stdout (both fall back to the empty-tools response).

Limitations

  • Only startup failures are masked. A server that starts fine and crashes later behaves normally (wrapper exits with the child's code) — Claude already knows about its tools by that point, so there's nothing sensible to hide.
  • The fallback responder does not advertise resources or prompts capabilities, but still answers resources/list/prompts/list with empty arrays defensively in case a client calls them anyway.
  • No shell is used to spawn the child (child_process.spawn without shell: true), so shell syntax in args won't be interpreted — pass a real command and argv, same as you would in the original unwrapped config.