MCP quick-start
The bubbl3 MCP server lets an AI agent send messages, read conversations and contacts, check lines and hand a conversation to a person. It uses the Model Context Protocol over Streamable HTTP.
1. Get a credential
The server takes the same credentials as the API, as a bearer token:
- An API key (
b3_live_...orb3_test_...), or - A service identity: create one under Developers > MCP for each agent. It authenticates with its own linked key, can be limited to some lines and given fewer scopes than that key, and can be disabled on its own. Prefer one identity per agent, so you can see and stop each one.
The agent can do exactly what the credential allows, with the API's permissions, rate limits and sandbox rules. Give it the least it needs:
| Tools | Scope they need |
|---|---|
send_message, reply_to_conversation, handoff_to_human | send |
list_conversations, get_conversation, get_message_status | read |
search_contacts, get_contact, check_imessage, list_lines | read or admin |
upsert_contact | admin |
Start with a test key: the agent's messages then go to the simulator, never to a real phone.
2. Connect your client
The server URL is:
https://mcp.bubbl3.com/mcp
Most clients take a JSON configuration like this:
{
"mcpServers": {
"bubbl3": {
"url": "https://mcp.bubbl3.com/mcp",
"headers": { "Authorization": "Bearer b3_test_..." }
}
}
}
The server is stateless: each request carries the bearer token and gets a JSON answer. A request
without a key-shaped bearer token gets 401 with WWW-Authenticate: Bearer.
3. Tools and resources
There are 11 tools and 2 resources. MCP tools lists each tool's input,
generated from the same schemas tools/list returns.
| Tool | What it does |
|---|---|
send_message | Send to a phone number or contact, with an optional pool and fallback |
list_conversations | Recent conversations, filtered by status and assignee |
get_conversation | The messages in a conversation |
reply_to_conversation | Reply in an existing conversation |
search_contacts | Search by name, phone, email or company |
get_contact | One contact, by id or phone |
upsert_contact | Create a contact or update the one with that phone number. Never changes consent |
check_imessage | Whether a number can receive iMessage (cached for 30 days) |
list_lines | Lines with health and the new-contact capacity left today |
get_message_status | A message's status and delivery timeline |
handoff_to_human | Hand the conversation to a person and notify the assigned team |
| Resource | Reads |
|---|---|
bubbl3://conversations/{id} | A conversation and its messages |
bubbl3://contacts/{id} | A contact |
send_message and reply_to_conversation take an optional idempotency_key, which works like
the API's Idempotency-Key header: a retry with the same key returns the first message instead of
sending again.
4. The sending rules
The sending tools' descriptions tell the model these rules, so it doesn't trip the governor. Your own prompts should agree with them:
- Consent. Never message a contact who opted out. When the workspace requires consent (the
require_consentcompliance setting), contacts who haven't opted in can't be messaged either. Both calls are refused. - Quiet hours. Sends outside 9 AM to 8 PM in the contact's local time (or your workspace's quiet hours) are deferred until the window opens, not refused.
- First messages. Until a contact replies, messages must be under 300 characters, with no links, media, phone numbers or emails, and at most 3 can be sent.
Lines also have daily and hourly limits on new contacts. list_lines shows the capacity left, and
a send over the limit waits for capacity rather than failing.
5. Errors and logging
An API error comes back as a tool result with isError: true, the error code, the HTTP status
and the request_id. The server never retries: the agent decides whether to call again.
Every tool call is logged in your workspace with the tool, the outcome, how long it took and which key or identity made it. Message bodies, phone numbers and credentials aren't stored in that log. See them under Developers > MCP.
6. Handoffs
When an agent calls handoff_to_human, the conversation is marked for a person, the team is
notified, and your agent.handoff webhook fires with reason: "tool" and the agent's note.
The note is model-written: treat it as untrusted text.