Remote MCP server (Streamable HTTP) wrapping the Bolna voice AI REST API (https://api.bolna.ai): 116 tools covering nearly every documented Bolna endpoint, TypeScript, deployed on Vercel via...
Overview
Remote MCP server (Streamable HTTP) wrapping the Bolna voice AI REST API (https://api.bolna.ai): 116 tools covering nearly every documented Bolna endpoint, TypeScript, deployed on Vercel via mcp-handler. — that page has the same connect instructions below with copy-paste buttons for each client. MCP is an open protocol, not Claude-specific — this server works with any MCP-compatible client: Claude, Codex, Cursor, Windsurf, Zed, and others. Endpoint corrections found while verifying tool paths against the live Bolna docs are documented in the comment block at the top of src/tools/index.ts. A directory listing is reviewed as a whole, so every tool on it is surface area to justify. /api/connector/mcp serves the nine tools named in src/tools/directory-surface.ts and nothing else, while /api/mcp keeps growing. Adding a name to that list means adding it to what Anthropic reviews, so do it deliberately. Both routes are built by the same factory in src/lib/mcp-route.
README
Bolna MCP Server
Remote MCP server (Streamable HTTP) wrapping the Bolna voice
AI REST API (https://api.bolna.ai): 116 tools covering nearly every documented
Bolna endpoint, TypeScript, deployed on Vercel via mcp-handler.
Live at mcp.bolna.ai — that page has the same connect instructions below with copy-paste buttons for each client.
MCP is an open protocol, not Claude-specific — this server works with any MCP-compatible client: Claude, Codex, Cursor, Windsurf, Zed, and others.
Endpoint corrections found while verifying tool paths against the live Bolna
docs are documented in the comment block at the top of
src/tools/index.ts.
Two endpoints
| Endpoint | Tools | For |
|---|---|---|
/api/mcp |
all 116 | Anyone connecting the server themselves |
/api/connector/mcp |
9 | The Claude connector directory listing |
The directory surface is a fixed subset. A directory listing is reviewed
as a whole, so every tool on it is surface area to justify.
/api/connector/mcp serves the nine tools named in
src/tools/directory-surface.ts and
nothing else, while /api/mcp keeps growing. Adding a name to that list
means adding it to what Anthropic reviews, so do it deliberately. Both
routes are built by the same factory in
src/lib/mcp-route.ts and share the same tool
implementations.
The restriction is applied at registration time, not per call: tools off the
list never enter the server’s registry, so they are absent from tools/list
and un-callable by name. Prompts are dropped the same way, so the
connector answers prompts/list with “method not found” rather than
advertising the four on /api/mcp.
Two things also differ on the connector, and only there:
- No
api_keyparameter. Every tool on/api/mcpaccepts an optionalapi_keyto target another account for one call (see above). It is stripped from both the schema and the incoming arguments on the connector, so it always acts as the OAuth-authenticated user. The strip lives in the same wrapper as the allowlist — no tool implementation is touched. - Its own server
instructions.SERVER_INSTRUCTIONSdescribes tools and prompts the connector doesn’t register, so it sendsCONNECTOR_INSTRUCTIONSfromsrc/tools/prompts.tsinstead.
OAuth is per-surface. RFC 9728 metadata must declare a resource equal
to the URL the client connected to, and Claude rejects a mismatch, so a
single hardcoded document can’t serve two paths. /api/connector/mcp points
at the path-insertion form
/.well-known/oauth-protected-resource/api/connector/mcp, served by a
catch-all route that answers only for known transport paths. /api/mcp
keeps the bare /.well-known/oauth-protected-resource it has always handed
out, so already-connected clients are unaffected. Both documents name the
same Supabase authorization server, and verifyToken
does no audience check of its own — the token is forwarded to
api.bolna.ai either way — so the sign-in flow at the two URLs is
identical.
Sub-accounts and switching between accounts
Sub-account API keys (format sa-...) work exactly like a main account’s key
(bn-...) — connect a separate client/connector with a sub-account’s key as
the Bearer token to work entirely within that sub-account.
To switch accounts within a single connected session instead of
reconnecting, every tool also accepts an optional api_key argument that
overrides the connection’s own credential for just that call. Get a
sub-account’s key from list_sub_accounts’ api_key field, then pass it to
any tool call to run that call against that sub-account instead of the main
account — e.g. “list agents in sub-account X” resolves to calling
list_sub_accounts, finding X’s key, then list_agents with that key passed
as api_key.
Available tools
116 tools across 17 categories. “Write” tools flagged Destructive modify, remove, or spend money and typically prompt for confirmation in MCP clients that respect tool annotations.
Agents & calls
| Tool | Type | Description |
|---|---|---|
list_agents |
Read | List agents in the account (id, name, status, created_at). Paginated. |
get_agent |
Read | Full config of one agent by ID. |
create_agent |
Write | Create a new agent. Returns its ID. |
update_agent |
Write, Destructive | Patch an existing agent’s name, prompts, welcome message, webhook, or voice settings. |
delete_agent |
Write, Destructive | Permanently delete an agent. Irreversible. |
stop_agent_queued_calls |
Write, Destructive | Cancel every queued call for an agent. |
start_outbound_call |
Write, Destructive | Place a real outbound call from an agent. Spends account balance. |
stop_call |
Write, Destructive | Cancel a queued or scheduled call. |
Executions
| Tool | Type | Description |
|---|---|---|
list_agent_executions |
Read | Call history for one agent. Paginated, defaults to the last 7 days. |
get_execution |
Read | Full details of one call: transcript, status, cost, telephony data. |
get_execution_raw_logs |
Read | Raw pipeline logs (transcriber/LLM/synthesizer) for a call. |
get_execution_debug |
Read | Everything needed to debug a call, in one compact request: execution, per-turn latency and transcript, logs with delta-encoded LLM requests. |
list_batch_executions |
Read | Every call execution within a batch. Paginated. |
Batches
| Tool | Type | Description |
|---|---|---|
create_batch |
Write | Create a batch of outbound calls from a list of recipients. |
get_batch |
Read | A batch’s status, schedule, and contact counts. |
list_batches |
Read | Call batches for one agent. |
schedule_batch |
Write, Destructive | Schedule a batch to start calling. Spends account balance. |
stop_batch |
Write, Destructive | Halt a running or queued batch. |
delete_batch |
Write, Destructive | Permanently delete a batch. |
Dispositions (structured call extraction)
| Tool | Type | Description |
|---|---|---|
list_dispositions |
Read | List dispositions, optionally scoped to an agent. |
get_disposition |
Read | Retrieve a single disposition. |
create_disposition |
Write | Create a disposition linked to an agent. |
bulk_create_dispositions |
Write | Atomically create multiple dispositions for one agent. |
update_disposition |
Write, Destructive | Update a disposition (may fork a private copy — Bolna’s copy-on-write). |
delete_disposition |
Write, Destructive | Permanently delete a disposition. |
test_dispositions |
Read | Run an agent’s dispositions against a sample transcript. |
Extraction categories
| Tool | Type | Description |
|---|---|---|
list_extraction_categories |
Read | List an agent’s extraction categories with their dispositions. |
create_extraction_category |
Write | Create an extraction category (name + model) on an agent. |
update_extraction_category |
Write, Destructive | Rename a category or change its model; renames rewrite the category text on its dispositions. |
delete_extraction_category |
Write, Destructive | Permanently delete a category and every disposition inside it. |
Phone numbers & inbound
| Tool | Type | Description |
|---|---|---|
list_phone_numbers |
Read | Phone numbers on the account. |
search_phone_numbers |
Read | Search phone numbers available for purchase. |
buy_phone_number |
Write, Destructive | Purchase a phone number. Real recurring charge. |
delete_phone_number |
Write, Destructive | Remove a phone number and stop its billing. |
setup_inbound_agent |
Write | Route inbound calls on a number to an agent, optionally with an IVR menu. |
unlink_inbound_agent |
Write, Destructive | Remove inbound routing from a number. |
SIP trunks (bring your own carrier)
| Tool | Type | Description |
|---|---|---|
list_trunks |
Read | SIP trunks on the account, with health, gateways, carrier IPs and numbers. |
get_trunk |
Read | Full details of one SIP trunk. |
create_trunk |
Write | Create a SIP trunk for your own carrier. |
update_trunk |
Write, Destructive | Partially update a trunk’s settings or carrier credentials. |
delete_trunk |
Write, Destructive | Permanently delete a trunk with its gateways, carrier IPs and numbers. |
add_trunk_gateway |
Write | Add an outbound gateway (the carrier’s SIP host). |
remove_trunk_gateway |
Write, Destructive | Remove an outbound gateway. |
add_trunk_carrier_ip |
Write | Allow a carrier signalling IP or CIDR to send inbound calls. |
remove_trunk_carrier_ip |
Write, Destructive | Stop accepting inbound calls from a carrier IP. |
add_number_to_trunk |
Write | Add an E.164 number the carrier routes to the trunk. |
remove_number_from_trunk |
Write, Destructive | Remove a number from a trunk. |
assign_trunk_number_agent |
Write, Destructive | Set or clear the agent that answers a number’s inbound calls. |
list_trunk_calls |
Read | Recent calls on a trunk’s numbers, with SIP codes. |
get_trunk_connection_details |
Read | Bolna’s SIP domain, IPs, port, transports and RTP range to configure at the carrier. |
start_trunk_test_call |
Write, Destructive | Ring a phone through the trunk with an echo test. |
get_trunk_test_call |
Read | State of an outbound test call. |
arm_trunk_inbound_test |
Write | Answer the trunk’s next inbound call with an echo test instead of an agent. |
get_trunk_inbound_test |
Read | Whether the trunk is armed and how the inbound test went. |
disarm_trunk_inbound_test |
Write, Destructive | Cancel an inbound test. |
Legacy SIP trunks (deprecated)
Kept for trunks that already exist on the legacy plane; new setups use the SIP trunk tools above.
| Tool | Type | Description |
|---|---|---|
create_sip_trunk |
Write | Create a legacy SIP trunk with gateway and auth config. Use create_trunk instead. |
get_sip_trunk |
Read | Full details of a legacy SIP trunk. Use get_trunk instead. |
list_sip_trunks |
Read | All legacy SIP trunks on the account. Use list_trunks instead. |
update_sip_trunk |
Write, Destructive | Partially update a legacy SIP trunk. Use update_trunk instead. |
delete_sip_trunk |
Write, Destructive | Permanently delete a legacy trunk and its gateways/numbers. Use delete_trunk instead. |
add_trunk_number |
Write | Add a DID phone number to a legacy trunk. Use add_number_to_trunk instead. |
remove_trunk_number |
Write, Destructive | Remove a number from a legacy trunk. Use remove_number_from_trunk instead. |
list_trunk_numbers |
Read | Numbers assigned to a legacy trunk. Use get_trunk instead. |
Sub-accounts (enterprise)
| Tool | Type | Description |
|---|---|---|
create_sub_account |
Write | Create an isolated sub-account workspace. Requires org admin access. |
list_sub_accounts |
Read | All sub-accounts in the organization. |
update_sub_account |
Write, Destructive | Update a sub-account’s name or concurrency limits. |
delete_sub_account |
Write, Destructive | Permanently delete a sub-account and all its data. |
get_sub_account_usage |
Read | Usage and cost breakdown for one sub-account. |
get_all_sub_accounts_usage |
Read | Usage and cost breakdown for every sub-account. |
Voice & providers
| Tool | Type | Description |
|---|---|---|
list_tts_providers |
Read | TTS providers and models Bolna supports. |
list_voices |
Read | Voices available for a TTS provider/model. |
list_providers |
Read | Third-party providers configured on the account (secrets masked). |
remove_provider |
Write, Destructive | Remove a configured provider by name. |
Knowledgebase (RAG)
| Tool | Type | Description |
|---|---|---|
create_knowledgebase |
Write | Create a knowledgebase by scraping a URL. PDF upload isn’t supported here. |
get_knowledgebase |
Read | A knowledgebase’s file name, status, and settings. |
list_knowledgebases |
Read | All knowledgebases on the account. |
delete_knowledgebase |
Write, Destructive | Permanently delete a knowledgebase. |
Violations
| Tool | Type | Description |
|---|---|---|
list_violations |
Read | List flagged call violations, optionally filtered by status. Paginated. |
Workflows
Multi-step, per-contact automations (call an agent, branch on the outcome, wait, call an API, wait for a callback from your own system, message over WhatsApp, retry, and so on) — distinct from a single agent’s own conversation. A workflow is edited as a draft, then published as an immutable version before it can run.
| Tool | Type | Description |
|---|---|---|
create_workflow |
Write | Create a new (empty) workflow. |
list_workflows |
Read | List workflows, optionally filtered by name. Paginated. |
get_workflow |
Read | A workflow’s status, settings (webhook header values masked), and full draft/published version history. |
rename_workflow |
Write, Destructive | Rename a workflow. |
update_workflow_settings |
Write, Destructive | Set, change, or remove the execution webhook (URL and headers) as a JSON merge patch. Takes effect immediately. |
test_workflow_webhook |
Write | POST one sample execution webhook to the saved URL and report the receiver’s response. |
delete_workflow |
Write, Destructive | Permanently delete a workflow and every version. |
get_workflow_draft |
Read | The current editable draft definition and its revision number. |
save_workflow_draft |
Write, Destructive | Overwrite the draft’s node graph. Requires the current revision to avoid clobbering concurrent edits. |
validate_workflow |
Read | Check the saved draft for errors before publishing. |
publish_workflow |
Write, Destructive | Freeze the draft as a new immutable published version. |
get_workflow_version |
Read | Retrieve a specific published version’s node graph. |
run_workflow |
Write, Destructive | Run a single contact through the latest published version. Places real calls/messages. |
list_workflow_node_types |
Read | Every node type the engine supports, with its config parameters. |
get_workflow_callback |
Read | A callback node’s URL and signing secret, optionally with the last payload it received. |
Workflow campaigns
Runs many contacts through one workflow version as a batch.
| Tool | Type | Description |
|---|---|---|
create_workflow_campaign |
Write | Create a campaign pinned to a workflow version. |
list_workflow_campaigns |
Read | List campaigns, optionally filtered by workflow, status, or kind. Paginated. |
get_workflow_campaign |
Read | A campaign’s status and totals, plus entry/validation-error breakdowns. |
delete_workflow_campaign |
Write, Destructive | Delete an empty draft campaign. |
upload_workflow_campaign_entries |
Write | Add contacts to a campaign (CSV/JSON columns beyond the standard fields go in custom_fields). |
list_workflow_campaign_entries |
Read | List a campaign’s uploaded contacts. Paginated. |
get_workflow_campaign_entries_template |
Read | The CSV header row the campaign’s pinned version expects. |
start_workflow_campaign |
Write, Destructive | Start dispatching a campaign’s pending entries. Spends account balance. |
pause_workflow_campaign |
Write, Destructive | Pause a running campaign’s dispatching. |
resume_workflow_campaign |
Write, Destructive | Resume a paused campaign. |
abort_workflow_campaign |
Write, Destructive | Permanently terminate a campaign. Cannot be undone. |
list_workflow_campaign_executions |
Read | List a campaign’s per-contact executions. Paginated. |
get_workflow_campaign_report |
Read | Aggregated execution/termination/per-node funnel counts. |
Workflow executions
A single contact’s run through a workflow, created by run_workflow or a campaign.
| Tool | Type | Description |
|---|---|---|
get_workflow_execution |
Read | Current node, node-by-node history, and recent timeline events. |
cancel_workflow_execution |
Write, Destructive | Terminate a running execution. |
resend_workflow_webhook |
Write | Queue a finished execution’s original webhook payload again, to the workflow’s current URL and headers. |
Graph agents
Node-based voice flows (llm_agent.agent_type: "graph_agent"). Create, edit, and
validate the graph, and push events into live calls.
| Tool | Type | Description |
|---|---|---|
create_graph_agent |
Write | Create a graph agent from nodes and edges. The graph is validated first; voice, transcriber, and telephony have defaults. |
get_graph_agent |
Read | A graph agent’s nodes, edges, routing settings, and a validation report. |
update_graph_agent |
Write, Destructive | Replace the graph, or add/replace/remove nodes and change routing settings, keeping the rest of the agent. Validated before saving. |
validate_graph_agent |
Read | Check a graph (or a saved agent) against the graph editor’s rules without saving. |
send_graph_agent_event |
Write | Push a named event into a live call; a matching event edge on the current node transitions and the agent speaks. |
Account & documentation
| Tool | Type | Description |
|---|---|---|
get_user_info |
Read | Account profile, wallet balance, concurrency limits. |
search_docs |
Read | Search the Bolna documentation site for matching pages. |
get_doc |
Read | Fetch the full markdown content of a Bolna documentation page. |
Not exposed as a tool, all for the same reason — each needs either a raw file upload or a third-party secret as its request body, neither of which maps cleanly to a tool argument or belongs in a chat conversation:
POST /providers(add a provider) — takes a third-party credential (e.g. a Twilio auth token)POST /knowledgebasewith a PDF file (the URL variant is supported, above)POST /violations/submit— takes an evidence screenshot/document
Install & use
Prerequisite: your own Bolna API key (from the Bolna dashboard). Every
client below needs the same two things: the server URL
(https://mcp.bolna.ai/api/mcp) and that key as a Bearer token.
Claude Code
One command:
./scripts/connect.sh
or the equivalent by hand:
claude mcp add --transport http bolna https://mcp.bolna.ai/api/mcp \
--header "Authorization: Bearer " \
--scope user
or by hand-editing ~/.claude.json (or .mcp.json for a project-scoped copy):
{
"mcpServers": {
"bolna": {
"type": "http",
"url": "https://mcp.bolna.ai/api/mcp",
"headers": {
"Authorization": "Bearer "
}
}
}
}
Claude Desktop
Desktop’s config only understands locally-run (stdio) servers, so reaching a
remote HTTP server with a custom header needs the
mcp-remote bridge, which runs
locally and forwards the header on your behalf. Add this to
claude_desktop_config.json:
{
"mcpServers": {
"bolna": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.bolna.ai/api/mcp",
"--header",
"Authorization: Bearer "
]
}
}
}
Restart Claude Desktop after editing the config.
Codex CLI
Reads the key from an environment variable rather than the config file:
export BOLNA_API_KEY=""
codex mcp add bolna \
--url https://mcp.bolna.ai/api/mcp \
--bearer-token-env-var BOLNA_API_KEY
Cursor
Add to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):
{
"mcpServers": {
"bolna": {
"url": "https://mcp.bolna.ai/api/mcp",
"headers": {
"Authorization": "Bearer "
}
}
}
}
Windsurf
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"bolna": {
"serverUrl": "https://mcp.bolna.ai/api/mcp",
"headers": {
"Authorization": "Bearer "
}
}
}
}
Zed
Add to settings.json. Zed doesn’t support environment-variable
interpolation in headers yet, so the key goes in directly:
{
"context_servers": {
"bolna": {
"url": "https://mcp.bolna.ai/api/mcp",
"headers": {
"Authorization": "Bearer "
}
}
}
}
Claude Desktop (extension)
Instead of the mcp-remote bridge above, you can install this as a proper
Desktop Extension (.mcpb) —
a local, stdio-based build of the same 116 tools, packaged with a manifest so
Claude Desktop can install it with one click and prompt you for your API key
itself (no config file editing).
Build it from source:
npm run package:mcpb # bundles src/stdio.ts, packs mcpb/ into bolna-mcp.mcpb
Then double-click bolna-mcp.mcpb, or drag it into Claude Desktop’s
Settings → Extensions, and enter your Bolna API key when prompted.
This local build talks directly to api.bolna.ai using only the raw API key
path in src/lib/auth.ts — OAuth is specific to the
remote server and isn’t used here.
Any other MCP client
Point it at:
URL: https://mcp.bolna.ai/api/mcp
Header: Authorization: Bearer
claude.ai web/mobile
This server now implements real OAuth (via Bolna’s Supabase-backed authorization server), so claude.ai’s own “Add custom connector” flow (Settings → Connectors) should work with just the URL — no manually pasted token needed, since claude.ai handles the login/consent redirect itself the same way Claude Code does. This hasn’t been separately verified on claude.ai web/mobile specifically yet (only Claude Code’s CLI flow has been tested end-to-end so far) — the CLI/config options above remain the proven paths in the meantime.
Try it
After connecting, start a new conversation and ask something like:
- “List my Bolna agents”
- “What’s my Bolna wallet balance?”
- “Call +1… using my [agent name] agent”
Auth
Two ways to connect:
- OAuth — sign in through Bolna’s own login (Supabase-backed). This is
what claude.ai’s custom-connector UI and Claude Code’s
Authenticateprompt use automatically; no manual token needed. - Raw API key — pass your own Bolna API key as a Bearer token in the
Authorizationheader, per the client examples above.
Either credential is verified before being forwarded to api.bolna.ai — see
the comment block at the top of src/lib/auth.ts for how.
For local development, set BOLNA_API_KEY in .env and omit the header —
the server falls back to it automatically.
Local dev
npm install
cp .env.example .env # then fill in BOLNA_API_KEY
npm run dev # or: vercel dev
The MCP endpoint is served at http://localhost:3000/api/mcp.
Deploy
Deployed under the Bolna AI Vercel team, aliased to mcp.bolna.ai:
vercel deploy --prod --scope bolna-ai
Set BOLNA_API_KEY as a Vercel environment variable only if you want a
server-wide fallback for testing; production callers should supply their own
key via the Authorization header.
Test with MCP Inspector
npx @modelcontextprotocol/inspector
In the Inspector UI, connect with:
- Transport: Streamable HTTP
- URL:
http://localhost:3000/api/mcp(or your deployed URL +/api/mcp) - Header:
Authorization: Bearer(optional locally ifBOLNA_API_KEYis set in.env)
Then use the Inspector’s “List Tools” and “Call Tool” panels to exercise each of the 13 core tools with valid and invalid input.
Privacy Policy
See mcp.bolna.ai/privacy.
License
Install
npx -y mcp-remote https://mcp.bolna.ai/api/mcp --header Authorization: Bearer <your BOLNA_API_KEY>Configuration
{
"mcpServers": {
"bolna": {
"type": "http",
"url": "https://mcp.bolna.ai/api/mcp",
"headers": {
"Authorization": "Bearer <your BOLNA_API_KEY>"
}
}
}
}