MCP
Connecting a client to Cracken over the remote MCP server — the same surface as the External API — requires the Pro plan and above on Cracken Cloud; a key on a lower plan is rejected at authentication. Self-hosted deployments include it on every tier. Giving the agent external MCP servers during an operation is part of every plan.
The Model Context Protocol (MCP) connects Cracken with other AI tooling in two directions:
- Your client → Cracken. Point an MCP-capable client (Claude, Cursor, and others) at Cracken to create and inspect operations and resolve approvals as tools, authenticated with your Cracken API key. See Connect a client to Cracken.
- Cracken's agent → external servers. During an operation, the agent starts or connects MCP servers on a Tentacle to gain domain-specific tools and data sources beyond Cracken's built-in Vessels and tools. See Give the agent external MCP servers.
The two are independent — you can use either, both, or neither.
Connect a client to Cracken
Connect your AI client to Cracken over the Model Context Protocol, using your Cracken API key as a bearer token. Create a key in Settings → API keys (tenant owners only — the settings entry is hidden for other roles), then pick your client:
claude mcp add --transport http cracken \
https://api.app.cracken.ai/mcp \
--header "Authorization: Bearer crk_live_<key_uuid>.<secret>"
On a self-hosted instance, replace https://api.app.cracken.ai with your own Cracken API URL. The path stays /mcp.
Tools
| Tool | Arguments | Returns | Requires |
|---|---|---|---|
list_realms | none | array of realms the key can access | operations:read |
list_playbooks | realm_uuid (required) | array of playbooks in the realm (pin one when creating an operation) | operations:read |
list_models | none | array of LLM models available in this deployment | operations:read |
list_operations | realm_uuid (required), limit (1–100, default 100), offset (default 0), include_archived (default false) | array of operations | operations:read |
create_operation | realm_uuid, task, idempotency_key (required); naming_task, autonomous, playbook_uuid, playbook_path, parent_uuid, background, root_operation_id, context, fork_from_operation_id, operational_goal, operational_goal_enabled, planning_enabled, plan_node_id, final_output_contract, model_id, llm_config, loadout (optional) — see notes below the table | the created operation | operations:create; forks also need operations:read; non-manual autonomy also needs operations:control; plan_node_id also needs operations:message |
create_tabular_operation | operation_uuid, task, idempotency_key (required); csv_file (filename, UUID, or attachment path), background (default false). Omit csv_file only when the parent has one CSV attachment. | the created tabular sub-operation | operations:create; blocking mode also needs operations:message |
get_operation | operation_uuid (required), include_archived (default false) | one operation | operations:read |
get_operation_ledger | operation_uuid (required), after_sequence (optional), limit (1–100, default 100) | operation records in sequence order, excluding private usage and receipt metadata; a page can end before limit under the response-size budget, so paginate with after_sequence until an empty page | operations:read |
get_current_approval_request | operation_uuid (required) | {"approval_request": {...} | null} | operations:read |
get_pending_gates | operation_uuid (required) | {"gates": [...]} containing the current user-blocking gates | operations:read |
update_operation_controls | operation_uuid (required); any of name, autonomous, semi_auto_policy (+ clear_semi_auto_policy), operational_goal (+ clear_operational_goal), operational_goal_enabled, planning_enabled; cascade (default false) applies autonomous, semi_auto_policy, operational_goal_enabled, and planning_enabled to descendant sub-operations; name, operational_goal, and clear_operational_goal remain root-only | the updated operation | operations:control + operations:read |
set_operation_lifecycle | operation_uuid and paused (required); cascade (default false) to also apply to eligible descendant sub-operations | the updated operation | operations:control + operations:read |
cancel_operation | operation_uuid (required) | the cancelled operation with its terminal cancellation result; cancellation does not cascade — running sub-operations keep running | operations:control + operations:read |
send_operation_message | operation_uuid, text, idempotency_key (required); parent_cell_uuid, generate_output_file, mentions (optional) — see notes below the table | {"operation_uuid", "realm_uuid", "cell_uuid"} | operations:message |
set_operation_archived | operation_uuid (required), archived (default true). Archiving stops the subtree; restoring does not resume it. | affected operation UUIDs | operations:archive |
approve_approval_request | operation_uuid, approval_request_id (both required), feedback, approve_always (optional). While the operation remains active, retrying an accepted ordinary approval returns the original accepted result and ignores replacement feedback; archived operations return not found. This does not apply when approve_always is true. | {"status": "accepted", operation_uuid, approval_request_id} | operations:approve; approve_always: true also requires operations:control and Autonomous Operations |
deny_approval_request | operation_uuid, approval_request_id (both required), feedback (optional) | {"status": "denied", operation_uuid, approval_request_id} | operations:approve |
rerun_action | operation_uuid, action_cell_uuid (required) | {"status": "accepted", operation_uuid, action_cell_uuid} | operations:approve |
submit_form_gate | operation_uuid, gate_id, response (required) | gate decision | operations:answer-ask; credential persistence also requires integration:write |
deny_form_gate | operation_uuid, gate_id (required) | gate decision | operations:answer-ask |
approve_config_gate | operation_uuid, gate_id (required) | gate decision | operations:approve + realm:update |
reject_config_gate | operation_uuid, gate_id (required), reason (optional) | gate decision | operations:approve |
allow_output_files_gate | operation_uuid, gate_id (required) | gate decision | operations:approve |
respond_monitor_gate | operation_uuid, gate_id (required), payload, summary (optional) | {"status", "operation_uuid", "gate_id"} | operations:message |
cancel_monitor_gate | operation_uuid, gate_id (required) | {"monitor_uuid", "status", "expires_at"} | operations:message; current blocking monitor only |
background_monitor_gate | operation_uuid, gate_id (required) | {"monitor_uuid", "status", "expires_at"} | operations:message; current blocking monitor only |
extend_monitor_gate | operation_uuid, gate_id (required), extend_seconds (optional; default 3,600, range 1–2,592,000) | {"monitor_uuid", "status", "expires_at"} | operations:message; current blocking monitor only; expiry is capped 30 days after the monitor was armed |
approve_sub_operation_gate | operation_uuid, gate_id (required) | gate decision | operations:approve |
reject_sub_operation_gate | operation_uuid, gate_id (required) | gate decision | operations:approve |
For each tool that requires idempotency_key, generate one stable key for one logical mutation. If
the outcome is uncertain, retry that same mutation with the key unchanged. Do not reuse the key for
a different mutation.
Record topology
get_operation_ledger and GET /api/v1/operations/{operation_uuid}/records return chronological
records with the same topology fields. Some records carry a populated topology (is_selected_root,
parent_record_uuid, selected_child_record_uuid, turn_id, action_record_uuid); others have
null topology fields and is_selected_root: false — retain those records in sequence order either
way.
Topology fields are optional and enrich the view; sequence order is the complete ordering and what you should render from. A record whose anchor is unresolved is stored unlinked rather than attached to a branch it does not belong to, so a traversal can legitimately end early or find no root at all — handle that as a normal case. Use the topology to enrich the sequence-ordered view, never as the source of truth for what happened.
When a topology-populated record has is_selected_root: true, it begins the selected history; follow
each selected_child_record_uuid until it is null — and stop, without inferring anything, if the
chain ends before the records you hold are exhausted. Linked projected records outside that traversal
are historical branches. parent_record_uuid identifies a projected record's structural
parent, while turn_id groups projected records generated for one turn. action_record_uuid is
nullable and identifies the owning TOOL_CALL only for projected tool results and execution output
whose owner has been resolved.
create_operation notes:
naming_taskis secret-scanned and only names the operation; it never changes the executedtask.playbook_uuidandplaybook_pathare mutually exclusive.- Send explicit
nullforroot_operation_idtogether withparent_uuidto create a top-level grouped child. plan_node_idis the index path of a node in the parent operation's plan, such as[1, 0]. The parent's plan then reports this operation's progress and result under that node. It requiresparent_uuidand a key withoperations:message.
send_operation_message notes:
- Sending to an active operation attaches the instruction and restarts it.
- Sending to an operation waiting on a blocking sub-operation queues the instruction; it applies once that sub-operation finishes.
- The call is rejected for an archived operation, an operation frozen and owned by another operation, or an operation that has already produced a final, delivered result.
Both cascade tools exclude archived and frozen descendants. Control cascades apply only autonomous, semi_auto_policy, operational_goal_enabled, and planning_enabled to descendants; name and operational-goal text changes remain root-only. Those descendant settings are still applied to terminal operations; only the follow-on execution transition caused by increasing autonomy skips them. An autonomy increase is MANUAL to SEMI_AUTO, MANUAL to AUTO, or SEMI_AUTO to AUTO. Lifecycle pause skips terminal descendants, while resume targets only non-terminal descendants that are paused or parked. A resume target blocked by an active ordinary monitor is skipped without stopping later descendants, so a cascade can be partial. Tool results contain only the updated root operation; call get_operation or list_operations again to observe descendant results.
An operation object is {uuid, name, realm_uuid, parent_uuid, playbook_uuid, playbook_version_uuid, playbook_sub_path, playbook_name, status, paused, activity_state, actionability, pending_gate_uuid, lifecycle_control_revision, status_projection_revision, autonomous, semi_auto_policy, operational_goal, operational_goal_enabled, planning_enabled, created_at, updated_at, archived_at, trigger}, where status is one of PENDING, THINKING, GENERATING, WAITING_FOR_APPROVAL, EXECUTING, SUSPENDED, COMPLETED, FAILED, CANCELLED. PENDING is reported while an operation is parked waiting on a blocking child operation. NO_USER_GATE actionability means there is no pending user-resolvable gate; use activity_state, not actionability, to determine whether the agent is actively progressing, paused, or waiting.
An activity_state of WAITING_FOR_CAPACITY means the sub-operation was accepted but has not started execution. It starts automatically when capacity becomes available in its execution pool.
status_projection_revision orders the status projection, not every field on the object. A lower revision is stale. At an equal revision, discard a replayed status event, but merge an authoritative MCP or REST snapshot because non-projection fields may have advanced while the projection stayed unchanged. A higher revision does not imply that another field changed. lifecycle_control_revision independently orders lifecycle control; compare lifecycle fields with that revision instead.
pending_gate_uuid is the UUID of the ledger record holding the user gate the operation is blocked on — the same value gate discovery returns as gate_id for action, form, config, output-files, and sub-operation gates; it is null when there is no such record gate, including monitor waits.
When actionability is USER_BLOCKED, call get_pending_gates and inspect each item in gates. The type discriminator selects the resolution tool: action uses the existing approval-request tools, form uses submit/deny, config uses approve/reject, output_files uses allow, monitor uses respond/cancel/background/extend, and sub_operation uses approve/reject. Discovery returns zero or one action, form, config, output-files, or sub-operation gate, but it can return several blocking monitors. Output-files gates intentionally have no deny tool. Form discovery strips secret defaults, and monitor discovery omits private trigger configuration, conditions, and tokens.
Sub-operation gates include task, execution_mode, created_at, and updated_at. Form, configuration, output-files, and sub-operation decisions, plus respond_monitor_gate, return {"status", "operation_uuid", "gate_id"}. Cancel, background, and extend monitor controls return {"monitor_uuid", "status", "expires_at"} instead.
// tools/call
{"name": "get_pending_gates", "arguments": {"operation_uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6"}}
// response
{
"gates": [
{
"gate_id": "b1e6c2a0-5717-4562-b3fc-2c963f66af11",
"operation_uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"realm_uuid": "3fa85f64-5717-4562-b3fc-2c963f66a000",
"type": "form",
"status": "pending",
"title": "Choose a target",
"description": "Select the host to assess.",
"form_schema": {"type": "object", "properties": {"host": {"type": "string"}}},
"default_values": null,
"secret_fields": null,
"created_at": "2026-07-02T10:15:00Z",
"updated_at": "2026-07-02T10:15:00Z"
}
]
}
All gate variants include gate_id, operation_uuid, realm_uuid, type, and status. Action, form, configuration, output-files, and sub-operation gates also include created_at and updated_at. Form gates add title, description, form_schema, redacted default_values, and secret_fields; configuration gates add config_type, proposed_settings, explanation, and changes_summary; output-files gates add name, summary, body, items, and reason; sub-operation gates add task and execution_mode; monitor gates add name, source_kind, armed_at, and expires_at.
Action and output-files body values, including the body returned by get_current_approval_request, are sanitized for tenant-sensitive values and sensitive JSON keys. Redacted values are replaced with ***REDACTED***, so the returned body is not guaranteed to match its source text byte for byte.
Example: reading and resolving a pending approval
// tools/call
{"name": "get_current_approval_request", "arguments": {"operation_uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6"}}
// response
{
"approval_request": {
"approval_request_id": "b1e6c2a0-5717-4562-b3fc-2c963f66af11",
"operation_uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"realm_uuid": "3fa85f64-5717-4562-b3fc-2c963f66a000",
"type": "action",
"status": "pending",
"tool": "run_bash",
"execution_layer": "TENTACLE",
"vessel_type": null,
"name": "Run shell command",
"summary": "Scan the target's open ports",
"body": "nmap -p 3000 <target>",
"reason": "Shell command requires approval",
"created_at": "2026-07-02T10:15:00Z",
"updated_at": "2026-07-02T10:15:00Z"
}
}
// tools/call
{
"name": "approve_approval_request",
"arguments": {
"operation_uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"approval_request_id": "b1e6c2a0-5717-4562-b3fc-2c963f66af11",
"feedback": "Looks scoped correctly"
}
}
// response
{
"status": "accepted",
"operation_uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"approval_request_id": "b1e6c2a0-5717-4562-b3fc-2c963f66af11"
}
While the operation remains active, retrying the same ordinary approval after acceptance returns the
original accepted result. Retry bodies do not replace the feedback stored by the first accepted
request. Archived operations return not found. This guarantee does not apply when approve_always
is true.
When get_current_approval_request returns {"approval_request": null}, there is nothing pending —
poll again once the operation has moved past its current step.
Auth
Every request carries Authorization: Bearer <api-key>, where the key has the shape
crk_live_<key_uuid>.<secret>. A key is scoped to a fixed set of realms at creation time — a
realm_uuid or an operation outside that set is rejected, not filtered.
| Capability | Grants |
|---|---|
operations:read | list_realms, list_playbooks, list_models, list_operations, get_operation, get_operation_ledger, get_current_approval_request, get_pending_gates, and the read prerequisite for operation controls |
operations:create | create_operation, create_tabular_operation |
operations:control | update_operation_controls (rename, autonomy, semi-auto policy, goal and planning controls), set_operation_lifecycle (pause/resume), and cancel_operation |
operations:archive | set_operation_archived |
operations:approve | action and delegated sub-operation decisions, action reruns, config rejection, and output-files allow |
realm:update | configuration approval, together with operations:approve |
operations:answer-ask | submit_form_gate, deny_form_gate |
integration:write | Persist credentials submitted through submit_form_gate; credential forms require both capabilities. |
operations:message | send_operation_message, respond_monitor_gate, cancel_monitor_gate, background_monitor_gate, extend_monitor_gate; also permits blocking child and tabular operations, and plan-linked children |
Requests answer 401 Unauthorized whenever the key cannot be used — it is unrecognised, an owner has
revoked it, or the organization the workspace belongs to has been deleted. On the REST API the
response body carries an error code, and a deleted organization reports 3325; an unrecognised or
revoked key reports 3203. The remote MCP server answers a fixed OAuth-style error for all of them
and does not distinguish.
Issuing a replacement key does not restore access while the organization is deleted, because a new
key is scoped to the same workspace. Clients that retry on 401 should back off rather than loop.
Existing keys that carry the legacy operations:reply or operations:autonomy aliases remain compatible: they keep every verb their grant shipped with. cancel_operation is the one exception — it was introduced together with the canonical grant, so cancelling requires a key that explicitly holds operations:control. New integrations should request the canonical operations:message and operations:control capabilities shown above.
Requirements
- A Cracken API key, created in Settings → API keys (available to tenant owners) and scoped to the realms you want to reach.
- To create operations, grant the key the Create operations capability (
operations:create). - Forking an operation also requires
operations:read. - Creating an operation in
SEMI_AUTOorAUTOalso requiresoperations:control. - Action and delegated sub-operation decisions, configuration rejection, and output decisions require
operations:approve. - Configuration approval additionally requires
realm:update. - Forms require
operations:answer-ask. - Blocking monitors require
operations:message. - Submitting a credential-saving form also requires
integration:write. approve_always: trueadditionally requiresoperations:controland the Autonomous Operations plan feature, because it persists a broad semi-auto-policy rule.- A read-only key can discover gates but cannot resolve them.
- On Cracken Cloud (SaaS): the Pro plan or higher. Self-hosted / on-premises deployments have no plans, so this requirement does not apply.
Give the agent external MCP servers
Give the agent extra tools and data sources during an operation by running MCP servers on a Tentacle. The agent discovers the tools an MCP server exposes and calls them alongside Cracken's built-in tools. Once a server is running, its tools — discovered as mcp__<server_name>__<tool_name> — are inherited by operations in the realm that start while it is running. The server belongs to the operation that started it and is stopped when that operation completes.
What an MCP server is
An MCP server is a process that implements the Model Context Protocol and exposes tools and data sources. The agent can start a local MCP server on a Tentacle, or connect to a remote MCP server over HTTP, and then invoke its tools during an operation. This extends what the agent can do beyond Cracken's built-in Vessels and tools.
Common uses:
- Domain-specific tools — connect proprietary or specialized security tools.
- Custom data sources — give the agent access to internal databases or APIs.
- Third-party services — let the agent call external services during operations.
How MCP servers work
Saved configurations
A realm can hold saved MCP server configurations. A configuration is a template: it records how to start or connect a server, not a running instance. Any operation in the realm can reference a saved configuration by name when the agent starts a server.
The agent starts servers
The agent runs MCP servers itself during an operation, using one of two tools:
start_mcp_server— starts a local server process on a Tentacle (for examplenpx,uvx, or a path to an executable). It can use a saved configuration by name (config_name) or take acommandandargsdirectly.connect_mcp_server— connects to a remote server whosecommandis anhttp://orhttps://URL.
Each call takes an optional tentacle_uuid — the connected Tentacle the server runs on or is reached from; when omitted, Cracken selects an available Tentacle for the realm. reasoning is always required, and connect_mcp_server also requires a command. The agent decides when to start or connect a server based on the task and the tools it needs.
Realm-wide availability
Once a server is running in an operation, it becomes available to operations in the same realm that start while it is still running — they pick it up at startup, so a server started after an operation began is not visible to that operation. It is not started once per realm: the server belongs to the operation that started it, and completing that operation stops its Vessel, after which nothing inherits it. Start it again from the operation that needs it.
Set up a saved configuration
Open Integration Center → MCP Servers in your realm. The tab shows a single MCP server input, an optional Realm selector (shown only when you belong to more than one realm), and an Install or Connect button.
- Type the server name in the MCP server field.
- If you belong to more than one realm, pick the target realm.
- Select Install or Connect.
This starts an operation that sets up the server in the realm; the agent does the actual install or connect. Saved configurations and currently running servers appear below the form in the Connected list and the saved-configuration list.
Agent tools
The agent uses these tools to manage MCP servers. Every call requires a reasoning string and takes an optional tentacle_uuid.
start_mcp_server
Starts a local MCP server process on a Tentacle. Use a saved configuration by name, or pass a command directly.
Example — why the agent starts one: mid-operation the agent needs a capability Cracken's built-in Vessels and tools don't cover — for instance, querying a PostgreSQL database it just found credentials for. Instead of scripting psql calls by hand, it starts the Postgres MCP server on the Tentacle (npx -y @modelcontextprotocol/server-postgres), then uses mcp_call to run that server's schema-inspection and query tools directly. The same pattern applies to any specialized toolset shipped as an MCP server — a cloud provider's CLI wrapper, a SAST scanner, or an internal API — that is more reliable to drive through structured tools than raw shell commands.
Pre-configured:
{
"tentacle_uuid": "11111111-1111-1111-1111-111111111111",
"config_name": "my-server",
"reasoning": "Need specialized tools from my-server"
}
Ad-hoc:
{
"tentacle_uuid": "11111111-1111-1111-1111-111111111111",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-time"],
"env": { "LOG_LEVEL": "info" },
"credential_bindings": {
"TAVILY_API_KEY": {
"service_slug": "tavily",
"field_name": "api_key"
}
},
"reasoning": "Need time-related tools for this task"
}
Put only non-secret variables in env. For secrets, save the service credential in Realm Settings → Secrets first, then reference it from credential_bindings: each entry maps an environment variable name to a service_slug and the field_name to read from that saved credential. Cracken resolves the value from the realm's saved credentials at execution time, so no plaintext secret is stored in the MCP configuration.
credential_bindings resolve against realm-scoped secrets, which are a different store from the tenant-wide Integration Center. A credential saved in the Integration Center is available to every realm but is not what an MCP binding reads — save it under the realm's Secrets as well. The agent can also pass protocol_version and a transport override.
Tentacle selection
For Tentacle-backed MCP servers, the agent can pass tentacle_uuid to bind startup to a specific ready Tentacle. When tentacle_uuid is omitted, Cracken selects an available Tentacle for the realm. After the server is running, mcp_call and stop requests continue using the Tentacle that owns that active server. Explicit selections stay pinned to the requested Tentacle and wait until it is ready.
connect_mcp_server
Connects to a remote MCP server. The command must be an http:// or https:// URL.
{
"tentacle_uuid": "11111111-1111-1111-1111-111111111111",
"command": "https://mcp.example.com/mcp",
"headers": { "X-Client": "cracken" },
"reasoning": "Connect to the remote tools server"
}
Put only non-secret values in headers. The agent can also pass protocol_version and a transport override.
mcp_call
Invokes a tool on a running MCP server.
{
"tentacle_uuid": "11111111-1111-1111-1111-111111111111",
"server_name": "my-server",
"tool_name": "query_database",
"arguments": { "query": "SELECT * FROM findings" },
"reasoning": "Querying internal database for existing findings"
}
timeout_seconds is optional. If a call times out, first check whether the requested action completed
before retrying. If completion is unclear, retry only when repeating the action is safe. Check for any
completed side effect or changed state before retrying. After that, start or reconnect the MCP server,
then retry with a higher timeout_seconds value when the tool needs longer to finish. Avoid retrying
requests that may have already completed unless repeating them is safe.
stop_mcp_server
Stops or disconnects a running MCP server. Identify the server by server_name or by vessel_uuid.
{
"tentacle_uuid": "11111111-1111-1111-1111-111111111111",
"server_name": "my-server",
"reasoning": "No longer need this server"
}
Discovering available tools
Once a server is running, the agent discovers the tools it exposes. These tools sit alongside the agent's built-in capabilities and can be invoked through mcp_call or as dynamic tools named mcp__<server_name>__<tool_name>. The agent selects an MCP tool when it is relevant to the task — the same way it selects built-in Vessels and tools.
Stopping MCP servers
You can stop a server from the Connected list in the MCP Servers tab: find the running server and select Disconnect. Stopping terminates the server instance and removes it from operations in the realm. The saved configuration stays intact and can be used to start a new instance later.
Automatic cleanup
When a server's Vessel is terminated — for example the Tentacle disconnects or the operation completes — Cracken stops inheriting that server and removes it from the affected operations.
Transport
The agent chooses how to reach a server by which tool it uses: start_mcp_server runs a local process on the Tentacle, and connect_mcp_server reaches a remote server over HTTP.
For remote servers, the wire transport defaults to auto. The agent can override it with the transport field:
| Value | Meaning |
|---|---|
auto | Let the connection negotiate the transport. This is the default. |
streamable_http | Streamable HTTP, the current MCP HTTP transport. |
legacy_sse | The older HTTP+Server-Sent Events transport, for servers that have not adopted Streamable HTTP. |
Related
- Tentacles — where the agent's MCP servers run and how Tentacles connect.
- Integrations — save the service credentials that
credential_bindingsreference. - Operations — where the agent starts servers and calls their tools, and the operations a client drives over MCP.