Skip to content

MCP server & Claude Code skill

The Voqalize MCP server exposes the platform’s management surface as tools your editor’s agent (Claude Code, etc.) can call. Paired with the voqalize skill, it takes a developer from an empty project to a running voice agent without leaving the editor.

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.

Add the server to your MCP client. In Claude Code:

Terminal window
claude mcp add --transport http voqalize https://app.voqalize.com/mcp

Or, project-scoped, drop an .mcp.json at your repo root (this is the file the voqalize skill ships):

{
"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.

  • 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; tenant is a selector. Every scoped tool takes a required tenant slug 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.
  • whoami first, then list_tenants. whoami returns your identity; list_tenants returns every workspace you can act on. Call them at the start of a session to learn which tenant slug to pass to everything else.

Sixteen tools. Every tool returns the control plane’s raw JSON. Errors surface with a platform code — 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).

ToolSignatureDoes
whoami() -> dictIdentify the authenticated developer. Call first.
list_tenants() -> dictEvery tenant (workspace) you can act on.
create_tenant(about="", display_name="") -> dictCreate your workspace + seed demo agents. Idempotent — returns your existing tenant if you have one.
ToolSignatureDoes
create_agent(tenant, name, description="", brain_url="") -> dictCreate an agent. Returns {agent, session_key (sk_…, once)}.
create_agent_credentials(tenant, agent_id, label="") -> dictMint Cortex outbound credentials for a brain that can’t accept inbound (localhost, serverless, egress-only). Returns {agent_secret (ak_…, once), cortex_url, brain_url, key_id, usage}.
get_agent(tenant, agent_id) -> dictOne agent: id, name, description, status, brain_url, Playground test_url, timestamps. It does not return STT/TTS config.
list_agents(tenant, status="", limit=20) -> dictList agents; optional draft|active|archived filter.
update_agent(tenant, agent_id, name="", description="", brain_url="") -> dictRename, re-describe, and/or point the brain at a WS URL.
archive_agent(tenant, agent_id) -> dictSoft 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 ak_ 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.

ToolSignatureDoes
create_api_key(tenant, label, kind="secret", allowed_origins=None) -> dictMint a runtime key. kind="publishable" (pk_, browser — pass origins) or "secret" (sk_, backend). Raw key shown once.
list_api_keys(tenant, include_revoked=False) -> dictList keys (prefixes only).
revoke_api_key(tenant, key_id) -> dictRevoke by id (irreversible).
ToolSignatureDoes
list_meetings(tenant, agent_id="", state="", limit=20) -> dictList calls, most recent first; filter by agent/state.
get_meeting(tenant, meeting_id) -> dictOne call’s detail.
list_meeting_events(tenant, meeting_id) -> dictThe event timeline for a call.
query_logs(tenant, meeting_id, severity_min="INFO", component="", limit=100) -> dictPlatform runtime log lines for one call.

These four are the inspect-a-call loop: find the call, read its transcript, check how far it got, then read the logs. query_logs returns the platform’s logs — your brain runs in your own environment and logs there. The meeting’s active_session_id is the brain’s session.id, so that string joins the two sides.

The skill (skill/SKILL.md) drives the end-to-end build on top of those tools. It is a short entry file plus references loaded on demand (skill/references/), and it walks the flow:

  1. Prereqs — confirm the MCP server is connected (whoamilist_tenants); Python 3.12+; a React app for the embed.
  2. Draft the brain — scaffold from templates/brain.py; implement on_session_start / on_interaction / on_client_message / on_user_idle.
  3. Create the agentcreate_agent(tenant, name){agent, session_key}.
  4. Run it and point brain_url at it — locally, create_agent_credentials and dial out over Cortex (no tunnel); in production, an inbound route. Either way finish with update_agent.
  5. 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.
  6. Embed in the browsercreate_api_key(tenant, label, kind="publishable", …)pk_…, then @voqalize/client-react.
  7. Instrument and observeon_inference_finalized / on_error brain-side, list_meetings / get_meeting / query_logs platform-side.

Templates ship alongside it: brain.py, run_cortex.py, inbound_app.py, test_brain.py, and react_embed.tsx.