A stateless CLI that turns any MCP server into a native shell experience. Every tool becomes a command with typed flags, auto-discovered help, OAuth handled automatically, and zero JSON-by-hand.
uv tool install mcpcaller
# or
pipx install mcpcallerAdd a stdio server (the official filesystem server is a good first target):
mcpcaller add fs -- npx -y @modelcontextprotocol/server-filesystem ~Add a remote HTTP server. If it requires OAuth, your browser opens once and the token is saved in your OS keychain:
mcpcaller add prod https://mcp.example.comFor headless/CI environments, pass an API token as a static header instead:
mcpcaller add prod https://mcp.example.com --header "Authorization=Bearer $API_TOKEN"Or use the device-code grant when there's no browser available:
mcpcaller add prod https://mcp.example.com --device-codeList what's available, then call a tool:
mcpcaller tools # all tools on the current server
mcpcaller describe read_file # full schema for one tool
mcpcaller read_file --path ~/notes.mdResources and prompts are first-class:
mcpcaller resources
mcpcaller read file:///etc/hosts
mcpcaller prompts
mcpcaller prompt summarise --doc file:///etc/hostsEach tool's inputSchema becomes typed flags automatically. Examples (against a
hypothetical issue tracker):
mcpcaller search_issues --query "bug" --status open --limit 20
mcpcaller search_issues --tags backend --tags urgent # repeated arrays
mcpcaller search_issues --tags backend,urgent # comma-split arrays
mcpcaller create_issue --title "x" --filter__env prod # nested objects via __
mcpcaller create_issue --filter '{"env":"prod","sev":"high"}' # JSON escape hatch
# Read the value from a file (@-) or stdin (@-)
mcpcaller ingest --body @payload.json
echo '{"a":1}' | mcpcaller ingest --body @-Run any tool with --help to see its schema-derived flag list:
mcpcaller search_issues --helpIn order of precedence:
- Explicit positional:
mcpcaller prod search_issues ... --serverflag:mcpcaller search_issues --server prod ...MCPCALLER_SERVERenv var (handy in CI)- The current server (
mcpcaller use <name>to switch)
Run mcpcaller help selection for the long version, including how the
positional-vs-tool collision rule works.
- Default: human-readable, status messages on stderr, data on stdout (so pipes work).
--jsonemits the rawtools/call/resources/read/prompts/getresult.--rawemits only the first text content block, no framing.--quiet/-qsuppresses spinners and status lines.--verbose/-vshows wire-level JSON-RPC for debugging.--no-colordisables ANSI colour output (also honoursNO_COLOR).
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | tool returned isError: true |
| 2 | usage error (bad flag, missing required) |
| 3 | auth error (token invalid, refresh failed) |
| 4 | transport error (server unreachable, schema lock contention) |
| 5 | schema mismatch after refresh |
| ≥64 | server-defined error mapped from JSON-RPC code |
OAuth 2.1 with discovery, Dynamic Client Registration, and PKCE. mcpcaller add
runs the whole flow if it sees a 401 on the first probe; if you skip it with
--no-login, the next call that returns 401 triggers it lazily. Tokens live in
the OS keychain (macOS Keychain / libsecret on Linux / Windows Credential
Manager) with a chmod-600 file fallback for headless setups.
mcpcaller login <name> # re-run OAuth (e.g. for a scope change)
mcpcaller logout <name> # wipe tokens
mcpcaller refresh-token <name> # force a refresh (debugging)In CI, set MCPCALLER_TOKEN_<NAME>=<token> to bypass the keychain entirely. (The
suffix is the server name uppercased with - replaced by _.)
mcpcaller help auth covers the full set of auth modes; mcpcaller help transports
covers stdio vs HTTP vs SSE.
mcpcaller servers # what's configured
mcpcaller current # which one is the default
mcpcaller use <name> # switch the default
mcpcaller status # auth + schema state per server
mcpcaller refresh # re-fetch schema cache for the current server
mcpcaller refresh --all # re-fetch everything
mcpcaller search <pattern> # grep across tool/resource/prompt names + descriptions# Pipe JSON results through jq:
mcpcaller search_issues --query "bug" --json | jq '.content[0].text'
# Use --raw when you want only the text payload:
mcpcaller get_token --raw | pbcopy
# Pin a server in CI:
MCPCALLER_SERVER=prod mcpcaller list_jobs --jsonuv sync
uv run pytest
uv run ruff check src/ tests/
uv run ruff format src/ tests/Shell completion:
mcpcaller completion bash # or zsh / fish / powershell