Skip to main content
Glama
galkremer1
by galkremer1
README.md
[![M8ven Score](https://m8ven.ai/badge/mcp/galkremer1-cnv-lab-mcp-6vag9f)](https://m8ven.ai/mcp/galkremer1-cnv-lab-mcp-6vag9f)
# cnv-lab-mcp

MCP server for reserving lab hardware in GLPI, triggering CNV cluster deployments in Jenkins, and importing ACM spoke clusters. No instance URLs, hardware names, or tokens are hardcoded — everything comes from tool arguments or environment variables.

## Add to Cursor

[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=cnv-lab-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImdpdGh1YjpnYWxrcmVtZXIxL2Nudi1sYWItbWNwIl0sImVudiI6eyJHTFBJX0JBU0VfVVJMIjoiIiwiR0xQSV9VU0VSX1RPS0VOIjoiIiwiSkVOS0lOU19CQVNFX1VSTCI6IiIsIkpFTktJTlNfVVNFUiI6IiIsIkpFTktJTlNfQVBJX1RPS0VOIjoiIn19)

## Prerequisites

- Node.js >= 20
- A GLPI personal API token (`My Settings` → `API tokens`). Optional `GLPI_APP_TOKEN` if the instance requires one.
- A Jenkins API token (`User` → `Configure` → `API Token`) plus the **short username**, not an email. `user@example.com` returns 401; `user` works.
- GLPI profile must have a non-zero `reservation` right on the target entity.
- Jenkins account needs Job/Build and Job/Read on jobs you will trigger.

### Internal TLS / `NODE_EXTRA_CA_CERTS`

Node's `fetch` (undici) does **not** use the OS/keychain CA store the way `curl` does. If `curl` works but the MCP server reports `fetch failed`, set `NODE_EXTRA_CA_CERTS` to your internal CA bundle and restart Cursor:

```bash
export NODE_EXTRA_CA_CERTS=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
```

TLS errors are reported separately from generic network failures so this is not mysterious.

## Configure

Copy [`.env.example`](.env.example) and set:

| Variable | Required | Notes |
| --- | --- | --- |
| `GLPI_BASE_URL` | yes | With or without `/apirest.php` |
| `GLPI_USER_TOKEN` | yes | Personal API token |
| `GLPI_APP_TOKEN` | no | Only if the instance requires it |
| `JENKINS_BASE_URL` | yes | Jenkins root URL |
| `JENKINS_USER` | yes | Short username (not email) |
| `JENKINS_API_TOKEN` | yes | API token |

Example `mcp.json` entry (secrets via env interpolation):

```json
{
  "mcpServers": {
    "cnv-lab-mcp": {
      "command": "npx",
      "args": ["-y", "cnv-lab-mcp"],
      "env": {
        "GLPI_BASE_URL": "https://glpi.example.internal",
        "GLPI_USER_TOKEN": "${env:GLPI_USER_TOKEN}",
        "JENKINS_BASE_URL": "https://jenkins.example.internal",
        "JENKINS_USER": "${env:JENKINS_USER}",
        "JENKINS_API_TOKEN": "${env:JENKINS_API_TOKEN}",
        "NODE_EXTRA_CA_CERTS": "${env:NODE_EXTRA_CA_CERTS}"
      }
    }
  }
}
```

Until the package is on npm, point `args` at this repo instead:

```json
"args": ["-y", "github:galkremer1/cnv-lab-mcp"]
```

## Tools

Reservation:

- `list_reservable_items` — browse GLPI reservable assets
- `find_available_cluster` — match requirements (structured + free-text comments) and rank by earliest free slot. **Does not book.**
- `check_availability` — reservations + gaps for one item
- `reserve_cluster` — book a window; always sets `users_id` from the session
- `cancel_reservation` — release a booking

Jenkins:

- `trigger_jenkins_job` — generic trigger; `start_at` becomes `delay=NNNsec`
- `get_build_status` — queue/build + console tail
- `abort_build` — stop a build

Cluster access:

- `get_cluster_info` — non-sensitive metadata and console/API URLs from `*-data.zip`
- `get_cluster_kubeconfig` — `auth/kubeconfig` + `auth/kubeadmin-password` in memory only. Cluster-admin credentials; do not echo them back in chat.

ACM:

- `import_spoke_cluster` — ManagedCluster + KlusterletAddonConfig, apply import manifests to the spoke, poll join
- `get_managed_cluster_status` — hub-side join/available conditions

Scenarios (confirm-before-booking):

- `list_scenarios`
- `plan_scenario` — propose clusters/windows/parameters, book nothing
- `execute_scenario` — reserve + trigger from a plan

Built-in scenarios live in [`src/scenarios/catalog.json`](src/scenarios/catalog.json) and can be extended by PR: `basic-bm-cnv`, `acm-hub-and-spoke`, `sriov-cnv-cluster`.

## Development

```bash
npm install
npm test
npm run lint
npm run typecheck
npm run build
```

Manual smoke test against real instances (not run in CI):

```bash
npx tsx scripts/smoke.ts
SMOKE_JOB=deploy-ocp-bare-metal-cluster-with-abi-cnv-5.0 SMOKE_CLUSTER=example-cluster npx tsx scripts/smoke.ts
```

## Security

- `.env` is gitignored. Examples use obviously fake placeholders.
- CI runs gitleaks on every push/PR.
- Client errors redact tokens and never log `Authorization` / `user_token` values.
- `get_cluster_kubeconfig` downloads the zip into a Buffer and extracts with JSZip in memory. It never writes the archive or `auth/*` to disk.
- `get_cluster_info` only reads `deploydata.json`, `metadata.json`, and `install-config.backup.yaml`.

TDQS

A3.7/5.0

Scored across 15 tools

Disambiguation4/5

Most tools have distinct resource-action targets, but a few boundaries could be sharper: check_availability and find_available_cluster both deal with availability, and get_cluster_info vs. get_cluster_kubeconfig both extract from the same deploy artifact. Descriptions clarify the difference well enough that an agent can usually choose correctly.

Naming Consistency5/5

All 15 tool names follow a consistent snake_case verb_noun convention (list, check, find, reserve, cancel, trigger, get, abort, import, plan, execute). There are no camelCase names, vague single verbs, or arbitrary suffixes.

Tool Count5/5

Fifteen tools is at the upper end of the ideal range but appropriate for the server's scope: reservation management, Jenkins build control, cluster artifact access, ACM import/monitoring, and scenario orchestration. Each tool covers a distinct step and no tool feels redundant.

Completeness3/5

The core path from finding/reserving assets, triggering and monitoring builds, extracting cluster credentials, and importing a spoke into ACM is covered. However, the lifecycle lacks cleanup operations (e.g., deleting/detaching a ManagedCluster, removing an imported spoke) and there is no dedicated way to list or look up existing reservations before canceling.

Maintenance

ActivityMaintained
ResponsivenessNo issues