| Server URL | https://api.voixcall.com/mcp |
|---|---|
| Transport | Streamable HTTP, stateless, JSON responses (no SSE stream to hold open). Mcp-Session-Id and Mcp-Protocol-Version request headers are accepted. |
| Authentication | OAuth 2.1 access token as a bearer token, obtained through the standard discovery flow below. API keys are not accepted here (they work on /v1 only); test-mode credentials are rejected. |
| Protected-resource metadata | /.well-known/oauth-protected-resource/mcp (RFC 9728) |
| Authorization-server metadata | /.well-known/oauth-authorization-server (RFC 8414) |
| Client registration | Client ID Metadata Documents (an https client_id that serves its own metadata; what Claude uses) or Dynamic Client Registration at /oauth/register. PKCE S256 is required; there are no client secrets. |
| Server name | voixcall, title “VoixCall”, version 0.1.0 |
| Rate limit | Reads budget: 60 requests/min per connection, 120/min per user. See rate limits. |
How authentication works
An unauthenticated request to /mcp gets a 401 whose WWW-Authenticate header points at the
protected-resource metadata. From there a client finds the authorization server, registers, runs the authorization-code
flow with PKCE and comes back with a bearer token bound to the https://api.voixcall.com/mcp resource. Clients that
implement the MCP authorization specification do all of this without configuration.
curl -si https://api.voixcall.com/mcp
# HTTP/1.1 401 Unauthorized
# WWW-Authenticate: Bearer error="invalid_token", resource_metadata="https://api.voixcall.com/.well-known/oauth-protected-resource/mcp"
# Request-Id: req_… curl -s https://api.voixcall.com/.well-known/oauth-protected-resource/mcp
{
"resource": "https://api.voixcall.com/mcp",
"authorization_servers": ["https://api.voixcall.com"],
"scopes_supported": ["rates:read", "credits:read", "calls:read", "calls:place", "contacts:read",
"numbers:read", "messages:read", "messages:history", "transcripts:read", "webhooks:manage"],
"bearer_methods_supported": ["header"],
"resource_name": "VoixCall MCP",
"resource_documentation": "https://voixcall.com/developers/mcp"
}
On the consent page at app.voixcall.com/oauth/consent the user sees the client's name, how it registered
(“registered by URL”, “self-registered” or “verified partner”), the host it redirects to, and each requested scope as a
sentence. Self-registered clients asking for calls:place, messages:read, messages:history
or transcripts:read need a second confirmation checkbox. After Allow, the user gets an email “New app connected”.
Tools
Every tool is prefixed voixcall_, sets title, and returns structuredContent matching its
outputSchema plus a one-line spoken summary an assistant can read aloud. Business failures are
returned as isError: true results with actionable text; protocol errors are reserved for unknown tools and
malformed arguments. The server's instructions tell the assistant: “Tools act only on the connected personal
account. Say what a tool returned; do not invent numbers, prices or call details.”
Live production
| Tool | Scope | Description and annotations | Input → output |
|---|---|---|---|
voixcall_whoamititle: Who am I | any | “Use this to confirm which VoixCall account is connected. Do not use for anything else.” readOnlyHint: true, idempotentHint: true, destructiveHint: false, openWorldHint: false | no input → email, display_name, spoken (one sentence: “You're connected to VoixCall as Name (email).”) |
Planned not live
These follow the read endpoints in phase 2 and later phases. Names, scopes and annotations are from the specification and may still change before they ship; the changelog announces each one. There will be no top-up tool, no number purchase and no message sending.
| Tool | Scope | Annotations | Input → output |
|---|---|---|---|
voixcall_get_rate planned | rates:read | read-only | destination (E.164 or country name) → rate, billing increment; estimate: true for a country name with a request for the full number |
voixcall_get_balance planned | credits:read | read-only | none → amount, currency, low_balance, billing_url (informational; there is no purchase tool) |
voixcall_search_contacts planned | contacts:read | read-only | query, limit ≤ 5 → matches |
voixcall_list_calls planned | calls:read | read-only | limit ≤ 10, direction → calls; contact names need contacts:read |
voixcall_get_call planned | calls:read | read-only | call_id → call with live status and cost so far |
voixcall_get_call_summary planned | transcripts:read | read-only | call_id → summary, action items, reference numbers; gated by the “Allow connected apps to read call summaries” switch (off by default); never starts a transcription |
voixcall_list_numbers planned | numbers:read | read-only | none → numbers with messages_opt_in |
voixcall_read_messages planned | messages:read | read-only | number (must be opted in), window_minutes ≤ 60 → messages; codes must not be forwarded to anyone |
voixcall_quote_call planned | calls:place | read-only | to (E.164 or contact id), record → quote with quote_id, rates, the number that will ring, the caller ID shown, expires_at |
voixcall_place_call planned | calls:place | not read-only, not idempotent | quote_id → call in ringing_you; a quote can be placed once; your phone rings first and you press 1 to connect |
voixcall_end_call planned | calls:place | idempotent | call_id → final call |
Placing a call will always be two steps: voixcall_quote_call shows the price and the number that will ring, and
voixcall_place_call accepts only that single-use quote. Your own phone rings first and you press 1 to connect, so
no assistant can spend your balance without you on the line.
Per-client setup
Claude Code
claude mcp add --transport http voixcall https://api.voixcall.com/mcp
claude mcp login voixcall
claude mcp list claude mcp login voixcall opens the consent page in your browser. Claude Code registers with its own client metadata
document, so the consent page shows it as “registered by URL: claude.ai”. It currently requests every advertised scope; you
can only allow or deny the set as a whole. Verified against production.
claude.ai (custom connectors)
Settings → Connectors → Add custom connector. Name it VoixCall, set the URL to https://api.voixcall.com/mcp
and leave the OAuth client id and secret empty. Click Connect, approve on app.voixcall.com, then enable the connector
in a chat. claude.ai connectors use the same standard MCP discovery and OAuth 2.1 flow. Unlike Claude Code, they connect
from Anthropic's servers with an https callback, which we have not yet exercised end to end.
ChatGPT
ChatGPT's connector settings take the same server URL and use the same standard MCP discovery and OAuth 2.1 flow. Unlike Claude Code, ChatGPT connects from OpenAI's servers with an https callback, which we have not yet exercised end to end; treat it as untested until the changelog says otherwise.
MCP Inspector (for testing)
npx @modelcontextprotocol/inspector
# In the UI: Transport Type "Streamable HTTP", URL https://api.voixcall.com/mcp,
# then Connect. The Inspector registers itself (Dynamic Client Registration),
# opens the consent page, and lists voixcall_whoami under Tools. Verified with Inspector 2.8.0: registration by Dynamic Client Registration, the sensitive-scope checkbox on consent, and voixcall_whoami returning structured output.
Other clients and your own code
Any client that implements MCP over Streamable HTTP with the authorization specification works. If you write your own,
use the quickstart's curl flow with resource=https://api.voixcall.com/mcp
to get a token, then POST JSON-RPC to /mcp with Authorization: Bearer vcat_…,
Content-Type: application/json and Accept: application/json.
What the user controls
- Settings → Connected apps in the web app lists every connected client with its name, registration kind,
redirect host, scopes, when it was first authorized and when it was last used. Disconnect revokes all of that
client's tokens immediately; the next call gets
invalid_tokenwith “Reconnect VoixCall in your assistant”. - Two sharing switches coming: “Allow connected apps to read call summaries” and, per number, “Allow connected apps to read messages on this number”. Both are off by default and are shown but not yet active; they gate the transcript and message tools when those ship.
- A password reset revokes every connected app and every API key.
- Every new connection is emailed to the account owner, and connections, disconnections and token refreshes are written to an audit log kept up to 400 days (automatic deletion job in progress; a view of it on the Connected apps page is coming).
More on what each scope exposes, and for how long, in privacy for connected apps.