Threll.ai
Guides

Connect AI Agents via MCP

Threll's MCP server lets AI agents operate your voice workforce conversationally: "Have Harald call these five leads tomorrow at 9" becomes a prepared, confirmed, scheduled batch — no REST plumbing on your side.

What an agent can do

With the mcp:account_admin scope, a connected agent can manage workers, place and schedule calls (with human confirmation), search call history and transcripts, manage call-script documents, and check telephony status. With mcp:tools:<integration> scopes, it can also act through your connected integrations — book in Cal.com or EasyPractice, look up Magento orders, and more. The full catalog is in the MCP reference.

Connect from Claude

1

Add the connector

In Claude's settings, add a custom connector with the server URL https://mcp.threll.io. Claude discovers the OAuth configuration automatically and registers itself as a client — no manual credentials.

2

Approve access

You're sent to the Threll consent page, where you sign in, pick the account, and approve the requested scopes. Grant the minimum the agent needs — scopes can't be escalated later without re-consent.

3

Use the tools

Threll's tools appear in Claude's tool list, filtered to your granted scopes. The server also ships usage instructions with the catalog, so the agent knows to check telephony status before dialing and how to handle confirmations.

Any other MCP-compatible client works the same way — the server uses standard OAuth 2.1 discovery, dynamic client registration, and the Streamable HTTP transport.

The call confirmation flow

Placing phone calls is the one destructive action an agent can take against the outside world, so it's gated. By default (account setting require_confirmation_for_outbound_calls, on unless disabled), agent-initiated calls are a two-step handshake:

Step 1 — initiate_phone_call returns a token, not a call
{
  "status": "confirmation_required",
  "confirmation_token": "conf_9b3e...",
  "summary": {
    "worker": "Harald",
    "from": "+4721000000",
    "to": "+15555550100",
    "scheduled_at": "2026-06-12T09:00:00Z",
    "instructions_preview": "Renewal reminder, contract expires..."
  }
}

The agent presents the summary — who's calling, from which number, to whom, when, and with what instructions — and only after the user agrees does it call confirm_phone_call with the token. The confirmation is consumed atomically: a token can only ever place one call.

Step 2 — confirm_phone_call places the call
{
  "name": "confirm_phone_call",
  "arguments": { "confirmation_token": "conf_9b3e..." }
}

Useful companions:

Build your own client

If you're not using an off-the-shelf MCP client, the flow is standard OAuth 2.1 with PKCE followed by JSON-RPC calls:

Minimal client — token to tool call
const MCP = 'https://mcp.threll.io';

// 1. One-time: register your client (public, PKCE-only)
const reg = await fetch(`${MCP}/oauth/register`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    client_name: 'My Agent',
    redirect_uris: ['https://my-agent.example.com/callback'],
  }),
}).then(r => r.json());

// 2. Send the user to the consent page with a PKCE challenge,
//    then exchange the returned code for tokens:
const tokens = await fetch(`${MCP}/oauth/token`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code: authCode,
    redirect_uri: 'https://my-agent.example.com/callback',
    client_id: reg.client_id,
    code_verifier: pkceVerifier,
  }),
}).then(r => r.json());

// 3. Call tools over JSON-RPC (or the REST /tools endpoints)
const result = await fetch(MCP, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${tokens.access_token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'tools/call',
    params: { name: 'list_workers', arguments: {} },
  }),
}).then(r => r.json());
import requests

MCP = "https://mcp.threll.io"

# 1. One-time: register your client (public, PKCE-only)
reg = requests.post(f"{MCP}/oauth/register", json={
    "client_name": "My Agent",
    "redirect_uris": ["https://my-agent.example.com/callback"],
}).json()

# 2. Send the user to the consent page with a PKCE challenge,
#    then exchange the returned code for tokens:
tokens = requests.post(f"{MCP}/oauth/token", json={
    "grant_type": "authorization_code",
    "code": auth_code,
    "redirect_uri": "https://my-agent.example.com/callback",
    "client_id": reg["client_id"],
    "code_verifier": pkce_verifier,
}).json()

# 3. Call tools over JSON-RPC (or the REST /tools endpoints)
result = requests.post(MCP,
    headers={"Authorization": f"Bearer {tokens['access_token']}"},
    json={
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tools/call",
        "params": {"name": "list_workers", "arguments": {}},
    },
).json()

Access tokens last 1 hour; refresh with grant_type=refresh_token (valid 30 days). If you're embedding tools straight into an LLM instead of speaking MCP, the REST endpoints return the catalog pre-formatted for Claude, OpenAI, or Gemini.

Reference

Reference: MCP · REST API