Read-only MCP server for Cisco Emergency Responder (CER) — unified facade over the v15su2 Configuration API and the legacy Read-Only API for E911 location and alert auditing.
Project description
mccer
Read-only MCP server for Cisco Emergency Responder (CER) — a unified facade over both of CER's administrative APIs (the v15su2 Configuration API and the older Read-Only API) for E911 location, alert, and license auditing.
Tested against CER 15.0.1. Compatible with CER 8.x+ (legacy API) and 14+ (Configuration API). Some
/v2/*Configuration endpoints require 15SU2; mccer transparently falls back to legacy equivalents on older builds.
What it looks like
Invoke the e911_coverage_audit prompt in any MCP-aware LLM client. Sample
findings the tool surfaces from a real cluster:
🔴 Smart License sync stalled.
LastSynchronizationTimeisMar 5 2025; today is over a year later.LicenseStatus: Waiting. The cluster still operates on its cached authorization, but new phones beyond the licensed count may not get tracked at the next reboot. Investigate the Smart License agent's outbound HTTPS path.🟡 CER↔CUCM CTI signaling configured plaintext.
EnableSecureConn: false. Call-setup signaling between the two systems travels unencrypted on the management network. For HIPAA-relevant deployments, plan CAPF certificate enrollment + secure CTI migration.🟡 Onsite-alert recipient email contains a typo. One named entry's
OnsiteAlertEmailis missing a single letter from the surname relative to the entry'sOnsiteAlertNamefield. 911 calls from that ERL produce no email notification to the intended recipient.🟢 Two onsite-alert entries (
Default,Operator) carry no contact. If any ERL points at these entries, 911 calls from that ERL trigger no internal notification at all. Confirm intentional vs incomplete config.
Each finding ties to specific tool calls (cer_e911_license,
cer_cucm_clusters, cer_onsite_alerts) with severity + recommended action,
not raw API output the operator has to interpret.
Scope and complement
mccer is intentionally narrow — read-only audit of CER configuration via
both of its admin APIs. It does NOT cover:
- CUCM-side configuration (dial plan, phones, registration) — use
mcaxl - CUCM live operations (log tail, CDR, RIS, packet capture) — use
mcsiphon - Write operations on CER (intentionally not exposed; see below)
The three projects compose well in the same MCP-aware LLM session for end-to-end UC audit narratives spanning ERL → phone → CUCM device → CDR.
Why this exists
CER's admin UI is great for one-config-at-a-time work but painful for the cross-cluster audit questions an E911 review actually needs to answer:
- "Which phones are registered to CUCM but have no ERL match in CER?"
- "Are CER's onsite-alert email addresses still pointing at active staff?"
- "Did the Smart License agent ever stop syncing? When?"
- "Is the CER↔CUCM CTI channel encrypted?"
- "Which switches does CER even know about, via which SNMP version?"
- "For every 911 call placed last quarter, which ELIN was sent to the PSAP?"
- "Is the audit account I created actually scoped to read-only on the API surface I think it is?"
mccer queries those answers programmatically and ships curated prompts that orchestrate the tools into ready-to-act audit findings.
Read-only by structural guarantee
The Configuration API supports full CRUD; mccer exposes only its GETs. Both
of mccer's HTTP clients implement only .get() — no .post(), .put(),
.delete(). Tools are wrappers around those reads. Defense-in-depth: even
if a future regression added a write helper, FastMCP would still need an
explicit @mcp.tool decorator to expose it.
This means the CER API service account mccer uses can be scoped to a read-only audit role on CER. mccer is structurally incapable of mutating CER state regardless of what permissions the account is granted.
Install
# Run directly from PyPI:
uvx mccer
# Or as a pinned dev install:
pip install mccer
# Or via Claude Code's MCP registry:
claude mcp add cer -- uvx mccer
Configure
# .env (in project root or anywhere on dotenv's search path)
CER_HOST=cer-pub.example.com # bare hostname → https; or full URL
CER_USERNAME=apiuser # CER local or remote-on-CUCM user
CER_PASSWORD=...
# Optional:
MCCER_VERIFY_TLS=false # self-signed certs / SSH-tunneled access
MCCER_CACHE_TTL=300 # response cache TTL in seconds (future)
CER user setup
Cisco's auth docs require the CER System Admin role for Configuration
API access. The narrower CER Audit Admin role is not sufficient on the
Configuration API — empirically (CER 15.0.1) those endpoints return
{"status":"Account is locked..."} with audit-admin alone. The legacy
Read-Only API has more permissive role gating but still uses Basic auth.
Cisco recommends a dedicated CER API account separate from the main admin so password rotations don't disrupt unrelated operations.
Reaching CER through an SSH tunnel
If your CER publisher isn't directly reachable from where mccer runs, the common pattern is an SSH local-port forward through a bastion:
ssh -fN -L 18443:cer-pub.example.com:443 your-bastion
# in .env:
CER_HOST=https://localhost:18443
MCCER_VERIFY_TLS=false # cert won't match localhost
Tool surface (19 total)
Each tool returns Cisco's actual response shape — parsed from XML to dict on
the legacy API, native JSON on the Configuration API. Empty-result responses
come back in a distinct shape (e.g. {"message":"No Manual Config Phone Record Available","TrackingID":"..."}) and tools propagate as-is rather than
normalizing.
Phone-bucket inventory (the four-bucket E911 partition)
Every CUCM-registered phone falls into exactly one of these buckets:
cer_phones_unlocated— registered but no ERL → 🔴 immediate 911 riskcer_phones_tracked— successful auto-location (switchport / IP-subnet / AP)cer_phones_nontracked— CER sees them but can't auto-locatecer_phones_manualconfig— operator-set ERL override
Alert recipients (who gets notified on 911)
cer_onsite_alerts— email recipients per ERL/zonecer_pager_alerts— pager addresses per ERL/zone
Network discovery (the auto-location chain)
cer_switchports— switchports CER probed via SNMPcer_lan_switches— LAN switches in CER's inventorycer_snmp_v2/cer_snmp_v3— SNMP credentials per targetcer_discovery_status— phone-tracking scan-cycle health
Configuration
cer_e911_license— Smart License tier + utilizationcer_cucm_clusters— CUCM clusters CER receives data from (fills a hole the Configuration API leaves:/clusteronly supports POST/PUT/DELETE, no GET)cer_call_history— retrospective 911 call log (caller / ERL / ELIN / time)
Admin user inventory
cer_list_users/cer_list_user_roles/cer_list_user_groups
Connectivity
cer_ping— verify both API surfaces are reachable + auth worksmccer_health— local introspection (no network call)
Resources (3)
URI-addressable read-only data the LLM ingests as ambient context (vs tools, which the LLM decides when to invoke):
cer://api-inventory— full 34-endpoint reference (static)cer://overview— live cluster snapshot (license + clusters + alert recipients), composing several tool callscer://erls— live ERL definitions (the location reference data every audit-flavored reasoning step needs)
Prompts (4)
Multi-turn workflow templates that orchestrate tool calls into audit-ready findings:
whoami— orientation: what mccer is, what's availablee911_coverage_audit(focus="full")— full E911 readiness audit walkthrough, ranked findings (🔴/🟡/🟢) with evidence + recommended actionsalert_recipient_review— focused stale-recipient hunt for 911 contactsadmin_account_audit— CER admin/API user staleness + role bloat review
CER version compatibility
| CER version | What works |
|---|---|
| 8.x+ | Legacy Read-Only API (most tools) |
| 14+ | Configuration API GETs (users/roles/groups, ERLs, IPSubnet) |
| 15SU1 / 15.0.1 base | Above + most /v2/* endpoints; /v2/unlocatedphones and /v2/callhistorydetails 415 → mccer falls back to legacy /unlocatedphones/info and /callhistory/info automatically |
| 15SU2+ | Full Configuration API surface including all /v2/* |
Falling back is silent — operators don't see a difference except slightly different response shapes between Config-API JSON and legacy-API XML→dict.
Caveats
- Both APIs run on the CER Publisher only. There is no Subscriber-side read failover.
- HTTPS required despite some legacy docs showing
http://— mccer hard-rejects plaintext at client construction. - No
429 Too Many Requestsstatus from CER. Failure modes under load are 500 / 503 / TCP timeout. Retry logic should backoff on those. - Caching is recommended but not yet shipped. Cisco's "no enforced rate
throttling" warning + Publisher-only deployment means client-side caching
matters operationally. A future release will add the SQLite-backed cache
pattern used by the sibling
mcaxl. - Cisco lockout policy is real. Repeated bad-auth attempts will lock the
CER user account; clear via the CER admin web UI (password reset / unlock
user). If your API password contains shell- or
.env-special characters (#,!,&,$, etc.), double-check the value reaching CER actually matches what's stored in.env— mismatch produces failed-auth attempts that trigger lockout, presenting asAccount is lockedin API responses.
Sibling projects
- mcaxl — CUCM AXL/RIS audit (dial plan, phones, registration, partition/CSS analysis)
- mcsiphon — CUCM serviceability + CDR (logs, real-time registration, control center, performance counters)
License
MIT — see LICENSE.
Source
- PyPI: https://pypi.org/project/mccer/
- Repository: https://git.supported.systems/mcp/mccer
- Issues: https://git.supported.systems/mcp/mccer/issues
- Changelog: CHANGELOG.md
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mccer-2026.5.8.1.tar.gz.
File metadata
- Download URL: mccer-2026.5.8.1.tar.gz
- Upload date:
- Size: 94.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af155eece7c2be9357cee5cc8c4778ee9df671d8849c9ac52ee2733eec86944f
|
|
| MD5 |
e8d48e53bc1c49c7230ba23271c6790c
|
|
| BLAKE2b-256 |
93f9bb6dfef5493cc8e91c32bee0249e5674b34bd91bba199a8e39c3aaf606b4
|
File details
Details for the file mccer-2026.5.8.1-py3-none-any.whl.
File metadata
- Download URL: mccer-2026.5.8.1-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65f227b3f52822719a6b45391d9b49ce8b16430dd1cf8982a7fb6e9e703da9dc
|
|
| MD5 |
face0e2b84be5ff361e126c6b9750cdd
|
|
| BLAKE2b-256 |
3a04df29fe29cdf2671fa9a23ccb2d7d795ebb40939aa29b45d1eee23f934246
|