Ryan Malloy a07f8c7291 prompts + docs: did_block_overlap, partition_summary, schema landmarks
Closes items 4-7 of cucx-docs's prompt-suggestions roadmap (see
axl/agent-threads/cucx-prompt-suggestions/ for the source thread).

did_block_overlap(block_pattern) — new prompt. LLM-orchestrated audit
that finds carveout patterns inside a DID block and surfaces silent
routing exceptions (e.g., 9498/9499 carved out of the 20878594XX block
to route to a different fax server). Composes the existing
route_patterns(filter=) tool with post-processing rather than
introducing a new tool — cucx-docs's #3 was originally pitched as a
tool, but the audit-narrative output is more naturally a prompt.

partition_summary(partition_name=None) — new prompt. "What is this
partition for?" orientation report composing route_partitions,
route_patterns, route_calling_search_spaces, and the new
route_patterns_targeting. No new SQL — this is pure orchestration.
Useful when walking into an unfamiliar cluster and seeing a partition
name like RTC-MGW-Inbound and needing to figure out its role before
touching anything.

cucm_sql_help — deepened with five schema-landmark sections that cost
real audit sessions 3-5 query attempts each to discover. Topics:
numplan↔device M:N via devicenumplanmap; non-existence of
sipdestination as a table; routelist (singular) ≠ numplan→RL;
LEFT-JOIN convention for type-decoder enum tables; CDR/CMR timestamp
localization (cluster-TZ-conditional). Also updated the docs-search
reference from "cisco-docs MCP" to "mcdewey MCP" to match yesterday's
rename.

cucm-schema-cheatsheet docs — appended a "Schema gotchas (from real
audit sessions)" section mirroring the cucm_sql_help content. Two
locations because they serve different consumers: the prompt is read
by an LLM at query time, the docs page is read by a human reviewing
the cluster offline.

Tests: registration sentinel updated to include the two new prompts
(catches the case where a new module is added without a server.py
shim — the prompt would otherwise be invisible to the LLM). Full
suite still 238 passing.

Q3 verification (CDR timestamp empirical) still pending — cluster TLS
intermittent this session. The schema-landmark text is conditional
on cluster TZ per cucx-docs's caveat, so even an unverified ship is
defensible.
2026-05-05 17:52:44 -06:00

mcaxl

PyPI Python License

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). Should work on any CUCM 12.5+.

What it looks like

Invoke the whoami prompt in any MCP-aware LLM client. With no arguments, it defaults to the AXL service account from your .env:

Account: axl-readonly (applicationuser) Access control groups: 1 — Read-Only-AXL Effective roles (5):

  • Standard CCM Admin Users ← write access
  • Standard AXL API Access ← full read-write AXL
  • Standard AXL Read Only API Access
  • Standard Packet Sniffing
  • Standard RealtimeAndTraceCollection

Finding: the group Read-Only-AXL contains two write-capable roles. The name implies read-only intent but the membership grants full administrative write access. Consider renaming the group OR removing the write-capable roles from its membership.

One tool call. One SQL join across four tables (applicationuser → applicationuserdirgroupmap → dirgroup → functionroledirgroupmap → functionrole). One audit finding with severity and remediation — not a raw query result the operator has to interpret on their own. That's the shape of every prompt mcaxl ships with: orchestrated queries, structured findings, ready-to-act recommendations.

Scope and complement

mcaxl is intentionally narrow — read-only audit of CUCM configuration via AXL, with RisPort70 cross-reference for live registration state. It does not cover operational debugging: log collection, packet capture, perfmon counters, service control.

For those, install @calltelemetry/cisco-cucm-mcp alongside this server:

claude mcp add cucm-ops -- npx -y @calltelemetry/cisco-cucm-mcp@latest

The two are designed to compose. mcaxl answers "what does the config say?"; cisco-cucm-mcp answers "what's happening right now?". An LLM session with both connected can produce compound findings like "audit found CSS X has 0 references AND RisPort confirms zero phones currently registered against any device pool that inherits it → confirmed safe to delete."

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.

This means the AXL service account mcaxl uses can be granted only the Standard AXL Read Only API Access role. Even if it had write roles attached (and operators sometimes do this for convenience), mcaxl is structurally incapable of using them.

Install

# 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):

AXL_URL=https://cucm-pub.example.com: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):

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__<name>:

  • 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 mcdewey 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 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

Description
Read-only MCP server for Cisco CUCM via AXL + RisPort70 — built for LLM-driven dial-plan and configuration auditing.
Readme MIT 867 KiB
Languages
Python 100%