2026.04.27.1: same-day post-release PII scrub

The original 2026.04.27 was published-then-deleted from PyPI within
hours after a stricter audit (against the unpacked sdist, not just
curated source paths) found cluster-fingerprint content that the
pre-publish grep had missed. This release supersedes the deleted one;
no functional differences.

Issues found in 2026.04.27 that this fixes:

1. docs/query-patterns/sip-trunk-report.md — "Live result snapshot"
   section (38 lines) contained the live cluster's actual SIP trunk
   inventory: real hostnames (exp-c-p.binghammemorial.org), real
   internal IPs (172.20.6.99, .104, .105, .114, .120, .222, plus
   172.20.2.22, 172.20.14.105, 172.24.10.10), real trunk-name +
   description rows. Section removed entirely. The query-pattern doc
   itself still ships — schema/SQL guidance is generic and useful.
   One inline FQDN example (`exp-c-p.binghammemorial.org`) replaced
   with `exp-c-p.example.com`. Status line that named the specific
   maintenance release (`Validated against CUCM 15.0.1.12900-234 on
   2026-04-25.`) genericized to `Validated against CUCM 15.`

2. .mcp.json shipping in sdist with `/home/rpm/bingham/axl` as the
   `--directory` argument. Local filesystem path = hostname leak.
   Added to `[tool.hatch.build.targets.sdist] exclude`. File stays
   in the source repo for development; no longer ships.

3. pyproject.toml comment about the audit workflow ironically
   contained the literal word "bingham" as the example grep token.
   Rewritten to use "site-specific tokens" generically.

Audit verification (against the unpacked sdist this time):
  tar -xzf dist/mcaxl-2026.4.27.1.tar.gz -C /tmp/sdist-inspect
  grep -rnEi 'bingham|binghammemorial|10\.[0-9]+\.[0-9]+\.[0-9]+|
              172\.(1[6-9]|2[0-9]|3[01])\.[0-9]+\.[0-9]+|
              192\.168\.[0-9]+\.[0-9]+|SupportedSystems|CCX-AXL|
              CER-AXL|CUC-AXL|TabSync|variphy|15\.0\.1\.12900|
              production cluster|/home/rpm|cucm-pub\.bingham'
       /tmp/sdist-inspect/
  → returns empty (verified)

Tests still 155/155.

Lesson encoded for next time: the pre-publish audit MUST run against
the unpacked sdist, not just the four explicitly-named paths in the
python.md rule (src/, tests/, README.md, pyproject.toml, .env.example).
The sdist also pulls in docs/, top-level dotfiles, and uv.lock.
CHANGELOG.md spells this out in the post-release note for next time.
This commit is contained in:
Ryan Malloy 2026-04-27 13:07:38 -06:00
parent 87d697f461
commit 0691ba8c46
3 changed files with 46 additions and 45 deletions

View File

@ -5,7 +5,37 @@ encode the date the package was tested against the upstream Cisco APIs
and published. Format: `YYYY.MM.DD` with optional `.N` post-release and published. Format: `YYYY.MM.DD` with optional `.N` post-release
suffix for same-day fixes. suffix for same-day fixes.
## 2026.04.27 — initial public release ## 2026.04.27.1 — same-day PII scrub
Post-release fix per the python.md immutability rule. The original
`2026.04.27` was published-then-deleted from PyPI after a stricter
audit found cluster-fingerprint content in the shipped sdist that the
pre-publish audit had missed:
- `docs/query-patterns/sip-trunk-report.md` contained a "Live result
snapshot" section with the test cluster's actual SIP trunk inventory
(real hostnames, real internal IPs). The query-pattern doc itself
still ships; the snapshot section has been removed and one inline
FQDN example replaced with `.example.com`.
- `.mcp.json` was shipping in the sdist with a local filesystem path.
Added to `[tool.hatch.build.targets.sdist] exclude`. The file
remains in the source repo for development; it no longer ships.
Lesson encoded for next time: **the pre-publish PII audit must run
against the unpacked sdist, not just curated source paths.** The
sdist also pulls in `docs/`, top-level dotfiles like `.mcp.json`, and
`uv.lock`. The python.md rule's example grep covers fewer paths than
the actual sdist blast surface; the durable fix is `tar -xzf
dist/*.tar.gz -C /tmp/inspect && grep -rnE 'site-token' /tmp/inspect`
before every publish.
No functional changes between 2026.04.27 and 2026.04.27.1 — just
metadata and shipped-doc content.
## 2026.04.27 — initial public release (deleted)
> Yanked-and-deleted from PyPI on the publish date due to a sdist
> PII leak. Superseded by `2026.04.27.1`. Retained here as a marker.
First public release on PyPI as `mcaxl`. Renamed from the internal First public release on PyPI as `mcaxl`. Renamed from the internal
working name `mcp-cucm-axl` to fit the operator's `mc<interface>` working name `mcp-cucm-axl` to fit the operator's `mc<interface>`

View File

@ -6,7 +6,7 @@ route-group/route-list membership. Useful for handoff documentation,
post-migration cleanup, and identifying single-points-of-failure on post-migration cleanup, and identifying single-points-of-failure on
specific trunks. specific trunks.
**Status:** Validated against CUCM 15.0.1.12900-234 on 2026-04-25. **Status:** Validated against CUCM 15.
Empty `prompts/` directory at `src/mcaxl/prompts/` is the Empty `prompts/` directory at `src/mcaxl/prompts/` is the
intended home for extracting this into a `@mcp.prompt` function. For intended home for extracting this into a `@mcp.prompt` function. For
now, prompts live inline in `server.py` (see `route_plan_overview`, now, prompts live inline in `server.py` (see `route_plan_overview`,
@ -111,7 +111,7 @@ ORDER BY d.name, std.sortorder;
**Notes:** **Notes:**
- `address` is `VARCHAR(255)` — IP literal *or* DNS name. Expressway-C - `address` is `VARCHAR(255)` — IP literal *or* DNS name. Expressway-C
trunks often use FQDNs (e.g., `exp-c-p.binghammemorial.org`) so SRV trunks often use FQDNs (e.g., `exp-c-p.example.com`) so SRV
resolution can shift the actual destination. resolution can shift the actual destination.
- `addressipv6` exists on the same table but is empty on most clusters. - `addressipv6` exists on the same table but is empty on most clusters.
- `port` is `INTEGER` — defaults to 5060 (SIP over UDP/TCP) or 5061 (TLS), - `port` is `INTEGER` — defaults to 5060 (SIP over UDP/TCP) or 5061 (TLS),
@ -203,43 +203,6 @@ LLM to surface:
--- ---
## Live result snapshot (Bingham, 2026-04-25)
11 SIP trunks. All on `Main-Campus-Sub1-Pub-DP` except `exp-c-p-SIP-Trk`
(on `Pub-Sub1-DP`). All preferred codec is `711ulaw`. All destinations
on port 5060 (no TLS).
| Trunk | Destination | SIP Profile | CSS | Location |
|---|---|---|---|---|
| `Forward-Advantage-SIP-Trk` | 172.24.10.10 | FWD Advantage SIP Profile | National-CSS | Main-Campus-LOC |
| `PSTN-Router-SIP-Trk` | 172.20.6.222 | PSTN Sparklite SIP Profile | PSTN-Inbound-CSS | Hub_None |
| `RightFax-SIP-TRK` | 172.20.2.22 | RightFax SIP Profile | FAX-CSS | Hub_None |
| `Unity-Pub-SIP-TRK` | 172.20.6.104 | Unity SIP Profile | Internal-CSS | Main-Campus-LOC |
| `Unity-Sub-SIP-TRK` | 172.20.6.105 | Unity SIP Profile | Internal-CSS | Main-Campus-LOC |
| `VG450-SIP-TRK` | 172.20.6.99 | RightFax SIP Profile | National-CSS | Hub_None |
| `Verba-SIP-TRK` | 172.20.6.120 | Verba Profile | Internal-CSS | Hub_None |
| `ZetaFax-SIP-TRK` | 172.20.14.105 | ZetaFax SIP Profile | FAX-CSS | Hub_None |
| `exp-c-p-SIP-Trk` | exp-c-p.binghammemorial.org | Expressway SIP Profile | (none) | Main-Campus-LOC |
| `exp-c-s-SIP-Trk` | exp-c-s.binghammemorial.org | Expressway SIP Profile | (none) | Main-Campus-LOC |
| `singlewireFusion-SIP-TRK` | 172.20.6.114 | singlewire SIP Profile | (none) | Hub_None |
**Observations from this snapshot** (templates for what the prompt should
flag):
- `VG450-SIP-TRK` (analog voice gateway) shares the `RightFax SIP Profile`
with `RightFax-SIP-TRK`*probably intentional* (both terminate at fax
endpoints) but worth confirming with the operator.
- The 3 trunks with `calling_search_space = NULL` (Expressway-C primary,
Expressway-C secondary, singlewireFusion) all serve specific
device-only paths — they don't originate generic outbound routing. Not
a finding, but a useful invariant to call out.
- `PSTN-Router-SIP-Trk` is the only trunk with `Hub_None` location *and*
`PSTN-Inbound-CSS` *and* a stripped-down CSS — consistent with its role
as the carrier-facing trunk (and as the H1 SPOF in the
[route-plan audit](../../../docs/src/content/docs/audits/2026-04-25-cucm-route-plan.mdx)).
---
## Proposed prompt name and signature ## Proposed prompt name and signature
```python ```python

View File

@ -1,6 +1,6 @@
[project] [project]
name = "mcaxl" name = "mcaxl"
version = "2026.04.27" version = "2026.04.27.1"
description = "Read-only MCP server for Cisco Unified Communications Manager (CUCM) — AXL SOAP API + RisPort70 registration state — purpose-built for LLM-driven dial-plan and configuration auditing." description = "Read-only MCP server for Cisco Unified Communications Manager (CUCM) — AXL SOAP API + RisPort70 registration state — purpose-built for LLM-driven dial-plan and configuration auditing."
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}] authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
readme = "README.md" readme = "README.md"
@ -58,10 +58,18 @@ packages = ["src/mcaxl"]
[tool.hatch.build.targets.sdist] [tool.hatch.build.targets.sdist]
# Keep the published source distribution focused on what's needed to # Keep the published source distribution focused on what's needed to
# build / install / run. Excluded files exist for local development only. # build / install / run. Excluded files exist for local development only.
#
# IMPORTANT: this list is the last line of defense for PII leakage.
# `tests/` contains real cluster fixtures; `.mcp.json` contains a
# local filesystem path; `audits/` contains cluster-specific findings.
# Pre-publish workflow: extract the sdist to /tmp and grep for any
# site-specific tokens (your org name, internal IP ranges, hostnames)
# across the full unpacked tree — MUST return empty before publish.
exclude = [ exclude = [
"CLAUDE.md", # operator-private project context for Claude Code "CLAUDE.md", # operator-private project context for Claude Code
".env", # never ship credentials ".env", # never ship credentials
".env.local", ".env.local",
".mcp.json", # contains local filesystem path; dev-only artifact
"axlsqltoolkit.zip", # Cisco-licensed; do not redistribute "axlsqltoolkit.zip", # Cisco-licensed; do not redistribute
"audits/", # cluster-specific audit reports "audits/", # cluster-specific audit reports
"tests/", # tests live in source repo, not the sdist "tests/", # tests live in source repo, not the sdist