Skip to content

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

Queues

Tool Does
create_queue Creates one, ready to take calls on an extension
update_queue Changes the roster, the strategy, the ring and wrap-up times

Campaigns

Tool Does
list_campaigns Every campaign: queue, type, status, how many leads
get_campaign One in full — queue, retries, cool-off, caller id
create_campaign Creates one against a queue, stopped
add_campaign_lead Adds one lead to dial
list_campaign_leads Where each lead is: waiting, being dialled, cooling off, finished
set_campaign_status Starts, pauses, stops, ends or archives it

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.

Building a queue

A queue created here is a queue the portal would create: give it a name, an extension for callers to reach it on, and the people who answer.

Create a queue called sales on extension 9500 with Maria and Carlos as agents.

It can take a call as soon as it exists — the routing that sends its extension to its agents is set up with it.

Agents are named by user id; list_users and list_agents find them. Adding someone later keeps whoever is already in the queue:

Add Sebastian to the sales queue.

There is a separate way to replace the roster outright, which drops anyone not named. Adding and replacing are kept apart on purpose, so "put this person in the queue" cannot quietly remove everyone else.

Setting up a campaign

A campaign is an outbound run: it belongs to a queue, takes its agents from that queue, and works through a list of leads. The queue is the part that catches people out. A campaign without one never dials and gives no sign why, so create_campaign refuses to create it rather than leaving you with a campaign that looks fine and does nothing.

The order is: queue, campaign, leads, start.

Create a progressive campaign called Q3 winback on the sales queue,
add 555-0101 as a lead, and start it.

A campaign is created stopped on purpose, so leads can be added before anything rings. set_campaign_status with start is what actually puts them on the phone, and stop takes them off.

Note

set_campaign_status takes an action, not a status. Not every action makes sense from where the campaign is: a running one can be paused or stopped, a stopped one started, paused or ended. Asking for something it cannot do is refused, and the refusal tells you what state it is actually in.

Watch a run with list_campaign_leads. The status says where each lead is: new and selectable are waiting, on_call is being dialled now, cooloff is waiting to be retried, success and failed are finished.

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-ID is 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.