MCP tools and resources
This page is the catalog an agent sees over Cetacean’s MCP server: 14 resources, 27 tools, 6 prompts, and 5 widgets. For enabling the server, authorization, and configuration, see MCP server.
Everything here is filtered per identity. A tool above the configured operations level isn’t registered at all; a tool or resource the identity holds no grant for is hidden from the listing and refused at call time.
Tools
Tools are gated by operations level (mcp.operations_level, inheriting server.operations_level)
and by per-resource write grants. Every MCP tool requires the same operations level as the API route for
the same operation, so there’s no MCP-specific scheme to learn.
Each tool advertises the behavioral hints (readOnlyHint, destructiveHint, idempotentHint,
openWorldHint) clients use to gate confirmation prompts and an output schema the server validates results
against. A mutating tool returns an error when someone else changed the resource first, so re-read it and retry.
Removals are irreversible: no spec history is retained after a service removal, and a config, secret, or volume payload can’t be recovered.
Orientation
Where to start, and how to turn a name into something you can act on. find and describe attach links to the
resources they returned, so a host can offer somewhere to go next.
get_cluster_status
Answers whether the cluster is healthy and, when it’s not, names the services not in their desired state or restarting in a loop, the nodes down or draining, the rollouts in flight, and reserved against total CPU and memory. Start here.
find
Locates resources. With type it enumerates that type, paged by offset and capped at the same 200 the default
sets. Without type it searches every type at once: offset is ignored, and the result carries counts, the
per-type breakdown behind total.
describe
Returns one resource as a Digest. id takes an ID or a name—a hostname for a node, the rendered
{service}.{slot} for a task, the plain name for everything else—and a name matching more than one
resource
is refused, naming the matching IDs.
get_topology
Projects the cluster as a graph: network joins services to the overlay networks they attach to, placement
joins nodes to the services they run, and drain-impact joins the services on one node to the nodes that could
take them.
In drain-impact a service with no edges is stranded, and its detail names the placement constraint that
blocked it; a global service reports as global, since draining stops its task rather than relocating it.
Constraints are evaluated the way Swarm enforces them; spare capacity isn’t considered, since
get_cluster_status already reports reserved against total. Where grants hid candidate
nodes, the graph carries
a note saying what the assessment was narrowed to.
Observation
What the cluster is doing now, and what it did recently.
get_logs
Fetches recent log lines from one scope.
A task reads one replica and is the only way to reach one that has already exited—the form to reach for after
a crash, and the most perishable, since Swarm discards a task’s output with the task record, five per replica
slot by default. A service in a restart loop may hold seconds of history, so read it first. stack and
cluster merge server-side and attribute each line to its service, so grepping the cluster with contains
costs one call; a wide read covers at most 25 services and reports what it couldn’t reach in errors rather
than failing.
get_events
The change timeline, newest first. resource takes a name as readily as an ID, resolved against types when
exactly one is given and otherwise only where no other type claims the name. A service’s timeline includes its
tasks’ entries unless types leaves out task.
get_metrics
Charts or ranks CPU, memory, or network use. It takes a target and a metric rather than PromQL, resolves the service or node against Cetacean’s cache, and checks the caller’s read grant before querying.
Asking for top turns it into a ranking: target: "cluster" ranks the busiest services, or nodes with
by: "node", and target: "node" with an id ranks the services on that host, which answers why a node is
hot.
Metrics need Prometheus, plus cAdvisor for service metrics and node-exporter for node metrics; without them the tool says metrics are unavailable rather than returning empty series.
get_recommendations
The recommendation engine’s current findings, the same ones cetacean://recommendations
returns, with totals counting what the caller may read. A finding with a remedy carries it as fix, naming the
tool to call, for example {"tool": "update_service", "section": "resources"}. A finding whose REST fixAction
route has no MCP tool behind it reports no fix.
watch
Blocks until a service has settled, then reports whether it did. It exposes the same convergence rule the converging mutations use, as a read; the wait can’t be canceled once started.
Service lifecycle
Running services: how many replicas, which image, and starting over. All four converging tools accept task augmentation and return a summary of where the service ended up rather than its full spec:
{
"id": "web",
"name": "web",
"image": "nginx:1.27",
"mode": "replicated",
"replicas": 5,
"running": 5,
"state": "running",
"version": 42
}
running is the live count, state is the same derivation the dashboard and the API report,
and version is the Swarm version index for a caller doing its own concurrency checks. replicas is omitted for a
global service.
scale_service
Sets the desired replica count. 0 stops every task without removing the service.
update_service_image
Sets the container image, triggering a rolling deploy per the service’s update policy.
rollback_service
Reverts to the previous spec. Only one previous spec is retained, so calling twice doesn’t unwind further.
restart_service
Forces a rolling restart by bumping ForceUpdate. No other spec field changes.
remove_task
Deletes a task; Swarm reschedules a replacement, so this is a forced reschedule.
remove_service
Deletes a service and stops every task. Doesn’t delete the volumes, networks, configs, or secrets it referenced.
Service configuration
Editing a service’s spec. These tools return the section they changed rather than the whole service:
{
"id": "web",
"name": "web",
"version": 42,
"section": "resources",
"details": {
"cpuLimitCores": 2,
"memoryLimitBytes": 1073741824
}
}
details reports the edited section as describe would, naming environment variables and log driver options
without their values—so raising a CPU limit never hands back the service’s credentials.
update_service_secrets, update_service_configs, and update_service_mounts replace their set wholesale:
pass every entry the service should end up with, because one left out is detached—and a container may lose
data it was writing to a dropped mount.
update_service
Changes one section of a service’s spec. section takes one of ten names, and value that section’s new value:
| Section | Value | Semantics |
|---|---|---|
env | Merge-patch object | A string sets a key, null deletes it, an omitted key is preserved |
labels | Merge-patch object | Same as env; the only service section that doesn’t trigger a rolling deploy, along with the two policies |
resources | ResourceRequirements | CPU and memory limits and reservations |
placement | Placement | Constraints, preferences, max replicas per node, platforms; may reschedule tasks |
ports | Array of PortConfig | Drops connections to any port it removes or remaps |
update-policy | UpdateConfig | Applies to the next spec change |
rollback-policy | UpdateConfig | Applies to rollback_service |
log-driver | Driver | Name plus options; routes subsequent log lines |
healthcheck | Probe | test as an array (["CMD-SHELL", "curl -f localhost || exit 1"]), plus interval, timeout, startPeriod, retries. Durations are strings such as "10s". An empty test removes the check |
command | command plus args | The entrypoint and what follows it, the split Docker makes. Omitting one clears it |
Every section other than env and labels replaces its section wholesale: a field you omit is cleared.
Secrets, configs, and mounts aren’t sections here. Each takes a list rather than a spec fragment, and
attaching a secret checks a read grant on the secret as well as a write grant on the service, so each is a tool
of its own. Passing one as a section is refused with the name of the tool to use.
update_service_secrets
Replaces the complete set of secrets a service receives.
update_service_configs
Replaces the complete set of configs a service receives.
update_service_mounts
Replaces the complete set of mounts: named volumes, host bind mounts, tmpfs.
A bind mount hands the container the host’s filesystem at that path, and binding /var/run/docker.sock gives
it control of the whole cluster, and a volume with driver options can bind the host too. Below level 3 a bind
mount the service doesn’t already have is refused, and so is a named volume the manager doesn’t hold as a plain
local volume; at level 3, update_service_mounts does it if asked.
The tool can’t set bind options, so a bind the service has with a propagation mode reads as changed: below level 3 it’s refused, and at level 3 resending it drops the mode.
Cluster resources
Configs, secrets, networks, and volumes. Swarm secrets and configs are immutable, so rotating one is three
calls in order: create_secret for the replacement, update_service_secrets to
repoint each service that uses it (describe the secret first, its related array names them), then
remove_secret once nothing references it. Configs follow the same sequence, and unlike a secret a config’s
content can be read back, so use a secret for anything sensitive.
create_secret
Creates a Swarm secret.
create_config
Creates a Swarm config.
remove_secret
Deletes a secret. Fails if any service still references it.
remove_config
Deletes a config. Fails if any service still references it.
remove_network
Deletes an overlay network. Fails if a service is attached; ingress and docker_gwbridge can’t be removed.
remove_volume
Deletes a volume by name. Fails if a container is using a local volume, even with force, which there only
overrides an error from the volume driver. On a cluster volume, force deletes it even while it is in use.
Nodes
Where work is allowed to run, and who votes in Raft.
update_node_labels
Patches a node’s labels (JSON Merge Patch).
update_node
Changes a node’s availability or role.
Draining a manager may destabilize the swarm, so prefer draining workers. Promoting a node adds a Raft voter
and demoting removes one: never reduce the manager count below the quorum threshold, and on a three-manager
cluster demote at most one at a time. Node labels stay a tool of their own,
update_node_labels, at level 2, so a deployment that lets an agent relabel a node doesn’t also let it demote a manager.
Resources
Resources are read-only views returned as application/json. Three are static, eleven are URI templates.
cetacean://cluster- Node, service, task, and stack counts, node readiness, task counts by state, total and reserved CPU and memory, the largest node’s capacity, converged and degraded service counts, last sync time
cetacean://recommendations- Current recommendation engine findings, each with severity, category, target resource, and rationale
cetacean://history- The most recent create, update, and delete events across every resource type, newest first, up to 100. A task event is named after its service (
demo_flaky.3) rather than by task ID;resourceIdstill carries the ID
Every numeric field in cetacean://cluster names its unit: totalCPUCores, reservedCPUCores,
maxNodeCPUCores, totalMemoryBytes, reservedMemoryBytes, maxNodeMemoryBytes. Both CPU figures are in
cores.
Templated resources return the same compact digest the describe tool builds. The exceptions are
services/{id}/logs, a raw log stream, and the two compose documents, which are YAML rather than JSON.
cetacean://nodes/{id}- Node digest, by ID or hostname
cetacean://services/{id}- Service digest, by ID or name, with cross-references
cetacean://services/{id}/logs- Service logs merged across replicas, subscribable; not a digest
cetacean://services/{id}/compose- The service as a one-service Compose file; everything it references is external
cetacean://tasks/{id}- Task digest; the parent service and node are in
related, not top-level fields cetacean://stacks/{name}- Stack digest, rolling up its member resources
cetacean://stacks/{name}/compose- The stack as a Compose file; secrets and configs are referenced, never exported
cetacean://configs/{id}- Config digest; the payload’s size in bytes, never the payload
cetacean://secrets/{id}- Secret digest; the payload is never read, not even its length
cetacean://networks/{id}- Network digest, with the attached services
cetacean://volumes/{name}- Volume digest, keyed by name
Subscriptions
Clients call resources/subscribe with a URI. When the underlying cluster state changes, the server sends
notifications/resources/updated for that URI and the client re-reads.
notifications/resources/list_changed fires when resources are created or removed. Both are filtered by grants per
notification: a client is only notified about resources its identity can read.
Argument completion
Templated resource URIs and prompt arguments complete, offering resource names rather than IDs. Matching is
a case-insensitive substring, so typing prometheus offers monitoring_prometheus. Completions are capped at
100 values and are filtered by the caller’s grants like every other read.
Compact resource shapes
Tools and resource reads never return raw Docker objects—they’re enormous, and they don’t carry the field a caller actually wants, since a service’s health is derived from its tasks. Every read returns one of two shapes.
Row is one entry in a list, returned by find. Every row carries id, name, and type (singular:
service, node, task, and so on). stack, state, and detail (the most identifying secondary fact,
such as a service’s image, a node’s role, or a network’s driver) are omitted where they don’t apply.
desired and running are populated only where a replica count means something:
- services set both, the desired replica count and how many are running
- stacks set
desiredalone, counting member services rather than replicas - nodes, tasks, configs, secrets, networks, and volumes set neither
Digest is the detail view of one resource, returned by describe and by every templated
cetacean://<type>/{id} read. Alongside id, name, type, and state it adds:
reason- The cause Swarm gave for a non-healthy
state; omitted when the state is healthy since- When the current state began: the oldest still-live failing task’s timestamp, or the resource’s own last-updated time
details- A type-specific map of facts, such as a service’s image, replica counts, reserved CPU and memory, ports, and placement constraints, or a node’s role and capacity
related- Always an array, even when empty: the resources this one references or is referenced by
recentFailures- Always an array: the task failures behind a failing state, newest first, capped at 5. Only a service digest populates it
restarts- A service’s involuntary task terminations as
lastHourandlastWeekcounts. Services only
restarts is the only field that reveals a restart loop. state reads running whenever a replica happens
to be up, and recentFailures drops tasks the orchestrator has already replaced, so a service crash-looping
every few seconds otherwise describes as healthy. lastHour answers whether it’s failing now; the ratio
between the two windows answers whether the fault is new.
Every numeric field in details names its unit, such as cpuLimitCores (a float, in cores) and
memoryLimitBytes, or is reported as a duration string like "10s" (healthcheckInterval, an update
policy’s delay and monitor). Docker’s own types express these as unlabeled NanoCPU or nanosecond
integers. Environment variables are reported as envNames: names only, never values.
The raw: true option
Both find (when type is given) and describe accept raw: true, adding the untouched Docker record to
the result under raw. Use it only when a specific field a row or digest omits is needed; the compact shapes
exist to avoid handing an agent a several-hundred-line object.
Raw records ride beside the compact shape rather than replacing it: find adds a raw array with one record
per row, describe a raw object beside the digest.
Prompts
Prompts are named sequences a client offers from a menu. Picking one seeds the conversation with an investigation or a runbook. A prompt only seeds the conversation; every read and write it then describes goes through the normal tools, grant checks included.
A prompt’s level is the highest level of the tools it walks, so it’s never offered where one of its steps
would be refused. At the default level 0 you get the three diagnostic prompts; level 1 adds roll_back_service.
Prompts are filtered by grants too, all or nothing: you are offered a prompt only if you can use every tool it walks and can read every resource type in its Reads column. A caller whose grants match nothing is offered none.
diagnose_serviceserviceReadsserviceexplain_unschedulableserviceReadsservice, nodereview_capacityroll_back_serviceserviceReadsserviceright_size_serviceserviceReadsservicedrain_nodenodeReadsnode, serviceApps
A host that supports the MCP Apps extension can render Cetacean’s data as an interactive view instead of JSON.
Cetacean advertises io.modelcontextprotocol/ui and serves each widget as a resource. Each tool names its
widget in _meta, so a host knows which view fits the result; the same tool called from a client without app
support returns ordinary JSON.
MCP Apps are self-contained: they load nothing from the network and read data only through Cetacean’s MCP tools, so a widget shows exactly what the calling identity’s grants allow.
Picking a finding in the recommendations widget sends the model a message in the user’s name, and that message quotes resource names and finding text from the cluster. Whoever can name a service or node chooses part of it. The widget passes those fields as one JSON value marked as cluster data, but the model still reads them, so treat a follow-up from a widget as you would any output that quotes the cluster.
ui://cetacean/tablefind resultui://cetacean/topologyget_topology resultui://cetacean/logsget_logs resultui://cetacean/metricsget_metrics resultui://cetacean/recommendationsget_recommendations result