Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dependencies

Everything mcpx needs, and why. Nothing here is incidental — if it is listed, something breaks without it.

Go libraries

Nine, and pkgs/mcpx/package.nix states the trade for each one beside the vendorHash that pays for them.

modulewhat it is for
modernc.org/sqlitethe log index
github.com/charmbracelet/bubbleteathe full-screen browser, mcpx tui
github.com/charmbracelet/bubblesits widgets
github.com/charmbracelet/lipglossits layout
gopkg.in/yaml.v3OpenAPI documents, which are published as YAML as often as JSON, and which one you get is not the caller’s choice
github.com/santhosh-tekuri/jsonschema/v6the JSON Schema validator (internal/schemaval): tool input/output schemas and elicitation schemas are checked with a real validator, not by hand
github.com/tetratelabs/wazerothe WebAssembly runtime: wasm tooling and the embeddings worker run without a cgo toolchain
golang.org/x/systhe OS-specific syscalls the standard library does not expose (peer credentials on the daemon socket, process groups)
tailscale.comthe tsnet identity transport (#1967): the daemon embeds a Tailscale node and is reachable by its tailnet identity, still as a pure-Go CGO_ENABLED=0 binary

The three charmbracelet modules are one decision, not three: they cost about 1.2 MB in the stripped binary and replace raw terminal handling – escape sequences, resize, a redraw loop – which is the kind of code that is never finished and never correct on every terminal.

Everything else – the MCP client, the JSON-RPC framing, the process pools, the JSON Schema to TypeScript compiler, the HTTP API and the CLI – is standard library, and that is worth keeping.

Why this driver

modernc.org/sqlite is a pure-Go translation of SQLite rather than a binding. The usual choice, mattn/go-sqlite3, is cgo, and cgo would mean:

  • a C toolchain required to build, which the Nix derivation would have to carry;
  • cross-compilation broken, so no building a Linux binary from a Mac;
  • CGO_ENABLED=0 builds failing outright.

That is a large bill for a database that is only ever an index. The JSONL files remain the source of truth: the index can be deleted at any time and is rebuilt on the next query. Paying in build complexity for something reconstructible was the wrong trade.

The cost of the pure-Go driver is a slower build and a larger binary. Both are acceptable; neither affects anyone running mcpx.

What this means for the build

  • vendorHash is pinned in pkgs/mcpx/package.nix and must be updated when the dependency changes.
  • The build still needs no network beyond the vendor fetch, and still needs no C toolchain.

The standard library packages doing real work: net/http (daemon and client), encoding/json (everything), os/exec + syscall (child processes and process groups), sync (pools), path/filepath (path identity), text/tabwriter (human output).

If a dependency is ever added, it should be for something genuinely hard — a tokeniser, a SQLite driver — and the trade should be stated here.

External programs

ProgramNeeded forIf missing
gitrepo and worktree scopes, for repository layouts native discovery does not readonly those layouts degrade to cwd, with a log line and a doctor warning saying why
deno, bun or nodemcpx run and mcpx execthose commands fail; mcpx call, ls, types, catalog, search still work
the configured MCP serverstheir own namespacesthat namespace reports error with the server’s stderr

git is optional. mcpx finds a repository by reading .git itself – a port of git’s discovery rules, docs/git-discovery.md – and runs git rev-parse --path-format=absolute --git-common-dir --show-toplevel only for a layout that port does not vouch for, such as a repository format newer than it knows. That needs git 2.31 (March 2021); an older git’s answer is refused rather than misread. Nothing else about git is used — no fetching, no writing, no config.

A JavaScript runtime is chosen at first use: deno, then bun, then node. Override with --runtime or the runtime config key. Deno runs with --no-check; the generated client is machine-written and a type error in the agent’s own script surfaces at runtime anyway.

MCP servers are whatever the config names. mcpx spawns them with the given command, args, env and cwd, or connects over Streamable HTTP for url servers. Their own configuration is exactly those argv and environment entries — MCP has no separate configuration channel.

Network

The daemon binds two listeners, both local: a unix socket, and 127.0.0.1 on an OS-assigned ephemeral port. Generated script clients prefer the socket — Deno fetches over it with a custom HTTP client and Bun with a fetch option (internal/codegen/emit.go) — and fall back to the TCP port where a runtime cannot address a socket (Node’s fetch cannot, without undici). Neither listener is reachable off the machine; the tailnet identity transport (#1967) is the separate, authenticated exception, and it is opt-in.

Outbound traffic happens only when a configured server is a url server, or when a script makes its own requests.

Nix packaging

pkgs/mcpx/package.nix wraps the binary with git, deno, bun-bin and nodejs suffixed onto PATH, so behaviour does not depend on the calling shell. git is in that list because repo and worktree scopes are silently weaker without it — that was a real gap, found by asking this question.

The unit suites run in the build sandbox. The end-to-end suite spawns JavaScript runtimes and binds unix sockets, so it runs outside with go test ./....