MCP server
The Voqalize MCP server exposes Voqalize’s management surface as tools your
editor’s agent (Claude Code, etc.) can call: create an agent, mint its keys, point
its brain_url at your route, and read back what a call did — without leaving the
editor.
The server hands your agent its own instructions on connect, and those link here.
There is no skill to install and nothing to keep in sync: every page on this site
is also served as raw markdown at the same URL plus .md, indexed at
/llms.txt.
It is a hosted, remote MCP endpoint — you don’t install or run anything. Point your MCP client at the URL, authenticate once with Google in the browser, and the tools are available.
Connect
Section titled “Connect”Add the server to your MCP client. In Claude Code:
claude mcp add --transport http voqalize https://app.voqalize.com/mcpOr, project-scoped, drop an .mcp.json at your repo root:
{ "mcpServers": { "voqalize": { "type": "http", "url": "https://app.voqalize.com/mcp" } }}On first use your client runs a browser Google sign-in and the tools light up. There is no API key, client ID, or secret to configure — the client registers itself dynamically and carries the resulting token.
Auth & tenancy
Section titled “Auth & tenancy”- Google OAuth (the same login as the console). Your MCP client authenticates with Google via Dynamic Client Registration — it self-registers, you complete the browser sign-in, and identity comes from your Google account. First sign-in provisions your Voqalize user automatically.
- The token is the credential;
tenantis a selector. Every scoped tool takes a requiredtenantslug and is checked against your membership before it runs — passing a slug you don’t belong to fails. You can’t act on a tenant just by naming it. whoamifirst, thenlist_tenants.whoamireturns your identity;list_tenantsreturns every workspace you can act on. Call them at the start of a session to learn whichtenantslug to pass to everything else.
Sixteen tools. Every tool returns the control plane’s raw JSON. Errors surface with
one of two codes — not_authorized (you’re not a member of that tenant, or your role
is too low) or validation_error (bad input, e.g. a non-wss:// brain_url on a
non-loopback host).
Identity & workspace
Section titled “Identity & workspace”| Tool | Signature | Does |
|---|---|---|
whoami | () -> dict | Identify the authenticated developer. Call first. |
list_tenants | () -> dict | Every tenant (workspace) you can act on. |
create_tenant | (about="", display_name="") -> dict | Create your workspace + seed demo agents. Idempotent — returns your existing tenant if you have one. |
Agents
Section titled “Agents”| Tool | Signature | Does |
|---|---|---|
create_agent | (tenant, name, description="", brain_url="") -> dict | Create an agent. Returns {agent, session_key (sk_…, once)}. |
create_agent_credentials | (tenant, agent_id, label="") -> dict | Mint Cortex outbound credentials for a brain that can’t accept inbound (localhost, serverless, egress-only). Returns {agent_secret (sk_…, once), cortex_url, brain_url, key_id, usage}. |
get_agent | (tenant, agent_id) -> dict | One agent: id, name, description, status, brain_url, Playground test_url, timestamps. It does not return STT/TTS config. |
list_agents | (tenant, status="", limit=20) -> dict | List agents; optional draft|active|archived filter. |
update_agent | (tenant, agent_id, name="", description="", brain_url="") -> dict | Rename, re-describe, and/or point the brain at a WS URL. |
archive_agent | (tenant, agent_id) -> dict | Soft delete (stops serving new sessions). |
There is no separate set_brain_url tool — pass brain_url to create_agent up
front, or set it later with update_agent. It must be wss:// (ws:// only for
localhost/127.0.0.1); an empty brain_url falls back to the hosted welcome
demo brain so a bare agent still greets.
create_agent_credentials returns two different URLs and they are not
interchangeable: cortex_url goes to the SDK’s cortex_url= argument (it already
carries the /agent path — pass it verbatim), while brain_url is what the agent’s
own brain_url must become so the runtime dials Cortex instead of your server.
Setting it is not automatic — finish with
update_agent(tenant, agent_id, brain_url=…). The sk_ secret is shown once, never
expires, and minting revokes nothing, so rotation is: mint → redeploy → revoke the
old key. This is what makes local development tunnel-free; see
Cortex relay.
| Tool | Signature | Does |
|---|---|---|
create_api_key | (tenant, agent_id, label, kind="secret", allowed_origins=None) -> dict | Mint another key for one agent. kind="publishable" (pk_, browser — pass origins) or "secret" (sk_, backend). Raw key shown once. |
list_api_keys | (tenant, include_revoked=False) -> dict | List keys (prefixes only), each with the agent it names. |
revoke_api_key | (tenant, key_id) -> dict | Revoke by id (irreversible). |
Calls (observability)
Section titled “Calls (observability)”| Tool | Signature | Does |
|---|---|---|
list_sessions | (tenant, agent_id="", state="", limit=20, cursor="") -> dict | List calls, most recent first; filter by agent/state. Page with next_cursor. |
get_session | (tenant, session_id) -> dict | One call in full: state, timing, agent_input, metadata, recordings summary. |
get_session_events | (tenant, session_id, source="all", frame="", disposition="", limit=2000) -> dict | What happened, merged: lifecycle milestones and the wire between runtime and brain — transcripts, replies, actions, interruptions. |
get_session_logs | (tenant, session_id, level="INFO", service="", limit=500) -> dict | The voice runtime’s own log lines for that call. |
get_recordings | (tenant, session_id, ttl_seconds=900) -> dict | Audio, one track per side, each with a short-lived signed download_url. |
get_usage | (tenant, period="") -> dict | Counters for one YYYY-MM billing period, broken down per agent. |
A call is a session, and that is the only noun. There is no Meeting above it:
list_meetings / get_meeting / list_meeting_events / query_logs were removed
on 2026-08-20 along with the entity, and the session id you already hold — the one
in connect_params, in {brain_url}?session_id={session_id}, in every log line — is the id
every one of these tools takes.
The inspect-a-call loop is events first, logs second:
get_session_events— authoritative and versioned. Safe to assert on in tests. Passsource="platform"for the lifecycle milestones alone (cheap: it skips the wire read), ordisposition="dropped_after_watermark"to see exactly what a barge-in threw away — the usual answer to “the agent replied but nothing happened”.get_session_logs— evidence, not contract. Written in our vocabulary and free to change; read them to understand a call, never to assert on one.
Both halves arrive as one bundle when the call ends, so a call still in progress
has neither. Check the wire / logs_availability field before concluding a call
was silent: found, missing (no bundle — still running, or the upload failed),
unavailable (the store could not be read) or skipped. An empty list is not the
same fact as any of those.
These are Voqalize’s records. Your brain runs in your own environment and
logs there; session.id is the same string on both sides, so it joins them.
The flow, end to end
Section titled “The flow, end to end”An agent with these tools connected takes a project from empty to a running voice agent in this order:
- Confirm the connection —
whoami, thenlist_tenantsfor thetenantslug every other tool requires. - Write the brain —
on_session_start/on_user_message/on_rtvi/on_user_idle. See the SDK README (sdk/python/README.md). - Create the agent —
create_agent(tenant, name)→{agent, session_key}. - Run it and point
brain_urlat it — locally,create_agent_credentialsand dial out over Cortex (no tunnel); in production, an inbound route. Either way finish withupdate_agent. - Test it unattended — the conformance harness drives
the brain in text mode, with no audio and no human. Then talk to it live at the
agent’s
test_url. - Embed in the browser —
create_api_key(tenant, agent_id, label, kind="publishable", …)→pk_…, then the handshake — no package to install. - Instrument it —
on_finalize/on_errorbrain-side,list_sessions/get_session_events/get_session_logson ours.
- Where the brain runs — inbound vs. Cortex.
- Testing a brain — the unattended test loop.
- Reading a call back — events first, logs second, and what an empty list does not mean.