# mcaxl Read-only MCP server for **Cisco Unified Communications Manager (CUCM)** — exposes the AXL SOAP API and RisPort70 real-time registration state to LLMs for dial-plan analysis, configuration auditing, and impact analysis. > Tested against CUCM 15.0.1.12900. Should work on any CUCM 12.5+. ## Why this exists CUCM's admin UI is great for one-config-at-a-time work but painful for audit / discovery questions like: - *"Which translation patterns rewrite the calling party number, and why?"* - *"Which CSSs include the `Internal-PT` partition, in what order?"* - *"Show me every route pattern targeting our PSTN carrier."* - *"Are there partitions defined but unreachable from any CSS?"* - *"Which phones are configured but not currently registered?"* `mcaxl` gives an LLM SQL access to CUCM's Informix data dictionary, schema-aware joins for common audit questions, and RisPort70 cross-reference for live registration state. Then a set of curated prompts orchestrates the tools toward audit *findings*, not just data. ## Read-only by structural guarantee The server **never registers** AXL write methods. There is no `executeSQLUpdate`, no `add*` / `update*` / `remove*` / `apply*` / `reset*` / `restart*` tool. Read-only is enforced by *absence* of write operations, not by runtime sanitization. Defense-in-depth: SQL queries are also client-side validated to begin with `SELECT` or `WITH`. For operations that require write access (service control, packet capture, log download, perfmon, etc.), install [`@calltelemetry/cisco-cucm-mcp`](https://github.com/calltelemetry/cisco-cucm-mcp) alongside this server. The two are complementary — `mcaxl` answers "what does the config say?", `cisco-cucm-mcp` answers "what's happening right now?". ## Install ```bash # Run directly from PyPI: uvx mcaxl # Or as a pinned dev install: pip install mcaxl # Or via Claude Code's MCP registry: claude mcp add cucm-axl -- uvx mcaxl ``` ## Configure Set these env vars (most operators use a `.env` file in the working directory): ```env AXL_URL=https://your-cucm-pub:8443/axl/ AXL_USER=your-axl-service-account AXL_PASS=your-password # Optional: AXL_VERIFY_TLS=false # CUCM ships self-signed certs; default off AXL_CACHE_TTL=3600 # response cache TTL in seconds; 0 disables AXL_RATE_LIMIT_RETRIES=3 # 502/503/504 retry count with backoff AXL_WSDL_PATH= # explicit WSDL location override AXL_WSDL_ZIP= # explicit toolkit zip path CISCO_DOCS_INDEX_PATH= # for prompt enrichment (see Prompts section) ``` The AXL service account needs the **`Standard AXL Read Only API Access`** role at minimum. It does *not* need the full `Standard AXL API Access` role (read-write) — `mcaxl` is structurally incapable of using write permissions even if granted. ## AXL WSDL bootstrap CUCM's AXL toolkit is Cisco-licensed and not redistributable, so it's not bundled. Download from your CUCM admin UI: > Application → Plugins → Find → "Cisco AXL Toolkit" → Download Drop the resulting `axlsqltoolkit.zip` into your working directory. On first launch, the server auto-extracts `schema/15.0/` (or whichever version matches your cluster) into `~/.cache/mcaxl/wsdl/15.0/`. Alternative resolution paths (in order): ```bash export AXL_WSDL_ZIP=/path/to/axlsqltoolkit.zip # explicit zip export AXL_WSDL_PATH=/path/to/schema/15.0/AXLAPI.wsdl # explicit WSDL # Or pre-populate the cache: mkdir -p ~/.cache/mcaxl/wsdl/15.0/ cp /path/to/schema/15.0/* ~/.cache/mcaxl/wsdl/15.0/ ``` ## Tool surface (19 total) ### Foundational | Tool | Purpose | |---|---| | `axl_version()` | Cluster version sanity check | | `axl_sql(query)` | Execute a SELECT against Informix data dictionary | | `axl_list_tables(pattern=None)` | Discover Informix tables | | `axl_describe_table(name)` | Column metadata for one table | | `cache_stats()`, `cache_clear(pattern=None)` | Cache plumbing | | `health()` | Subsystem self-check (cache / AXL / docs / RisPort init state) | ### Route plan | Tool | Purpose | |---|---| | `route_partitions()` | All partitions with pattern + CSS-member counts | | `route_calling_search_spaces(name=None)` | CSS list with ordered partitions | | `route_patterns(kind=None, partition=None, filter=None)` | Route Plan Report — patterns + transformations | | `route_inspect_pattern(pattern, partition=None)` | Deep dive: transforms, route filter, reachable-from CSS, full destination chain (route list → groups → gateways) | | `route_lists_and_groups(name=None)` | Route list → route group → gateway chain | | `route_translation_chain(number, css_name=None)` | Wildcard-aware pattern matcher | | `route_digit_discard_instructions()` | DDI catalog | | `route_device_pool_route_groups(device_pool_name=None)` | Local Route Group resolution | | `route_devices_using_css(css_name)` | Impact analysis across 71 known fk-CSS columns | | `route_filters(name=None, include_members=False)` | Route filter clauses + member rules | ### Real-time device registration (RisPort70) | Tool | Purpose | |---|---| | `device_registration_status(device_class, status, name_filter, page_size)` | Page through CUCM's RisPort `selectCmDevice` for live registration state | | `device_registration_summary()` | Cluster-wide breakdown across Phone, Gateway, SIPTrunk, HuntList, etc. | ## Prompts (10 total) Each prompt orchestrates multiple tool calls toward a specific audit narrative. They appear in Claude Code's slash menu under `/mcp__cucm-axl__`: - `route_plan_overview` — fresh audit conversation seed - `investigate_pattern(pattern, partition=None)` — single-pattern deep dive - `audit_routing(focus="full")` — comprehensive walkthrough with checklist - `cucm_sql_help(question)` — catch-all SQL helper - `sip_trunk_report(name_filter=None)` — SIP trunk inventory + findings - `phone_inventory_report(filter=None)` — phone fleet aggregates with anomaly findings (cross-references RisPort) - `user_audit(focus="full")` — end users + app users + role assignments - `inbound_did_audit()` — XFORM-Inbound-DNIS inventory + screening pipeline - `hunt_pilot_audit()` — hunt pilots, queue settings, line group membership - `whoami(userid=None)` — single-user role chain (defaults to AXL service account) ### Optional: schema-grounded prompt enrichment Set `CISCO_DOCS_INDEX_PATH` to a directory containing `chunks.jsonl` and `index_meta.json` (produced by the [`mcp-cisco-docs`](https://github.com/...) indexer or any compatible embedding pipeline) to have prompts pull relevant Cisco documentation chunks inline. Without this, prompts gracefully degrade to a fallback notice instructing the LLM to use the sibling cisco-docs server's `search_docs` tool. ## Cache Responses are cached in SQLite at `~/.cache/mcaxl/responses/axl_responses.sqlite`. The cache is **cluster-isolated** by SHA-256 of `AXL_URL` — pointing the server at a different cluster never serves stale data from a previous one. Cache survives restarts. Clear with `cache_clear()` after a known config change. ## Caveats - `route_translation_chain` evaluates CUCM wildcards (`X`, `!`, `[0-9]`, `@`, `\+`) but does *not* model route-filter constraints on `@` patterns. Use as guidance, not authoritative. - The package's `recordingprofile` / `usageprofile` / `vipre164transformation` reference categories were schema-verified against CUCM 15. If a future CUCM version adds new `fkcallingsearchspace_*` columns, `route_devices_using_css`'s coverage will lag until the package is updated. The `test_complete_schema_coverage_against_known_columns` test enforces the current snapshot — failing red surfaces the drift loudly. ## License MIT. See `LICENSE`. ## Source - Repo: [git.supported.systems/mcp/mcaxl](https://git.supported.systems/mcp/mcaxl) - Issues: [git.supported.systems/mcp/mcaxl/issues](https://git.supported.systems/mcp/mcaxl/issues) - Changelog: [`CHANGELOG.md`](./CHANGELOG.md)