MCP server
Hosted MCP server
DialerBee runs an MCP server for you at https://api.dialerbee.com/mcp (MCP Streamable HTTP, JSON
answers). Connect your assistant to it with an API key in the Authorization header:
{
"mcpServers": {
"dialerbee": {
"type": "http",
"url": "https://api.dialerbee.com/mcp",
"headers": { "Authorization": "Bearer dbk_live_..." }
}
}
}
- Same rules as the API. Every tool call is one request to the API with your key: the scopes of your API client, the Tier-2 switches of your account, the rate limits, the idempotency keys and the audit trail all apply. A tool your API client cannot use is not listed.
- You confirm every action that can reach a person (a call, adding a lead, a WhatsApp message,
an SMS, a booking, a campaign resume, consent) and every webhook change. Adding a lead counts:
in a running campaign, or with
dial_now, the dialler calls the person within seconds. Operations that are not available yet (click-to-call, WhatsApp) are not offered as tools. The server asks your assistant to show you the action and its exact values; it runs once, only if you tick "Run this action once". This needs MCP protocol2026-07-28with form elicitation. An assistant on an older protocol can use every other tool; for these it gets a refusal and nothing is sent. - Results are data, not instructions. Account data comes back inside a marked block, and webhook signing secrets are never shown to the assistant.
- Recordings are not a hosted tool (use the SDK to stream one).
Use a key of a client with only the scopes the assistant needs; a read-only client is a good start.
Local MCP server
A local, stdio Model Context Protocol server for the DialerBee
public API, version 1. It runs on your own machine with your own API key and calls the same REST
API as the SDKs (https://app.dialerbee.com/api/v1), so every scope, account switch, limit and audit
of your API client applies. It is built on the dialerbee JavaScript SDK and holds no business
logic of its own.
Status: pre-release, not yet on npm. It is built from API contract 1.0.0-rc5 (see
contract-lock.json); until the contract is final, tool shapes may still change. DialerBee also
runs a hosted MCP server at https://api.dialerbee.com/mcp (the same tools and rules, with your
key in the Authorization header); use this local one when you want the key and the audit file
on your own machine.
Requires Node.js 18.17 or later. No runtime dependency besides the dialerbee SDK.
Quick start
Create an API client in the DialerBee console with only the scopes the assistant needs (a read-only client is a good start), put its key in a file only you can read, and point your MCP client at the server.
install -m 600 /dev/null ~/.config/dialerbee-key && $EDITOR ~/.config/dialerbee-key
Claude Desktop (claude_desktop_config.json), once published:
{
"mcpServers": {
"dialerbee": {
"command": "npx",
"args": ["-y", "@dialerbee/mcp", "--read-only"],
"env": { "DIALERBEE_API_KEY_FILE": "/home/you/.config/dialerbee-key" }
}
}
}
Claude Code: claude mcp add dialerbee --env DIALERBEE_API_KEY_FILE=$HOME/.config/dialerbee-key -- npx -y @dialerbee/mcp --read-only
Until it is published, build it from this repository and use node <repo>/developer/mcp-local/dist/cli.js
as the command:
cd developer/sdk-js && npm ci && npm run build
cd ../mcp-local && npm ci && npm run build
Configuration
| Setting | Default | |
|---|---|---|
DIALERBEE_API_KEY |
none | dbk_live_... or dbk_test_... |
DIALERBEE_API_KEY_FILE |
none | a file holding the key; refused unless only you can read it (chmod 600) |
DIALERBEE_BASE_URL |
https://app.dialerbee.com/api |
https only (plain http for localhost) |
--read-only / DIALERBEE_MCP_READ_ONLY=1 |
off | only read tools |
--enable-tier2 / DIALERBEE_MCP_ENABLE_TIER2=1 |
off | also offer Tier 2 tools (see below) |
--audit-file PATH / DIALERBEE_MCP_AUDIT_FILE |
none | also append the local audit to PATH (JSON lines, 0600) |
The key is never accepted on the command line (other users of a machine can read process
arguments) and never as a tool argument. It reaches only the Authorization header; it is never
logged, printed, listed or returned.
What the assistant can do
One tool per API operation, named after its route id (leads.list -> leads_list). Each tool is
in one of four capability classes, which the API contract sets for its operation:
| Class | Offered when | Each call |
|---|---|---|
read |
your API client has the scope | runs |
safe_write |
the scope, and the server is not --read-only |
runs, with an idempotency key (webhook writes: you confirm it, see below) |
communication (Tier 1: leads_create) |
the scope, and the server is not --read-only |
you confirm it in your MCP client |
communication (Tier 2) |
--enable-tier2, the account's Tier-2 switch is on, and the scope |
you confirm it in your MCP client |
compliance_reducing (Tier 2) |
same | you confirm it in your MCP client, and it is audited |
- The server reads
GET /v1/me(cached for 60 s) and lists only the tools your API client can use. When that call fails, Tier 1 tools stay listed (the API still decides) and Tier 2 tools are refused. - Confirmation uses MCP elicitation: the server asks your MCP client to show you the action and
its exact values, and runs it once only if you tick "Run this action once". A client that cannot
ask (no form elicitation, or protocol 2025-03-26 and older) cannot run these tools. No tool
argument can confirm. Under the 2026-07-28 protocol the question travels as an
input_requiredresult; itsrequestStateis signed by this process, bound to the tool and the exact arguments, valid for 5 minutes and usable once. - Webhook writes need your confirmation too.
webhooks_create,webhooks_update,webhooks_test,webhooks_rotate_secretandwebhook_events_replayaresafe_writein the API, but they send account data (customer names, phone numbers, call summaries) to a web address, or change how it is signed. Text inside records could ask the assistant to point a webhook somewhere else, so this server asks you before every one of these calls, whatever your settings. - Webhook signing secrets never reach the assistant. The API shows a signing secret once, in
the answer to
webhooks_createandwebhooks_rotate_secret. This server replaces it withnulland passes on onlysecret_hint(its last four characters). To get a secret you can copy into your webhook receiver, rotate it outside the assistant (in the DialerBee console's webhook settings, or with the SDK or curl) and copy it from that one-time answer. Never paste a signing secret into a chat. - Adding a lead and SMS are confirmed every time.
leads_createcan reach a real person (a lead in a running campaign, or withdial_now, is called by the dialler within seconds) andsms_sendandcalls_createare Tier 2 (they need a sending key and the account's switch), so this server asks you before every call. WhatsApp (POST /v1/whatsapp/messages) is not available yet for any account, so it is not a tool. - Removing a number from the Do-Not-Call list is not an API action, so it is not a tool: it is done
in the DialerBee console by an account administrator. The assistant can add a number
(
dnc_add) and check numbers (dnc_check). - Tier 2 switches are off for every account by default, so in practice Tier 2 tools are not offered until DialerBee turns a switch on for your account.
| Tool | API route | Class | Tier | Scope | You confirm each call |
|---|---|---|---|---|---|
dnc_add |
POST /v1/dnc | safe_write | 1 | dnc:add | no |
appointments_cancel |
POST /v1/appointments/{appointment_id}/cancel | communication | 2 | appointments:write | yes |
callbacks_cancel |
POST /v1/callbacks/{callback_id}/cancel | safe_write | 1 | callbacks:cancel | no |
dnc_check |
POST /v1/dnc/check | read | 1 | dnc:check | no |
appointments_create |
POST /v1/appointments | communication | 2 | appointments:write | yes |
callbacks_create |
POST /v1/callbacks | communication | 2 | callbacks:write | yes |
consents_create |
POST /v1/consents | compliance_reducing | 2 | consent:write | yes |
leads_create |
POST /v1/leads | communication | 1 | leads:write | yes |
webhooks_create |
POST /v1/webhooks | safe_write | 1 | webhooks:write | yes |
webhooks_delete |
DELETE /v1/webhooks/{webhook_id} | safe_write | 1 | webhooks:write | no |
appointments_get |
GET /v1/appointments/{appointment_id} | read | 1 | appointments:read | no |
calls_get |
GET /v1/calls/{call_id} | read | 1 | calls:read | no |
calls_recording |
GET /v1/calls/{call_id}/recording | read | 1 | recordings:read | no |
calls_summary |
GET /v1/calls/{call_id}/summary | read | 1 | summaries:read | no |
calls_transcript |
GET /v1/calls/{call_id}/transcript | read | 1 | transcripts:read | no |
campaigns_get |
GET /v1/campaigns/{campaign_id} | read | 1 | campaigns:read | no |
campaigns_progress |
GET /v1/campaigns/{campaign_id}/progress | read | 1 | campaigns:read | no |
reports_dispositions |
GET /v1/reports/dispositions | read | 1 | reports:read | no |
leads_get |
GET /v1/leads/{lead_id} | read | 1 | leads:read | no |
leads_history |
GET /v1/leads/{lead_id}/history | read | 1 | leads:read | no |
me_get |
GET /v1/me | read | 1 | none | no |
sms_usage |
GET /v1/sms/usage | read | 1 | sms:read | no |
webhooks_get |
GET /v1/webhooks/{webhook_id} | read | 1 | webhooks:read | no |
webhook_events_get |
GET /v1/webhook-events/{event_id} | read | 1 | webhooks:read | no |
leads_import |
POST /v1/leads/import | safe_write | 1 | leads:write | no |
appointments_list |
GET /v1/appointments | read | 1 | appointments:read | no |
callbacks_list |
GET /v1/callbacks | read | 1 | callbacks:read | no |
calls_list |
GET /v1/calls | read | 1 | calls:read | no |
campaigns_list |
GET /v1/campaigns | read | 1 | campaigns:read | no |
leads_list |
GET /v1/leads | read | 1 | leads:read | no |
sms_delivery_reports |
GET /v1/sms/delivery-reports | read | 1 | sms:read | no |
webhooks_list |
GET /v1/webhooks | read | 1 | webhooks:read | no |
webhook_events_list |
GET /v1/webhook-events | read | 1 | webhooks:read | no |
campaigns_pause |
POST /v1/campaigns/{campaign_id}/pause | safe_write | 1 | campaigns:pause | no |
webhook_events_replay |
POST /v1/webhook-events/{event_id}/replay | safe_write | 1 | webhooks:write | yes |
appointments_reschedule |
POST /v1/appointments/{appointment_id}/reschedule | communication | 2 | appointments:write | yes |
callbacks_reschedule |
POST /v1/callbacks/{callback_id}/reschedule | communication | 2 | callbacks:write | yes |
campaigns_resume |
POST /v1/campaigns/{campaign_id}/resume | communication | 2 | campaigns:resume | yes |
webhooks_rotate_secret |
POST /v1/webhooks/{webhook_id}/rotate-secret | safe_write | 1 | webhooks:write | yes |
sms_send |
POST /v1/sms | communication | 2 | messages:send | yes |
calls_create |
POST /v1/calls | communication | 2 | calls:originate | yes |
webhooks_test |
POST /v1/webhooks/{webhook_id}/test | safe_write | 1 | webhooks:write | yes |
leads_update |
PATCH /v1/leads/{lead_id} | safe_write | 1 | leads:write | no |
webhooks_update |
PATCH /v1/webhooks/{webhook_id} | safe_write | 1 | webhooks:write | yes |
calls_recording returns the audio inline up to 8 MiB; for a larger recording pass range (for
example bytes=0-1048575) or use the SDK.
Untrusted data (prompt injection)
Lead fields and notes, transcripts, summaries, CRM fields and webhook payloads are written by other people. The server hands every result to the model inside one marked block,
<dialerbee-untrusted-data>
{ ... JSON ... }
</dialerbee-untrusted-data>
with a notice that the block is data and never instructions. Inside the block every <, > and
& is a JSON \u003c-style escape, so no record can close the block early. Text inside a record
cannot authorize an action: Tier 2 actions are off unless the person running the server opted in,
and Tier 2 actions and webhook writes each need that person's confirmation in the client. The
injection tests (test/injection.test.mjs) run on every build and must pass before every release.
Writes, retries and errors
- A write tool generates an idempotency key when you pass none and returns it with the result
(
idempotency_key). Retry the same change only with the same key. outcome_unknown: DialerBee could not confirm whether the change happened. Never retry it with a new key; check the object later, or quote therequest_idto support.- API errors come back as tool errors with the HTTP status, the error
codeand therequest_id. - The SDK's own safe retries apply (at most 3 attempts; only GET or keyed requests).
Local audit
Every write-class call leaves intent and result records (and refused ones), on stderr and in
--audit-file when set: time, tool, route, class, tier, confirmation, idempotency key, status,
error code and request id. Never arguments, bodies, phone numbers or the key. When an audit file is
set and cannot be written, Tier 2 calls are refused; Tier 1 calls go on with a warning (DialerBee
keeps the authoritative audit either way).
Protocol
stdio transport, JSON-RPC 2.0, one message per line. Supported protocol versions:
2026-07-28 (per-request _meta, server/discover, multi round-trip confirmation) and the
handshake versions 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 (initialize). The server
offers tools only (no resources, prompts or sampling) and never sends a request to the client
except the confirmation question inside a tool call you started.