Edge Admin MCP — v0.2.0¶
Tool catalog for every operation the Edge Admin MCP server exposes at POST /mcp. Companion to guide.md §4 — that section explains how to connect; this page enumerates what's available once you do.
Why this exists¶
Unlike OpenAPI (REST) and AsyncAPI (events), the Model Context Protocol does not yet have a standardised static spec format or off-the-shelf renderer. Discovery happens at runtime via the tools/list JSON-RPC method — any connected MCP client (Claude Desktop, Cursor, mcp-inspector) sees the live list. Operators who don't run a client also deserve visibility into what's there, so we maintain this catalog by hand.
For interactive browsing of the live surface, run @modelcontextprotocol/inspector against /mcp with your MCP_KEY.
Reading this catalog¶
- Required parameters are listed before optional ones; the parameter name links to the tool's input contract.
- List tools all accept
page,page_size,order_by, andorder_directionsunless noted otherwise. - Annotations in the tool table use MCP-standard hints:
- 🔍
readOnlyHint— does not mutate state - ⚠️
destructiveHint— irreversible or fleet-affecting - ♻️
idempotentHint— safe to retry - 🌐
openWorldHint— calls external systems (Netmaker, agents, exporters) - Write tools whose REST counterpart is gated by degraded-mode are also blocked here — see § Operations blocked in degraded mode below.
One MCP-only tool (check_admin_health) has no REST equivalent; every other tool maps 1:1 to a REST endpoint documented in admin-openapi-v0.2.0.json.
Filter value conventions¶
MCP list tools use typed parameters rather than REST query strings:
- Wildcard text filters — single-value string parameter.
*matches prefix (prod*), suffix (*east), or contains (*prod*). Examples:cluster_name: "prod*",username: "*admin". - IN filters —
<field>_inarray parameter. Passes one or more exact values; any match is returned. Single-element arrays work. Examples:cluster_name_in: ["prod", "staging"],status_in: ["healthy", "unhealthy"],node_id_in: ["<uuid>"]. This mirrors the REST__inoperator (?cluster_name__in=prod,staging). - Boolean filters are native JSON booleans:
trueorfalse. Passnullor omit the field to clear the filter. MCP Inspector renders these as checkbox controls with a null/clear option. If sending raw JSON, do not quote boolean values;"true"/"false"are strings and will be rejected by MCP validation. - Range filters use
_gte/_ltesuffixes:inserted_at_gte,timeout_lte. Mirrors the REST__gte/__lteoperators. - REST-only operators (
field__null, bare commafield=a,b) have no MCP equivalent — use the explicithas_*/is_*booleans and_inarrays instead.
1. Admin info¶
The local admin instance's identity, its admin-cluster topology, and cross-cluster discovery of other admins.
| Tool | Title | Hints | Description |
|---|---|---|---|
get_admin |
Get Admin Info | 🔍 | This admin's metadata: id, name, peer capacity, last recompute time. |
get_my_admin_cluster |
Get This Admin's Admin Cluster | 🔍 | This admin's admin-cluster — totals, topology, weak leader. |
list_admin_clusters |
List All Admin Clusters | 🔍 🌐 | Every admin cluster Netmaker knows about (incl. zombies). |
list_edge_clusters |
List Edge Clusters (Admin View) | 🔍 | Edge clusters grouped by owning admin instance. |
list_orphaned_clusters |
List Orphaned Clusters | 🔍 | Clusters with no assigned admin — cannot receive commands. |
check_admin_health |
Check Admin Health | 🔍 | MCP-only. Runs every subsystem check in parallel: DB, Membership, Metadata, Netmaker API, Netclient, Proxy Servers, Event Broker. Same checks as /healthz, flattened for AI consumption. |
All six tools take no parameters.
2. Clusters¶
Logical groups that map 1:1 to Netmaker WireGuard networks. One full mesh per cluster.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_clusters |
List Clusters | 🔍 | Filter/sort/paginate. Filters: name (wildcard), name_in (array), node_id_in (array), ipv4_range, node_count_gte/lte, node_limit, node_limit_gte/lte, has_node_limit, inserted_at_*, updated_at_*. |
get_cluster |
Get Cluster | 🔍 | Required: cluster_name. |
create_cluster |
Create Cluster | 🌐 | Required: name (lowercase alphanumeric + hyphens, ≤24 chars, default reserved). Optional: ipv4_range (CIDR — auto-assigned if omitted), node_limit. |
update_cluster |
Update Cluster | ♻️ | Required: cluster_name. Optional: node_limit (pass null to remove the limit). |
delete_cluster |
Delete Cluster | ⚠️ 🌐 | Required: cluster_name. Deletes the VPN network — all nodes lose connectivity. |
3. Nodes¶
Edge machines running the agent. Addressed only by VPN hostname.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_nodes |
List Nodes | 🔍 | Filter/sort/paginate. Filters: node_id_in (array), status_in (array: healthy/unhealthy/unreachable), id_type_in (array: persistent/random), cluster_name (wildcard), cluster_name_in (array), version, self_update_enabled, last_seen_at_*, inserted_at_*, updated_at_*. |
get_node |
Get Node | 🔍 | Required: node_id. |
delete_node |
Delete Node | ⚠️ 🌐 | Required: node_id. Removes from VPN mesh — agent must re-enroll. |
change_node_cluster |
Move Node to Cluster | ⚠️ 🌐 | Required: node_id, cluster_name. Best-effort, not transactional — reconciliation worker heals inconsistencies. |
4. Aliases¶
Friendly DNS names for nodes. Resolved as <alias>.<cluster>.<vpn_domain>.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_aliases |
List Aliases | 🔍 | Filter/sort/paginate. Filters: name (wildcard), node_id_in (array), cluster_name (wildcard), cluster_name_in (array), inserted_at_*, updated_at_*. |
get_alias |
Get Alias | 🔍 | Required: alias_id. |
create_alias |
Create Alias | 🌐 | Required: node_id, name (lowercase alphanumeric + hyphens, 1–63 chars). |
delete_alias |
Delete Alias | ⚠️ 🌐 | Required: alias_id. |
5. Enrollment keys¶
Tokens agents use to join a cluster's VPN mesh.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_enrollment_keys |
List Enrollment Keys | 🔍 | Filter/sort/paginate. Filters: cluster_name (wildcard), cluster_name_in (array), name, has_name, key, uses_remaining, uses_remaining_gte/lte, is_unlimited, is_spent, is_expired, is_never_used, has_expiry, expires_at_*, last_used_at_*, inserted_at_*, updated_at_*. |
get_enrollment_key |
Get Enrollment Key | 🔍 | Required: enrollment_key_id. |
create_enrollment_key |
Create Enrollment Key | Required: cluster_name. Optional: name (label), uses_remaining (default 1), expires_at (ISO8601). |
|
update_enrollment_key |
Update Enrollment Key | ♻️ | Required: enrollment_key_id. Optional: name, uses_remaining, expires_at. Pass null on any field to clear it (unlimited / no expiry / no label). |
delete_enrollment_key |
Delete Enrollment Key | ⚠️ | Required: enrollment_key_id. |
6. Commands¶
Shell jobs fanned out across the fleet. Creating a command produces one command_execution per targeted node.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_commands |
List Commands | 🔍 | Filter/sort/paginate. Filters: command_text, has_timeout, timeout_gte/lte, has_expires_at, expires_at_*, inserted_at_*, updated_at_*. |
get_command |
Get Command | 🔍 | Required: command_id. |
create_command |
Create Command | Required: command_text (multi-line shell supported), targeting (see below). Optional: timeout (ms), expires_at (ISO8601, future). |
|
delete_command |
Delete Command | ⚠️ | Required: command_id. Only deletes commands where every execution is terminal. |
Targeting (required nested object on create_command):
%{"type" => "all"}— every node in the fleet%{"type" => "nodes", "node_ids" => [...]}— explicit list%{"type" => "clusters", "cluster_names" => [...]}— by cluster
Both nodes and clusters forms accept optional node_filters / cluster_filters for AND-refinement.
Command executions¶
| Tool | Title | Hints | Description |
|---|---|---|---|
list_command_executions |
List Command Executions | 🔍 | Filter/sort/paginate. Filters: command_id_in (array), node_id_in (array), status_in (array: pending/sent/completed/cancelled/expired), target_all, exit_code, exit_code_gte/lte, output (wildcard text search), has_output, cluster_name (wildcard), cluster_name_in (array), has_cluster, inserted_at_*, updated_at_*, sent_at_*, completed_at_*, cancelled_at_*. |
get_command_execution |
Get Command Execution | 🔍 | Required: execution_id. Returns status, output, exit code, timestamps. |
cancel_command_execution |
Cancel Command Execution | ⚠️ | Required: execution_id. pending → cancelled immediately. sent → cancellation forwarded to agent (best-effort). Terminal statuses return 409. |
delete_command_execution |
Delete Command Execution | ⚠️ | Required: execution_id. Only terminal executions can be deleted. |
completed is the only terminal success status — read exit_code to distinguish success (0) from failure (non-zero).
7. SSH usernames¶
Centralised SSH credentials. The agent's embedded SSH server (:40022) verifies against these via the admin.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_ssh_usernames |
List SSH Usernames | 🔍 | Filter/sort/paginate. Filters: username (wildcard), username_in (array), node_id_in (array), has_password, cluster_name (wildcard), cluster_name_in (array), key_name (wildcard), key_name_in (array), inserted_at_*, updated_at_*. |
get_ssh_username |
Get SSH Username | 🔍 | Required: ssh_username_id. |
create_ssh_username |
Create SSH Username | Required: node_id, username (3–32 chars, starts with letter or _, lowercase + digits + hyphens + underscores). Optional: password (12–128 chars, Argon2-hashed at rest), public_keys (list of %{key_name, public_key}). |
|
delete_ssh_username |
Delete SSH Username | ⚠️ | Required: ssh_username_id. Deletes all associated public keys. |
8. SSH public keys¶
Authorized keys attached to a username.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_ssh_public_keys |
List SSH Public Keys | 🔍 | Filter/sort/paginate. Filters: ssh_username_id_in (array), node_id_in (array), username (wildcard), username_in (array), key_name (wildcard), key_name_in (array), public_key, cluster_name (wildcard), cluster_name_in (array), inserted_at_*, updated_at_*. |
get_ssh_public_key |
Get SSH Public Key | 🔍 | Required: ssh_public_key_id. |
create_ssh_public_key |
Create SSH Public Key | Required: ssh_username_id, public_key (OpenSSH format: ssh-ed25519, ecdsa-sha2-nistp256/384/521, ssh-rsa), key_name (1–255 chars, unique within the username). |
|
delete_ssh_public_key |
Delete SSH Public Key | ⚠️ | Required: ssh_public_key_id. |
9. Self-update requests¶
Managed agent upgrades across the fleet.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_self_update_requests |
List Self-Update Requests | 🔍 | Filter/sort/paginate. Filters: status_in (array: pending/processing/completed), inserted_at_*, updated_at_*. |
get_self_update_request |
Get Self-Update Request | 🔍 | Required: request_id. Watch status and summary (%{total, triggered, failed}). |
create_self_update_request |
Create Self-Update Request | ⚠️ | Required: targeting (same shape as create_command). Only healthy nodes with self_update_enabled=true are updated. No cancel — durable once triggered. |
delete_self_update_request |
Delete Self-Update Request | ⚠️ | Required: request_id. Only completed requests can be deleted. |
10. Metrics¶
Parsed, human-friendly JSON (not raw Prometheus text). For scraping endpoints see guide.md §6.
| Tool | Title | Hints | Description |
|---|---|---|---|
get_node_metrics |
Get Node Metrics | 🔍 🌐 | Required: node_id. Unified host + agent sources. Best-effort: if one source fails, that section is reported unavailable. Always returns ok — verify the node exists first with get_node if you need to distinguish "missing" from "not scraping". |
get_host_metrics |
Get Host Metrics | 🔍 🌐 | Required: node_id. From Node Exporter: CPU, memory, disk, uptime. |
get_agent_metrics |
Get Agent Metrics | 🔍 🌐 | Required: node_id. From edge_agent PromEx: BEAM, commands, discovery, proxy, SSH, VPN pulls, health check reports, Oban queues. |
get_admin_metrics |
Get Admin Metrics | 🔍 | No parameters. 16 sections covering this admin's full operational surface: application, metadata, membership, discovery, nodes, quantum, vpn, commands, ssh, reconciliation, self_updates, gateways, proxy, event_broker, webhook, oban_queues. |
11. Webhooks¶
User-configured HTTP delivery destinations for events. Webhooks are immutable after create — to change a field, delete and recreate.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_webhooks |
List Webhooks | 🔍 | Filter/sort/paginate. Filters: url, event_type (post-filter: which webhooks fire on this event), inserted_at_*, updated_at_*. Secret and headers are never returned. |
get_webhook |
Get Webhook | 🔍 | Required: webhook_id. Secret and headers are never returned. |
create_webhook |
Create Webhook | Required: url (absolute http(s), ≤2048 chars, SSRF-checked), secret (HMAC-SHA256 key, 32–256 chars), subscribed_events (explicit list, 1–20 known event types). Optional: headers (≤20 entries, values ≤4096 chars). |
|
delete_webhook |
Delete Webhook | ⚠️ | Required: webhook_id. There is no enable/disable toggle — delete to pause delivery. |
12. Event catalog¶
MCP-only tools surfacing the same content as /asyncdoc. Useful when picking values for create_webhook's subscribed_events.
| Tool | Title | Hints | Description |
|---|---|---|---|
list_event_types |
List Event Types | 🔍 | No parameters. Returns %{event_types: [%{type, description}, ...], count}. |
explain_event_type |
Explain Event Type | 🔍 | Required: event_type. Returns %{type, description, data_example, reference} — the data payload shape inside the CloudEvents envelope. |
Operations blocked in degraded mode¶
When the admin is in degraded mode (total nodes exceed total edge-capacity across the admin cluster), the following write tools return a degraded-mode error. Reads, alias ops, webhook ops, SSH ops, and commands all run unconditionally.
create_cluster update_cluster delete_cluster
change_node_cluster delete_node
create_enrollment_key update_enrollment_key delete_enrollment_key
create_self_update_request
This mirrors the REST degraded-mode block list — same operations are blocked on both surfaces.