Recipes
Short, problem-driven snippets. Each section answers one question.
Replace <ref> with any server reference — see
Concepts → Server reference.
Real-server cookbook
Concrete examples against publicly known MCP servers. Tokens go to
the OS keyring (mcpal auth login); none of the commands write
secrets to disk.
Filesystem (local sandbox, stdio)
A scoped read/write surface over a directory tree. Useful for any command that wants file access without giving the LLM the whole machine.
mcpal server add fs -- \
npx -y @modelcontextprotocol/server-filesystem $HOME/projects
mcpal tool list fs --names-only
mcpal tool call fs read_file --path README.md
mcpal tool call fs list_directory --path .
mcpal tool call fs search_files --pattern '*.toml' --path .
mcpal --query 'content[0].text' tool call fs read_file --path Cargo.toml
The sandbox is the path passed at spawn time; you can pass multiple paths after the package name.
Fetch (HTTP client, stdio)
A bounded HTTP fetcher for one-off requests:
mcpal server add fetch -- npx -y @modelcontextprotocol/server-fetch
mcpal tool call fetch fetch --url https://httpbin.org/json
Time
mcpal server add time -- npx -y @modelcontextprotocol/server-time
mcpal tool call time get_current_time --timezone Europe/Warsaw
GitHub (HTTP, bearer)
The hosted GitHub MCP at api.githubcopilot.com/mcp/ accepts a
personal access token (classic or fine-grained) as a bearer:
mcpal server add gh --http https://api.githubcopilot.com/mcp/
mcpal auth login gh --bearer ghp_xxx # or use $GITHUB_TOKEN
mcpal tool list gh --names-only | head
mcpal --query '[].name' \
tool call gh list_repositories_for_user --username anthropics
mcpal --output json \
tool call gh search_issues --q 'repo:anthropics/claude-code is:open label:bug' \
| jq '.[].title'
For CI, put the token in an env var and reference it from config.toml:
[server.gh]
transport = "http"
url = "https://api.githubcopilot.com/mcp/"
auth = { type = "bearer_env", env = "GITHUB_TOKEN" }
Linear (HTTP, OAuth)
Linear's MCP authenticates users via OAuth 2.1. mcpal runs the full PKCE + DCR flow for you; no developer-console step:
mcpal server add linear --http https://mcp.linear.app/mcp
mcpal auth login linear --oauth # browser → consent → done
mcpal --query '[].name' tool list linear
# Find issues assigned to me, in progress:
mcpal --query 'content[0].text' \
tool call linear list_my_issues --state in_progress
# Comment on one:
mcpal tool call linear create_comment \
--issueId ENG-123 --body 'updated branch is up'
mcpal auth refresh linear rotates the access token when it expires;
mcpal also refreshes eagerly within 30 s of expiry. See the
OAuth walk-through for what each
step does on the wire.
Notion (HTTP, OAuth)
mcpal server add notion --http https://mcp.notion.com/v1
mcpal auth login notion --oauth
mcpal tool list notion --names-only
mcpal --query 'content[0].text' \
tool call notion search --query 'meeting notes 2026 Q2'
mcpal tool call notion append_block \
--pageId 8a7…b21 \
--blocks '[{"type":"paragraph","text":"shipped v0.1"}]'
Context7 (HTTP, anonymous)
A free hosted docs-search MCP — no auth, useful as a sanity check:
mcpal server add ctx7 --http https://mcp.context7.com/mcp
mcpal tool call ctx7 search --query 'rmcp ServiceExt'
Postgres (stdio, env-injected creds)
mcpal server add db -- \
npx -y @modelcontextprotocol/server-postgres \
postgres://user:pass@localhost:5432/app
mcpal tool list db
mcpal tool call db query --sql 'select count(*) from users'
Put the connection string in an env var and --env it through:
mcpal server add db \
--env DATABASE_URL="$DATABASE_URL" \
-- npx -y @modelcontextprotocol/server-postgres '$DATABASE_URL'
opencode's already-configured servers
If you run opencode, every server in ~/.config/opencode/opencode.json
is callable directly:
mcpal server discover --source opencode
mcpal tool list opencode:tavily
mcpal tool call opencode:tavily search --query 'Rust async runtimes'
Same pattern for cursor:, claude-code:, zed:, etc. See
Concepts → Discovery for the source list.
Install a server from the MCP Registry
mcpal server search filesystem --limit 5
mcpal server install io.github.Oncorporation/filesystem-server --as fs
mcpal server install io.github.foo/bar --env API_KEY=$KEY
mcpal tool list fs
server install resolves the registry entry's package
(npm → npx, pypi → uvx, oci → docker run) or its
streamable-http remote into a ServerSpec and writes it to your config.
Required env vars without defaults must be supplied via --env K=V.
List servers already on the machine
mcpal server discover
Use an mcp.json without registering it
mcpal --mcp-json ./mcp.json tool list <name>
Global flag; overlays for the session and writes nothing to disk.
Call a tool with a JSON arg body
mcpal tool call ev some-tool --params '{"message":"hi","count":3}'
From stdin or file:
echo '{"message":"hi"}' | mcpal tool call ev some-tool --params -
mcpal tool call ev some-tool --params @args.json
--cli-input-json accepts a base body from a path or - (stdin). Mix --params with
--key value overrides; flag values win:
mcpal tool call ev some-tool --params @base.json --message override
Or generate a skeleton:
mcpal --output json tool template ev some-tool \
| jq '.field = "value"' \
| mcpal tool call ev some-tool --cli-input-json -
Extract one field from a response
mcpal --query 'content[0].text' tool call ev echo --message hi
--query is JMESPath. See the tutorial.
Watch a long-running tool
# terminal 1
mcpal watch ev
# terminal 2
mcpal tool call ev trigger-long-running-operation --duration 10 --steps 5
One YAML doc per notification (progress, log, resource-updated, list-changed). Ctrl-C to exit.
Send a raw JSON-RPC method
mcpal raw <ref> some/new-method --params '{"foo":"bar"}'
mcpal raw <ref> some/method --params @payload.json
mcpal raw <ref> some/method --params -
Loop a tool over many inputs
for q in rust go python; do
mcpal tool call github search --q "$q stars:>1000" --per_page 3
done
seq 1 50 | xargs -P 8 -I {} \
mcpal tool call worker process --batch-id {}
Branch on exit code
mcpal tool call ev echo --message hi
case $? in
0) echo "ok" ;;
3) echo "ref not found"; exit 1 ;;
4) mcpal auth login ev --oauth ;;
5) mcpal auth refresh ev ;;
*) echo "see above"; exit $? ;;
esac
Disable interactive prompts (CI)
mcpal --no-interactive tool call <ref> …
Elicitation requests auto-decline. Bearer tokens come from
MCPAL_BEARER or --bearer, never a TTY prompt.
External sampling handler
mcpal --sampling-handler "claude --output json" \
tool call <ref> trigger-sampling-request --prompt "summarize"
mcpal pipes CreateMessageRequestParams JSON to the handler's stdin and
reads CreateMessageResult JSON from its stdout.
Expose workspace roots
mcpal --root ~/src/my-project --root /tmp \
tool call <ref> get-roots-list
Resources
mcpal resource list <ref>
mcpal resource read <ref> demo://resource/static/document/architecture.md
mcpal resource template list <ref>
mcpal resource subscribe <ref> some://uri
Prompts
mcpal prompt list <ref>
mcpal prompt get <ref> some-prompt --city Dallas --state Texas
Diff two servers' capabilities
mcpal diff <ref-a> <ref-b>
mcpal diff <ref-a> <ref-b> --only tools
Reports added, removed, and changed entries per category
(tools, resources, prompts). A tool counts as changed when its
inputSchema differs between the two servers.
Shell completions
mcpal completion zsh > ~/.zfunc/_mcpal
bash and fish work the same way.
Completing tool / resource / prompt names
tool list, resource list, and prompt list accept --names-only,
which prints one name (or URI) per line on stdout. Wire it into your
shell's completion. For zsh, with the cursor after mcpal tool call ev :
_mcpal_tools() {
# $words: (mcpal tool call <ref> …); the ref is words[-2] from CURRENT.
local ref=${words[-2]}
compadd -- $(mcpal tool list "$ref" --names-only 2>/dev/null)
}
compdef _mcpal_tools 'mcpal tool call'
Bash equivalent (inside your complete -F function, with $prev already
set to the ref token):
COMPREPLY=( $(compgen -W "$(mcpal tool list "$prev" --names-only 2>/dev/null)" -- "$cur") )
stdio servers may leak their own stderr (Starting default (STDIO) server... and similar) during completion. The 2>/dev/null above
suppresses it. Setting MCPAL_CHILD_STDERR=inherit un-silences it
again.