Page MenuHomeVyOS Platform

T9030-mcp-tools.md

Authored By
mihakralj
Jul 1 2026, 2:50 AM
Size
9 KB
Referenced Files
None
Subscribers
None

T9030-mcp-tools.md

# T9030: MCP Tools
| Field | Value |
|-------|-------|
| Task | [T9030](https://vyos.dev/T9030) |
| Status | Draft / design |
| Related specs | [Architecture & Transport](T9030-mcp-architecture.md) · [Dynamic Schema](T9030-mcp-dynamic-schema.md) · [Security & Concurrency](T9030-mcp-security.md) |
## Design Principle
Tools are strictly bifurcated into **operational** and **configuration** modes,
mirroring the VyOS CLI barrier between `$` (op-mode) and `#` (config-mode).
Rather than generating one tool per CLI command, a small set of **meta-tools**
accept path arrays and are validated against the live schema cache (see
[Dynamic Schema](T9030-mcp-dynamic-schema.md)). This keeps the tool catalog
small and the protocol stable as the CLI grows.
## Operational Tool: `execute_operational_command`
### Schema
```json
{
"name": "execute_operational_command",
"description": "Run a VyOS operational-mode (show/monitor/...) command.",
"parameters": {
"path": {
"type": "array",
"items": { "type": "string" },
"description": "Command path (e.g. [\"show\", \"interfaces\", \"wireguard\"])"
}
}
}
```
### Behavior
1. Validates the command path against the `op-mode-definitions/` XML cache
(`vyos.xml_ref.op_cache.op_reference`).
2. Rejects invalid/hallucinated commands — no arbitrary shell execution; the
path is never concatenated into a shell string.
3. Delegates to `/usr/libexec/vyos/op_mode/*.py` scripts (or the
`vyatta-op-cmd-wrapper` for legacy commands).
4. Requests `--raw`/`--for-api`/`--json` output when the script supports it
(e.g., container, traceroute) for structured results.
5. Parses tabulate-formatted output into structured data when JSON is not
available.
6. Runs the (blocking) script via `asyncio.to_thread()` so the event loop
stays responsive (see [Concurrency](T9030-mcp-security.md#concurrency--event-loop)).
### Example call / result
```json
// tools/call
{ "name": "execute_operational_command",
"arguments": { "path": ["show", "interfaces", "ethernet", "eth0"] } }
```
```json
// result (structured when available, else text content)
{ "content": [ { "type": "text", "text": "{\"eth0\": {\"oper_state\": \"up\", ...}}" } ] }
```
### Parity
Equivalent to REST `GET /show?path=...`.
## Configuration Tool: `modify_configuration`
### Schema
```json
{
"name": "modify_configuration",
"description": "Stage set/delete operations and atomically commit them.",
"parameters": {
"operations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"op": { "enum": ["set", "delete"] },
"path": { "type": "array", "items": { "type": "string" } },
"value": { "type": "string" }
},
"required": ["op", "path"]
}
}
}
}
```
### Lifecycle (per invocation)
1. **Validation**: every operation `path` is checked against the
`vyos.xml_ref` config cache (`cli_defined`); unknown paths are rejected before
any session is opened.
2. **Session Init**: `ConfigSession(uid, app="vyos-mcp-agent")` where `uid` is
derived from the worker PID.
3. **Staging**: iterates `operations`, mapping each to `session.set(path, value)`
or `session.delete(path)`.
4. **Atomic Commit**: `session.commit()` triggers, in order:
- `verify()` on all affected conf-mode scripts
- `generate()` to produce daemon configs (FRR, nftables, dnsmasq, ...)
- `apply()` to reload daemons gracefully
5. **Result**: commit stdout/stderr and status serialized and streamed to the LLM.
The whole staging+commit sequence runs in a worker thread and is serialized
against all other API writes (REST included) through the shared commit lock —
see [Serializing commits](T9030-mcp-security.md#serializing-commits). The
session is ephemeral and discarded after commit or error.
### Commit-Confirm & Rollbacks
A configuration change can disrupt network connectivity and break the session
before the agent can confirm the result. To bound that risk, `modify_configuration`
accepts an optional `commit_confirm_minutes` integer. When present, the dispatcher
calls `ConfigSession.commit_confirm(minutes)` (which wraps `config-mgmt
commit_confirm -y -t <minutes>`) instead of `commit()`; if no follow-up
confirmation arrives within the window, VyOS automatically rolls the
configuration back to the prior revision.
The agent confirms a good change by issuing a subsequent operation that maps to
`ConfigSession.confirm()`. `minutes` defaults to
`vyos.defaults.DEFAULT_COMMIT_CONFIRM_MINUTES` and must be validated as a
positive integer before dispatch.
### Example call
```json
// tools/call — assign an address and description
{ "name": "modify_configuration",
"arguments": { "operations": [
{ "op": "set", "path": ["interfaces","ethernet","eth1","address"], "value": "10.0.0.1/24" },
{ "op": "set", "path": ["interfaces","ethernet","eth1","description"], "value": "LAN" }
] } }
```
### Error Handling
- `ConfigError` / `ConfigSessionError` and `vyos.opmode` error classes are
intercepted and serialized to a JSON-RPC error carrying the exact VyOS
semantic message.
- This enables **self-healing**: the LLM analyzes the error, re-reads the schema
resource to recalculate parameters, and reissues a corrected
`modify_configuration`.
- A failed `verify()`/`commit()` leaves the running config untouched (atomic);
no partial application.
### Parity
Equivalent to REST `POST /configure`.
## Administrative Tools
These are **write-class** tools and are registered only in `read-write` mode
(see [Tool Filtering](#tool-filtering-by-mode)).
| Tool | Parameters | Internal Handler | REST Parity |
|-------------------------|-------------------------------------------|----------------------------------------|--------------|
| `manage_system_image` | `action` (add/delete/set), `url`, `name` | `image_installer.py` | `/image` |
| `reboot_system` | `path` (default: `["now"]`) | `vyatta-op-cmd-wrapper reboot` | `/reboot` |
| `poweroff_system` | `path` (default: `["now"]`) | `vyatta-op-cmd-wrapper poweroff` | `/poweroff` |
| `save_configuration` | `file` (optional path) | `vyatta-op-cmd-wrapper save` | `/config-file` |
`save_configuration` is included so an agent can persist a validated change set
to `config.boot` after a successful `modify_configuration`.
## Tool Filtering by Mode
When `set service https api mcp mode read-only` is configured (the default):
- `modify_configuration`, `manage_system_image`, `reboot_system`,
`poweroff_system`, and `save_configuration` are **not** registered during the
MCP handshake (`tools/list` omits them).
- The LLM is structurally unaware of write capabilities — this is enforced at
registration time, not merely refused at call time.
- Only `execute_operational_command` and read-only resources remain available.
When `mode read-write` is set, the full tool set is registered. A `tools/call`
for an unregistered tool returns a JSON-RPC method-not-found error.
## Prompts
Reusable prompt templates exposed via MCP (`prompts/list`, `prompts/get`) to
guide LLM reasoning across multi-branch VyOS workflows:
| Prompt | Purpose |
|-----------------------------------|----------------------------------------------------------------------|
| `troubleshoot_asymmetric_routing` | Guide through routing-loop diagnosis (FRR, conntrack, BGP). |
| `provision_site_to_site_vpn` | IPsec tunnel workflow across IKE/ESP/peer config branches. |
| `audit_firewall_posture` | Security audit aware of nftables (1.4+) vs. legacy firewall model. |
Prompts are static templates shipped with the module; like tools they require no
per-feature maintenance because the steps reference schema/state resources rather
than hardcoded command output.
### Custom/User-Defined Prompts
In addition to the static templates built into the image, the loader may scan a
persistent location under `/config/user-data/` (which survives image upgrades,
unlike `/run` or `/usr`) — e.g. `/config/user-data/mcp/prompts/` — for
administrator-supplied workflow prompts at initialization.
This is untrusted on-disk input and must be handled defensively:
- Treat the files as **plain text only**; never `eval`/`exec`/`import` them or
render them through a templating engine that could execute embedded code.
- Resolve and confine paths to the prompts directory (reject symlinks escaping
it and any `..` traversal) before reading.
- Bound the number of files and the size of each file, and skip anything that is
not a regular file, so a malformed drop-in cannot exhaust memory or stall
startup.
- Log and skip (do not crash `mcp_init`) on a malformed or oversized file.
## Tool ↔ backend mapping summary
| MCP surface | Backend | Read/Write |
|-------------|---------|------------|
| `execute_operational_command` | `op_mode/*.py`, `vyatta-op-cmd-wrapper` | read |
| `modify_configuration` | `vyos.configsession.ConfigSession` | write |
| `manage_system_image` | `image_installer.py` | write |
| `reboot_system` / `poweroff_system` | `vyatta-op-cmd-wrapper` | write |
| `vyos-config://` / `vyos-state://` resources | `vyos.config.Config`, op-mode scripts | read |
| `vyos-schema://` resources | `vyos.xml_ref` cache | read |

File Metadata

Mime Type
text/plain
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3820423
Default Alt Text
T9030-mcp-tools.md (9 KB)

Event Timeline