Skip to main content

MCP Webex Calling Server

CI

An MCP (Model Context Protocol) server for administering Webex Calling. It exposes 90 tools over MCP stdio, so an AI assistant such as Claude Desktop can query and manage your Webex Calling organization in natural language:

  • "What call queues do we have configured?"
  • "Generate an activation code for user john@example.com"
  • "Get call statistics for the last 24 hours"

The server talks to the official Webex REST APIs with your access token. It is a thin layer over those APIs: every tool maps to documented Webex endpoints, and where the public API has limits (see API limitations), the docs and the tool descriptions say so.

Quickstart

Requirements: Python 3.10+. Setup walks you through the Webex side.

1. Install

Once released on PyPI, the one-line route is uv:

uvx mcp-webexcalling --version          # runs without installing anything

Or install from a clone (works today):

git clone https://github.com/lutztalk/mcp-webexcalling.git
cd mcp-webexcalling
python3 -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate
python -m pip install -U pip   # editable installs need a reasonably new pip
pip install -e .

2. Sign in

mcp-webexcalling setup

It offers two routes:

  • Sign in properly (recommended). It walks you through creating a private Webex app — it prints the exact redirect address and the exact permissions to tick — then opens your browser to approve it. Takes about five minutes, once. After that the server renews its own access and you never sign in again.
  • Quick trial. Paste a personal access token from developer.webex.com. Fastest way to look around, but it stops working after about 12 hours.

Either way, setup checks the connection before it finishes and tells you who you signed in as and whether you have admin rights.

3. Connect your MCP client

mcp-webexcalling print-config

That prints the exact JSON block for Claude Desktop (with the absolute path already filled in) and the one-line claude mcp add command for Claude Code. No token goes in the configuration — credentials live in your own config directory, not in a file you might share.

Restart your client, then ask it to run test_connection.

Something not working?

mcp-webexcalling doctor

One line per check: version, where credentials came from, when they expire, whether renewal is working, whether you have admin rights, and which permissions are missing — each with the fix.

For a step-by-step walkthrough, see SETUP.md. For example conversations, see USAGE.md.

Installing in Claude Desktop without a terminal

Each release also ships a .mcpb bundle on the releases page. Download it and double-click: Claude Desktop shows an install dialog and, if you want to try it with a pasted token, a field to type it into. To get the sign-in that renews itself, run mcp-webexcalling setup afterwards — the bundle reads the same credentials.

What can it do?

The server registers exactly 90 tools. The complete reference — every tool with its arguments and description, generated directly from the code so it cannot drift — is in docs/TOOLS.md. By category:

Category Tools Highlights
Diagnostics 1 test_connection health/permission check
Organizations & Locations 7 list/create/update/delete locations, per-location feature summary
Users 9 CRUD, search by display name or email, extensions, calling settings
Licenses 5 list licenses, assign/remove per user
Phone Numbers 6 list/lookup, assign to users or locations, find unassigned numbers
Devices 11 list/details, activation codes, create by MAC, re-home, remove
Call Queues 8 CRUD plus agent management (location-scoped)
Auto Attendants 5 CRUD (location-scoped)
Hunt Groups 7 CRUD plus member management (location-scoped)
Trunks & Call Park 4 trunk groups, call park extensions, per-user park monitoring
User Calling Features 5 call forwarding, simultaneous ring, feature bundle updates
Voicemail 5 per-user settings; the authenticated user's messages
Call Recordings 2 list and fetch recording metadata (Converged Recordings API)
Call History, CDRs & Analytics 10 detailed call records, statistics, per-user/queue/state breakdowns, CSV/JSON export
Webhooks 5 CRUD for webhook registrations

Webex API limitations worth knowing

These are constraints of the public Webex APIs, not bugs in this server. The tools work within them and say so in their output:

  • Call history / analytics windows are short. Every CDR-based tool (get_call_history, get_call_detail_records, all *_statistics_* and analytics tools) uses the Detailed Call History feed, which only accepts time ranges between 48 hours and 5 minutes ago — you cannot pull last month's calls. Tools whose times are optional default omitted bounds into that window. An explicitly supplied out-of-window range (e.g. "last month") is rejected by most tools: the API's 400 comes back annotated with exactly which bound is out of range. The exceptions are the statistics tools get_call_statistics_from_cdr, get_user_call_statistics, and get_call_statistics_by_state, which clamp a supplied range into the window instead. The feed also requires the "Webex Calling Detailed Call History API access" role on the token's (admin) user, granted in Control Hub.
  • No real-time metrics or server-side export. get_real_time_call_metrics and export_call_records aggregate the same CDR feed locally (the feed lags real time by at least 5 minutes); Webex has no public real-time metrics or export endpoint.
  • Devices cannot be reassigned in place. The Devices API has no "move to another user" operation. associate_device_to_user performs the documented flow instead: delete the device, then generate an activation code for the new owner. Activation codes are short-lived (days, not months) — the response's expiryTime is authoritative.
  • Voicemail messages are me-scoped. list_voicemail_messages, get_voicemail_message, and delete_voicemail_message operate on the authenticated user's mailbox only; the Webex voiceMessages API cannot target another user's messages. (Per-user voicemail settings are admin-manageable.)
  • Number search is inventory, not shopping. search_available_phone_numbers lists unassigned numbers your organization already owns at a location; the public API cannot purchase new numbers.
  • Webhook tools manage registrations only. They create/update/delete webhook subscriptions pointing at your HTTPS receiver. A stdio MCP server has no HTTP listener and does not itself receive events.

Authentication & tokens

mcp-webexcalling setup handles all of this for you. This section explains what it does and why, for anyone who wants to know or needs to do it by hand.

Why you create your own Webex app

Webex has no secret-free public OAuth client: every token-issuing call requires an app's client secret. A public repository cannot ship one, so "sign in with Webex" using our app is impossible — everybody gets their own app instead. setup makes that a guided three-minute task: it prints the exact redirect address to paste and the exact permissions to tick, then opens the browser to approve it.

The three routes

Route Lifetime Good for
Your own integration (setup, recommended) Renews itself indefinitely Everyday use
Personal access token (setup --trial) ~12 hours, no renewal Evaluating the server
Service App Renews itself; org-owned Unattended use that must survive its author leaving

Access tokens from an integration last about 14 days and refresh tokens about 90 — and the 90-day clock resets every time it is used, so a server that refreshes itself keeps working indefinitely (see Webex's integrations guide). That renewal is now built in; it used to require an external process.

Service Apps are supported in the sense that their tokens work here, but setup does not automate them: they need a full administrator to authorize them in Control Hub, and it is unconfirmed whether call-history scopes work under one.

When renewal stops working

Renewal is not literally forever. It ends if the sign-in goes unused for 90 days, an administrator revokes the app, or the authorizing user's account changes. When that happens every tool returns a plain-language error telling you to run mcp-webexcalling setup again, and doctor reports what failed.

Permissions (scopes)

This is the canonical list, generated from mcp_webexcalling/scopes.py, which is also what setup prints and what doctor checks against — the three cannot drift apart.

Scope Needed for
spark-admin:organizations_read organization info and the test_connection admin probe
spark-admin:locations_read / spark-admin:locations_write location tools
spark-admin:people_read / spark-admin:people_write user tools, number assignment, license assignment
spark-admin:telephony_config_read / spark-admin:telephony_config_write queues, auto attendants, hunt groups, phone numbers, trunks, call park, and per-user calling features
spark-admin:devices_read / spark-admin:devices_write device tools, activation codes, MAC provisioning
spark-admin:licenses_read license listing and assignment lookups
spark:webhooks_read / spark:webhooks_write webhook tools
spark-admin:calling_cdr_read call detail records, analytics, and every call-statistics tool — additionally requires the 'Webex Calling Detailed Call History API access' role on the authorizing admin in Control Hub — a role, not a scope
spark:calls_read / spark:calls_write the voicemail message tools (list, get, delete) — these are user-context scopes: the voicemail message API reads the authorizing user's own mailbox, not the organization's
spark-admin:recordings_read / spark-admin:recordings_write the call recording tools — a compliance officer uses spark-compliance:recordings_read / _write instead

setup reports any permission you did not grant, and doctor re-checks them by probing one endpoint per family, so a missed checkbox is named rather than surfacing later as unexplained failures.

Admin scopes only work when the person approving is a full or read-only administrator of the organization. If someone else installs the server, send them the sign-in address setup prints so an administrator can approve it.

A warning about bot tokens

Webex bot tokens do not work for Webex Calling administration. Bots cannot be granted spark-admin:* scopes or admin roles, so /telephony/config/*, org-wide /people, /licenses, and the CDR feed all return 403 with a bot token. A bot is only useful here for the unrelated push-notification workflow. Don't put a bot token in WEBEX_ACCESS_TOKEN expecting the telephony tools to work.

Where credentials live, and how to revoke them

setup writes them to a file in your own configuration directory (~/.config/mcp-webexcalling/credentials.json, or %APPDATA% on Windows), which you can override with WEBEX_CONFIG_DIR. On Unix the file is created mode 0600 and is never even briefly world-readable. On Windows those permission bits do not exist; the file relies on the same per-user profile permissions that protect the rest of %APPDATA%.

Be clear-eyed about what that file holds in the recommended mode: an access token, a self-renewing refresh token, and the app's client secret. Anyone who reads it can keep minting admin tokens. It protects against other local users, casual file sharing, and backups of your MCP client's config — not against malware running as you, or an unencrypted stolen disk. That is the same posture as the gcloud and aws CLIs.

Deleting the file does not cancel the access you granted. Webex has no public revoke endpoint, so to fully revoke:

mcp-webexcalling logout    # removes the local file and names the app to revoke

then delete (or regenerate the secret of) that integration at developer.webex.com/my-apps, or have an administrator remove its authorization in Control Hub under Management → Apps. Do this whenever a laptop is lost or someone leaves the team.

Precedence

The token is resolved in this order:

  1. WEBEX_ACCESS_TOKEN in the environment — a client's env block, or your shell. Always wins, which keeps CI and scripted use predictable.
  2. The credential store written by setup — the only source that renews itself.
  3. A .env file — one in the working directory, else one beside the package (which only exists for an editable clone install).

Because the environment wins, a stale token left in a client's env block will silently mask a working sign-in. doctor checks for exactly that and says so.

Reliability & robustness

Each of these is implemented in mcp_webexcalling/webex_client.py / config.py and covered by tests:

  • Self-renewing sign-in — with credentials from setup, the server refreshes its own access token before it expires and again if a call is rejected, so it keeps working without anyone re-authorizing it. Refreshes are serialised within the process and across processes with a lockfile, so several MCP clients sharing one sign-in cannot clobber each other's rotated token. A refresh that genuinely fails is recorded, so subsequent calls fail fast with an actionable message instead of hammering the token endpoint.
  • Flexible configuration — credentials come from the environment, the credential store, or a .env file, in that order. The env block in claude_desktop_config.json works exactly as documented.
  • Automatic retries — transient failures (HTTP 429, 5xx, network/timeout errors) are retried with exponential backoff and jitter, honoring the Retry-After header. Configurable via WEBEX_MAX_RETRIES / WEBEX_RETRY_BACKOFF.
  • Connection pooling — one HTTP client is reused across all requests.
  • Automatic pagination — list operations follow Webex Link headers, so max_results above the API's per-page cap returns the full set (max_results=0 fetches everything available).
  • Concurrency-safe — per-request base URLs (used for the analytics/CDR host) never mutate shared client state, so parallel tool calls don't interfere.
  • Actionable errors — API errors carry their HTTP status and explanation; missing or wrongly-typed tool arguments are rejected with a clear message before any API call; test_connection diagnoses auth/permission problems.
  • Structured logging — diagnostics go to stderr (configurable with WEBEX_LOG_LEVEL) and never corrupt the MCP stdio protocol on stdout.

Using the client library directly

The underlying async client is usable on its own. This snippet is runnable as written (see examples/basic_usage.py for a fuller, guarded version — it calls the real Webex API, so it needs a real token):

import asyncio

from mcp_webexcalling.webex_client import WebexClient


async def main() -> None:
    async with WebexClient(access_token="your_token") as client:
        # Who am I / does the token work?
        status = await client.test_connection()
        print(status)

        # List call queues
        queues = await client.list_call_queues()
        print(f"{len(queues)} call queues")

        # Call statistics — the CDR feed only accepts windows between
        # 48 hours and 5 minutes ago; omitted bounds default into that window.
        stats = await client.get_call_statistics_from_cdr()
        print(stats)

        # Generate a device activation code for a user
        code = await client.generate_activation_code(person_id="person_id_here")
        print(code["code"], "expires", code["expiryTime"])


asyncio.run(main())

WebexClient is an async context manager; if you don't use async with, call await client.aclose() when done.

Development

pip install -e ".[dev]"
pytest        # full test suite
ruff check .  # lint (config in pyproject.toml)

The test suite needs no credentials and never touches the network: unit tests use httpx.MockTransport, and the end-to-end suite (tests/test_mcp_e2e.py) spawns the real server as a subprocess over stdio, pointed at a local mock Webex API, completes the MCP handshake, and invokes every one of the 90 tools — with valid, missing, and wrongly-typed arguments. The OAuth flow is exercised against a local loopback listener, and every test runs with WEBEX_CONFIG_DIR pointed at a temporary directory, so the suite can never read or write your real credentials.

CI (.github/workflows/ci.yml) runs lint + tests on Python 3.10–3.14 on Linux, plus macOS and Windows, for every pull request and for pushes to main/master (pushes to other branches don't trigger it). The extra platforms matter here: file permissions, config paths, and the loopback listener all behave differently off Linux.

To build the Claude Desktop bundle locally:

python scripts/build_bundle.py     # writes dist/mcp-webexcalling.mcpb

After adding or changing a tool, regenerate the tool reference:

python scripts/generate_tool_docs.py

Contributions are welcome — open a pull request. Please keep pytest and ruff check . green, and remember that stdout is the MCP protocol channel: any diagnostics must go to stderr.

GitHub Actions: Webex push notifications

Separately from the MCP server, .github/workflows/webex-notifications.yml posts a message to a Webex space when commits land on main. This is the one place a bot token is the right tool. Setup: create a bot at developer.webex.com (My Apps → Bots), add the bot to the target space, then add repository secrets WEBEX_BOT_TOKEN and WEBEX_ROOM_ID.

License

MIT — see LICENSE.

Metadata

Release files for mcp-webexcalling 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-webexcalling 0.2.1
File Size Uploaded
mcp_webexcalling-0.2.1.tar.gz 99.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-webexcalling 0.2.1
File Interpreter ABI Platform
mcp_webexcalling-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 172.0 kB

Release files / mcp_webexcalling-0.2.1.tar.gz

Download URL mcp_webexcalling-0.2.1.tar.gz
Size 99.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3b861ecb7bd665871858358c2a321dff7f3a913826f2f731d4763ca02e85b090
BLAKE2b-256 checksum
How to use checksums
4bd68a00ace27a3662c7d0ac50a29ed75eccdd5d9f8074b8d5dc6cba28908e0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.

Transparency log

Release files / mcp_webexcalling-0.2.1-py3-none-any.whl

Download URL mcp_webexcalling-0.2.1-py3-none-any.whl
Size 72.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5cd294f035266a02dabe11cfd916ffdc7a7549d1dd81efbd28f83cfd08b9d77f
BLAKE2b-256 checksum
How to use checksums
094b205654e075b14f51fc7c5e690772057a17dda782e2a5e349fcf1e27d77be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page