mcpx is a command line tool that turns registered MCP servers into an agent-friendly command surface.
It keeps MCP server registrations in a global user registry, discovers tool schemas, handles OAuth where possible, and exposes each MCP server as a root command:
mcpx posthog.projects-get
mcpx sentry.search-issues '{"query":"is:unresolved"}'mcpx is designed for agents. The command surface is intentionally schema-first,
stable, and explicit: commands use dotted paths and receive one structured input
token, while mcpx's own control commands live under the @ namespace.
- Bun
- macOS, Linux, or any environment that can run Bun executables
Install the latest release:
curl -fsSL https://raw.githubusercontent.com/celados/mcpx/main/install.sh | bashBy default, the installer downloads the executable JS bundle from GitHub Releases
and installs it to ~/.local/bin/mcpx.
Set MCPX_INSTALL_DIR to choose another install directory.
Register MCP servers globally:
mcpx @add "{ name: 'posthog', url: 'https://mcp.posthog.com/mcp' }"
mcpx @add "{ name: 'sentry', url: 'https://mcp.sentry.dev/mcp' }"
mcpx @add "{ name: 'cf-docs', url: 'https://docs.mcp.cloudflare.com/mcp' }"
mcpx @add "{ name: 'cf-bindings', url: 'https://bindings.mcp.cloudflare.com/mcp' }"
mcpx @add "{ name: 'cf-observability', url: 'https://observability.mcp.cloudflare.com/mcp' }"
mcpx @add "{ name: 'browser', url: 'http://127.0.0.1:9000/mcp' }"browser-os is an agent-native browser built on Chrome. It exposes a local MCP server that lets agents control the browser directly — navigating pages, extracting content, and interacting with web UIs.
mcpx keeps its state under ~/.agents/mcpx/state-v2/: server declarations in
registry.json, credentials (OAuth grants, bearer values, secret headers) in
credentials.json, and observed tool schemas in schema-cache.json. The MCP
Runtime is the only writer.
Project directories do not need MCP config files. A project should decide which global servers are relevant by using schema selectors or a generated skill.
Call tools through the server command:
mcpx <server>.<tool> '<json-or-json5>'Examples:
mcpx posthog.projects-get '{}'
mcpx sentry.whoami '{}'For complex payloads, put the whole object on stdin as YAML — block scalars need no escaping:
mcpx cf-graphql.graphql_query - <<'EOF'
query: |
query GetWorkerAnalytics($accountTag: String!, $scriptName: String!, $since: Time!, $until: Time!) {
viewer { accounts(filter: {accountTag: $accountTag}) {
workersInvocationsAdaptive(limit: 1000, filter: {scriptName: $scriptName, datetime_geq: $since, datetime_leq: $until}, orderBy: [datetimeHour_ASC]) {
dimensions { datetimeHour scriptName status }
sum { requests subrequests errors duration }
quantiles { cpuTimeP50 cpuTimeP99 }
}
} }
}
operationName: GetWorkerAnalytics
variables:
accountTag: abc123def456
scriptName: my-worker
since: "2026-04-20T00:00:00Z"
until: "2026-04-27T00:00:00Z"
EOFThe command accepts exactly one input object: inline JSON5, or the whole object
from @file / - stdin as YAML (JSON5 when it starts with {). Field values
are literal; there is no per-field - / @path.
Print the command schema for agents:
mcpx @schemaFocus on one server:
mcpx @schema .posthogFocus on a set of servers:
mcpx @schema '.{posthog,sentry}'Focus on an internal mcpx command:
mcpx @schema '.["@add"]'mcpx caches discovered tool schemas so commands and @schema answer without
asking the server. The cache is a hint, not the authority, and repairs itself
(ADR 0011):
- calling a tool the cache does not know, or omitting a field the cached schema requires, re-lists that server before the call is rejected;
- a server's Invalid Params rejection, a new session (for schemas over an hour
old) and
tools/list_changedre-list it in the background; mcpxdre-lists every schema older than a week when it starts and daily while it runs.
Background listings never open a browser; there is no manual refresh command.
mcpx control commands use the @ namespace so they do not collide with server
names:
mcpx @add "{ name: '<server>', url: '<mcp-url>' }"
mcpx @add "{ name: '<server>', url: '<mcp-url>', bearer: 'env:<TOKEN_ENV>' }"
mcpx @add "{ name: '<server>', transport: 'stdio', command: '<command>', args: ['<arg>'] }"
mcpx @remove "{ name: '<server>' }"
mcpx @login "{ name: '<server>' }"
mcpx @daemon.status
mcpx @daemon.stop
mcpx @daemon.server
mcpx @skill
mcpx @skill-installServer names cannot start with @.
HTTP is the default transport. Pass bearer credentials as a string or array.
env:NAME reads a token from an environment variable at runtime; literal values
are accepted but are stored in the registry:
mcpx @add "{ name: 'posthog', url: 'https://mcp.posthog.com/mcp', bearer: 'env:POSTHOG_TOKEN' }"
mcpx @add "{ name: 'posthog', url: 'https://mcp.posthog.com/mcp', bearer: ['env:POSTHOG_TOKEN_A', 'env:POSTHOG_TOKEN_B'] }"Multiple bearer credentials use round-robin selection by default. Stdio servers
are local process registrations with structured args and env:
mcpx @add '{
"name": "open-design",
"transport": "stdio",
"command": "node",
"args": ["/path/to/open-design/apps/daemon/dist/cli.js", "mcp"],
"env": { "OPEN_DESIGN_TOKEN": "..." }
}'Stdio servers are called through mcpxd, a user-local daemon that reuses stdio
MCP sessions across CLI invocations. mcpxd starts on demand, keeps idle stdio
servers warm, and can be inspected or stopped with mcpx @daemon.status and
mcpx @daemon.stop. The daemon process is started through the explicit
mcpx @daemon.server subcommand. HTTP servers continue to use the direct client path.
Bearer auth is configured explicitly in the @add input. Prefer env:NAME
references so secrets stay out of the registry. ${NAME} is also accepted when
passed through the shell literally.
When several bearer credentials are registered, mcpx advances a local
round-robin cursor before each HTTP request.
OAuth follows the MCP authorization spec through the official MCP SDK. A server
does not need to declare OAuth up front: whenever it answers a request with
401 and a WWW-Authenticate: Bearer resource_metadata=… challenge, mcpx
discovers its authorization server from that challenge, authorizes, records
the server as OAuth, and retries the request. This covers servers whose tools
are partly anonymous — @add lists their tools without credentials, and the
first call to a protected tool starts sign-in. A 403 insufficient_scope
challenge re-authorizes with the additional scope the same way. When @add
itself is challenged, it signs in right away and then lists the tools.
To switch accounts, run mcpx @login "{ name: '<server>' }". It opens a new
browser sign-in (with OIDC prompt=login, so the old browser session is not
reused silently) and replaces the stored grant only once the new sign-in
succeeds.
For OAuth servers that support dynamic client registration, mcpx registers a client automatically for each browser sign-in.
For OAuth servers that do not support dynamic client registration, mcpx prompts for a manual client_id and client_secret. These providers
usually require a preconfigured redirect URL. mcpx uses:
http://127.0.0.1:65245/callback
Add and save that exact Redirect URL in the provider app settings before continuing the prompt.
OAuth credentials repair themselves during ordinary tool calls:
- When a token is close to expiry, mcpx refreshes it silently before the call.
- When the server rejects a token with 401, mcpx refreshes it and retries the call once. Another call that already replaced the token is not undone.
- When no refresh is possible (no token yet, or the refresh grant was revoked), the call opens browser authorization, prints the authorization URL to stderr, and waits up to 5 minutes for it to complete before continuing. Concurrent calls for the same server share one authorization.
Providers that need a manual OAuth client can only prompt in an interactive
terminal; without one, the call fails immediately with reauth-required and
mcpx @login "{ name: '<server>' }" must be run in a terminal.
Tool calls use a 5 minute request timeout by default. Set
MCPX_TOOL_CALL_TIMEOUT_MS when a remote MCP server legitimately needs longer.
mcpx buffers MCP subscription notifications and returns them with the next daemon
tool-call result by default. Set MCPX_NOTIFICATION_MODE=discard to ignore those
notifications for a command; accepted values are buffer and discard.
mcpx optimizes output for humans and agents by default:
- text MCP content is printed directly
- JSON text content is rendered as YAML
- non-text content is saved to a temporary file and printed as
file saved <path> - supplemental MCP
structuredContentis printed as$structuredonly when it is not already represented by the primary content
Use argc context to preserve raw server output:
mcpx posthog.projects-get --context "{ output: 'raw' }"Generate a project-local skill that tells agents which global MCP servers to use:
mcpx @skill-install "{ servers: 'posthog,sentry' }"This writes:
.agents/skills/mcpx/SKILL.md
.agents/skills/mcpx/references/install.md
.agents/skills/mcpx/references/servers.md
The generated skill instructs agents to discover tools with focused schema
selectors and call MCP tools through mcpx. A Troubleshooting section at the
bottom points collaborators at install and server-setup references so their
agent can recover when mcpx is missing or a listed server is not registered.
For temporary agent guidance without writing .agents/skills, print a one-server
skill to stdout:
mcpx @skill-install "{ show: 'sentry' }"General mcpx skill definition lives at src/index.md (served by mcpx @skill).
MIT