-
Notifications
You must be signed in to change notification settings - Fork 0
mcp server
A local stdio MCP server that exposes a KVM device to MCP-capable agents
(Claude Desktop, Claude Code, other agent hosts). It is a separate component
from the stdlib-only core library — it depends on the
mcp SDK (mcp>=1.10, see
requirements.txt).
⚠️ Experimental alpha. The core library is largely unverified — mostly unit-tested against mocks and emulators, with only a handful of device+capability combos exercised on real hardware (see the Hardware-Compatibility list for what actually has; issue #7). Treat every result as unverified, and strongly consider running with dry-run enabled (below).
| Tool | Annotations | What it does |
|---|---|---|
info |
readOnlyHint |
Device / system info |
healthcheck |
readOnlyHint |
Preflight audit of the KVM itself — readiness/recovery, security posture, firmware currency (issue #80). Run this first, on connecting to any device, before trusting it for real work; a CRITICAL (e.g. no out-of-band recovery path) should gate any subsequent destructive op |
capabilities |
readOnlyHint |
Which capabilities the driver supports — structural/offline (no network, no preflight). Answers "which tools/actions can this device serve?" so you can pick the right interface up front |
power_state |
readOnlyHint |
powered_on plus ATX detail where the driver has it |
logs |
readOnlyHint |
Device/host event log as text (seek = seconds of lookback). The text diagnostic when video/streamer/power looks wrong — it names a fault (e.g. a stuck encoder behind a snapshot 503) a screenshot can't |
snapshot |
readOnlyHint |
Current screen, returned as a real JPEG image content block the model can see |
classify_screen |
readOnlyHint |
Boot/run phase via the vision backend (Anthropic or a local VLM, see below) |
power |
destructiveHint |
on / off / off-hard / reset — disabled unless the operator opts in
|
Every tool result names the host and driver it acted on, and read-only tools always run with a deny-all confirm callback, so a bug in a read path can never trip a destructive operation. Drivers are built per call and closed afterwards — important for Redfish BMCs, which cap concurrent sessions device-side (a leaked session can lock operators out of the BMC).
| Driver kind | info |
healthcheck |
capabilities |
power_state |
logs |
snapshot |
classify_screen |
power |
|---|---|---|---|---|---|---|---|---|
pikvm / glkvm / blikvm
|
✅ | ✅ | ✅ | ✅ (with ATX detail) | ✅ | ✅ | ✅ | ✅ |
redfish (iDRAC, iLO, XCC, OpenBMC, …) |
✅ | ✅ (checks it can't serve are omitted) | ✅ | ✅ | ✅ | ❌ no video capability | ❌ no video capability | ✅ |
fake (in-process test double) |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
A tool the active driver cannot serve returns a clean MCP tool error naming
the driver kind and the missing capability (it never AttributeErrors).
The MCP surface is deliberately small. For anything outside the table, pick the right interface (the skill's Choosing an interface matrix is the full guide):
-
firmware-check/firmware-update,events,watch,type/key,mount/eject→ the CLI (kvm-pilot <cmd>); no MCP tool. - Mouse move/click, MSD mode switching → the Python library.
-
Reboot the KVM appliance / restart
kvmd→ out-of-band SSH to the appliance. No kvm-pilot interface reboots the box;poweracts on the managed host, not the appliance. -
View the screen when
snapshotfails (503, or a tiny frame while a signal is present — typically H.264 at the panel's native resolution) → the WebRTC/Janus stream or the vendor web UI.
Preflight first. On connecting to any device, call healthcheck before you
trust it for real work (issue #80) — it surfaces readiness/recovery, security,
and firmware risks up front (most importantly, whether there is any out-of-band
recovery path if the guest hangs). A CRITICAL finding should gate the
destructive power tool below. Note: the server does not yet auto-run this on
connect, so the agent must call it as the first step.
The power gate is layered; no single layer is trusted on its own:
-
Operator opt-in (the real gate). The
powertool is disabled by default. It only works when the human operator setsKVM_PILOT_MCP_ALLOW_POWER=1in the server's own environment (e.g. theenvblock of the MCP host config) before starting the server. The tool's refusal message deliberately does not spell out the variable assignment — an agent must not be able to relay the incantation; a human has to read this README and decide. -
confirm=truesecond factor. Even with the gate open,powerrequires an explicitconfirm=trueargument. This flag is model-supplied — it is not human approval and must not be treated as such. -
Client-side human approval. MCP hosts should require per-call human
approval for
power(in Claude Desktop / Claude Code, do not put this tool on an "always allow" list). ThedestructiveHint/readOnlyHintannotations are published so hosts can render a differentiated approval UI — but annotations are hints, never a security boundary. -
Dry-run. With
KVM_PILOT_MCP_DRY_RUN=1every driver is built withdry_run=True: destructive commands are logged and skipped, and every tool result says it ran in dry-run mode. Recommended default for this largely-unverified alpha. -
Untrusted screen content.
snapshotandclassify_screenfeed target-controlled console output into the agent's context. A compromised or hostile host can render text designed to steer the agent (prompt injection) — one more reason to keep layer 3 on.
Every tool accepts an optional profile argument, and a model-supplied value
takes precedence over the KVM_PILOT_PROFILE pin. ~/.config/kvm-pilot/config.toml
therefore acts as a credential store the agent can roam: any profile the
file defines can be targeted by the agent, not just the pinned one. Keep only
hosts you are willing to expose in the config file this server can read (or
point KVM_PILOT_CONFIG at a dedicated file). Pinning via
KVM_PILOT_HOST/KVM_PILOT_USER/KVM_PILOT_PASSWD env vars instead is
immune to retargeting — env values beat file-profile values. Result
provenance (host / driver in every result) makes the acted-on target
visible in transcripts.
# from the repo root
pip install -e ".[totp,ws]" # the core library
pip install -r mcp_server/requirements.txt # the MCP SDK
python mcp_server/server.py| Variable | Meaning |
|---|---|
KVM_PILOT_PROFILE |
Default profile from ~/.config/kvm-pilot/config.toml
|
KVM_PILOT_HOST / KVM_PILOT_USER / KVM_PILOT_PASSWD / … |
Direct device config (see kvm_pilot.config); beats file-profile values |
KVM_PILOT_CONFIG |
Path of the config file (default ~/.config/kvm-pilot/config.toml) |
KVM_PILOT_MCP_ALLOW_POWER |
Operator-only opt-in that enables the power tool (1/true/yes) |
KVM_PILOT_MCP_DRY_RUN |
Build every driver with dry_run=True; destructive calls are logged, never sent |
KVM_PILOT_VISION_BACKEND |
Vision backend for classify_screen: anthropic (default) or local (any OpenAI-compatible VLM: LM Studio, Ollama, vLLM) |
KVM_PILOT_VISION_URL |
Base URL of the local VLM (required for local) |
KVM_PILOT_VISION_MODEL |
Vision model id — required for local; optional pin for anthropic (otherwise the newest model is auto-resolved once per process) |
ANTHROPIC_API_KEY |
Required for the anthropic vision backend |
Add to your MCP config (claude_desktop_config.json). Dry-run is enabled here
on purpose — remove it only once you accept the alpha status, and add
KVM_PILOT_MCP_ALLOW_POWER only if you want the power tool live:
{
"mcpServers": {
"kvm-pilot": {
"command": "python",
"args": ["/abs/path/to/kvm-pilot/mcp_server/server.py"],
"env": {
"KVM_PILOT_PROFILE": "homelab",
"KVM_PILOT_MCP_DRY_RUN": "1",
"ANTHROPIC_API_KEY": "sk-..."
}
}
}
}Keep per-call approval enabled for power in the host UI.
A single-file .mcpb bundle (zero-toolchain install) is tracked in issue #7.
- Home
- Getting started
- Architecture
- CLI reference
- Configuration
- Design decisions
- Reflexes (RFC)
- Redfish reference
- Firmware registry
- Remote firmware update
- Claude skill
- MCP server
- Contributing
- Security policy
- Analysis: 2026-07-01 deep review
- Analysis: 2026-07-03 RM1PE firmware + encoder
- Hardware compatibility