Pinecall as an MCP server. Point a coding agent — Claude Code, Cursor, Codex, Antigravity, Windsurf, anything that speaks MCP — at this server and it can take you from zero to a production voice agent answering a real phone number without leaving the editor.
Not "an API wrapper with tools". The server ships a playbook: its MCP
instructions teach the whole journey, and every tool carries its own manual, so a
coding agent that has never seen Pinecall discovers the catalog, builds a sandboxed
agent, talks to it, wires a phone number, and reads the live call log of the call
you just made — on its own.
you: "build me a voice agent for my restaurant that takes reservations"
agent: whoami → org, plan, credits
docs_search / get_doc → reads the real docs before writing anything
list_models · list_voices → picks STT/LLM/voice from the LIVE catalog
play_voice → 🔊 plays voice samples through your speakers
configure_agent (dev-…) → registers a sandboxed agent
chat → talks to it until the prompt behaves
list_phones → finds a free number, wires it
observe → 📞 you call; it watches the transcript live
npx -y @pinecall/mcp installOne command, every IDE, and nothing else needs to be installed first — the
server installs itself. It detects the assistants on the machine and writes the
pinecall entry into each one's own config, in that host's own format: Claude
Code, Codex (TOML), Antigravity, Cursor, Windsurf, Gemini CLI. Re-running
repairs a drifted entry instead of duplicating it; --remove takes exactly the
pinecall entry out and nothing else. Every write leaves a .bak next to the file.
npx -y @pinecall/mcp install --list # what is detected, changes nothing
npx -y @pinecall/mcp install cursor # only the ones you name
npx -y @pinecall/mcp install --remove # uninstallIf you already have the Pinecall CLI, pinecall mcp install is the same thing
with a friendlier spelling.
Or by hand, in any MCP client config:
Set PINECALL_API_KEY in your environment and you are done. But IDEs launch MCP
servers without your shell — a macOS GUI app never sources ~/.zshrc — so the
server discovers the key in order:
PINECALL_API_KEYin the process environment~/.pinecall/credentials(mode0600, shared with thepinecallCLI)- a read-only scan of your shell rc files (
~/.zshrc,~/.bashrc, …) for the export — never executed, and on a hit the key is persisted to the credentials file so the fragile path runs exactly once
No key anywhere? The set_api_key tool stores one for the session (memory only;
persist: true writes the credentials file). The key never appears in any tool
result, log line, or error — the server scrubs it from everything it emits.
Eighteen, in journey order. Each ships a manual that is assembled into the
server's instructions — a tool cannot exist here without documenting itself.
| Tool | What it does |
|---|---|
whoami |
The auth probe: org, plan, credits, and where the key was found. Call it first. |
set_api_key |
Store a key for the session — memory only, or persist: true for ~/.pinecall/credentials. Never echoed. |
docs_search |
Semantic search over the Pinecall docs. Retriever only, no LLM — you read the chunks. |
get_doc |
A whole docs page as markdown, by the path docs_search returned. Read pages, not snippets. |
knowledge |
Knowledge bases (RAG): list, query, push. Push is idempotent by path; re-training is automatic. |
list_models |
Every LLM/STT/TTS the server accepts, with the exact config string — and usable: whether your org can run it today (managed, or you brought the key). |
list_voices |
TTS voices with exact config strings, filterable by provider and language. |
play_voice |
🔊 Plays a voice sample through your machine's speakers, so you pick a voice by ear. |
list_phones |
The org's numbers and who holds each — agent: null is the one you may take. |
list_agents |
Who exists, who is online, and which slugs are dev- sandboxes. |
configure_agent |
Create or hot-reload a dev agent (prompt, LLM, STT, voice, phone). Held live by the session. |
run_agent |
Run your own agent file (with real code tools) as a managed child — start/stop/status/logs, via the same machinery as pinecall run. |
chat |
Talk to any agent, production included. The returned session id is the thread. Chat is the testing story. |
observe |
Long-poll live tail: loop it while a human calls the agent and read the transcript as it happens. |
list_calls |
The lifecycle index of an agent's calls — find the id, then read it. |
get_call |
One call's full log, reduced: transcript with seqs, tool calls with results, turn latencies, summary. Cursor-paged. |
subscribe |
Plan, credits, and a Stripe link the human opens — the server never touches card data. |
byok |
Bring-your-own provider keys: list (never key material), set, remove. |
- No spec-runner. Testing is conversational:
chatwith the agent, read the transcript, fix the prompt. A transcript you read is the check. - No outbound dialling, no account creation. Real phones cost money and ring real people; accounts come from pinecall.io.
- No production configuration.
configure_agentrefuses any slug that does not start withdev-— no flag, no override. Registering an agent hot-reloads the live one, so touching a production slug would clobber the agent a running process owns, and its next reconnect would clobber you back. Production agents are deployed from code, never from an editor session.
dev- is the whole convention: anything the MCP creates is a sandbox, and the one
mutating tool that could reach production refuses to. chat works against
production because it only talks. subscribe hands the human a Stripe URL rather
than touching billing itself — and warns that switching plans on an existing
subscription is an immediate, prorated, real charge. Keys — yours and your
providers' — transit once and are scrubbed from every output; the single sanctioned
write is ~/.pinecall/credentials at 0600, and only when asked.
Things this server tells you that a thinner wrapper would not:
configure_agentverifies phone routing after claiming a number and answers{routed: false, routedTo: "dev-other"}when another agent actually holds it — because the underlying protocol's refusal is fire-and-forget, and echoing the request back as the result would have you inviting calls to a number that reaches someone else.list_modelsjoins the catalog with your org's BYOK keys:usable: false, unusableReason: "needs-byok"before you build a config that fails at call time.observedistinguishes silence from refusal: a quiet log returns{timedOut: true}forever without erroring; a bad token errors immediately with the reason.get_callnever truncates silently:truncated: trueplus thenextAftercursor to continue from.
The published package is a self-contained bundle — consumers need nothing but
Node ≥ 20. Building it, however, reuses the Pinecall SDK's internals (its HTTP
client, the chat WebSocket client, the call-log reducer), which the published
@pinecall/sdk deliberately does not export. So development uses a sibling
checkout:
git clone https://github.com/pinecall/sdk
git clone https://github.com/pinecall/mcp
cd sdk && npm install && npm run build # dist/cli.js — run_agent drives it
cd ../mcp && npm install # links pinecall-sdk → ../sdk
npm run build # tsup bundles sdk internals in
npm test # 215 tests; live ones need PINECALL_API_KEYThe pinecall-sdk/src/** imports resolve through an esbuild/vitest alias because
the sdk's exports map — correct for its consumers — would block them. Releases
are tag-driven (v* → GitHub Actions → npm Trusted Publishing with provenance);
CI reproduces the sibling layout.
MIT
{ "mcpServers": { "pinecall": { "command": "npx", "args": ["-y", "@pinecall/mcp"] } } }