@calltelemetry/ct-lab-mcp
v0.5.0
Published
Expanded Model Context Protocol (MCP) server for Call Telemetry lab, fleet, and appliance management across Proxmox VE and VMware ESXi.
Maintainers
Readme
@calltelemetry/ct-lab-mcp
Model Context Protocol (MCP) Server for Unified Call Telemetry Lab, Fleet, and Appliance Management across Proxmox VE and VMware ESXi
@calltelemetry/ct-lab-mcp is the single public MCP façade for Call Telemetry lab operations. It exposes high-level multi-hypervisor management (Proxmox VE & VMware ESXi), disposable VM workflows, and secure appliance diagnostics while delegating canonical asset identity, lease ownership, and release-line policy to the private lab-inventory CLI supplied by ct-meta.
Architecture
AI Clients (Claude Desktop / Cursor / Web / AI Agents)
│
▼
[MCP Server / Transports]
├── Stdio Transport (stdout JSON-RPC, stderr SafeLogger)
└── Express SSE Transport (/sse, /messages, /health)
│
▼
[MCP Tool Registry]
├── list_hosts (Proxmox + ESXi capacity, health & 8GB slot quotas)
├── list_inventory (Unified VM fleet inventory with Linear tags)
├── claim_slot (8GB slot leases with TTL & Linear issue tracking)
├── release_slot (Release lease + optional automated VM destruction)
├── provision_vm (Disposable VM provisioning on Proxmox/ESXi)
├── vm_action (Power lifecycle with non-bypassable safety guardrails)
└── appliance_exec (Mediated cli.sh commands over SSH:2222 with redaction)
│
┌───────────────┼───────────────┬───────────────────┐
▼ ▼ ▼ ▼
[MultiHypervisor] [Slot Engine] [Safety Guardrails] [Appliance SSH]
├── Proxmox VE ├── ProxmoxLab ├── Proxmox fixtures ├── ssh2 port 2222
│ (2x8GB) │ (2x8GB max) │ (100-104, 9000s) ├── cli.sh / db vacuum
└── VMware ESXi ├── ProxmoxMini └── ESXi fixtures ├── migrate / logs
(8x8GB) │ (4x8GB max) (CUCM, CUBE, └── Redaction Engine
└── ESXi (8x8GB) Runners, K8s)
│
▼
[Lab Credential Resolver]
├── Tier 1: Process Environment (LAB_PROXMOX_*, GOVC_*, LAB_VM_*)
├── Tier 2: Doppler REST API v3 (DOPPLER_TOKEN)
└── Tier 3: In-Memory Mock Fallback / Offline DevIn a governed ct-meta installation there is only one registered MCP server:
@calltelemetry/ct-lab-mcp. The Python lab-inventory program remains a
private canonical policy engine behind the Node façade; it is not registered as
a second public MCP. Set CT_LAB_CANONICAL_REQUIRED=1 to fail closed rather
than use the legacy Node VMID/name slot state when the bridge is unavailable.
Key Features
- Multi-Hypervisor Fleet Management: Polymorphic abstraction unifying Proxmox VE clusters (
proxmox-lab,proxmox-mini) and VMware ESXi hosts (esxi-intel-192-168-123-176). - Disposable VM Provisioning: Clone test VMs from hypervisor templates, configure static IP/VLAN networking, and bind 8GB slot leases in one atomic tool invocation.
- Global 8GB Slot Allocation Engine:
proxmox-lab: Strictly capped at 2 concurrent 8GB slots (16 GB allocated, 16 GB reserved for CUCM & core fixtures).proxmox-mini: Strictly capped at 4 concurrent 8GB slots (32 GB allocated).esxi-intel-192-168-123-176: Managed candidate slot ranking with 8 concurrent 8GB slots and datastore space checks.
- Non-Bypassable Safety Guardrails: Hard protection preventing destructive actions (
destroy,stop,reboot) against core infrastructure:- Cisco Unified Communications Manager (CUCM 14 / 15 publishers and subscribers)
- Cisco CUBE Voice Gateways / Cat8000v
- GitHub Actions CI Runners (
GH-Runner) - Lab Kubernetes Clusters (
LAB-K8S) - VM Templates (VMIDs
9000–9999or template flags)
- Remote Appliance CLI Control: Mediated execution of
cli.shcommands over SSH (port 2222) for status inspection, health diagnostics, Ecto migrations, table vacuums, and container logs. - Dynamic Secret Redaction: Real-time masking prevents passwords, Doppler API tokens, and private keys from leaking into MCP responses or stderr logs.
- Dual Transports: Clean standard I/O transport (
stdio) and Express HTTP/SSE transport (sse) with/health,/sse, and/messagesendpoints. - 3-Tier Credential Resolution:
- Tier 1: Direct Environment Variables (
LAB_PROXMOX_*,GOVC_*,LAB_VM_CREDENTIALS_*) - Tier 2: Doppler Secrets REST API v3 (
DOPPLER_TOKEN) - Tier 3: In-Memory Mock Hypervisor & Appliance Fallback (for offline test suites)
- Tier 1: Direct Environment Variables (
Installation
# Global installation
npm install -g @calltelemetry/ct-lab-mcp
# Or run directly with npx
npx @calltelemetry/ct-lab-mcpMerges to main publish automatically. If package.json version is not on npm,
that version is published, tagged, and released. If it is already on npm, CI
bumps the patch version, publishes, tags, and creates the GitHub Release.
--dry-run / docs-only work that should not ship must not land on main, or
bump the version only when the package should ship.
# Current published line
npm view @calltelemetry/ct-lab-mcp versionCLI Usage
ct-lab-mcp [options]Command-Line Options
| Flag | Description | Default | Environment Variable |
|------|-------------|---------|----------------------|
| -t, --transport <type> | Transport mode (stdio or sse) | stdio | CT_LAB_MCP_TRANSPORT |
| -p, --port <number> | HTTP port for SSE transport | 3000 | PORT |
| -H, --host <string> | Host interface to bind SSE server | 127.0.0.1 | HOST |
| -c, --config <path> | Path to optional JSON configuration file | None | None |
| --doppler-token <token> | Doppler service or personal API token | None | DOPPLER_TOKEN |
| --doppler-project <name>| Doppler project name | None | DOPPLER_PROJECT |
| --doppler-config <name> | Doppler environment / config | None | DOPPLER_CONFIG |
| --force-mock | Force in-memory mock hypervisor & appliance mode | false | CT_LAB_MOCK_MODE |
| --redact-secrets | Enforce strict secret redaction on all logs | true | None |
| preflight --asset-id <id> --linear-issue <key> | Validate canonical inventory, exact VM identity, and release-line qualification before guest commands | n/a | CT_LAB_CANONICAL_CLI, LAB_INVENTORY_FILE, CT_LAB_CANONICAL_REQUIRED |
| -v, --version | Output the current version | None | None |
| -h, --help | Display CLI help text | None | None |
Examples
# Run over stdio (default for Claude Desktop and Cursor)
ct-lab-mcp
# Run SSE server on port 3001
ct-lab-mcp --transport sse --port 3001 --host 0.0.0.0
# Run with Doppler credentials
ct-lab-mcp --doppler-token "dp.st.prd.xxx" --doppler-project "calltelemetry-lab" --doppler-config "prd"
# Offline mock mode for development / unit testing
ct-lab-mcp --force-mock
# Governed fixture qualification (the release is cut by ct-release; the lab
# only qualifies the exact fixture against that release)
ct-lab-mcp preflight --asset-id proxmox-vm-uat-01 --linear-issue UAT-1202Canonical lab mode
ct-meta starts the public MCP with these environment variables:
CT_LAB_CANONICAL_CLI=/path/to/lab-inventory
CT_LAB_CANONICAL_REQUIRED=1In this mode lab_preflight, inventory reads, claims, releases, reclaim,
provisioning, and VM lifecycle calls execute through the canonical CLI. The
Node façade never creates a second lease store and never treats a VMID, name,
IP address, Linear key, or operation ID as ownership. Mutation calls require
the canonical asset identity, exact operation inputs, and the CLI's dry-run /
apply receipt workflow. Legacy guest, storage, snapshot, CUBE, and appliance
mutations that do not yet have an identity-bound bridge still return
CANONICAL_OPERATION_REQUIRES_BRIDGE.
Claiming policy is intentionally conservative: prefer a fresh unclaimed stopped candidate or a new disposable VM; reuse an active running VM only when the current run proves the exact lease, coordinator ref, and live managed metadata. Reclaim is permitted only for the oldest eligible expired claim when all eligible capacity is full. Protected and unknown assets are never reclaimed. A disposable claim that failed authentication may be destroyed and recreated only after preserving the failure receipt.
Canonical mutation parameters are deliberately explicit:
claim_slotandprovision_vmrequire an exactassetId, activelinearIssue, and uniqueoperationId; apply also requires matchingconfirmAssetId.release_slotrequires the exactleaseIdand anartifactReceipt.purge_slotsdelegates only toclaims gc; age, force, orphan, and VM-name shortcuts are rejected.list_expired_vmsclassifies live VMs as legal, expiring, expired, dead leftover, or protected. It never deletes; terminal Linear-ticket VMs are marked for cleanup.cleanup_expired_vmsdeletes delete-ready disposable VMs and purges disks by default, including VMs bound to Done/cancelled Linear issues. A running terminal-ticket guest receives an exact-identity graceful shutdown before deletion. PassdryRun: trueto classify only, ordeleteTerminalIssues: falseto exclude the terminal-ticket policy. Protected fixtures and non-terminal running guests are never candidates.vm_renewandvm_transferpreserve the exact asset and lease identity. Running-guest renewal and expired-running transfer are accepted only when the canonical CLI proves their narrow recovery conditions.vm_actionmaps only to canonical asset-bound status, guest reboot, power, and evidence-first delete operations.
MCP Tools Reference
The server registers 7 comprehensive tools conforming to the MCP JSON Schema specification:
1. list_hosts
Query capacity, health, CPU/RAM/Disk metrics, and 8GB slot limits across Proxmox VE (proxmox-lab, proxmox-mini) and VMware ESXi (esxi-intel-192-168-123-176).
{
"host": "all", // "proxmox-lab" | "proxmox-mini" | "esxi-intel-192-168-123-176" | "all"
"hypervisorType": "all", // "proxmox" | "esxi" | "all"
"refresh": false // boolean: force live hypervisor API poll
}2. list_inventory
Query unified fleet inventory across Proxmox VE and VMware ESXi VMs with power status, IP addresses, VLAN, protected flags, release version, and Linear lease tags.
{
"hypervisor": "all", // "proxmox" | "esxi" | "all"
"host": "proxmox-mini", // optional host filter
"status": "all", // "all" | "running" | "stopped" | "claimed" | "disposable"
"includeTemplates": false, // boolean (default: false)
"filter": "0.8.7-stable", // text search across name, ID, IP, Linear issue, tags
"tags": ["uat", "disposable"]
}3. claim_slot
Lease an 8GB appliance slot on a specified or least-loaded auto-selected host with lease TTL and Linear issue tracking.
{
"tag": "CTUAT-693", // required string: issue tag or session identifier
"linearIssue": "CTUAT-693", // Linear issue key
"host": "auto", // "proxmox-lab" | "proxmox-mini" | "esxi-intel-192-168-123-176" | "auto"
"hypervisorType": "any", // "proxmox" | "esxi" | "any"
"durationMinutes": 60, // integer (1 - 1440 minutes / 24 hours, default: 60)
"purpose": "UAT Testing" // optional description
}Enforces strict capacity quotas: Attempting to claim a 3rd slot on proxmox-lab or a 5th slot on proxmox-mini is strictly rejected.
4. release_slot
Release an active 8GB slot claim by claimId, tag, requester, or linearIssue, with optional automatic destruction of attached test VMs.
{
"claimId": "claim-a1b2c3d4", // optional string: specific claim ID
"linearIssue": "CTUAT-693", // optional string: release all claims matching Linear issue
"destroyVm": true // boolean: automatically destroy attached test VM
}5. provision_vm
Provision a new disposable Call Telemetry appliance VM on Proxmox VE or VMware ESXi from a base template with IP/VLAN networking and auto-attached 8GB slot lease.
{
"sourceVmIdOrName": 9000, // template VMID or template name
"newVmName": "ct-appliance-uat-105", // name for the new VM
"targetHost": "proxmox-mini", // optional host (auto-placed if omitted)
"vlan": 124, // optional VLAN tag
"ipAddress": "192.168.124.105", // optional static IP
"linearIssue": "CTUAT-693", // Linear issue tag
"startOnDeploy": true // power on immediately after cloning
}6. vm_action
Execute power lifecycle operations across Proxmox and ESXi with non-bypassable guardrails protecting core infrastructure.
{
"vmIdOrName": "ct-appliance-uat-105", // VMID or VM name/path
"host": "proxmox-mini", // target host name
"action": "start", // "start" | "stop" | "reboot" | "shutdown" | "reset" | "status" | "destroy"
"force": false // boolean: force immediate action (guardrails cannot be bypassed)
}Destructive actions on protected VMs (CUCM, CUBE, base templates, K8s) are strictly denied with PROTECTED_VM_ACTION_DENIED.
7. appliance_exec
Execute mediated Call Telemetry appliance CLI operations over secure SSH on Port 2222 with output sanitization and secret redaction.
{
"targetIp": "192.168.124.105", // appliance IP or hostname
"port": 2222, // SSH port (default: 2222)
"action": "db_vacuum", // "status" | "health" | "diag" | "db_vacuum" | "migrate" | "docker" | "logs" | "version"
"args": {
"table": "cdr_records",
"vacuumFull": true,
"vacuumAnalyze": true
},
"timeoutMs": 30000
}Client Configuration
Claude Desktop Configuration
Add the server to your claude_desktop_config.json:
{
"mcpServers": {
"calltelemetry-lab": {
"command": "npx",
"args": ["-y", "@calltelemetry/ct-lab-mcp"],
"env": {
"DOPPLER_TOKEN": "dp.st.prd.your_token_here",
"DOPPLER_PROJECT": "calltelemetry-lab",
"DOPPLER_CONFIG": "prd"
}
}
}
}Cursor Configuration
Add to .cursor/mcp.json or Cursor Settings > MCP:
{
"mcpServers": {
"calltelemetry-lab": {
"command": "ct-lab-mcp",
"args": ["--transport", "stdio"],
"env": {
"DOPPLER_TOKEN": "dp.st.prd.your_token_here"
}
}
}
}Programmatic TypeScript Usage
import { createMcpServer, startStdioServer, SlotManager, MultiHypervisorManager } from "@calltelemetry/ct-lab-mcp";
const server = createMcpServer({
name: "custom-lab-mcp",
version: "1.0.0",
});
await startStdioServer(server);Development & Verification
# Typecheck TypeScript source
npm run typecheck
# Run full Vitest test suite
npm test
# Build production distribution to dist/
npm run build
# Verify npm bundle package
npm pack --dry-runLicense
MIT © 2026 Call Telemetry / Contributors
