MCP
Phone.inc exposes a Model Context Protocol (MCP) server so AI assistants can read your account data and take actions on your behalf. The MCP endpoint sits on the same host as the REST API and uses the same credentials.
MCP is built for AI clients — Claude Desktop, Cursor, and similar tools. If you're writing your own integration, the REST API is usually simpler.
Setup
The MCP server URL is https://app.phone.inc/mcp. Nothing to install — it's a remote HTTP endpoint. Pick your client below and connect once.
You'll authenticate with Phone.inc the same way as the REST API: an API key you paste into a config file, or OAuth where the client opens a browser sign-in for you. Claude uses OAuth automatically; Cursor uses an API key in mcp.json.
Claude Desktop
- Open Claude → Settings → Connectors.
- Click Add custom connector.
- Enter the server URL:
https://app.phone.inc/mcp - Save, then click Connect on the new connector. Claude opens a browser window — sign in to Phone.inc and approve access.
- Start a new chat, make sure the Phone.inc connector is enabled, and try: "List my main phone numbers."
Claude registers itself via dynamic client registration and stores a refresh token. You don't need to create an API key or OAuth app by hand.
Cursor
Add the server to your MCP config — either ~/.cursor/mcp.json (all projects) or .cursor/mcp.json in a project root:
mcp.json
{
"mcpServers": {
"phone-inc": {
"url": "https://app.phone.inc/mcp",
"headers": {
"Authorization": "Bearer phk_YOUR_API_KEY"
}
}
}
}
Create an API key at app.phone.inc/api_keys and replace phk_YOUR_API_KEY. Restart Cursor after saving — Phone.inc tools should appear in the agent tool list.
Endpoint
The MCP server uses Streamable HTTP in stateless mode:
- URL:
https://app.phone.inc/mcp - Methods:
POSTfor JSON-RPC requests (tools/list,tools/call,initialize, and so on) - Accept header:
application/json, text/event-stream - Content-Type:
application/json
Responses are returned as application/json (not SSE) because the server runs in stateless mode — each request is self-contained and no session is required.
GET and DELETE on /mcp are not used in stateless mode (GET returns 405 Method Not Allowed).
Authentication
Every MCP request must be authenticated. Use the same credentials as the public REST API:
- API key — send
X-Api-Key: phk_...orAuthorization: Bearer phk_... - OAuth bearer token — send
Authorization: Bearer <access_token>with theapiscope (see OAuth 2.0 for partners)
Unauthenticated requests receive 401 Unauthorized with a WWW-Authenticate header pointing at our protected-resource metadata:
WWW-Authenticate: Bearer realm="Phone.inc MCP",
resource_metadata="https://app.phone.inc/.well-known/oauth-protected-resource/mcp",
scope="api"
MCP clients use that URL to discover the authorization server and start the OAuth flow. The metadata document at /.well-known/oauth-protected-resource/mcp advertises resource: https://app.phone.inc/mcp.
Tools
Call tools/list to see what's available. Each tool is a thin wrapper around the public REST API — request and response shapes match the corresponding endpoint documented on the resource pages linked below.
Business numbers
Manage business numbers and their call routing. The three routing tools and request_business_number_porting take business_number_id; get and update take id.
| Tool | REST equivalent |
|---|---|
list_business_numbers | GET /api/v1/business_numbers |
get_business_number | GET /api/v1/business_numbers/:id |
update_business_number | PATCH /api/v1/business_numbers/:id |
request_business_number_porting | POST /api/v1/business_numbers/:business_number_id/porting_request |
terminate_business_number | POST /api/v1/business_numbers/:id/termination |
cancel_business_number_termination | DELETE /api/v1/business_numbers/:id/termination |
send_message | POST /api/v1/business_numbers/:business_number_id/messages |
update_business_number_incoming_call_config | PUT /api/v1/business_numbers/:id/incoming_call_config |
update_business_number_outside_business_hours_config | PUT /api/v1/business_numbers/:id/outside_business_hours_config |
update_business_number_no_answer_config | PUT /api/v1/business_numbers/:id/no_answer_config |
The retired update_outside_opening_hours_config tool is no longer available. Use update_business_number_outside_business_hours_config with business_hours.
send_message sends a real, chargeable SMS from an active SMS-capable inMobile number owned by the authenticated employee's company. Number responses group effective SMS/MMS capabilities under messaging; the company's messaging feature flag must be enabled by Phone.inc (default off). Messaging tools return an endpoint 404 while disabled; inbox tools still return calls and voicemails but exclude SMS. It requires business_number_id, to in E.164 format, text, and idempotency_key. Acceptance is not delivery. Reuse the same idempotency key when replaying the same send after a transport error. See outgoing SMS, especially the retry warning.
The routing update tools accept signed welcome_audio_preview_id, message_audio_preview_id, and greeting_audio_preview_id values matching the number and text. See voice previews.
Read get_business_number before changing routing. Switching the call-flow type creates a new configuration; it does not copy the previous type's greeting or destinations. Collect the caller-facing text and destinations before calling the update tool:
| New type | Required details |
|---|---|
VoicemailConfig | greeting: the exact text callers hear before recording |
WelcomeTransferConfig | welcome and either non-empty employee_ids or department_id |
TransferConfig | Either non-empty employee_ids or department_id |
RejectConfig | message: the text played before hanging up |
PhoneMenuConfig | welcome and at least one valid menu_options entry, with prompts and either employee targets or a department_id for each transfer action |
For outside-hours updates, omit type to preserve routing while changing business hours, or send type: null to clear routing. For no-answer updates, missing or null type clears the fallback; include the current type when changing only the timer. Omitting timeout_secs preserves the timer.
Calls
Use business_number_id to filter list_calls by business number.
| Tool | REST equivalent |
|---|---|
list_calls | GET /api/v1/calls |
get_unseen_activity | GET /api/v1/activity/unseen |
set_call_thread_seen | POST /api/v1/calls/seen |
get_call | GET /api/v1/calls/:id |
get_active_call | GET /api/v1/calls/active |
start_call_transfer | POST /api/v1/calls/:id/transfers |
complete_call_transfer | POST /api/v1/calls/:id/transfers/:transfer_id/completion |
cancel_call_transfer | DELETE /api/v1/calls/:id/transfers/:transfer_id |
list_calls accepts a query to search call transcriptions and saved caller context by keywords or related topics, plus business-number, caller/callee phone-number, direction, and state filters. limit is optional, defaults to 10, and is capped at 50. get_call includes the call transcription when one exists. Both tools include outside_business_hours: the business-hours decision recorded at call arrival, or null when unknown. There is no separate get_call_transcription tool.
For complete call reports, use start_at (inclusive) and end_at (exclusive) with explicit time-zone offsets, then follow next_cursor with the same filters until null. This retrieves underlying calls rather than a purpose-built report. Cursor pagination is not supported with transcription query; search results are a capped sample.
The dashboard assistant can visualize any retrieved product dataset with its general-purpose render_chart tool. This local, presentation-only tool renders area, bar, line, and pie charts without changing account data. It is not part of the public MCP catalog because external clients own their own visualization UI.
Inbox
| Tool | REST equivalent |
|---|---|
list_inbox | GET /api/v1/inbox |
list_activity | GET /api/v1/inbox/:phone_number/activity |
mark_activity_seen | POST /api/v1/inbox/:phone_number/seen |
Inbox tools group calls, voicemails, and messages by external phone number. No conversation resource is created. Pagination cursors are bound to the company, filters, and original observation boundary. Seen state is shared by the team; marking a thread seen does not mark its voicemails listened. The catalog has 69 tools including compatibility aliases.
Voicemails
| Tool | REST equivalent |
|---|---|
list_voicemails | GET /api/v1/voicemails |
get_voicemail | GET /api/v1/voicemails/:id |
update_voicemail | PATCH /api/v1/voicemails/:id |
delete_voicemail | DELETE /api/v1/voicemails/:id |
Caller context
| Tool | REST equivalent |
|---|---|
get_caller_context | GET /api/v1/caller_context |
Integrations
| Tool | REST equivalent |
|---|---|
list_integrations | GET /api/v1/integrations |
get_integration | GET /api/v1/integrations/:id |
list_integration_providers | GET /api/v1/integration_setups |
get_integration_setup | GET /api/v1/integration_setups/:provider |
Read configured connection names, providers, statuses, and timestamps, including disconnected connections. Discover available providers and prepare a browser authorization link with get_integration_setup. The dashboard assistant displays this as a card with an authorization button and automatically updates it after a verified setup callback. External MCP clients can present setup_url as a link. The user must sign into the same company and complete setup in the browser; obtaining a link does not install anything. Verify the result with list_integrations afterward. These tools never expose credentials, endpoint URLs, or raw error messages.
Contacts
| Tool | REST equivalent |
|---|---|
list_contacts | GET /api/v1/contacts |
get_contact | GET /api/v1/contacts/:id |
create_contact | POST /api/v1/contacts |
update_contact | PATCH /api/v1/contacts/:id |
delete_contact | DELETE /api/v1/contacts/:id |
Employees
| Tool | REST equivalent |
|---|---|
list_employees | GET /api/v1/employees |
get_employee | GET /api/v1/employees/:id |
create_employee | POST /api/v1/employees |
update_employee | PATCH /api/v1/employees/:id |
delete_employee | DELETE /api/v1/employees/:id |
get_employee_do_not_disturb | GET /api/v1/employees/:id/do_not_disturb |
set_employee_do_not_disturb | PUT /api/v1/employees/:id/do_not_disturb |
Departments
| Tool | REST equivalent |
|---|---|
list_departments | GET /api/v1/departments |
get_department | GET /api/v1/departments/:id |
create_department | POST /api/v1/departments |
update_department | PATCH /api/v1/departments/:id |
delete_department | DELETE /api/v1/departments/:id |
Use department_id instead of employees for TransferConfig and WelcomeTransferConfig in the incoming, outside-hours, and no-answer routing slots, clearing employee_ids when changing destination. Phone-menu options continue to select employees. Department queues are shared across numbers; each caller retains the number's waiting and fallback rules.
Webhooks
| Tool | REST equivalent |
|---|---|
list_webhooks | GET /api/v1/webhooks |
create_webhook | POST /api/v1/webhooks |
update_webhook | PATCH /api/v1/webhooks/:id |
delete_webhook | DELETE /api/v1/webhooks/:id |
Texting
| Tool | REST equivalent |
|---|---|
get_message | GET /api/v1/messages/:id |
get_message_attachment | GET /api/v1/messages/:message_id/attachments/:id |
retry_message | POST /api/v1/messages/:id/retries |
list_text_snippets | GET /api/v1/text_snippets |
create_text_snippet | POST /api/v1/text_snippets |
get_text_snippet | GET /api/v1/text_snippets/:id |
update_text_snippet | PATCH /api/v1/text_snippets/:id |
delete_text_snippet | DELETE /api/v1/text_snippets/:id |
get_texting_settings | GET /api/v1/business_numbers/:id/texting_settings |
update_texting_settings | PATCH /api/v1/business_numbers/:id/texting_settings |
get_message includes attachment metadata only when the company has Message attachments enabled. get_message_attachment requires both Messaging and Message attachments. It returns a download link valid for five minutes when the file is ready, or null download fields while pending. Links are temporary bearer credentials; do not log or share them. Disabling the flag blocks new links but does not revoke links already issued before their expiry.
send_message requires idempotency_key; reuse it only to replay the same send. retry_message requires a key and sends another real, chargeable text only after definitive failure. Enabling auto-replies can also send texts without an app open.
Company
| Tool | REST equivalent |
|---|---|
get_company | GET /api/v1/company, including plan and employee, web outbound-call, and additional-number entitlements; native outbound calling is not gated |
update_company | PATCH /api/v1/company |
Compatibility
send_message requires idempotency_key and accepts optional format: "sms"; MMS is not yet supported. REST sends accept an optional key. The old proof-of-concept send_sms tool is not exposed.
Notes
- The tools cover the public API. Voice preview generation and standalone call transcription retrieval are excluded; transcriptions are included in
get_call. - Tool arguments mirror REST query parameters, path ids, and JSON bodies. See the linked REST docs for field names, validation rules, and response shapes.
- Successful
DELETErequests return MCP text content{"success":true}(the REST API returns204 No Contentwith an empty body). - Results are scoped to the company tied to your API key or OAuth token.
Example tools/call request (list_business_numbers):
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_business_numbers",
"arguments": {}
}
}
Example response content (returned as MCP text content):
{
"business_numbers": [
{
"id": "mn_abc123",
"name": "Support",
"number": "+14155551234",
"number_formatted": "+1 415-555-1234"
}
]
}