Token-efficient MCP client built for LLMs and humans.
Why MCPX? Other MCP clients dump giant JSON schemas that waste tokens and confuse humans. MCPX returns TypeScript signatures instead—Claude and developers instantly understand tool signatures. Call tools with LLM-native syntax (server.func({ args })), run batch operations via stdin, and get TOON output (90% fewer tokens than JSON).
MCPX helps you lean into the "code execution" workflows highlighted in Anthropic's Code Execution with MCP guidance: discover the MCP servers already configured on your system, call them directly, and compose richer automations in TypeScript. All of that works out of the box -- no boilerplate, no schema spelunking.
# Add the tap
brew tap AIGC-Hackers/mcpx
# Install mcpx
brew install mcpx
# Or install in one command
brew install AIGC-Hackers/mcpx/mcpxbunx mcpx listbrew tap steipete/tap
brew install steipete/tap/mcpxThe steipete tap publishes alongside MCPX 0.3.2. Run
brew updatebefore reinstalling if you see an older build.
| Feature | MCPX | Typical MCP Client |
|---|---|---|
| Schema output | TypeScript (readable, token-efficient) | JSON Schema (verbose, token-heavy) |
| Call syntax | linear.createIssue({ title: "Bug" }) |
Manual JSON construction |
| Batch calls | ✅ Multi-line, stdin, comments | ❌ One call at a time |
| Output format | TOON (90% fewer tokens) | Raw JSON |
| OAuth flow | ✅ Auto-triggered, bug-free | Manual or buggy |
| LLM-ready | ✅ Designed for Claude/GPT workflows |
- Zero-config discovery.
createRuntime()merges./mcp.jsonwith~/.mcpx/mcp.json, expands${ENV}placeholders, and pools connections so you can reuse transports across multiple calls. First run migrates Cursor/Claude/Codex/Windsurf/VS Code configs into~/.mcpx/mcp.jsonautomatically. - Flexible data formats. Call tools with function syntax or structured JSON/JSON5/YAML/TOML data. Output defaults to TOON (LLM-friendly), with
--outputflags for raw/JSON/text formats. - Friendly composable API.
createServerProxy()exposes tools as ergonomic camelCase methods, automatically applies JSON-schema defaults, validates required arguments, and hands back aCallResultwith.text(),.json(), and.content()helpers. - OAuth and stdio ergonomics. Built-in OAuth caching, log tailing, and stdio wrappers let you work with HTTP, SSE, and stdio transports from the same interface.
- Ad-hoc connections. Point the CLI at any MCP endpoint (HTTP or stdio) without touching config, then persist it later if you want. Hosted MCPs that expect a browser login (Supabase, Vercel, etc.) are auto-detected and OAuth is triggered automatically when needed. See docs/adhoc.md.
MCPX auto-discovers the MCP servers you already configured in Cursor, Claude Code/Desktop, Codex, or local overrides. Install via Homebrew or run instantly with bunx mcpx. Need a full command reference (flags, modes, return types)? Check out docs/cli-reference.md.
# Function syntax - natural for agents and interactive use
mcpx call 'linear.create_comment({ issueId: "ENG-123", body: "Great!" })'
# Data format - explicit and structured (JSON5/YAML/TOML also supported)
mcpx call '{ tool: "linear.create_comment", args: { issueId: "ENG-123", body: "Great!" } }'mcpx list
mcpx list context7
mcpx list https://mcp.linear.app/mcp --all-parameters
mcpx list shadcn.io/api/mcp.getComponents # URL + tool suffix auto-resolves
mcpx list --stdio "bun run ./local-server.ts" --env TOKEN=xyz- Add
--output jsonto emit a machine-readable summary with per-server statuses (auth/offline/http/error counts) and, for single-server runs, the full tool schema payload.
You can now point mcpx list at ad-hoc servers: provide a URL directly or use the new --http-url/--stdio flags (plus --env, --cwd, --name, or --persist) to describe any MCP endpoint. Until you persist that definition, you still need to repeat the same URL/stdio flags for mcpx call—the printed slug only becomes reusable once you merge it into a config via --persist or mcpx config add. OAuth is triggered automatically when servers require authentication. Full details live in docs/adhoc.md.
Single-server listings now show TypeScript Spec format so you can copy/paste the signature straight into mcpx call:
linear - Hosted Linear MCP
23 tools · 1654ms · HTTP https://mcp.linear.app/mcp
/**
* Create a comment on a specific Linear issue
*/
type CreateCommentSpec = {
tool: 'create_comment'
args: {
issueId: string
body: string
parentId?: string
notifySubscribers?: boolean
labelIds?: string[]
mentionIds?: string[]
}
}
/**
* List documents in the user's Linear workspace
*/
type ListDocumentsSpec = {
tool: 'list_documents'
args: {
query?: string
limit?: number
before?: string
after?: string
orderBy?: 'createdAt' | 'updatedAt'
projectId?: string
initiativeId?: string
creatorId?: string
includeArchived?: boolean
}
}Here's what that looks like for Vercel when you run mcpx list vercel:
vercel - Vercel MCP (requires OAuth)
/**
* Search the Vercel documentation.
* Use this tool to answer any questions about Vercel's platform, features, and best practices.
*/
type SearchVercelDocumentationSpec = {
tool: 'search_vercel_documentation'
args: {
topic: string
tokens?: number
}
}
/**
* Deploy the current project to Vercel
*/
type DeployToVercelSpec = {
tool: 'deploy_to_vercel'
args: Record<string, unknown>
}mcpx call 'context7.resolve-library-id({ libraryName: "react" })'
mcpx call 'context7.get-library-docs({ path: "/websites/react_dev", topic: "hooks" })'LINEAR_API_KEY=sk_linear_example mcpx call 'linear.search_documentation({ query: "automations" })'mcpx call 'chrome-devtools.take_snapshot()'
mcpx call 'linear.create_comment({ issueId: "LNR-123", body: "Hello world" })'Call Syntax: Function syntax
server.tool({ args })or data format{ tool: "server.tool", args: {...} }. Both are parsed (not executed as JS). Supports JSON5/YAML/TOML.
Helpful flags:
--config <path>-- custom config file (overrides the default./mcp.json+~/.mcpx/mcp.jsonmerge).--root <path>-- working directory for stdio commands.--log-level <debug|info|warn|error>-- adjust verbosity (respectsMCPX_LOG_LEVEL).--oauth-timeout <ms>-- shorten/extend the OAuth browser wait; same asMCPX_OAUTH_TIMEOUT_MS/MCPX_OAUTH_TIMEOUT.--tail-log-- stream the last 20 lines of any log files referenced by the tool response.--output <format>-- control output format:toon(default, LLM-friendly),json,text, orraw.--http-url <https://…>/--stdio "command …"-- describe an ad-hoc MCP server inline (pair with--env KEY=value,--cwd,--name, and--persist <config.json>as needed).- OAuth-protected servers trigger authentication automatically on first use.
Tip: You can skip the verb entirely—
mcpx firecrawlautomatically runsmcpx list firecrawl, and dotted tokens likemcpx linear.list_issuesdispatch to the call command (typo fixes included).
Timeouts default to 30 s; override with MCPX_LIST_TIMEOUT or MCPX_CALL_TIMEOUT when you expect slow startups. OAuth browser handshakes get a separate 60 s grace period; pass --oauth-timeout <ms> (or export MCPX_OAUTH_TIMEOUT_MS) when you need the CLI to bail out faster while you diagnose stubborn auth flows.
# Point at an HTTPS MCP server directly
mcpx list --http-url https://mcp.linear.app/mcp --name linear
# Run a local stdio MCP server via Bun
mcpx call --stdio "bun run ./local-server.ts" --name local-tools- Add
--persist ~/.mcpx/mcp.json(or any path) to save the inferred definition for future runs. - Use
--allow-httpif you truly need to hit a cleartext endpoint. - See docs/adhoc.md for a deep dive (env overrides, cwd, OAuth).
Two formats, both parsed (not executed as JavaScript):
Function syntax (natural for agents):
mcpx call 'linear.create_issue({ title: "Bug", priority: 2, labels: ["urgent"] })'Data format (explicit, supports JSON5/YAML/TOML):
mcpx call '{ tool: "linear.create_issue", args: { title: "Bug", priority: 2 } }'Features:
- Auto-correct. Typo
listIssue? MCPX suggestslist_issuesautomatically. - Type specs.
mcpx list <server>prints TypeScript specs—copy/paste directly into calls.
Execute multiple MCP tool calls in one shot from stdin or a file. Perfect for LLM-driven workflows, automation scripts, and data migrations.
# From file
cat batch-calls.txt | mcpx call
# From heredoc (function syntax)
mcpx call << 'EOF'
linear.list_issues({ limit: 5 })
github.search_repos({ query: "mcp" })
EOF
# Data format also works
mcpx call << 'EOF'
[
{ tool: "linear.list_issues", args: { limit: 5 } },
{ tool: "github.search_repos", args: { query: "mcp" } }
]
EOFInput format supports function syntax and structured data:
// Function syntax (one per line, natural for agents)
linear.create_issue({ title: "Fix bug", priority: 2, labels: ["bug", "urgent"] })
github.search_repos({ query: "mcp" })
// Data format (explicit structure)
[
{ tool: "linear.create_issue", args: { title: "Fix bug", priority: 2, labels: ["bug", "urgent"] } },
{ tool: "github.search_repos", args: { query: "mcp" } }
]Output is in TOON format (compact, LLM-friendly):
[2]:
- tool: linear.create_issue
output:
id: ISS-123
title: Fix bug
- tool: github.search_repos
output:
items[1]{name,stars}: repo1,42
Override with --output json for standard JSON:
[
{ tool: 'linear.create_issue', output: { id: 'ISS-123', title: 'Fix bug' } },
{ tool: 'github.search_repos', output: { items: [...] } },
]Error handling: If any call fails, the error is captured in the output and the exit code is 1:
{
tool: 'linear.bad_tool',
output: "Parameter validation failed:\n- Field 'id': Required (expected string, got undefined)",
}Use cases:
- LLM tool calling: Generate batch files and pipe to mcpx
- Automation scripts: Daily standups, status reports
- Data migration: Export from one system, import to another
See examples/README.md and examples/batch-calls.txt for complete examples.
Need runtime or automation samples? Head to docs/tool-calling.md and docs/cli-reference.md. Call mcpx list <server> any time you need the TypeScript-style signature, optional parameter hints, and sample invocations that match the CLI's function-call syntax.
mcp.json (project) and ~/.mcpx/mcp.json (user) share the Cursor/Claude schema:
{
mcpServers: {
context7: {
description: 'Context7 docs MCP',
baseUrl: 'https://mcp.context7.com/mcp',
headers: {
Authorization: '$env:CONTEXT7_API_KEY',
},
},
'chrome-devtools': {
command: 'bunx',
args: ['chrome-devtools-mcp@latest'],
},
},
}What MCPX handles for you:
${VAR},${VAR:-fallback}, and$env:VARinterpolation for headers and env entries across both files. Entries defined in./mcp.jsonoverride duplicates from~/.mcpx/mcp.json.- Automatic OAuth token caching under
~/.mcpx/<server>/unless you overridetokenCacheDir. - Stdio commands inherit the directory of the file that defined them (imports or local config).
- First run populates
~/.mcpx/mcp.jsonby migrating Cursor/Claude/Codex/Windsurf/VS Code configs; legacyimportsarrays are only read during that migration helper.
Provide configPath or rootDir to CLI/runtime calls when you juggle multiple config files side by side.
Use tmux to keep long-running CLI sessions visible while you investigate lingering MCP transports:
tmux new-session -- bun run mcpx:listLet it run in the background, then inspect the pane (tmux capture-pane -pt <session>), tail stdio logs, or kill the session once the command exits. Pair this with MCPX_DEBUG_HANG=1 when you need verbose handle diagnostics. More detail: docs/tmux.md and docs/hang-debug.md.
MCPX was inspired by mcporter but rebuilt from scratch with token-efficiency and LLM workflows as first-class design goals.
MIT -- see LICENSE.
Further reading: docs/tool-calling.md, docs/adhoc.md, docs/tmux.md.