Skip to content

Xen Orchestra's Full REST API — 252 Tools from One DADL

Xen Orchestra has spent the last two releases turning its REST API into something you can build on. XO 6.4 (April 2026) marked /rest/v0/ as stable and added fine-grained RBAC v2; 6.5 added PATCH /vms/{id} for partial VM updates and a separate /vm-controllers collection; 6.6 (June 2026) continued the same trickle — scoped Storage and Network Administrator roles, writable backup repositories, and PATCH for VDIs and VIFs. In February, Vates also shipped a first-party MCP server bundled with XO 6.2 — seven tools, deliberately read-only, so teams can enable it on production from day one.

We took a different cut of the same API. The DADL for Xen Orchestra covers the entire 252-endpoint REST surface — read and write — as one YAML file. This post is the build log, and an honest answer to the obvious question: why two MCP surfaces for the same product?

The XO REST API is no longer a thin read layer. As of 6.6 it exposes the full object graph — VMs (including PATCH partial updates and snapshot revert), VM controllers, VM templates, VM/VDI snapshots, hosts, pools, storage (SR/VDI/VBD, with PATCH rename and disk-resize on VDIs since 6.6), networking (VIF/PIF/PBD, with PATCH on VIFs since 6.6), hardware passthrough (PCI/PGPU/SM) — plus every lifecycle action: power, snapshot, clone, migrate, export/import, hotplug. On top of the objects sit the operational surfaces: tasks, backups (jobs, logs, repositories — writable since 6.6 — restore, schedules), real-time change events over Server-Sent Events, RBAC v2 (users, groups, acl-roles, acl-privileges), and host/pool maintenance (rolling reboot, rolling update, emergency shutdown).

That is a large API, and almost all of it mutates infrastructure. None of the write side is reachable through the first-party MCP server — by design, not by omission.

DADL (xen-orchestra.dadl)@xen-orchestra/mcp (first-party)
Tools2527
Scopefull read and writeread-only by design
Coverageevery REST object + lifecycle action (incl. PATCH on VMs, VDIs, VIFs), backups with writable repositories, RBAC v2, SSE events, SDN traffic rules, dashboards, auth tokensinfrastructure summary, list/inspect pools / hosts / VMs, pool dashboard, documentation search
Create / start / migrate / delete?yesno — intentionally
Distributionone 252-tool YAML fileNode server bundled with XO 6.2+
Tracking a new API release~30 lines of YAML per endpointships with the XO release
Governance layercredentials, authorization, audit (via ToolMesh)none — local, in-process

The local validation, run from the registry checkout:

> cd dadl-registry && npm run validate
✅ xen-orchestra.dadl — 252 tools

The full definition is live and browsable at dadl.ai/d/xen-orchestra — the authoritative source, always current with the latest XO release. The DADL spec itself: dadl.ai/spec.

A worked example: a VM change, made undoable

Section titled “A worked example: a VM change, made undoable”

The point of the write side is that it actually changes infrastructure — safely, with a trail. Here is a three-call round trip: find a running VM, snapshot it so the change is reversible, then patch it.

// 1. Find the VM by name (XO filter syntax, compact fields)
const [vm] = await toolmesh.xen_orchestra_list_vms({
filter: "name_label:web-fra-01",
fields: "uuid,name_label,tags"
});
// 2. Snapshot first, so the change is undoable
await toolmesh.xen_orchestra_snapshot_vm({
id: vm.uuid,
name_label: `pre-change ${new Date().toISOString().slice(0, 10)}`
});
// 3. PATCH the live VM — tags is a hot field, applied while running (XO 6.5)
await toolmesh.xen_orchestra_update_vm({
id: vm.uuid,
tags: [...vm.tags, "managed:toolmesh"]
});

Three calls. No glue code. Steps 2 and 3 are exactly what a read-only MCP server cannot do — and the DADL transparently handles:

  • the unusual cookie-based auth (the token is injected server-side as Cookie: authenticationToken=… — the model never sees it);
  • the async-by-default action model — most writes return a task reference; you add sync only for short operations and poll get_task for long ones (migrate, export) to avoid HTTP client timeouts;
  • the cold-vs-hot field distinction on update_vmmemoryStaticMax, cpusStaticMax, secureBoot need a halted VM, while tags, nameLabel, and cpus apply live (the example uses tags, so it runs without downtime);
  • retries on transient failures and terminal classification for 404 (“no such VM”) and RBAC 403.

Same calls from Claude or any other agent. No Xen-Orchestra-specific code on the client side.

The natural framing — “252 beats 7” — is the wrong one. The two surfaces answer different questions.

The first-party MCP server is read-only on purpose. It runs in-process, ships with XO, needs no external infrastructure, and cannot break anything. If the question is “let an assistant look at my infrastructure and answer questions about it,” that is the right shape, and you should use it. We are not trying to replace it — the DADL even includes a get_mcp_status tool that reports whether XO’s own built-in MCP server is enabled.

The DADL answers a different question: “let an agent operate my infrastructure — create, patch, snapshot, migrate, back up, provision RBAC — without handing it raw admin credentials.” That only makes sense with a control layer underneath. Through ToolMesh, every one of the 252 tools runs behind centralized credentials (the XO token never reaches the model), authorization (an agent can be granted list_vms and snapshot_vm but not delete_sr), and an audit log (every call recorded with what, when, and why). The write side is the whole point, and the governance layer is what makes the write side safe to expose.

Both definitions live in the same registry. They are complements: read-only discovery in-process, full lifecycle management through a governed gateway.

XO 6.4 added RBAC v2; 6.5 added PATCH /vms/{id}, /vm-controllers, and a REST snapshot-revert endpoint; 6.6 added scoped Storage and Network Administrator roles, writable backup repositories, and PATCH for VDIs and VIFs. Reflecting each of those in the DADL is the same small unit of work: open xen-orchestra.dadl, add the tool block (~30 lines of YAML per endpoint), run npm run validate, open a PR, review the diff, merge. ToolMesh picks the new tools up on next reload. No SDK changes, no release to cut, no npm publish, no “please upgrade” to send users.

That is what every XO release looks like from here. 6.6 was seven new tool blocks and a version bump from 245 to 252 — an afternoon’s diff, not an event. The releases worth a second look are not the ones that add endpoints, but the ones that close a gap you had written down as a limitation.

Two concrete examples from 6.6:

  • The release added PATCH /vdis/{id}. An earlier version of this post listed “VDIs cannot be renamed via REST” under honest caveats — 6.6 closed that gap, and reflecting it was a single update_vdi tool block (rename a disk or grow it), annotated “added in XO 6.6 — returns 404 on older builds” so an agent against an older host gets a clean terminal error instead of a silent failure.
  • Backup repositories became writable: POST / PATCH / DELETE /backup-repositories. Three new tool blocks, the same shape as every other write endpoint, behind the same credentials, authorization, and audit — nothing about the gateway changed. The new built-in Storage Administrator and Network Administrator roles need no new tools at all; they compose out of the RBAC v2 surface the DADL already exposed.

We made the general version of this argument in an earlier post; XO is the concrete case study for a write-heavy API.

There is a second reason the tool count matters less than it looks: Code Mode. Instead of injecting all 252 XO tool descriptions into the model’s context window, ToolMesh exposes two meta-tools (list_tools and execute_code). The model discovers what is available on demand and calls flat tool names from inside a sandboxed JavaScript runtime — toolmesh.xen_orchestra_list_vms, toolmesh.xen_orchestra_snapshot_vm, not a nested namespace.

The token cost is roughly constant: ~1 k tokens of meta-tool description, whether the registry holds one backend with 7 tools or 30 backends with 4,010. A 252-tool backend adds zero tokens to every Claude conversation that does not actively touch Xen Orchestra. That is also why the JavaScript in the worked example above is not an illustration — it is literally what the model writes to call the tools.

The DADL is not a universal win. Real limits:

  • The SSE event stream is half-declarative. open_events_stream returns a subscription ID and the DADL describes the subscribe handshake, but the Server-Sent Events stream itself is a streaming protocol — DADL describes REST, not long-lived streams. For polling task state, get_task?wait=true long-polls cleanly; for a true real-time feed you are at the edge of what a declarative REST definition models.
  • Host power actions aren’t in the REST API. Hosts have no REST route for start / shutdown / reboot / restart_toolstack — only disable, enable, and management_reconfigure are exposed. For host power you use pool-level rolling_reboot / rolling_update or drop to the JSON-RPC API. We expose what exists; we cannot expose what does not. (The entry that used to sit here — “VDIs cannot be renamed via REST” — was retired when 6.6 shipped PATCH /vdis/{id}.)
  • SDN traffic rules need a Premium plugin. The six traffic-rule tools (add / delete / update_network_traffic_rule, add / delete / update_vif_traffic_rule — the two update variants new in 6.6) drive XO’s SDN Controller — a plugin bundled with XOA Premium, not part of the documented core REST API. Without it loaded the routes return 404, so the agent gets a clean terminal error rather than a silent no-op. The other 246 tools work against a stock REST API.
  • The write side needs privileges. Full coverage assumes an admin user or an RBAC v2 user with the right acl-privileges. If you only need to look, the read-only first-party server is less setup and a smaller blast radius — use it.
  • Every call hops through ToolMesh. For interactive operation this is invisible; for high-throughput batch jobs against XO, calling the REST API directly is faster.

DADL is good at one specific thing — making a REST API surface available to agents quickly, with a real control layer underneath. For Xen Orchestra, that surface happens to be 252 endpoints of mostly-mutating operations, which is exactly the case where a governance layer earns its keep.

The substantive question is not “DADL vs the first-party MCP server” — ToolMesh runs both, and they coexist in the same registry. The question is: what does it cost to give an agent the write side of an API, safely?

For a read-only server, the answer is “you don’t” — that is the deliberate trade. For a hand-coded read-write server, it costs an engineering cycle per endpoint: SDK shapes, handlers, tests, a release, an npm publish, user upgrades, plus the authorization and audit you have to build yourself. For a DADL behind ToolMesh, it costs one PR with a YAML diff, and the credentials, authorization, and audit come from the runtime.

That is the asymmetry. Not “everyone should ditch the first-party server” — keep it for read-only discovery. Just: when you need an agent to actually operate Xen Orchestra, the unit of work is a YAML diff, and the safety is in the layer underneath rather than in withholding the write tools.