mcpbox is a self-contained Model Context Protocol client and command line, written in MoonBit. It is for developers and coding agents that want the MCP tools they already have configured — from Cursor, Claude Code/Desktop, Windsurf, VS Code or an mcpbox file — available in a terminal, a script or a generated agent.
It is deliberately small: one-shot by default, with an optional keep-alive daemon for repeated commands. No Node runtime is needed by mcpbox itself.
Try it without installing anything:
npx @tomasko/mcpbox --versionInstall it for repeated use:
npm install -g @tomasko/mcpbox
# or
curl -LsSf https://raw.githubusercontent.com/tomasky/mcpbox/master/install.sh | bash
# or, with the MoonBit toolchain: builds from source into ~/.moon/bin
moon install kupas/mcpbox/cmd/mcpboxList the servers mcpbox found. This only reads configuration and starts nothing:
mcpbox listStart them and show the tools each one advertises:
mcpbox list --allCall a tool. The first argument is <server>.<tool>; the rest are arguments:
mcpbox call context7.resolve-library-id query="React hooks docs" libraryName=reactPoint it at a server you have not configured yet:
mcpbox list --http-url https://mcp.context7.com/mcp
mcpbox call adhoc.echo --stdio "python3 echo_server.py" name=hi count=5- Schema-aware arguments.
count=5becomes the JSON number5,flag=truea boolean andtags=a,ban array, using the tool'sinputSchema. You only reach for JSON when you want to:meta:={"any":"json"}. - Fast discovery.
mcpbox listprints configured server names without starting any of them.mcpbox list --allstarts them in parallel and shows their tools. - Documented discovery.
mcpbox list <server>andmcpbox list --allshow tool descriptions and parameter docs, including choices and defaults. Use--no-docsfor bare signatures. - Optional keep-alive.
-bkeeps initialized sessions in a background process, avoiding repeated stdio server startup. - Stable JSON.
--jsonproduces output meant to be parsed, while the default is meant to be read. - Real exit codes. A failed call exits non-zero, so a script or an agent can tell the difference.
- Two transports. stdio child processes and Streamable HTTP, including
text/event-streamresponses, session ids, gzip decoding, and same-origin redirects that keep POST and DELETE semantics.
| Command | Purpose |
|---|---|
mcpbox list [server ...] |
List configured server names; --all starts them and shows tools |
mcpbox call <server>.<tool> [key=value ...] |
Call a tool |
mcpbox config [filter ...] |
Show config files and the resolved server set |
mcpbox stop <server> |
Close kept-alive sessions with this name |
mcpbox stop all |
Close all kept-alive sessions and exit the daemon |
mcpbox version |
Print the version |
mcpbox help |
Show help |
Useful flags: --all, -b / --background, --no-docs, --quiet / -q,
--json, --brief, --raw, --timeout <seconds>,
--http-url <url>, --stdio "<command>". A -- stops option parsing so
option-like words after it remain tool arguments. config filters match name
substrings, with * matching any sequence and ? one character; repeat the
filter to match any of several names.
mcpbox list is configuration-only and instant: it prints server names without
starting anything. Naming a server (mcpbox list ct7) or passing --all starts
the selected servers. Human-readable tool listings then include JSDoc-style
descriptions and @param comments, plus server instructions when supplied.
JSON output keeps its existing structured format; --brief stays compact.
While servers start, a terminal gets a one-line spinner with elapsed seconds on
stderr. Pipes and CI logs get plain mcpbox: connecting... lines instead;
--quiet hides progress, and --json suppresses it automatically so
machine-readable output stays clean.
mcpbox list ct7 -b # start once; keep the MCP session alive
mcpbox call ct7.query-docs libraryId=/facebook/react query="hooks"
mcpbox list ct7 # reuse the running service automatically
mcpbox stop ct7 # stop every warm session named ct7
mcpbox stop all # stop every session and the service itselfWithout -b and without an existing service, commands remain one-shot. The
service is per-user; sessions are isolated by working directory, config override,
and server name. Changed transport settings or environment replace the warm
session. Each command sends its resolved config and environment rather than
relying on the service's original working directory.
The daemon serializes requests to avoid concurrent readers on an MCP stream. A request that loses its response is not retried, since a tool may already have performed side effects. After a transport error, the affected session is dropped; the next command can open a new one.
IPC uses loopback TCP authenticated by a random secret in a private (0700)
directory: $XDG_RUNTIME_DIR/mcpbox, or ~/.cache/mcpbox/run when unset.
Server stderr is appended to daemon.log there. Treat this log as potentially
sensitive. The daemon runs until stop all; stop <server> leaves it available.
Shutdown closes stdin, then sends TERM/KILL if a stdio child ignores EOF.
Background mode currently requires POSIX (Linux/macOS).
An agent skill is available at skills/mcpbox/SKILL.md.
The standalone lifecycle regression script accepts an already-built binary:
bash tests/daemon.sh /absolute/path/to/mcpbox. It does not build anything.
mcpbox reads the same mcpServers blocks the other MCP clients use, so an
existing configuration works as-is. Files are JSONC: // and /* */ comments
and trailing commas are accepted.
A minimal mcpbox.json:
{
"mcpServers": {
"context7": {
"url": "https://mcp.context7.com/mcp"
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
"env": { "TOKEN": "${FS_TOKEN}" }
}
}
}Files are read in precedence order, with the first definition of a name winning.
MCPBOX_CONFIG is trimmed and a leading ~ is expanded, so a quoted
"~/mcp.json" behaves like the layers below.
$MCPBOX_CONFIG./mcpbox.json,./.mcpbox.json,./config/mcpbox.json,./.mcpbox/config.json./.cursor/mcp.json,./.vscode/mcp.json~/.mcpbox.json~/.config/mcpbox/config.json~/.cursor/mcp.json,~/.claude.json,~/.codeium/windsurf/mcp_config.json,~/.config/Claude/claude_desktop_config.json,~/Library/Application Support/Claude/claude_desktop_config.json
String values support ${VAR} and ${VAR:-fallback}, including placeholders
embedded inside a longer value. An unset variable with no fallback expands to
the empty string. An explicit stdio cwd is resolved against the config file
that declares it, so relative paths do not depend on where mcpbox was
launched.
moon build --target native --release
_build/native/release/build/cmd/mcpbox/mcpbox.exe --helpThe release binaries are linked statically against musl on Linux, so they run on any distribution regardless of its glibc version.
moon check --target native --deny-warn
moon test --target native
moon info && moon fmtThe stdio and HTTP transports are exercised end to end against the fixture
server in tests/fixtures/echo_server.py:
moon build --target native
_build/native/debug/build/cmd/mcpbox/mcpbox.exe list \
--stdio "python3 tests/fixtures/echo_server.py"Apache-2.0