Portal MCP Server¶
Connect an MCP client — Claude Code, Claude Desktop, or anything else that speaks the protocol — to your QVOICE Platform account and let it read and change the account's configuration in plain language.
Ask it "which personas do we have and on what extensions?" and it answers from your account. Ask it to change an AI agent's greeting and it changes it, through the same path the portal uses.
Note
This is a different surface from the MCP Server an AI agent uses while it is on a call. That one offers the tools a call needs — transfer, hang up, look up a contact. This one offers the tools an administrator needs, and neither is offered the other's.
Connect¶
https://{{ domain }}:9443/api/v2/mcp/portal
Transport is HTTP Streamable. Authenticate with an API key:
| Header | Description |
|---|---|
X-API-Key |
An API key created in your account |
A bearer token works too (Authorization: Bearer <jwt>), which is what a browser-based client uses. Either way the account is resolved on the server — from the key's own record, or from the token — so a client cannot ask about an account it was not issued for.
Working on a sub-account¶
A partner whose key belongs to a parent account can act on one of its children by naming it:
| Header | Description |
|---|---|
X-Account-ID |
The account to work on, if the key's account is above it |
Naming an account the key has no claim to is refused. This is the same check that guards every other endpoint, so what a key can reach here is exactly what it can reach through the API.
Claude Code¶
claude mcp add --transport http --scope user qvoice-portal \
https://{{ domain }}:9443/api/v2/mcp/portal \
-H "X-API-Key: YOUR_API_KEY"
--scope user makes it available in every project; leave it out to add it only where you run the command.
Any other client¶
Point it at the URL above with the X-API-Key header. Most clients take a JSON block:
{
"mcpServers": {
"qvoice-portal": {
"type": "http",
"url": "https://{{ domain }}:9443/api/v2/mcp/portal",
"headers": { "X-API-Key": "YOUR_API_KEY" }
}
}
}
See API Key Authentication to create the key.
Warning
The key is the scope: it decides which accounts the client may see and change — its own, and any beneath it. Give each person their own; sharing one shares everything it reaches. An administrator's key is required, because these tools do what an administrator does in the portal.
Tools¶
Reading the account¶
| Tool | Answers |
|---|---|
get_account_info |
Name, timezone, features, basic configuration |
list_users / get_user |
Users, their extensions and status |
list_queues / get_queue |
Queues and how they are configured |
list_devices |
Phones, optionally for one user |
list_callflows |
Call routing |
list_agents / get_agent_states |
Contact centre agents and what they are doing now |
AI personas¶
| Tool | Does |
|---|---|
list_personas |
Every persona: name, job, extension, conversation shape |
get_persona |
One in full — prompt, greeting, voice, extension, gate flow |
create_persona |
Creates one, with an extension and the routing that makes it answer |
update_persona |
Changes prompt, greeting, voice, extension or conversation shape |
Creating a persona¶
Four things are required: a name, the job it does, a description, and the prompt that tells it how to behave.
Create a persona called Front Desk that answers our main line, greets
callers in English, finds out who is calling and why, and offers to take
a message when it cannot help.
The persona comes back with an extension already assigned and its inbound routing in place — it can take a call the moment it exists. Name an extension yourself and it is used, unless another callflow already answers it, in which case the request is refused rather than taking the number away from whatever has it.
Note
The greeting is spoken as written. Write the sentence the caller should hear — "Thanks for calling Acme, how can I help?" — not an instruction like "greet the caller warmly", which is what the caller would hear read out.
Changing a persona¶
Only the fields you name are changed; everything else is left alone. Changing a voice does not mean restating the prompt.
Change the Front Desk greeting to "Thanks for calling Acme, how can I help?"
A change made here is the same change the portal makes: the persona's inbound routing is rebuilt to match, and the same rules apply — an extension another callflow already answers is refused, with a message naming what holds it.
Conversation shape¶
A persona runs a call one of two ways:
| Shape | The call is |
|---|---|
legacy |
One prompt, for the whole conversation |
gate_runtime |
A graph of stages, each with its own prompt and its own tools |
The second is what stops a stage being talked into something it has no business doing: a stage that has no reason to transfer a call is not given the tool to transfer one. Draw the graph in the portal, or pass it as JSON to update_persona. A flow that will not parse, or that names no entry stage, is refused rather than saved for a call to fail on.
Reading a persona returns the flow summarised — its entry stage and the stages in it — because a flow runs to thousands of characters and its shape is usually what you want. Ask for the document itself with include_gate_flow.
What it will not do¶
- Take an account it was not given. The account comes from the key or the token, and an
X-Account-IDis honoured only when the account tree allows it — never on the client's word alone. - Touch a live call. Transferring and hanging up belong to the agent's surface; this one is not offered them.
- Work without an administrator. These tools do what an administrator does in the portal, and the same permission is required.