ct-release-mcp
Governed, read-first release planning for Call Telemetry agents and operators.
@calltelemetry/ct-release-mcp exposes the same commands as stdio MCP tools,
federated by ct-mcp, and as a structured CLI:
get_release_stateget_stable_nightlyget_release_trainlist_versionsplan_releaseshow_planvalidate_planget_appliance_build
The package does not dispatch Unified Release and cannot create tags, releases,
images, bundles, or channel markers. plan_release is the one mutating command:
it dispatches only the side-effect-free Release Plan workflow. Candidate
identities come only from a successful calltelemetry/ct-release Release Plan
workflow artifact.
Install
Run the pinned package with no arguments as a stdio MCP server:
{
"command": "npx",
"args": ["-y", "@calltelemetry/[email protected]"]
}
Run any governed command directly through the same pinned package:
npx -y @calltelemetry/[email protected] get_release_state \
--channel stable \
--line 0.8.7
npx -y @calltelemetry/[email protected] plan_release \
--target-branch 0.8.7-stable
Use --input-json to pass the exact MCP input object without translating field
names:
npx -y @calltelemetry/[email protected] list_versions \
--input-json '{"line":"0.8.7","limit":20,"includePrereleases":true}'
Node.js 22 or newer is required.
Package release workflow recovery
The docker.yml workflow runs only for pushes to main. After registry login
but before any versioned-image push, it checks the exact package-version git
tag and both GHCR versioned image aliases (vX.Y.Z and X.Y.Z). An existing
release is accepted only when both aliases share one digest and both platform
images identify the tagged commit as their source. If verified, it skips the
build and npm dispatch. A push to a git tag does not rebuild or overwrite that image.
Image builds for this repository are serialized, including workflow reruns.
Pending main pushes use GitHub's expanded concurrency queue, which admits up
to 100 waiting jobs; overflow is canceled and must be investigated. Since
GitHub does not guarantee dispatch order, an older versioned build does not
move latest when main has a different package version.
If the git tag or either image alias exists without the complete set, the
digests or source commit differ, or any lookup fails, the workflow stops
before pushing an image. Investigate the exact tag, image, and failed run
before recovery; do not rerun a build against an existing image or
move an existing tag. A package that was already published should not be
published again. The hosted MCP is the Cloudflare Worker that ci.yml
deploys from main.
Authentication
The server resolves GitHub authentication in this order:
- An explicit token supplied by an embedding application
GH_TOKENGITHUB_TOKEN- The active
gh auth token
The credential must be able to read the governed Call Telemetry repositories,
release records, tags, workflow runs, and artifacts. plan_release also needs
permission to dispatch the calltelemetry/ct-release Release Plan workflow.
Tokens are never returned in tool results or error context.
User and PAT credentials are correlated to the workflow actor returned by
GET /user. GitHub App installation tokens cannot call that endpoint, so the
server proves installation identity through the installation-only repository
listing and requires access to calltelemetry/ct-release. It then derives the
App bot identity from the token-authenticated GraphQL viewer and binds both
the workflow actor and triggering_actor login, database ID, and node ID to
that identity. Only first-attempt runs can correlate. Optionally set
CT_RELEASE_GITHUB_APP_ACTOR to a trusted App bot login (for example,
ct-pr-agent[bot]) as an additional allowlist. The workflow ref defaults to
main and can be configured with CT_RELEASE_GITHUB_DEFAULT_BRANCH.
Installation-token dispatches remain bound to the exact App identity,
repository, workflow ref, target-branch nonce, prior-run snapshot, and bounded
dispatch time; a generic 403 is never accepted as installation identity.
Catalog source
0.3.15 reads scripts/release-storage.json from the release repository through
the existing authenticated GitHub client. Each operation resolves its catalog
URL once from the configured Cloudflare R2 release origin (https://get.calltelemetry.com), and reports
that same source. Configuration and fetch failures stop planning before branch
snapshot or dispatch. Configuration is read again for the next operation; a
failed read does not become a cached source.
The configuration read is bounded to 16 KiB. Provider, schema, bucket, region, endpoint and public origin must agree. Catalog fetch bounds, train validation, plan identity and the shared MCP/CLI command registry retain their existing contracts.
Command behavior
MCP tool names and CLI subcommands are identical. CLI field flags use the MCP
camelCase name as canonical spelling and accept a kebab-case alias, such as
--includePrereleases or --include-prereleases. Boolean flags require an
explicit true or false; integers must be safe integers. --input-json
cannot be combined with field flags. The adapter-only, valueless --pretty
flag may appear anywhere after a command and indents JSON without changing the
MCP-equivalent input or result.
get_release_statereads the public appliance catalog, the lifecycle and composition ledgers, GitHub releases, and component tags. Catalog and release records are primary authority; component tags are labeled secondary evidence. It exposes the promoted channel tip (tip_marker ?? tip), downloadablecatalogDownloadTip, the bundle-only fallback flags (catalogTipFallback/catalogTipMarkerDeployable), materialized candidate head (catalogVersionTip), and variant. A newer candidate is not promoted authority and is not drift. A sanctioned bundle-only fallback (tip_fallback: true,tip_marker_deployable: false) is also not drift: the marker stays the release pin whilecatalogDownloadTipnames the older appliance. A final channel that its lifecycle ledger explicitly marks unpublished returnschannelStatus: "not_published"instead of false catalog/owner drift. Secondary component tag lag is returned inadvisories;driftis reserved for authoritative disagreements. Secondary evidence reads use GitHub's release-line matching-ref endpoint instead of crawling every component tag page. Whentrains/<line>.yamlownsstable-nightly,get_release_statealso surfacesstableNightlywith explicit non-qualification (qualifiedForStable: false) while keepingcatalogTipstrictly bound to promoted mainline authority (nullwhenchannelisstable-nightly).get_stable_nightly(CLI alias:stable-nightly) reads the opt-inchannels/stable-nightly.jsonpointer from the R2 release origin with a 256 KiB bound, rejects duplicate JSON keys and non-integer numbers, verifies the canonical JSON SHA-256admission_id, enforcesqualified_for_stable: falseandpackage_originprovenance, verifies that the pointer line ownsstable-nightlyintrains/<line>.yaml, and evaluatesreadyForStablePromotionagainstpromotion_receipts.stable(securityanduatverification lanes). HTTP 404 returnsstatus: "uninitialized"rather than treating an unpublished nightly channel as an operational crash.get_release_trainreturns the separately versioned lifecycle and component composition documents with their exact Git SHAs.list_versionslists catalog andct-releaseversions for one release line, including clearly labeled draft identity reservations, and includes the activestable-nightlyprerelease record (qualified_for_stable: false) whenincludePrereleasesistrueon a line that ownsstable-nightly.plan_releasedispatches only the side-effect-free Release Plan workflow, then promptly returns its exact run ID and pre-dispatch authority snapshot. It also validates the requested stable branch against the lifecycle and component manifests, snapshots the exactcalltelemetry/cisco-cdrbranch SHA, and dispatches the resulting four-field release target without adding caller-facing inputs. This exact-target contract became mandatory incalltelemetry/ct-releasecommit579ff34720cf1a273201f12174b242ec926ebc39(PR #1570);v0.3.4was tagged but not published after its trusted-publisher workflow identity was rejected.@calltelemetry/[email protected]is the first published coordinated caller version, and0.3.6adds strict validation of the target persisted in current plan artifacts and dispatch inputs.0.3.7introduced direct catalog reads for package-side planning; its GCS default is retired in0.3.15. The hosted Release Plan authority must independently use the same canonical source; this package change alone does not prove a plan succeeds. Current artifacts require a candidate-bound R2release_bundleonhttps://get.calltelemetry.com; retiredgcs_bundleartifacts cannot qualify current release planning.0.3.12stages the plan-artifact transition: contract version 1 remains readable only in its legacy no-trainshape, while contract version 2 requires the governed top-leveltrain. Exact release targets may identify either the cisco-cdr/Web or ct-quasar/Vue branch-latest snapshot; the selected repository's source SHA must match that immutable snapshot.0.3.13supports rotating GitHub App installation tokens without weakening workflow-run correlation or accepting unrelated PAT authorization failures. The hosted workflow rejects a plan if that bound authority changes before artifact creation. Useshow_planafter completion to read the immutable artifact. An explicitly active, unpublished train without a public channel can plan its first release with a null catalog tip. This requires no contradictory catalog artifact or published final and preserves the exact catalog, lifecycle, and composition snapshot; it never borrows another line's channel or creates a public marker.0.3.23adds contract version 4 for authenticated Quasar image identities; versioned Quasar tags require the exact source, producer workflow, recipe, build-input, platform, candidate, and attempt projection. Legacy short-SHA snapshots remain supported, while contract version 3 stays reserved for the separate 0.8.6 fixed-snapshot shape.show_planretrieves and verifies a successful plan artifact by workflow run ID.get_appliance_buildreads onecalltelemetry/ai-workspaceBuild OVA Appliance run by id, or the newest run whose title contains the version, image format, and correlation id. It returns status, the current step, and the failed step. It does not dispatch or publish.validate_planre-reads current authority and fails closed on changed train SHAs, invalid candidate identity, dropped or changed dispatch pins, or known authority drift. For a still-unpublished train with no channel, current state reportschannel: null,channelOwner: null, andchannelStatus: "not_published". An explicitly hidden channel (customer_visible: false) with a null promoted marker may validate a newer, immutable draft-only mainline RC under a v2 or v4 plan contract. The active lifecycle train must be explicitly unpublished, and itslatest_stablemust equal the same-line published previous final. OnlyCATALOG_LEDGER_MISMATCHandCATALOG_RELEASE_MISMATCHentries with matching owner, requested line, null marker and previous-final contexts qualify; unknown visibility or incomplete/mismatched drift context fails closed. Beta promotion must be false in both the plan and dispatch inputs, and OVA dispatch must be false with no enabling override. An accepted plan returns the nonblockingDRAFT_RC_WITHOUT_STABLE_MARKERentry in its optionaladvisoriesarray; rejected plans omit it, and current state retains the missing-marker drift. This exception never authorizes a final, alias, OVA dispatch, automatic promotion, or any other authority disagreement.
Named variant RCs such as 0.8.7-i18n-rc17 remain separate from the normal
channel versions. They are sorted by their numeric RC suffix and never satisfy
normal channel ownership checks. Normal <line>-stable planning always binds
to a mainline channel even when a variant preview is the only customer-visible
channel on that line.
plan_release returns after exact run correlation rather than waiting for the
hosted workflow, keeping it inside ordinary MCP request deadlines. While the
run is queued or active, show_plan returns retryable PLAN_RUN_PENDING; a
failed workflow returns PLAN_RUN_FAILED with its run URL.
The plan schema retains the producer's readiness candidate and OCI index/amd64/arm64 digests in the authenticated receipt. Frozen plans require a complete digest set bound to a readiness candidate; legacy 0.8.6 fixed-pin plans may carry null digests and an empty candidate. Historical artifacts that omit these fields remain readable. Malformed or partial bindings and unrecognized fields remain errors.
All MCP and CLI command errors use a structured
{ error: { code, message, retryable, context } } shape. CLI command results and
errors are one-line JSON on stdout by default, or indented JSON with
--pretty. Help and version are plain text. CLI exit
codes are 0 for a successful command, 2 for CLI usage or input-schema
errors, and 1 for operational failures. A successful validate_plan call can
still return valid: false; that is the governed result when authoritative
sources disagree.
Use release-control dispatch --plan-run <run-id> only after show_plan and
validate_plan succeed. That separate dispatcher owns Unified Release. This
package deliberately stops at authoritative planning and validation.
CLI reference
ct-release-mcp get_release_state --channel <channel> --line <line>
ct-release-mcp get_stable_nightly [--line <line>]
ct-release-mcp stable-nightly [--line <line>]
ct-release-mcp get_release_train --line <line>
ct-release-mcp list_versions --line <line> [--limit <n>] [--includePrereleases true|false]
ct-release-mcp plan_release --targetBranch <branch> [--version <candidate>]
ct-release-mcp show_plan --planRunId <run-id>
ct-release-mcp validate_plan --planRunId <run-id>
ct-release-mcp get_appliance_build [--runId <run-id>] [--version <version>] [--correlationId <id>] [--imageFormat qcow2|ova]
Run ct-release-mcp --help or ct-release-mcp <command> --help for the live
reference generated from the shared command registry.
Development
npm ci
npm run validate