Skip to main content
Glama
burkecampbell

twilio-call-data-mcp

twilio-call-data-mcp

An MCP server that gives any MCP client read-only access to Twilio call data. It exposes six tools for searching calls, pulling recordings, querying agent activity, and generating summaries — with explicit handling for the ways Twilio's data misleads you if you take it at face value.

This server is read-only. It cannot place calls, send messages, modify resources, or access billing. That constraint is a feature.

Install

npm install twilio-call-data-mcp

Or clone and build:

git clone https://github.com/burkecampbell/twilio-call-data-mcp.git
cd twilio-call-data-mcp
npm install
npm run build

Credentials

Set three environment variables. The server refuses to start without the first two.

export TWILIO_ACCOUNT_SID=your_account_sid
export TWILIO_AUTH_TOKEN=your_auth_token
export TWILIO_WORKSPACE_SID=your_taskrouter_workspace_sid  # required for agent tools

Copy .env.example to .env for local development. Never commit .env.

MCP Client Configuration

Add to your MCP client config (e.g., Claude Desktop):

{
  "mcpServers": {
    "twilio-call-data": {
      "command": "npx",
      "args": ["twilio-call-data-mcp"],
      "env": {
        "TWILIO_ACCOUNT_SID": "your_account_sid",
        "TWILIO_AUTH_TOKEN": "your_auth_token",
        "TWILIO_WORKSPACE_SID": "your_workspace_sid"
      }
    }
  }
}

Related MCP server: airspeed-mcp

Tools

search_calls

Filter call records by date range, phone number, direction, status, minimum duration, or agent. Returns rows plus a pagination cursor.

Parameter

Type

Required

Description

start_date

string

No

Start of range (YYYY-MM-DD or ISO 8601), interpreted in timezone

end_date

string

No

End of range

phone_number

string

No

E.164 format, matches from or to

direction

enum

No

inbound, outbound-dial, outbound-api, outbound

status

enum

No

completed, busy, no-answer, canceled, failed, etc.

min_duration

integer

No

Minimum CDR duration in seconds

agent

string

No

Agent name or worker SID

page_size

integer

No

1-100, default 50

cursor

string

No

From a previous response

timezone

string

No

IANA timezone, default UTC

get_call

Full detail for one call SID, including child legs.

Parameter

Type

Required

Description

call_sid

string

Yes

Starts with "CA"

timezone

string

No

IANA timezone, default UTC

get_recording

Recording metadata for a call: signed URL with expiry, duration, channel count, and transcript if one exists. Never returns audio bytes.

Parameter

Type

Required

Description

call_sid

string

Yes

Starts with "CA"

timezone

string

No

IANA timezone, default UTC

list_agents

TaskRouter worker roster with current activity status.

Parameter

Type

Required

Description

timezone

string

No

IANA timezone, default UTC

get_agent_activity

Activity and reservation history for one worker over a time window, with summary statistics.

Parameter

Type

Required

Description

worker_sid

string

Yes

Starts with "WK"

start_date

string

Yes

Window start

end_date

string

Yes

Window end

timezone

string

No

IANA timezone, default UTC

call_summary

Aggregates by day, agent, or queue: call counts, talk time, and abandonment rate.

Parameter

Type

Required

Description

start_date

string

Yes

Range start

end_date

string

Yes

Range end

group_by

enum

No

day, agent, or queue (default day)

timezone

string

No

IANA timezone, default UTC


Talk Time: Why This Server Returns Two Numbers

Every tool that reports call duration returns two fields, not one:

{
  "duration_seconds": { "value": 42, "source": "cdr", "measures": "ring_to_hangup" },
  "talk_seconds":     { "value": 35, "source": "taskrouter", "measures": "agent_audio_open" }
}

What is happening: Twilio's CDR (Call Detail Record) duration counts from the moment the call connects — including ring time — to hangup. TaskRouter and Flex Insights count from when the agent's audio path actually opens. For the same call, these numbers disagree. In the example above, seven seconds of ringing are counted as "duration" in the CDR but are not part of the actual conversation.

Why this matters: An LLM handed a bare duration: 42 will average it, bill against it, and compare it to a talk_time: 35 from another source without knowing these are different measurements of the same call. It will produce confident, wrong totals. A human reviewing those totals will not know to question them because the numbers look precise.

What we do: Every duration field in every response carries its source (where the number came from) and measures (what the number actually counts). A consumer that receives both fields can choose which one fits their question. A consumer that ignores the labels is making that choice explicitly rather than accidentally.

What this costs: Slightly larger payloads. Every response carries two labeled objects instead of one bare integer. The trade-off is worth it — seven seconds of mislabeled data across a month of calls adds up to hours of phantom talk time.


What This Server Does Not Do

  • No call control. Cannot place, transfer, hold, or end calls.

  • No messaging. Cannot send or read SMS/MMS.

  • No resource modification. Cannot update call records, recordings, worker states, or any Twilio resource.

  • No billing access. Cannot read account balance, usage records, or pricing.

  • No audio streaming. Recording endpoints return signed URLs and metadata, never audio bytes.

If you need write operations, use the Twilio SDK directly. This server exists specifically to be safe to point an LLM at.


Decision Log

1. Never return one number for talk time

Naive approach: Return duration: 42 like Twilio's API does.

How it fails: A downstream consumer averages CDR durations against TaskRouter talk times from another report. The 7-second ring-time gap per call compounds across hundreds of calls into hours of phantom talk time. Worse, this looks correct — both numbers are real, they just measure different things.

What we do instead: Every duration field is a structured object with value, source, and measures. CDR duration and TaskRouter talk time are never presented as the same kind of number.

What it costs: Larger JSON payloads. Consumers must destructure an object instead of reading a bare integer. This friction is the point — it forces acknowledgment of what the number means.

2. Timezones are explicit or the tool refuses

Naive approach: Accept date strings and assume UTC, or worse, assume the server's local time.

How it fails: A twelve-hour offset between a source timestamp and the server's timezone assumption put an entire day of calls on the wrong date. The symptom looked like missing data — "we had no calls on Tuesday" — when the calls were there, just bucketed into Monday and Wednesday by a timezone mismatch.

What we do instead: Every tool accepts an optional timezone parameter (IANA identifiers only — "America/New_York", not "EST"). Defaults to UTC. Date range filters are interpreted in the caller's timezone. Every timestamp in every response carries its timezone. No implicit conversions.

What it costs: Callers must know their timezone, which they already do. Responses are slightly larger with the timezone field. The alternative — silent misattribution of calls to wrong dates — costs debugging hours.

3. Pagination that admits truncation

Naive approach: Return the first 50 rows. If there are more, include a next_page link.

How it fails: A consumer receives 50 rows and computes totals. There were actually 200 rows. Nothing in the response distinguished "there were 50 calls" from "we stopped at 50." The consumer's summary is confidently wrong by 75%.

What we do instead: Every paginated response includes has_more (boolean), truncated (boolean), total_available (integer or null), and cursor (opaque string). When a result set hits the page size limit, truncated: true tells the consumer explicitly that totals computed from this page are incomplete.

What it costs: One extra boolean and one nullable integer per response. Consumers that want complete data must page through. But now they know they need to.

4. Rate limits handled, not hoped about

Naive approach: Make the API call. If it fails, return the error.

How it fails: Twilio returns 429 under load. A bare error response gives the consumer no information about whether this is a transient throttle or a persistent problem, and no way to distinguish "the data doesn't exist" from "we couldn't get to it."

What we do instead: Exponential backoff with jitter on 429 and 5xx responses, up to 5 retries. Every response includes a retry_info object with the retry count and a throttled boolean. A consumer seeing retries: 3, throttled: true knows the data is fresh but the API is under pressure.

What it costs: Worst case, a single tool call takes 30+ seconds instead of failing immediately. Retry count in the response adds a small object to every payload. The alternative — silent failures that look like empty data — is worse.

Development

npm install
npm test          # runs Vitest with fixture data, no credentials needed
npm run build     # compiles TypeScript
npm run dev       # runs with tsx for development

Tests use recorded and scrubbed Twilio API responses. No credentials are needed to run CI.

License

MIT

Install Server
A
license - permissive license
A
quality
C
maintenance

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Read-only MCP server for ClassQuill, a tutoring-business-management platform.

  • Official MCP server for OmniDimension. Drive voice agents, dispatch calls, and run bulk campaigns.

  • A paid remote MCP for AI SDK data query MCP, built to return verdicts, receipts, usage logs, and aud

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/burkecampbell/twilio-call-data-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server