five9-mcp
☎️ five9-mcp
Your Five9 contact center, in your AI's hands.
An open-source MCP server that connects Claude, ChatGPT, or any MCP client to the Five9 cloud contact center — running on Cloudflare Workers with zero dependencies.
Quick start · Connect Claude · Connect ChatGPT · Tools · Architecture
Ask your AI things like:
"Who's on a call right now, and how deep is the sales queue?" 📊 "Create a preview campaign for the win-back list, attach the sales skill, and start it." 🛠️ "Stop the OUTBOUND_AGED campaign and add these 3 leads to the callback list." 📞 "Onboard the new agent: create the user, assign the billing skill at level 2." 🧑💼 "Is 555-867-5309 on our DNC? Check before anyone dials it." 🚫 "Pull yesterday's Call Log report and summarize abandon rates." 📈 "Build me a complete IVR: option 1 scheduling, option 2 billing, after hours goes to voicemail." 🧩
Under the hood, this server speaks Five9's Configuration (admin) and Statistics (supervisor) SOAP Web Services — the APIs that still run Five9's admin surface — and exposes them as clean JSON tools over MCP streamable HTTP. Hand-rolled envelopes, a ~60-line XML parser, no npm packages. Every tool has been exercised against a live Five9 domain.
✨ Built-in web UI
Deploy it and your Worker serves more than an API:
Page | What you get |
| A polished landing page: live server status, this setup guide, click-by-click AI connection walkthroughs, and the full tool catalog |
| The setup wizard — enter Five9 credentials in your browser, get them verified live, receive your access key. No terminal, no secrets commands |
| An interactive console — paste your access key, pick any of the 77 grouped tools, fill a form generated from its schema, and run it against your live Five9 domain right from the browser |
| The MCP endpoint itself (streamable HTTP, stateless) |
| JSON healthcheck |
The console is the fastest way to sanity-check credentials, explore what each tool returns, or debug a campaign — no AI required.
🚀 Quick start — no terminal needed
You need a free Cloudflare account and a Five9 user with API access — create a dedicated Five9 API user scoped to what you want an AI to do, don't reuse a personal admin login.
1 — Deploy to Cloudflare (one click, in your browser)
Sign in to Cloudflare and click through — it creates your own copy of this Worker (plus the KV namespace it needs) and gives you a URL like https://five9-mcp.you.workers.dev.
2 — Run the setup wizard (in your browser)
Open /setup on your new server. Enter your Five9 username, password, and region — the wizard verifies them live against Five9 before saving, then hands you your access key (shown once — store it in a password manager).
3 — Connect your AI (walkthroughs below), then ask it to "check the connection and list my campaigns." 🎉
git clone https://github.com/ryanshatz/five9-mcp
cd five9-mcp
npx wrangler deploy # provisions the CONFIG KV namespace on first deployThen either use the /setup wizard, or skip it and manage credentials as Wrangler secrets (secrets override the wizard):
npx wrangler secret put FIVE9_USERNAME # e.g. apiuser@yourdomain
npx wrangler secret put FIVE9_PASSWORD
npx wrangler secret put MCP_AUTH_TOKEN # a long random string — this is the key to your serverDefaults live in wrangler.toml and work for US domains:
Var | Default | Notes |
|
| EU: |
|
| Config Web Services WSDL version |
|
| Statistics Web Services WSDL version |
🔌 Connect your AI
Connect Claude (web & desktop)
Custom connectors are available on Free (one connector), Pro, Max, Team, and Enterprise plans.
In claude.ai or the Claude desktop app, open Settings → Connectors.
Click Add custom connector.
Name it Five9 and paste your server URL including the
/mcppath:https://<your-worker>.workers.dev/mcpClick Add, then Connect. Claude auto-discovers this server's built-in OAuth and opens its authorization page.
On the 🔐 five9-mcp screen, paste your
MCP_AUTH_TOKENas the access key and click Authorize.In any chat, open the search & tools (+) menu and make sure the Five9 connector is toggled on.
Team/Enterprise: an Owner first adds the connector under Organization settings → Connectors; members then click Connect in their own settings to authorize.
Connect ChatGPT
Custom MCP connectors require Developer mode (Plus/Pro; on Business/Enterprise an admin must allow custom connectors).
In ChatGPT on the web, open Settings → Apps & Connectors (sometimes labeled just Connectors).
Under Advanced settings, toggle Developer mode on.
Back on the Connectors page, click Create.
Name it Five9, set the MCP server URL to
https://<your-worker>.workers.dev/mcp, and choose OAuth authentication.Acknowledge the trust prompt and save. ChatGPT opens this server's authorization page — paste your
MCP_AUTH_TOKENand click Authorize.In a new chat, open the + / tools menu and enable the Five9 connector (Developer mode connectors are enabled per-conversation). ChatGPT asks you to confirm each tool call — sensible for anything that can start a dialer. 😄
Connect Claude Code
claude mcp add --transport http five9 https://<your-worker>.workers.dev/mcp \
--header "Authorization: Bearer <your MCP_AUTH_TOKEN>"The raw access key works directly as a bearer token — no OAuth dance. Run /mcp inside Claude Code to verify.
Any other MCP client
Anything that speaks MCP streamable HTTP works — complete the OAuth flow or send the access key as a bearer token:
curl -X POST https://<your-worker>.workers.dev/mcp \
-H "Authorization: Bearer <MCP_AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_connection","arguments":{}}}'src/oauth.js implements a minimal OAuth 2.1 authorization server (metadata discovery, dynamic client registration, PKCE S256, refresh tokens) designed for a single-operator deployment:
The "login" on the consent screen is the server's access key (
MCP_AUTH_TOKEN).Everything is stateless — client IDs, auth codes, and tokens are HMAC-SHA256-signed blobs keyed by
MCP_AUTH_TOKEN. No KV, no Durable Objects.Both auth paths work simultaneously: OAuth-minted tokens and the raw key as a bearer credential.
Revoke everything at once by rotating the secret:
npx wrangler secret put MCP_AUTH_TOKEN.
🧰 The toolbox
77 tools. 🟢 = read (always safe) · ✏️ = write (changes your domain — the server tells AIs to confirm with you first)
69 SOAP tools (username/password) + 8 OAuth New Platform REST tools (Consumer Key/Secret — see OAuth New Platform APIs).
The headline trick: describe a call flow in a paragraph and the AI designs it, shows you a Mermaid diagram in chat, and deploys a working IVR script. The model never freestyles Five9's IVR XML: it fills a constrained JSON flow spec (play / menu / business-hours / skill transfer / voicemail / hangup), a graph validator checks every branch and reference, and deterministic code emits designer-shaped XML (module wiring, prompt encoding, and field order all derived from real exported scripts).
Tool | What it does | |
🟢 |
| Graph-check a flow spec + verify referenced skills/prompts exist on the domain |
🟢 |
| Render a flow spec or an existing IVR script as a Mermaid flowchart |
✏️ |
| Compose the full script XML and create it on the domain ( |
✏️ |
| Voice a prompt with a modern AI voice and upload it as a Five9-ready G.711 u-law WAV. No API key needed: powered by Workers AI (Deepgram Aura, ~40 voices) built into your Worker |
Recommended flow: validate → render (show the human!) → generate prompts → build → attach to an inbound campaign. generate_prompt_audio runs on Cloudflare Workers AI out of the box: no external TTS account, no API key, fractions of a cent per prompt billed to the Cloudflare account you already deployed to. ElevenLabs/OpenAI work too if you set their key secrets, and {tts} prompts (Five9's built-in robot voice) need nothing at all.
Tool | What it does | |
🟢 |
| Operator context for the AI — who runs this server and the ground rules |
🟢 |
| Verify Five9 credentials work; returns visible skill count |
🟢 |
| Current Five9 API usage counters vs rate limits |
Tool | What it does | |
🟢 |
| List campaigns (name, type, state, mode) |
🟢 |
| State + attached lists + DNIS in one call |
🟢 |
| FULL campaign config (dialing mode, ratios, recording, wrap-up…) |
✏️ |
| Create outbound or inbound campaigns, BASIC or ADVANCED |
✏️ |
| Edit any campaign setting — read-modify-write, pass only the changes |
✏️ |
| Rename a campaign |
✏️ |
| Delete a campaign |
✏️ |
| start / stop / force_stop / reset / reset_list_positions |
✏️ |
| Attach/detach dialing lists with priority |
✏️ |
| Add/remove routing skills on a campaign |
✏️ |
| Attach/detach inbound numbers |
✏️ |
| Add/remove agent dispositions on a campaign |
🟢 |
| List campaign profiles (ANI, attempts, timeouts) |
✏️ |
| Create / modify / delete campaign profiles |
✏️ |
| Read / edit a profile's CRM record-selection criteria and dialing order |
Tool | What it does | |
🟢 |
| List dialing lists + record counts |
✏️ |
| Create or delete a dialing list |
✏️ |
| Push a lead into a list (async import) |
✏️ |
| Bulk-add many leads in one async import (configurable CRM/list modes) |
✏️ |
| Remove matching records from a list |
🟢 |
| Outcome of an async list/CRM import |
Tool | What it does | |
🟢 |
| Look up contacts by exact field values |
✏️ |
| Update a contact (sole-match safety by default) |
✏️ |
| Update many CRM contacts in one async import (poll with type "crm") |
✏️ |
| Delete a contact (only when exactly one matches) |
🟢 |
| The domain's contact field schema |
✏️ |
| Create / modify / delete custom CRM fields |
Tool | What it does | |
✏️ |
| Check / add / remove numbers on the domain DNC list |
🟢 |
| Domain dialing rules (time/state restrictions) |
Tool | What it does | |
🟢 |
| List users with general info |
🟢 |
| One user's full record: roles, skills, groups |
✏️ |
| Create a user with roles, skills, and groups |
✏️ |
| Edit a user's info — pass only the changes |
✏️ |
| Delete a user |
🟢 |
| Role/permission templates |
🟢 |
| Skills, with or without assigned users |
✏️ |
| Create / modify / delete skills |
✏️ |
| Assign skills to users, set levels |
✏️ |
| Grant / revoke roles (agent, admin, supervisor, reporting, crmManager) with permission tabs |
🟢 |
| Agent groups + members |
✏️ |
| Create / delete groups, add/remove agents |
✏️ |
| Not Ready / Logout reason codes |
Tool | What it does | |
🟢 |
| Call dispositions and their settings |
✏️ |
| Create / modify / rename / delete dispositions (incl. redial timers) |
🟢 |
| IVR scripts — metadata, or one script's full XML |
✏️ |
| Create / modify / delete IVR scripts (push a full xmlDefinition) |
🟢 |
| Voice prompts on the domain |
✏️ |
| Create / modify / delete text-to-speech prompts |
✏️ |
| Create / modify / delete pre-recorded WAV prompts (base64; G.711 µ-law 8kHz mono) |
🟢 |
| Provisioned inbound numbers (optionally unassigned only) |
🟢 |
| Call variables and variable groups |
✏️ |
| Create / delete custom call variables |
🟢 |
| Web connector integrations |
✏️ |
| Create / delete web connectors (URL pops agents trigger) |
✏️ |
| List / create / delete speed-dial codes |
🟢 |
| Domain-level VCC settings |
Tool | What it does | |
🟢 |
| Kick off any report by folder + name, optional time range |
🟢 |
| Poll for the report's CSV output |
🟢 |
| AgentState, ACDStatus, CampaignState, campaign statistics (incl. dialer-manager & autodial views) |
These tools speak Five9's modern OAuth 2.0 "New Platform" REST APIs, not the SOAP APIs the tools above use. They require an API Access Control credential (Consumer Key/Secret), not the SOAP username/password — see OAuth New Platform APIs.
Tool | What it does | |
🟢 |
| Verify the OAuth credential — acquires a bearer token (no domain data) |
🟢✏️ |
| Generic authenticated call to any New Platform endpoint (method + path + body), with rate-limit/backoff and ETag support |
🟢✏️ |
| Circles — list / get / create / delete (no SOAP equivalent) |
🟢 |
| Voice prompts via the New Platform prompts API (paginated) |
🟢 |
| Dispositions via the interactions API (richer than the SOAP list; read-only) |
🟢 |
| Domain metadata (id, name, tenant, service endpoints) |
🟢 |
| Data Tables (structured lookup tables; no SOAP equivalent) — uses a separate |
🟢 |
| Rows of a Data Table by id (paginated) |
🔐 OAuth New Platform APIs
Alongside the SOAP tools, the server can call Five9's newer OAuth 2.0 New Platform REST APIs (e.g. Circles, interactions, prompts, domain metadata). These use a different credential from the SOAP username/password:
An API Access Control Consumer Key and Consumer Secret, generated in the Five9 Admin Console → API Access Control (a Controlled-Availability feature). Generating one needs the
security → applications → Create applicationspermission, and the account must be migrated to Five9 Identity Service (users with legacy API/Agent/Supervisor roles are excluded from migration until those roles are removed).Configure them as env/secret vars (all separate from the SOAP creds):
FIVE9_CONSUMER_KEY=... # "All APIs access" family credential (default)
FIVE9_CONSUMER_SECRET=...
FIVE9_DOMAIN_ID=131109 # your Admin Console domain id
FIVE9_REST_REGION=US # US | US-ALPHA | CA | EU | IN | UK
# or pin the base URL directly: FIVE9_REST_BASE_URL=https://api.prod.us.five9.net
# Optional second credential for the "Data Tables access" family (its own key):
FIVE9_DT_CONSUMER_KEY=...
FIVE9_DT_CONSUMER_SECRET=...Then run rest_check_connection to confirm the token flow. What each credential can reach is governed by its API family + scopes — all-apis-access does not literally grant every service, and write access is per-service.
Multiple credentials / families. Each API Access Control credential belongs to one family (mapped to an Apigee API Product), and that family decides which services the key may call. The server supports named credentials: default (from FIVE9_CONSUMER_KEY/SECRET) plus data-tables (from FIVE9_DT_CONSUMER_KEY/SECRET). The Data Tables tools use the data-tables credential automatically; rest_call and rest_check_connection accept a credential argument to pick one.
Note: Five9's getting-started doc lists the token endpoint as
/v1/auth/token, but the live endpoint is/oauth2/v1/token(what this client uses).
🎨 Customizing the operator context
src/about.js holds the text served to connected AIs via the MCP instructions field and the about tool: who operates the server, why it exists, and how the AI should behave (e.g. "confirm before write actions"). Edit it to describe your own deployment — it ships with the original operator's context as an example.
🏗️ Architecture
No build step, no dependencies — plain JS modules in src/:
src/
├── index.js # router, CORS, MCP JSON-RPC handler, /setup endpoint
├── five9.js # SOAP client: envelope builder, ~60-line XML parser, one method per Five9 op
├── tools.js # MCP tool definitions (JSON Schema) + dispatch
├── oauth.js # stateless OAuth 2.1 server (single-operator model)
├── config.js # config resolution: Wrangler secrets > KV (setup wizard)
├── ui.js # landing page, setup wizard, interactive console
└── about.js # operator context — edit this for your deploymentRequests are stateless: every MCP call opens a fresh Five9 SOAP exchange with HTTP Basic auth. The Statistics API additionally requires a setSessionParameters call, which get_realtime_stats performs per invocation.
Five9's endpoints are generated by JAXB and validate child-element order against the WSDL sequence. If you extend this server, pull the WSDL (
https://api.five9.com/wsadmin/v13/AdminWebService?wsdl, HTTP Basic auth) and match the<xs:sequence>order exactly — including base types likebasicImportSettings, whose elements come before the extension's.addToListCsvrequirescleanListBeforeUpdate,crmAddMode,crmUpdateMode, andlistAddModeeven though the WSDL marks most of themminOccurs="0".List/CRM imports are asynchronous: the call returns an import identifier immediately; poll
get_import_resultfor the outcome.Contact record values come back wrapped (
<values><data>…</data></values>); several responses return a single object where you'd expect a one-element array.toArray()infive9.jsnormalizes this.Report time criteria order is
<end>before<start>(JAXB alphabetical ordering).IVR
xmlDefinitionis the visual designer's persisted format: modules are wired by GUID (ascendants/singleDescendant/branches), inline TTS text is stored as gzip+base64speakElementdocuments, and business-hours checks compare the__DAY__(SUN=1..SAT=7) and__TIME__(minutes since midnight) system variables.ivr.jsencapsulates all of it.getPromptsreturns no prompt ids (name + type only). File-prompt references inside IVR XML are accepted withid 0+ the prompt name and normalized server-side; the pushed script round-trips with the server-stampeddomainIdadded.
🛡️ Security
Five9 credentials live in your Cloudflare account only — as Worker secrets, or (wizard path) in a Workers KV namespace, encrypted at rest. No tool ever returns them, and Wrangler secrets always override KV.
The setup wizard is open only on a fresh, unconfigured server — run it right after deploying. Once configured, any change requires the current access key, and env-managed servers refuse wizard changes entirely.
Always complete setup (or set
MCP_AUTH_TOKEN). An unconfigured server with no access key runs open — anyone who finds the URL can drive your contact center.Write tools (✏️ above) change your domain. Scope the Five9 API user's role to what you actually want an AI to do — Five9 permissions are the real security boundary.
manage_dnc removeanddelete_listdeserve extra caution; theaboutinstructions tell AIs to confirm before using them.The console stores your access key in your browser's localStorage only, and calls go same-origin to your own Worker.
💻 Development
npm run dev # wrangler dev on http://localhost:8787
npm run deploy # wrangler deployPut local secrets in .dev.vars (gitignored):
FIVE9_USERNAME=apiuser@yourdomain
FIVE9_PASSWORD=...
MCP_AUTH_TOKEN=dev-local-token
# Optional — external AI voice providers for generate_prompt_audio.
# The default (Workers AI / Deepgram Aura) needs no key at all.
ELEVENLABS_API_KEY=...
OPENAI_API_KEY=...
# Optional — OAuth New Platform REST tools (separate credential; see below)
FIVE9_CONSUMER_KEY=...
FIVE9_CONSUMER_SECRET=...
FIVE9_DOMAIN_ID=131109
FIVE9_REST_REGION=US
FIVE9_DT_CONSUMER_KEY=... # optional: "Data Tables access" family
FIVE9_DT_CONSUMER_SECRET=...Then open http://localhost:8787/console, paste dev-local-token, and run tools against your domain — or smoke-test from the CLI with the curl snippet above.
🤝 Contributing
PRs welcome! The Five9 Config API has ~180 operations and this server wraps 69 of the most useful — the pattern in five9.js + tools.js is easy to extend (read the SOAP notes first and save yourself a fight with the WSDL). Please keep the zero-dependency constraint.
📄 License
MIT · built by Ryan Shatzkamer
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/kenniole/five9-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server