> ## Documentation Index
> Fetch the complete documentation index at: https://docs.voice.wixzel.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Wixzel Voice MCP server: connect Claude, Cursor or any MCP client

> The Wixzel Voice MCP server lets Claude, Cursor or any MCP client build voice agents and place real phone calls over your own SIP trunk.

**The Wixzel Voice MCP server lets Claude, Cursor or any MCP client build voice
agents and place real phone calls over your own SIP trunk.**

It is the official [Model Context Protocol](https://modelcontextprotocol.io)
server for Wixzel Voice, published as `wixzel-voice-mcp`. It puts every `/v1`
operation in front of an AI agent as a tool, 65 of them, with each tool
annotated so the client knows which are read-only, which delete, and which
**spend money or ring a real phone**. Those ask before they run.

Two ways to use it:

| | Runs where | Auth | Best for |
| - | - | - | - |
| **Hosted**: `https://mcp.voice.wixzel.com/mcp` | Our servers | OAuth sign-in, or a bearer key | claude.ai, Claude Desktop, Claude Code, any remote-capable client |
| **Local**: `npx -y wixzel-voice-mcp` | Your machine, over stdio | An API key in the environment | Claude Code, Cursor, Windsurf, offline work, a self-hosted API |

The console's **AI clients** page at [voice.wixzel.com/connect](https://voice.wixzel.com/connect)
generates the exact commands below with a key already filled in.

<Note>
  Added it while the product was called Wixzel Phone? A connector pointing at
  `https://mcp.phone.wixzel.com/mcp`, and the `wixzel-phone-mcp` package up to
  0.3.0, keep working; there is nothing to redo. New setups use the addresses on
  this page.
</Note>

## Claude Code

<Steps>
  <Step title="Add the server">
    <CodeGroup>
      ```bash Hosted, sign in when asked theme={null}
      claude mcp add --transport http wixzel-voice https://mcp.voice.wixzel.com/mcp
      ```

      ```bash Local, with a key theme={null}
      claude mcp add wixzel-voice -e WIXZEL_API_KEY=wv_live_... -- npx -y wixzel-voice-mcp
      ```

      ```json .mcp.json, shared with the repo theme={null}
      {
        "mcpServers": {
          "wixzel-voice": {
            "command": "npx",
            "args": ["-y", "wixzel-voice-mcp"],
            "env": { "WIXZEL_API_KEY": "${WIXZEL_API_KEY}" }
          }
        }
      }
      ```
    </CodeGroup>

    With the hosted form, type `/mcp` in Claude Code and choose **Authenticate**.
    Your browser opens the console, you sign in, tick what the client may do, and
    Claude Code is connected. No key changes hands.
  </Step>

  <Step title="Let it set things up">
    ```
    /mcp__wixzel-voice__quickstart
    ```

    The server ships three prompts, which Claude Code exposes as slash commands:
    `quickstart` connects a carrier, registers a number, builds an agent and
    places a first call, stopping before anything that dials; `diagnose_call`
    explains why a call failed from its record, usage events, trunk status and
    SIP logs; `spend_report` turns a period's usage into dollars by component and
    by call.
  </Step>
</Steps>

## Claude.ai and Claude Desktop

1. **Settings → Connectors → Add custom connector.**
2. Enter `https://mcp.voice.wixzel.com/mcp` and add it.
3. Press **Connect**. You are sent to the console to sign in and approve the
   scopes the client may use.
4. Approve. The client receives a key of its own; nothing is pasted anywhere.

Disconnect it at any time from **AI clients** in the console. The key stops
working immediately.

## Other clients

Clients that launch stdio servers from an `mcpServers` JSON config (Claude
Desktop's `claude_desktop_config.json`, Cursor and Windsurf) use the local form:

```json theme={null}
{
  "mcpServers": {
    "wixzel-voice": {
      "command": "npx",
      "args": ["-y", "wixzel-voice-mcp"],
      "env": { "WIXZEL_API_KEY": "wv_live_..." }
    }
  }
}
```

VS Code reads `.vscode/mcp.json`, which names the same server under a top-level
`servers` key instead:

```json theme={null}
{
  "servers": {
    "wixzel-voice": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "wixzel-voice-mcp"],
      "env": { "WIXZEL_API_KEY": "wv_live_..." }
    }
  }
}
```

Clients that connect over HTTP but cannot do OAuth send a key as a bearer:

```
Authorization: Bearer wv_live_...
```

## Scopes

An OAuth-connected client asks for a set of scopes, and you can untick any of
them on the consent screen. A client that asks for nothing specific is offered
the standard set for agents: everything under **Agents**, **Calls**,
**Telephony**, **Contacts**, **Knowledge & scheduling**, plus `usage:read` and
`billing:read`.

<Warning>
  `billing:write` (start a top-up) and `api_keys:write` (mint more keys) are
  never offered by default. Grant them deliberately, if at all. There is no admin
  scope, and one cannot be created.
</Warning>

For a locally run server, the key you put in the environment decides what the
agent can do. The console preselects the same standard set when you create one
from the AI clients page.

## Security

* **The token is a key.** When a client signs in with OAuth, the API mints an
  ordinary scoped API key for that grant and hands it over as the access token.
  Nothing downstream needs to know OAuth happened; revoking the grant revokes
  the key. Because the token is valid against the API directly, it carries only
  the scopes you approved, and it is hidden from the ordinary key list so that
  the AI clients page is its one home.
* **PKCE only, S256 only.** The authorization server refuses the `plain`
  method and any request without a code challenge. Redirect URIs match exactly
  against what the client registered; an unregistered one gets an error in
  place, never a redirect. A code is exchanged once and expires in a minute; a
  code presented with the wrong verifier burns the grant.
* **Money asks first.** `place_call`, `test_call_agent`, `start_campaign` and
  `create_topup` are annotated as open-world and their descriptions tell the
  model to confirm with you. `place_call`, `test_call_agent` and `create_topup`
  send an idempotency key, generated when the model does not pass one. A retry
  is safe only when the client passes the same `idempotency_key` again, which
  the tool descriptions ask it to do; a retry with a new key dials again.
  `start_campaign` takes no key, and a second start while the campaign is
  running is refused.
* **Nothing leaks through tools.** SIP passwords are write-only. A minted key
  appears once, in the tool result that created it. The key the server itself
  runs with is never exposed through any tool or resource.
* **Errors are actionable.** A failure returns the API's machine-readable
  `code`, the `request_id`, and a hint: which scope is missing, that the balance
  is short, that a cursor was mangled.

## Tools

| Family | Tools |
| - | - |
| Engines | `list_engines` `list_engine_languages` `list_engine_voices` |
| Agents | `list_agents` `get_agent` `create_agent` `test_call_agent` `update_agent` `delete_agent` |
| Calls | `place_call` `list_calls` `get_call` `get_call_transcript` `hangup_call` `delete_call` |
| Realtime | `create_realtime_session` |
| SIP trunks | `list_sip_trunks` `get_sip_trunk` `create_sip_trunk` `update_sip_trunk` `delete_sip_trunk` `check_sip_trunk_status` `test_sip_trunk` `get_sip_trunk_logs` |
| Phone numbers | `list_phone_numbers` `get_phone_number` `create_phone_number` `update_phone_number` `delete_phone_number` |
| Leads | `list_leads` `get_lead` `create_lead` `update_lead` `delete_lead` `import_leads` |
| Campaigns | `list_campaigns` `get_campaign` `create_campaign` `start_campaign` `pause_campaign` `delete_campaign` |
| Knowledge bases | `list_knowledge_bases` `get_knowledge_base` `create_knowledge_base` `update_knowledge_base` `delete_knowledge_base` |
| Appointments | `list_appointments` `get_appointment` `create_appointment` `update_appointment` `delete_appointment` |
| Billing & usage | `get_balance` `list_ledger_entries` `create_topup` `get_usage_summary` `list_usage_events` |
| Webhooks | `get_webhook` `update_webhook` `rotate_webhook_secret` `test_webhook` `list_webhook_deliveries` |
| API keys | `list_api_keys` `create_api_key` `rotate_api_key` `revoke_api_key` |

Resources: `wixzel://guide` (the operating guide the server also sends as its
instructions) and `wixzel://connection` (base URL and whether the key is live
or test; never the key itself).

## Are these docs available to coding agents over MCP?

Yes, from a second and separate server. These docs are also served by a
read-only documentation MCP server at `https://docs.voice.wixzel.com/mcp`,
which Mintlify hosts for this site. It searches and reads these pages, so a
coding agent in Claude Code, Cursor or VS Code can look up an endpoint while it
writes your integration. It has no access to your account: it cannot place a
call, read a transcript or spend credit.

| | Account MCP server | Docs MCP server |
| - | - | - |
| Address | `https://mcp.voice.wixzel.com/mcp` | `https://docs.voice.wixzel.com/mcp` |
| What it reaches | Your Wixzel Voice account, through the API | These public docs |
| Sign-in | OAuth, or an API key as a bearer | None |
| Places calls | Yes, after asking | No |

```bash theme={null}
claude mcp add --transport http wixzel-voice-docs https://docs.voice.wixzel.com/mcp
```

The page menu at the top of every docs page can also copy that address, or add
it to Cursor or VS Code in one step.

## Self-hosting

The package runs the hosted mode too:

```bash theme={null}
MCP_PORT=3939 MCP_PUBLIC_URL=http://localhost:3939/mcp npx wixzel-voice-mcp --http
```

| Variable | Purpose |
| - | - |
| `MCP_PORT` | Port to listen on. Default 3939. |
| `MCP_BIND` | Interface. Default `127.0.0.1`; put TLS in front rather than binding wider. |
| `MCP_PUBLIC_URL` | The URL clients use, advertised as the OAuth resource. |
| `WIXZEL_API_BASE_URL` | The API, which is also the OAuth authorization server. Default `https://api.voice.wixzel.com`. |
| `WIXZEL_API_KEY` | Local only: a fallback for requests without a bearer. Refused when `MCP_PUBLIC_URL` is not a loopback address. |

A connector discovers sign-in on its own: a request without a bearer is
answered `401` with `WWW-Authenticate: Bearer resource_metadata="…"`, that
document names `https://api.voice.wixzel.com` as the authorization server, and
`/.well-known/oauth-authorization-server` there lists the endpoints.
