Skip to main content
Version: 13 - TBD

MCP server for LLM agents

Cloudomation ships a built-in MCP (Model Context Protocol) server. It lets your own LLM agents (Claude, Cursor, OpenAI agents, and any other MCP-compatible client) use Cloudomation as one tool among others to create, run and monitor automation content.

The MCP server is served by your workspace, so there is nothing extra to deploy.

Endpoint

https://<your-workspace>.cloudomation.com/api/latest/mcp

The server uses the MCP Streamable HTTP transport. All interaction happens as request/response over POST.

Authentication

Agents authenticate with an API key on every request, sent either as a bearer token or via a dedicated header:

Authorization: Bearer cldm_<prefix>_<secret>

or

x-cloudomation-api-key: cldm_<prefix>_<secret>

The same API key also works against the full REST API and the GraphQL API.

Creating an API key

  1. Open the Cloudomation Engine UI and go to the user you want the agent to act as (see "Scoping access" below).
  2. In the user's detail page, find the API keys (MCP / programmatic access) section.
  3. Click Create API key, give it a descriptive name, and copy the key. The key is shown only once.

API keys can also be minted via the REST API:

curl -X POST \
-H "x-cloudomation-session-id: <your-session-id>" \
-H "Content-Type: application/json" \
-d '{"name": "Claude agent", "user_id": "<agent-user-id>"}' \
https://<your-workspace>.cloudomation.com/api/latest/api_key

The response contains the plaintext token. Revoke a key with DELETE /api/latest/api_key/{id} or from the UI.

Scoping access (RBAC)

An API key is bound to a Cloudomation user identity and inherits exactly that user's permissions. To give an agent fine-grained access:

  1. Create a dedicated user for the agent (for example claude-agent).
  2. Create a role with role_permission entries scoped to the specific projects, record types and operations the agent should be allowed to use (for example: READ/CREATE/UPDATE on FLOW and EXECUTION in a single project).
  3. Assign the role to the agent user.
  4. Mint an API key for the agent user.

Because every MCP tool call runs through the same permission checks as the REST API, the agent can only ever see and change what the bound user is allowed to. If a tool call fails with a permission error, the bound user is missing a role.

An execution started with run_flow runs as its own identity, which by default inherits the roles the calling user holds with the propagate flag set. A role assigned to the user without propagate is not inherited by the executions it starts. To control this per call, pass the roles argument to run_flow: a list of {name, propagate} objects assigns exactly those roles to the new execution, each with its own propagate flag, and an empty list starts the execution with no roles. This lets an agent grant an execution a role the calling user holds only non-propagating, or deliberately drop privileges for a run.

Connecting a client

Most MCP clients accept a remote server URL plus custom headers. For example, a generic configuration looks like:

{
"mcpServers": {
"cloudomation": {
"url": "https://<your-workspace>.cloudomation.com/api/latest/mcp",
"headers": {
"Authorization": "Bearer cldm_<prefix>_<secret>"
}
}
}
}

Refer to your client's documentation for the exact configuration format. MCP prompts defined in your workspace appear in the client's prompt picker (slash commands) once the server supports the prompts capability.

A flow can be an MCP client

MCP's transport is JSON-RPC 2.0, so a Cloudomation flow can itself act as an MCP client — call tools/list / tools/call on this or any other MCP server with the JSONRPC connector.

Available tools

The server exposes the following tools (discoverable via MCP tools/list):

ToolDescription
run_flowStart an execution of a flow.
get_executionGet the status, message and output of an execution.
get_execution_logsGet the log entries of an execution.
wait_for_executionsBlock until one or more executions end, then return their details.
list_executionsList executions newest-first, filtered by flow and/or status.
list_record_typesList record types (FLOW, PROJECT, CONNECTOR, …).
list_subrecord_typesList subrecord types (OBJECT_TEMPLATE_ATTRIBUTE, RECORD_METADATA, …).
describe_record_typeJSON Schemas and field lists for a record type.
describe_subrecord_typeJSON Schemas and field lists for a subrecord type.
list_recordsList records of one type (uses that type's REST resource and filters).
list_subrecordsList subrecords of one type, scoped to a parent record.
get_recordGet one record by id or name; folds the record's recent comments into the result (see Record comments in get_record).
grep_recordSearch one record's large text/JSON body for a pattern; returns only the matching lines with context — token-lean vs. fetching the whole body with get_record.
read_record_linesRead a line-range slice of one record's body (token-lean paging of a big script/value/file).
search_recordsFull-text search across all indexed records — locate a record by any word it contains, ranked and permission-filtered.
search_toolsDiscover tools on demand: return the full definitions of the tools whose name/description best match a query (or fetch exact names with select:). The entry point to any tool hidden by progressive tool disclosure.
get_subrecordGet one subrecord row by id.
create_recordCreate a record of any type.
update_recordPatch a record (replace whole fields by name).
patch_recordEdit one record's large plain-text body (a flow script, file content, hook script) with find/replace edit blocks — token-lean and stale-write-guarded.
delete_recordDelete a record (soft-delete to trash by default; permanently / recursive flags).
create_subrecordCreate a subrecord row (including RECORD_METADATA).
update_subrecordPatch a subrecord row.
delete_subrecordDelete a subrecord row.
list_pending_changesList writes staged with commit=false that are not yet committed to git.
commit_pending_changesCommit previously staged (commit=false) writes to git as one commit.

Most workspace content is accessed through the generic record/subrecord tools. Dedicated execution tools (run_flow, list_executions, get_execution, get_execution_logs, wait_for_executions) remain for the common run-and-monitor workflow.

Read cloudomation://usage-guide for examples (listing projects/flows/connectors via list_records, metadata via RECORD_METADATA subrecords, type-specific filter_ fields).

Restricting the built-in tool surface

An installation can ship a locked-down MCP surface by disabling individual built-in tools with the MCP_DISABLED_TOOLS workspace configuration option — a list of built-in tool names. A disabled tool is neither advertised in tools/list nor callable via tools/call (it is treated as if it did not exist). Typical uses:

  • Read-only surface — disable the write/content-maintenance tools (create_record, update_record, patch_record, delete_record, create_subrecord, update_subrecord, delete_subrecord, commit_pending_changes, run_flow) so agents can only read.
  • Curated surface — disable all built-in tools and expose only a chosen set of user tools.

The default (empty list) enables every built-in tool. Unknown names in the list are ignored. This setting affects only built-in tools; user tools are governed by RBAC and are unaffected.

Disabling is the "enabled" axis of the built-in tool surface. A separate "announced" axis lets you hide a tool from the initial list while keeping it callable — see Progressive tool disclosure.

Listing parameters

list_records, list_subrecords, and list_executions share optional listing parameters:

ParameterDescription
fieldsList of field names to return (each tool has a sensible default set).
orderOrder by a field; - descending, + ascending (e.g. -created_at). Defaults to -modified_at, or -created_at for executions.
filter_Extra filter on type-specific columns, AND-combined with convenience filters. A {"field","op","value"} comparison or an {"and":[...]} / {"or":[...]} of such. Ops: eq, neq, like, notlike, lt, gt, lte, gte, set, unset, in, notin.
limit / offsetPage through large result sets.

To check the status of a flow's runs, use list_executions with flow_id or flow_name (optionally status): it returns status, message and timestamps directly and is ordered newest-first, so the latest run is the first result.

get_execution_logs and list_pending_changes accept the same limit / offset / order paging parameters, so you can page long log or pending-change lists (get_execution_logs defaults to +created_at, oldest first).

Listing custom objects by template

A custom object carries a second type layer on top of the CUSTOM_OBJECT record type: its object template. To list only the custom objects of one template, pass object_template_name to list_records:

{
"record_type": "CUSTOM_OBJECT",
"object_template_name": "support-ticket",
"filter_": {"field": "value.status", "op": "eq", "value": "open"}
}

object_template_name is only valid for record_type="CUSTOM_OBJECT"; it AND-combines with name and filter_.

Attribute projection (custom objects)

A fields entry of the form value.<attr> returns just that attribute of a custom object's JSON value instead of the whole value blob — a token-lean way to read only the fields you need. It works on both get_record and list_records and combines with plain fields:

{
"record_type": "CUSTOM_OBJECT",
"object_template_name": "support-ticket",
"fields": ["id", "name", "value.status", "value.priority"]
}

Record comments in get_record

Comments (RECORD_COMMENT subrecords) are a threaded discussion trail attached to any record — the place humans leave feedback, questions, and re-open notes. Because they are a separate subrecord type, they do not appear in a plain record fetch, so an agent could read, act on, or close a record without ever seeing that a person had commented on it.

To prevent that, get_record folds a record's most recent comments into its result under a comments key: an array of {id, author, created_at, body} (oldest first), with assignee, parent_comment_id, and resolved_at / resolved_by included only when set. author is resolved to a display name. The list is bounded to the 20 most recent comments; when older ones exist, comments_truncated: true and a comments_note point you to list_subrecords(subrecord_type="RECORD_COMMENT", parent_record_id="<id>") for the full thread. A record with no comments has no comments key at all, so the lookup adds no output for the common case.

Pass include_comments: false to skip the comment lookup when you do not need the discussion (for example, a bulk read where feedback is irrelevant).

Token-lean body access

The generic read tools return a record's whole body. For large bodies (a long flow script, a big custom-object value, a file content) three tools let an agent read and edit only the part it needs, spending far fewer tokens:

  • grep_record — search one record's body for a regular-expression (or, with fixed: true, a literal) pattern and get back only the matching lines with surrounding context, plus a total match count and a truncated flag. By default it searches the record type's primary body field (script for FLOW/WRAPPER/SCHEDULE, value for CUSTOM_OBJECT/SETTING/CONNECTOR/SCHEMA, content for FILE); pass field to target another column (e.g. on_create on an OBJECT_TEMPLATE). JSON fields are searched as their pretty-printed JSON. For a search across many records use list_records with filter_ (or search_records for full-text) instead.
  • read_record_lines — read a line_count slice of a body starting at start_line (1-indexed). Line numbers match the UI code view and grep_record, so a typical flow is grep to find the line, then read around it; page on with start_line = end_line + 1.
  • patch_record — edit a plain-text body (a flow script, file content, an object-template hook script) by sending only the changed hunks as an ordered edits array of {old_string, new_string} find/replace blocks, instead of resending the whole body with update_record. old_string must match the current content exactly and (unless replace_all is true) uniquely; if it does not, the patch is refused rather than applied — a built-in stale-write guard against clobbering a concurrent change. Edits apply in order and atomically (all or none). Structured JSON value fields are not supported — use update_record for those.

Deleting records

delete_record soft-deletes by default: the record is moved to the trash and can be restored. Two flags change that:

  • permanently (default false) — hard-delete, bypassing the trash. This is irreversible. RBAC and referential rules still apply, and tables that cannot be trashed are always deleted permanently regardless of the flag.
  • recursive (default true) — also delete the record's child/dependent records. Set it to false to delete only the record itself; the delete then fails if the record still has children that block it.

Batching writes into one commit

In a git-synced project or bundle, every write is committed to git. The write tools (create_record, update_record, patch_record, delete_record and their subrecord equivalents) commit immediately by default. To group several related writes into a single, well-described commit, pass commit: false on each write and then finalize them together:

  1. Make the writes with commit: false — they are staged but not yet committed.
  2. Optionally inspect what is staged with list_pending_changes (filterable by project_id / bundle_id / record_type).
  3. Call commit_pending_changes with the container_type (project / bundle / workspace), its id, and a message. With no record_names / git_log_ids, it commits everything currently pending for that container.

A workspace may enforce immediate commits, in which case commit is unavailable and every write commits on its own.

Set the commit message on a single write

For a single write you do not need the staging dance: include a commit_message in the payload of create_record / update_record / delete_record (and their subrecord equivalents). The immediate per-record commit then uses that message — one call, instead of a commit: false write followed by commit_pending_changes. Omitting commit_message keeps the autogenerated change note.

{
"record_type": "FLOW",
"id": "my-flow",
"payload": {
"description": "tune retry backoff",
"commit_message": "flow(my-flow): tune retry backoff"
}
}

commit_message is honoured on create, update, delete, move and rename. It is ignored when the write does not commit (commit: false); use commit_pending_changes's message for the batched case above.

Provenance: created_via

Records created through the MCP surface are tagged with a created_via=mcp RECORD_METADATA row (best-effort; it never fails the create). This lets you tell agent-created records apart from those made in the UI or via other API clients — filter on the metadata key/data with list_subrecords or the record_metadata filters.

Progressive tool disclosure

A workspace that accumulates many tools — a large catalog of user tools, plus every built-in — forces each connecting agent to ingest the full name, description and input schema of every tool before it does any work. On a big surface that fixed context cost is significant.

Progressive tool disclosure removes it: a tool can be gated — omitted from tools/list, yet left fully callable via tools/call and discoverable on demand through the search_tools tool. The initial list stays small (the tools an agent needs up front), and everything else is loaded only when it is actually needed. This is opt-in per workspace and changes nothing by default.

Gating is orthogonal to disabling:

In tools/list?Callable?Found by search_tools?
Announced (default)yesyesyes
Gatednoyesyes
Disablednonono

Discovering tools with search_tools

search_tools is the entry point to every gated tool. It is always present in tools/list and can be neither disabled nor gated.

  • query (string) — ranks all enabled tools (built-in and user tools) by how well their name and description match, and returns the full MCP definitions (name, description, inputSchema, outputSchema, annotations) of the best matches, so the agent can call them immediately.
  • max_results (int, optional) — cap the number of matches returned.
  • select:<name>,<name> — pass this as the query to fetch specific tools by exact name instead of ranking, when the agent already knows what it wants.

Gated and announced tools are both searchable; only disabled tools are excluded (they do not exist as far as the surface is concerned).

Gating built-in tools

Gate built-in tools with the MCP_GATED_TOOLS workspace configuration option — a list of built-in tool names, the "announced" axis alongside MCP_DISABLED_TOOLS's "enabled" axis. The default (empty list) announces every enabled built-in, exactly as if the setting were absent. search_tools itself can never be gated. Unknown names are ignored.

Gating user tools

Each user tool carries an announced flag that places it on the same axis (see Announcing user tools). New user tools are gated by default, so a growing catalog does not inflate the initial list; flip announced on for the handful an agent should always see.

User tools

Built-in MCP tools let an agent maintain and monitor workspace content. User tools add a second class: domain-specific capabilities you define for agents (for example lookup_customer, provision_vm, run_monthly_report).

Each user tool is an MCP_USER_TOOL record that:

  • exposes the record name as the MCP tool name in tools/list and tools/call
  • uses the record description as the MCP tool description
  • references a flow by flow_id (the implementation)
  • takes argument schemas from that flow's input_schema (MCP inputSchema)
  • returns the flow execution's output_value when the agent calls the tool
  • advertises that flow's output_schema as the MCP outputSchema — and returns the result as structuredContent — whenever the schema is an object schema (see Return contract)
  • exposes optional MCP readOnlyHint / destructiveHint annotations via read_only_hint and destructive_hint on the record (defaults: read-only off, destructive on)
  • carries an announced flag that controls whether the tool appears in the initial tools/list or is hidden behind progressive tool disclosure (see Announcing user tools)

Configure user tools in the Engine UI under Create → Configuration → MCP user tool, or via the REST/GraphQL API and MCP record tools (list_records(record_type="MCP_USER_TOOL"), create_record, …).

How invocation works

When an agent calls a user tool:

  1. Cloudomation creates a flow execution with input_value set to the tool arguments. The execution is linked back to the tool via its mcp_user_tool_id field, so you can list all executions started by a tool.
  2. The MCP server waits for the execution to finish, up to a bounded poll window (min(timeout_sec, 45s) — see Timeouts). A flow that finishes inside the window returns its result directly.
  3. On success (isError: false) the result contains the execution's output_value; when the flow declares an object output_schema the same value is also returned as MCP structuredContent (see Return contract).
  4. When the flow ends in error, the result is an error (isError: true) that includes execution_id so the agent can inspect logs with get_execution / get_execution_logs. When the flow is still running at the end of the poll window, the result is a non-error continuation handle (see Timeouts) — the flow keeps running server-side and the agent polls it to completion via execution_id.

The MCP user tool detail screen in the Engine UI shows the tool's latest executions and a link to the execution live monitor filtered to that tool (filtering executions by mcp_user_tool_id).

Timeouts

A user-tool call is synchronous: the MCP server holds the request open while the backing flow runs. Three independent layers bound how long a single call can block, and the effective limit is the smallest of them:

LayerLimitSet by
MCP client per-call timeoutclient-specific (commonly around 60s)the calling agent's MCP client
Ingress (nginx) proxy_read_timeout600sthe Cloudomation deployment
Backend poll windowmin(timeout_sec, 45s) (timeout_sec default 300s)the MCP_USER_TOOL record, per tool

To stay safely under the client timeout — which is outside Cloudomation's control and differs between clients (IDE agents, CLI agents, and custom clients each set their own) — the server bounds its own synchronous wait to a poll window of min(timeout_sec, 45s). A tool's timeout_sec lowers the window when set below 45s; a timeout_sec above 45s leaves the window at 45s, and a timeout_sec above the client's own limit has no effect because the client aborts the call first.

The execution wait endpoint the continuation handle points at, GET /api/latest/execution/<id>/wait?timeout=<seconds>, blocks server-side under the same 20s poll window per request: each request returns as soon as the execution ends or, at the latest, when the window (or the remaining timeout budget, whichever is smaller) elapses. A poller therefore re-issues the GET at most once every 20s, never faster.

What the agent receives depends on where the flow is when the window closes:

  • Flow finished inside the window — the call returns normally: isError: false with the execution's output_value (and structuredContent when the flow declares an object output_schema; see Return contract) on success, or isError: true with the error message and execution_id when the flow ends in error.

  • Flow still running at the end of the window — the call returns a non-error continuation handle (isError: false). The flow keeps running server-side, and the handle carries the execution_id, the execution's live status, and a poll descriptor telling the agent how to follow it to completion:

    {
    "execution_id": "6908c9e4-…",
    "status": "RUNNING",
    "message": "Execution still running after 45s. It keeps running server-side — await it via the blocking poll handle below …",
    "poll": {
    "method": "GET",
    "url": "/api/latest/execution/6908c9e4-…/wait?timeout=20",
    "done_when": "the returned status begins with 'ENDED_'. This endpoint BLOCKS server-side for up to 20s per request and returns as soon as the execution ends; if status has not yet reached a terminal 'ENDED_' value, GET the same URL again to keep waiting."
    },
    "cancel": {
    "tool": "cancel_execution",
    "arguments": { "id": "6908c9e4-…" },
    "rest": { "method": "PATCH", "url": "/api/latest/execution/6908c9e4-…/process/cancel" },
    "when": "call this to abandon the run if it becomes stuck or is no longer needed — it cancels this execution and all of its children."
    }
    }

    Following the handle costs no further agent context until the flow ends. The poll.url points at the execution wait endpoint, GET /api/latest/execution/<id>/wait?timeout=<seconds>, which blocks server-side until the execution ends or timeout seconds elapse (bounded per request by the 20s poll window; see Timeouts), then returns the execution record. Blocking means the caller re-issues the GET only once per window — not on a fixed sleep interval — so it costs fewer round-trips and returns with lower latency the moment the flow ends. An agent, or a background shell loop, re-issues the GET until status begins with ENDED_, then reads output_value:

    # Await a continuation handle to completion via the blocking wait endpoint.
    eid=6908c9e4-…
    while :; do
    r=$(curl -s -H "Authorization: Bearer $TOKEN" \
    "https://<workspace>/api/latest/execution/$eid/wait?timeout=20")
    status=$(echo "$r" | jq -r .status)
    case "$status" in
    ENDED_*) echo "$r" | jq .output_value; break;;
    esac
    done

    Omit timeout to block until the execution ends (the request bounces the client back to the same URL every 20s poll window). Provide timeout to bound the total wait: when the execution has not ended by then, its current record is returned with HTTP 200 so a caller with a fixed deadline gets a definite response.

    The handle also carries a cancel descriptor for abandoning a run that is stuck or no longer needed. It offers two equivalent affordances: the cancel_execution MCP tool (call it with the handle's execution_id), and the PATCH /api/latest/execution/<id>/process/cancel REST endpoint. Either cancels the execution and all of its children (recursive). Cancellation is applied asynchronously — the execution reaches ENDED_CANCELLED shortly after; poll get_execution or the wait endpoint to confirm. Cancelling an already-ended execution is a no-op.

Because the server returns a handle rather than holding the connection until a client abort, a flow that outlives the poll window yields a pollable execution id instead of an orphaned call. It is therefore safe to let a flow run longer than the window — the agent recovers its result through the handle. A flow that starts the long work and returns an identifier immediately still keeps the common call a single fast round-trip.

Return contract

What the agent receives back depends on whether the backing flow declares an object output_schema:

  • Flow has an object output_schema (type: object) — the tool advertises that schema as its MCP outputSchema in tools/list, and a successful call returns the execution's (already schema-validated) output_value as MCP structuredContent alongside the text result. Agents that understand outputSchema can consume the structured result directly.
  • No output_schema, or a non-object one — nothing is advertised and the tool keeps its raw passthrough: the call returns output_value as text only. This is the backwards-compatible default.

A successful call returns isError: false; a failed or timed-out call returns isError: true with the error message and execution_id.

Make a tool first-class

Declare both an object input_schema and an object output_schema on the backing flow. If either is missing the tool still works (raw passthrough), but the server logs a warning — a flow without an input_schema accepts unstructured arguments, and one without an output_schema cannot advertise outputSchema. Both schemas make the tool self-describing to agents.

License usage

Each user tool call consumes one connection from your connection allowance, the same way an incoming productive webhook call does. The connection is counted when the execution is created; if the allowance is exhausted the call fails.

Define input_schema and output_schema on the flow, not on the tool record. Changing the flow updates the MCP contract on the next tools/list.

Set read_only_hint when the flow only reads data (for example a lookup or status check). Leave destructive_hint enabled (default) when the flow may delete data, call external systems with irreversible effects, or otherwise do something an agent should confirm first. MCP clients use these hints for auto-approval and confirmation dialogs; they do not restrict what the flow can do.

Announcing user tools

Because the first-class user-tool model invites a workspace to accumulate many domain tools — one per recurring operation — user tools take part in progressive tool disclosure so a large catalog does not inflate every agent's initial context.

Each MCP_USER_TOOL record has an announced boolean:

  • announced: true — the tool appears in tools/list, as always.
  • announced: false (the creation default) — the tool is gated: omitted from tools/list, but still callable via tools/call and discoverable through search_tools by name or description.

New user tools are gated by default, so growing the catalog is free in context terms — set announced: true on the handful of tools an agent should always see up front. Whether a tool is announced or gated, it is fully callable and searchable; gating only affects the initial listing.

Upgrade behaviour

The migration that introduces announced sets it to true for every pre-existing user tool, so upgrading a workspace keeps its current tools in tools/list. The gated default (false) applies to user tools created afterwards.

Audit logging

Every user-tool invocation leaves a record_log audit entry, so the workspace keeps a durable trail of who called which tool, when, and with what outcome. Each entry is authored by the calling identity (the user behind the API key).

  • A successful dispatch writes an INFO entry on the tool's own record, linked to the backing execution. The entry records only the argument keys — the argument values may contain secrets, so they are read from the linked execution's input_value rather than copied into the log.
  • A failed call — the backing execution ends in error — writes an ERROR entry on the tool's record, linked to the execution.
  • A call that returns a continuation handle (the flow is still running at the end of the poll window) writes an INFO entry on the tool's record, linked to the still-running execution.
  • A call rejected before any execution is created also writes an ERROR entry:
    • an unknown or disabled tool name is logged against the workspace record (there is no tool record to attach it to);
    • a tool with no flow configured, or a call whose arguments are not a JSON object, is logged against the tool's record.

Read a tool's entries on its detail screen, or query record_log for the tool record or the workspace record. Because rejected probes are logged as well, the trail doubles as a security signal: it captures attempts to call tool names that do not exist or are disabled.

Example

  1. Create a flow get-customer with an input_schema requiring customer_id and an output_schema describing the returned customer object.
  2. Create an MCP user tool named lookup_customer pointing at that flow, add a description, set Enabled.
  3. The agent sees lookup_customer in tools/list and can call it with {"customer_id": "C-42"} without knowing the underlying flow name.

Naming rules

  • The tool name must be unique across the workspace (same rule as any record name).
  • Names matching a built-in MCP tool (for example run_flow, list_records) are rejected when saving. Built-in tools always take precedence at runtime.
  • Prefer descriptive names such as lookup_customer or provision_vm.

RBAC

The agent user's API key needs at least:

  • READ on MCP_USER_TOOL (to list and invoke tools)
  • CREATE and READ on EXECUTION
  • READ on the referenced FLOW

Grant these via roles scoped to the relevant projects.

Real-world examples

User toolTypical argumentsFlow implements
reset_user_password{ "username": "alice" }AD/LDAP reset and notification
check_server_health{ "hostname": "app-01" }Monitoring connectors, status summary
provision_environment{ "template": "small-linux", "ttl_hours": 24 }DevStack or internal provisioning
export_sales_summary{ "from_date", "to_date", "region" }Warehouse query and report output
integrationtest_status{ "limit": 5 }Wraps an existing test orchestrator flow

Resources (discovery and typings)

The server exposes MCP resources so an agent can learn how to use Cloudomation and author correct flows:

Resource URIContents
cloudomation://usage-guideHow to connect, authenticate and author flows.
cloudomation://developer-artifactsREST endpoints and layout for local editor typings and JSON Schemas.
cloudomation://connector-typesThe available connector types and a description of each.
connector-type://<TYPE>The description and input/output JSON schemas of one connector type (e.g. connector-type://REST).
flow_api://<module>The flow_api typings (one per module), with full docstrings.

The flow_api typings are the authoritative type definitions for the system and this objects and all resource classes. An agent should read them before writing or editing a flow script. They are also available over REST:

EndpointDescription
GET /api/latest/flow_api/typingsflow_api Python typings (?format=zip for archive)
GET /api/latest/schemas/exportJSON Schemas for export YAML files
GET /api/latest/schemas/connectorsJSON Schemas for connector values
GET /api/latest/developer-artifactsIndex of all developer artifact endpoints

See Local editor setup for manual download and editor configuration. Agents should follow the Local editor setup (agent-driven) section in cloudomation://usage-guide when the user edits flows or Cloudomation YAML locally.

Metadata and MCP prompts

Key/value metadata on records (including workspace agent instructions and MCP prompt templates) is stored as RECORD_METADATA subrecords. Use list_subrecords(subrecord_type="RECORD_METADATA", parent_record_id="<record-id>") and the subrecord CRUD tools.

The workspace record id is included in the MCP server's initialize instructions and in the cloudomation://usage-guide resource (section "This workspace"). Use that id as parent_record_id for workspace-level metadata.

KeyParent recordPurpose
agent_instructionsworkspaceFree-form guidance appended to MCP initialize
mcp_prompt:<slug>workspaceMCP prompt template (slug: [a-z][a-z0-9_-]*)

Example: set workspace instructions with create_subrecord(subrecord_type="RECORD_METADATA", payload={"record_id": "<workspace-id>", "key": "agent_instructions", "data": "..."}).

MCP prompts (customer workflows)

The server exposes MCP prompts so users can start repeatable agent tasks from their client (slash-command style). There are no built-in prompts — you define your own on the workspace as RECORD_METADATA subrecords with keys mcp_prompt:<slug>.

Example prompt data object:

{
"title": "Check integration test status",
"description": "Summarize the latest run-integrationtest executions.",
"arguments": [
{
"name": "flow_name",
"description": "Orchestrator flow name",
"required": false
}
],
"template": "Check integration test status on this workspace.\n\n1. list_executions(flow_name=\"{{flow_name}}\", limit=10)\n2. Summarize running/failed runs and link to failing child executions.\n\nDefault flow_name: run-integrationtest"
}

Use template (one user message) or a messages array (role + text per item). Placeholders {{argument_name}} are filled when the user runs the prompt.

Example templates worth creating (not shipped by Cloudomation):

  • integrationtest-status — monitor a test orchestrator flow (as above).
  • investigate-execution — required argument execution_id; pull status, logs, failure summary.
  • onboard-workspace — list projects, flows and connectors via list_records; produce a short workspace map.

Create and update prompts with create_subrecord / update_subrecord on RECORD_METADATA; remove with delete_subrecord. The MCP client lists them via prompts/list.

Instructions for agents

You can give agents additional guidance in three ways:

  • Workspace-level instructionsRECORD_METADATA key agent_instructions on the workspace (delivered automatically in MCP initialize).
  • MCP promptsRECORD_METADATA keys mcp_prompt:<slug> (user-invoked workflows).
  • Record descriptions — a flow or project description may contain agent-directed notes, but may also be ordinary documentation; agents treat clearly operational guidance as advisory only.

Instructions never widen what the API key is allowed to do; RBAC always applies.

Local editor setup (agents)

When the user edits flows or Cloudomation YAML locally, agents should read cloudomation://developer-artifacts and follow the Local editor setup (agent-driven) section in cloudomation://usage-guide to:

  1. Download flow_api/typings, schemas/export, and schemas/connectors (prefer ?format=zip).
  2. Extract into .cloudomation/ in the project root.
  3. Merge .vscode/settings.json with python.analysis.extraPaths and yaml.schemas from the export manifest.

Tell the user to re-run setup after workspace upgrades.

For manual setup without an agent, see Local editor setup.

Flow authoring contract

A flow is a Python module that defines a handler function:

import flow_api

def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
# automation logic
return this.success('done')
  • system is the gateway to the workspace.
  • this is the running execution.
  • inputs is a dict validated against the flow input schema.

See the flow_api reference for the full API.