mcpx - MCP tools as Unix commands
Use mcpx as the MCP execution surface. Never call MCP servers directly via SDK or protocol code when mcpx is available.
Workflow
# 1. Discover what's available
mcpx # list configured servers
mcpx --json # list configured servers as JSON
mcpx <server> # list tools (short descriptions)
mcpx <server> --json # list tools as JSON
mcpx <server> -v # list tools (full descriptions)
# 2. Inspect before calling (always do this for unfamiliar tools)
mcpx <server> <tool> --help # shows params, types, output schema
mcpx <server> <tool> --help --json # raw schema payload JSON
# Note: tool listings can be large (50k+ chars). Pipe through grep or head for discovery:
# mcpx <server> | grep -i "search"
# 3. Call with JSON for complex shapes (preferred) or flags for simple scalars
mcpx <server> <tool> '{"param":"value","filters":{"state":"open"}}'
mcpx <server> <tool> --param=value
# 4. Pipe output through standard Unix tools
mcpx <server> <tool> --param=value | jq '.items[:5]'
mcpx <server> <tool> --param=value | grep "pattern"
mcpx <server> <tool> --param=value | head -20
# Optional setup: bootstrap a server config entry
mcpx add https://example.com/mcp-manifest.json
mcpx add https://mcp.deepwiki.com/mcp
mcpx add https://mcp.devin.ai/mcp --name deepwiki --header "Authorization=Bearer ${DEEPWIKI_API_KEY}"
# Optional setup: install a convenience shim command for a server
mcpx shim install github
mcpx shim list
Tool Names
Use tool names exactly as exposed by the MCP server. mcpx does not rename or alias tool names.
Flag conventions vary by tool and server. Always inspect each tool's --help before first use.
--json only formats mcpx-owned outputs:
mcpxmcpx <server>mcpx <server> <tool> --help
Tool-call output (mcpx <server> <tool> ...) is not transformed by --json.
Exit Codes
| Code | Meaning | Agent action |
|---|---|---|
0 |
Success | Parse stdout |
1 |
Tool error (isError: true) |
Tool understood the call but returned an error - check stderr |
2 |
Usage error (bad flags, missing required params) | Fix the invocation - re-read --help |
3 |
Transport error (server down, timeout) | Retry or report - not a tool logic issue |
Important caveat: some servers encode domain errors in a successful (exit 0) response body. Do not rely only on ||; inspect response fields when scripting.
Basic branching pattern:
mcpx <server> <tool> --param=value || echo "transport-or-tool-failure"
Caching
For read-only tools, default to adding --cache=<ttl> on the first call whenever you may repeat the same call shape in the current task (loops, retrying pipelines, multiple jq/Python passes).
# first fetch (stores cache)
mcpx <server> <tool> --param=value --cache=10m
# same call shape during the analysis window (hits cache)
mcpx <server> <tool> --param=value --cache=10m | jq '.items[:5]'
Notes:
- Cache key is
(server, tool, args). Any arg change is a different cache entry. --cachealways requires a duration.- Use
-vto confirmcache hit/cache miss. - Use
--no-cachewhen you must force fresh data. - Never use
--cachewith mutating tools (create,delete,update,post) or when safety is unknown.
Output
mcpx unwraps MCP transport envelopes and writes content directly to stdout:
structuredContent-> JSON- single text content block -> text as-is
- multiple text content blocks -> newline-joined text
mcpx does not add a wrapper like {"content": ...}.
Example — a tool that returns structured JSON:
$ mcpx github search-repositories --query=mcp --cache=60s | head -c 200
{"total_count":1042,"items":[{"full_name":"modelcontextprotocol/servers","description":"MCP servers",...}]}
Compare with raw MCP SDK output for the same call:
[{"type":"text","text":"{\"total_count\":1042,\"items\":[...]}"}]
mcpx strips the [{type, text}] envelope. You get the content directly.
Some tools still return text that itself contains serialized JSON (JSON-in-JSON). In that case, pipe through one extra fromjson step:
# Tool returns a JSON string inside a text field — double-decode
mcpx <server> <tool> --param=value | jq 'fromjson | .content'
Check --help for declared output schema, then pipe accordingly:
- JSON:
jq - Plain text:
grep,awk,head - CSV:
cut,awk
Parameter Patterns
Arrays can be passed in either form:
# repeat flag
mcpx <server> <tool> --item=a --item=b
# JSON array string
mcpx <server> <tool> --items='["a","b"]'
Common Pipelines
# Search -> pick URL -> read -> extract
url="$(mcpx <server> <search-tool> --query='topic' --maxResults=5 | jq -r '.results[0].url')"
mcpx <server> <read-tool> --inputs="[\"$url\"]" | jq '.content'
# JSON-in-JSON: when a tool returns serialized JSON inside a text response,
# use fromjson to unwrap it before extracting fields
mcpx <server> <tool> --id=123 --cache=5m | jq 'fromjson | .content.packageDiffs'
# Large output: cache the call and run multiple jq passes against it
mcpx <server> <tool> --id=123 --cache=5m | jq '.summary'
mcpx <server> <tool> --id=123 --cache=5m | jq '.files | length'
Rules
- Always inspect first. Run
--helpbefore the first call to any unfamiliar tool. It shows params, types, required/optional, and output schema. - Use
--jsononly for mcpx discovery/help surfaces (mcpx,mcpx <server>,mcpx <server> <tool> --help). - Prefer JSON payloads for nested objects, arrays, and complex call shapes. Use flags for simple one-off scalar params.
- Booleans from schema.
--flagsends true,--no-flagsends false when the tool parameter is boolean in the declared schema. - Stdin. Only consumed as JSON args when no flags are provided. If flags are present, stdin is not consumed.
- Flag collisions. If a tool has a param named
cache,verbose,help, etc., use--tool-cacheor pass everything after--:mcpx server tool -- --cache=value. - Keep it composable. Pipe, filter, and chain calls:
id="$(mcpx <server> <tool-a> --param=value | jq -r '.items[0].id')"
mcpx <server> <tool-b> --id="$id"
- Read-only repetition: if you might repeat a call with identical args in the same task, add
--cache=<ttl>on the first call. - No interactive use. Every mcpx call is a single command that exits. No shell mode, no prompts.
Source: lydakis/mcpx — distributed by TomeVault.