Skip to content

MCP interface (AI agents)

The MCP interface (Model Context Protocol) exposes read-only properties of the servers managed by LCM to AI agents. It is deliberately narrow:

  • No secrets. Passwords, login users, private/public keys, host-key fingerprints and tokens are never returned. Data goes through a curated whitelist DTO.
  • Read-only. There are no write or configuration tools.
  • Own port + mandatory authentication via an MCP API key (bearer token).
  • Fully toggleable under Settings → MCP.

Settings → MCP:

  1. Turn on Enable MCP interface. The separate listener starts immediately (no restart); turning it off stops it just as immediately. Changing the bind address/port also takes effect at runtime (the old listener is shut down cleanly).
  2. Set bind address and port (default 127.0.0.1:9330). Binding to 127.0.0.1 means local access only.
  3. Create an MCP API key (its own separate list on the same page). The plaintext is shown once.

An MCP API key has its own scope mcp and is strictly separated from the rest of the application:

  • It works only on the MCP listener (POST /mcp).
  • On the REST API/UI an MCP key is rejected with 403 Forbidden - it can neither read nor write there.
  • Conversely, normal API keys (read/readwrite) do not work on the MCP endpoint.
  • If the bearer token is missing or invalid, the endpoint responds with 401 Unauthorized (header WWW-Authenticate: Bearer).

That way an agent key intended for read-only server data can never accidentally act with write access.

ToolPurpose
list_serversAll servers with their read-only properties (OS, version, status, reachability, hardware, update state, security posture).
get_serverOne server by id or exact name.
fleet_summaryAggregate: total count, reachable, status distribution, servers with updates/critical CVEs.

Both tools return the same server view. The whitelist covers only these fields - deliberately no credentials:

FieldMeaning
id, name, host, ip_addressesidentifier & addresses
os_name, os_version, os_idoperating system
transportssh, agent or routeros
reachable, last_seen_atreachability
statustraffic light: excellent / green / yellow / red
insightsplaintext findings behind the status (e.g. “firewall not active”)
cpu_model, cpu_coresCPU
mem_total_mb, mem_used_mbmemory
disk_total_mb, disk_used_mb, disk_usage_percentstorage
kernel_version, virtualizationkernel & virtualization
outdated_packages, reboot_requiredpackage/update state
routeros_channel, routeros_latest_version, routeros_update_availableRouterOS-specific
cve_critical, cve_highvulnerability counts
ssh_hardened, firewall_active, firewall_toolsecurity posture
hardening_index, proxmox_typehardening index & Proxmox role

Aggregates all visible servers. Example result:

{
"total": 12,
"reachable": 11,
"unreachable": 1,
"by_status": { "excellent": 5, "green": 3, "yellow": 3, "red": 1 },
"updates_available": 4,
"servers_with_critical_cve": 1
}

updates_available counts servers with outdated packages or an available RouterOS update; servers_with_critical_cve counts servers with at least one critical CVE.

The settings page shows a ready-made example. For an MCP client with HTTP transport (e.g. Claude Desktop / VS Code MCP clients):

{
"mcpServers": {
"lcm": {
"type": "http",
"url": "http://127.0.0.1:9330/mcp",
"headers": { "Authorization": "Bearer <YOUR_MCP_KEY>" }
}
}
}

Several instances can be registered in parallel - each with its own key:

{
"mcpServers": {
"lcm-prod": {
"type": "http",
"url": "https://lcm.example.com/mcp",
"headers": { "Authorization": "Bearer <PROD_KEY>" }
},
"lcm-lab": {
"type": "http",
"url": "http://127.0.0.1:9330/mcp",
"headers": { "Authorization": "Bearer <LAB_KEY>" }
}
}
}

(The lcm-prod URL here points at a TLS-terminating reverse proxy.)

Test directly (JSON-RPC 2.0 via curl):

Terminal window
# 1) list available tools
curl -s http://127.0.0.1:9330/mcp \
-H "Authorization: Bearer <YOUR_MCP_KEY>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# 2) fetch one server by name
curl -s http://127.0.0.1:9330/mcp \
-H "Authorization: Bearer <YOUR_MCP_KEY>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"get_server","arguments":{"server":"web01"}}}'
# 3) fleet aggregate
curl -s http://127.0.0.1:9330/mcp \
-H "Authorization: Bearer <YOUR_MCP_KEY>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"fleet_summary","arguments":{}}}'
  • The endpoint speaks JSON-RPC 2.0 over POST /mcp (MCP “Streamable HTTP”, stateless). Methods: initialize, tools/list, tools/call, ping plus the notifications notifications/initialized / notifications/cancelled (acknowledged with HTTP 202).
  • Offered protocol version: 2025-06-18; if the client requests a different one, the server mirrors it in initialize.
  • GET /mcp is not used (no server-initiated SSE) and answers with 405 Method Not Allowed.
  • Enable state, bind address and port can be switched at runtime via Settings → MCP at any time - without restarting the application.