Local MCP clients
Tickr ships an opt-in Model Context Protocol (MCP) facade for local stdio clients. An MCP client launches tickr-cli mcp; normal Tickr startup does not launch or supervise the process, and the MCP process does not start or stop a Data plane formation. It connects to an API component that is already available.
The facade exposes exactly twenty-five curated MCP tools. It is not an automatic projection of the HTTP API. The API component remains the canonical operation authority: every accepted tool call maps to one documented HTTP operation, and the facade does not read repositories, coordination subjects, or tickr-ctx directly.
Configure a local client
MCP clients use different configuration-file locations, but the local stdio server entry has the same command, arguments, and environment shape. To use the default API endpoint:
{
"mcpServers": {
"tickr": {
"command": "<PATH_TO_TICKR_CLI>",
"args": ["mcp"]
}
}
}
The endpoint resolves in this order:
tickr-cli mcp --api-endpoint <URL>;TICKR_MCP_API_ENDPOINT;http://127.0.0.1:6000.
For an HTTPS endpoint with optional bearer authentication:
{
"mcpServers": {
"tickr": {
"command": "<PATH_TO_TICKR_CLI>",
"args": [
"mcp",
"--api-endpoint",
"https://<API_HOST>:<API_PORT>"
],
"env": {
"TICKR_MCP_API_BEARER_TOKEN": "<BEARER_TOKEN>"
}
}
}
}
These examples contain placeholders only. TICKR_MCP_API_BEARER_TOKEN is the only bearer-token input. It is environment-only: there is no bearer CLI option and no secret-bearing MCP tool argument. Supply it through the MCP client's secret-aware environment mechanism, not in args or a shared configuration file.
One MCP process binds one API component endpoint and therefore one Tenant boundary. It does not select or multiplex Tenants. Launch separate, explicitly configured processes for different Tenant endpoints.
Endpoint and transport safety
- Plain
http://is accepted only forlocalhostor a loopback IP address. Every non-loopback endpoint requires HTTPS. - HTTPS uses normal platform certificate verification. There is no insecure-verification bypass.
- HTTP redirects are prohibited. The facade does not follow a redirect with credentials or a mutation request.
- Endpoint user information such as
https://<USER>:<PASSWORD>@<API_HOST>is rejected. - Endpoint URLs, bearer values, request bodies, response headers, and internal diagnostics are excluded from protocol errors and normal diagnostics.
- MCP transport is local stdio. Stdout contains only newline-delimited JSON-RPC frames; diagnostics use stderr. Do not merge stderr into stdout.
Protocol-wide contracts
tools/list returns the exact ordered inventory documented below. Every input schema is a closed JSON object: additional properties and explicit null values are rejected. Required identity strings must be UUIDs. Optional fields must be omitted when unused.
All tools set openWorldHint: true because they call the configured API component. The other protocol-visible annotations are:
| Tool class | readOnlyHint | destructiveHint | idempotentHint |
|---|---|---|---|
| Every read-only tool | true | false | true |
register_workflow, patch_workflow_instance | false | true | false |
trigger_workflow, cancel_workflow_instance, cancel_task, replay_workflow_instance, redrive_compaction_quarantine | false | true | true |
activate_workflow, deactivate_workflow | false | false | true |
The first argument-valid tools/call in a process fetches /api-docs/openapi.json and proves compatibility with API version 0.2 and all twenty-five canonical operations. initialize and tools/list do not contact the API component. A compatible result is cached for that MCP process; operation data is not cached.
Every accepted read performs exactly one fresh HTTP GET. The facade does not poll, traverse cursors, enumerate all pages, perform follow-up reads, or retry. Every accepted mutation performs exactly one HTTP POST. The facade never retries a mutation automatically, including after a timeout or another ambiguous transport outcome.
Every operation returns a structured JSON envelope with:
okand UTCobserved_aton every result;- optional observed
http_status; - complete
dataand optionalmetaon success; or - a bounded
erroron failure.
observed_at is the facade observation time, not an entity transition or persistence time. Public error messages are limited to 4,096 UTF-8 bytes. The complete compact envelope is limited to 524,288 encoded bytes. An oversized result is replaced atomically by response_too_large, with observed_bytes and limit_bytes; the facade never clips fields or returns partial data.
Facade validation happens before compatibility or operation HTTP. Malformed successful API responses fail as invalid_api_response. API absence, not-found, conflict, and other canonical public failures remain distinct; the facade does not reinterpret an unavailable projection as an empty successful result.
Read-only tools
The tools in this section have readOnlyHint: true, destructiveHint: false, idempotentHint: true, and openWorldHint: true.
Workflow and instance reads
list_workflows
- Inputs: optional
limit, integer1..=200, default50. - HTTP: one
GET /api/workflows?limit=<limit>. - Result: the complete canonically ordered Workflow array under
data;meta.truncatedpreservesX-Tickr-Result-Truncated. This is a bounded top projection, not a complete-enumeration or snapshot-consistency claim.
get_workflow
- Inputs: required
workflow_id; optionalversion, integer>= 1. Omittingversiondelegates Default-version selection to the API component. - HTTP: one
GET /api/workflows/{workflow_id}, withversiononly when supplied. - Result: the complete Workflow detail under
data, including selected version and status, completeworkflow_definition, exact authorednickel_source, available versions, aggregate run fields, and future public fields. Unknown Workflow or version isnot_found.
get_workflow_calendar
- Inputs: required
workflow_idand integeryear; optional canonical IANAtz.yearmust identify a representable calendar year whose following year is also representable. Omittingtzpreserves the API component'sUTCdefault. - HTTP: one
GET /api/workflows/{workflow_id}/calendar?year=<year>, addingtzonly when supplied. - Result: the complete requested-year projection under
data, including timezone, ordered day counts, andlive_data_available. The facade does not reconstruct calendars or infer future Workflow instances.
list_workflow_instances
- Inputs: required
workflow_id; optionaldateinYYYY-MM-DD; optional canonical IANAtz; optionallimit, integer1..=200, default50. Omittingtzpreserves the API component's UTC default. - HTTP: one
GET /api/workflows/{workflow_id}/instances, forwarding supplied date/timezone filters and the effective limit. - Result: the complete newest-first Workflow-instance array under
data;meta.truncatedandmeta.live_data_availablepreserve the canonical headers.live_data_available: falseis a degraded archive-backed view, not a complete current view.
list_terminal_workflow_instances
- Inputs: optional
workflow_id; optional terminalstateinCompleted | Failed | Cancelled; optional inclusive RFC 3339scheduled_fromandscheduled_towith lower bound not after upper bound; optional non-empty opaquecursor; optionallimit, integer1..=100, default50. - HTTP: one
GET /api/runs, forwarding only supplied filters and cursor plus the effective limit. - Result: the complete canonical page under
data, including ordereditemsand nullablenext_cursor. Cursors are API-owned and opaque; a page does not claim snapshot consistency or complete history.
get_workflow_instance
- Inputs: required
workflow_instance_id. - HTTP: one
GET /api/workflows/instances/{workflow_instance_id}. - Result: one complete canonical Instance snapshot under
data, including canonical live or archivestorage. Identity mismatch or malformed success isinvalid_api_response; not-found and live-data-unavailable remain distinct failures.
list_task_instances
- Inputs: required
workflow_instance_id; optionallimit, integer1..=200, default50. - HTTP: one
GET /api/workflows/instances/{workflow_instance_id}/tasks?limit=<limit>. - Result: the complete deterministic Task-instance array under
data, ordered by Task identity, Attempt, then Task-instance identity.meta.truncatedandmeta.live_data_availablepreserve the canonical headers independently.
get_task_logs
- Inputs: required
workflow_id,workflow_instance_id, andtask_instance_id; optionaltail_batches, integer1..=200, default50; optionalbefore_seq, non-negative integer naming an existing Log sequence boundary. - HTTP: one
GET /api/workflows/{workflow_id}/instances/{workflow_instance_id}/tasks/{task_instance_id}/logs, forwarding the effective tail bound and optional sequence boundary. - Result: one canonical bounded Log page under
data, preserving batches, sequence metadata, terminal status and reason, earlier-page indication, and terminal-marker presence. The facade does not download complete Logs or follow a Log as it grows.
Signal, context, and Server event reads
get_signal
- Inputs: required
signal_id. - HTTP: one
GET /api/signals/{signal_id}. - Result: the complete canonical Signal status under
data:signal_id,signal_code,status(pending,materialized, orterminal), and applicableworkflow_id,workflow_instance_id,workflow_instance_code,captures_summary,applied_count,name, andmatched_workflows. Trigger, Wakeup, and Cancel audit shapes are preserved. The facade does not infer materialization or make a follow-up read.
get_workflow_instance_context
- Inputs: required
workflow_instance_id. - HTTP: one
GET /api/workflows/instances/{workflow_instance_id}/context. - Result: the complete canonical
tickr-ctxscope projection underdata, includingstorage, run, trigger, every gate scope, ordered entries, and each scope'savailabilityandcompletefields.availablewith empty entries is distinct from incompletenot_retained. Required live-scope unavailability remainsapi_unavailable, not not-found or an empty scope.
Shared scoped Server event filters
list_workflow_instance_events and list_task_instance_events accept these optional filters:
- inclusive RFC 3339
occurred_afterand exclusive RFC 3339occurred_before; category, a unique array of1..=3values fromWorkflow | Task | Gate;event_type, a unique array of1..=21values fromCreateWorkflowInstance,CancelWorkflowInstance,WorkflowSubmitted,WorkflowInstanceCreated,WorkflowTriggered,WorkflowCompleted,WorkflowFailed,WorkflowCancelled,WorkflowInstancePatched,TaskInstanceCreated,TaskQueued,TaskDelivered,TaskAssigned,TaskStarted,TaskCompleted,TaskFailed,TaskParked,TaskCancelled,GateDispatched,GateOutcome, orGateTimeoutFired;attention, a unique array of1..=4values fromSuccess | Failure | Cancellation | Waiting;- optional exact
workflow_id,edge_id,signal_id, andpatch_idUUID filters; and - one optional non-empty opaque
cursor, copied fromolder_cursorornewer_cursorof a prior scoped page.
Repeated filter values are ORed. The facade forwards filters exactly and does not decode cursors or post-filter results. The API component fixes page size; these tools expose no limit input.
list_workflow_instance_events
- Inputs: required
workflow_instance_id, the shared filters above, and optional exacttask_instance_idUUID filter. - HTTP: one
GET /api/workflows/instances/{workflow_instance_id}/events. - Result: one complete canonical Server event page under
data, preserving ordereditems, every event field,matching_count,snapshot_high_water,older_cursor,newer_cursor, andat_live_head. An empty page is successful; unavailable history isapi_unavailablerather than an empty page.
list_task_instance_events
- Inputs: required parent
workflow_instance_idand requiredtask_instance_id, plus the shared filters above. - HTTP: one parent-enforcing
GET /api/workflows/instances/{workflow_instance_id}/tasks/{task_instance_id}/events. - Result: the same complete canonical Server event page contract as the Workflow-instance tool. The parent path prevents cross-Workflow-instance Task history access.
Replay, Patch, and Compaction reads
list_workflow_instance_replays
- Inputs: required
source_workflow_instance_id. - HTTP: one
GET /api/workflows/instances/{source_workflow_instance_id}/replays. - Result: complete canonical newest-first reverse-link rows under
data, including replay/source identities and codes, status, nullable name,resume_fromTask references, names-onlyshadowed_keys, and RFC 3339created_at. Both a known source with no replays and an unknown source are successful empty lists.
get_patch_status
- Inputs: required
patch_id. - HTTP: one
GET /api/patches/{patch_id}. - Result: the complete canonical Patch status under
data, including Patch identity, Workflow-instance identity, durable Patch key, lifecycle status, applied version, outcome, reason, update time, and future public fields. This is one observation, not a subscription or polling operation.
get_patch_source
- Inputs: required
patch_key, the durable UUID returned by accepted Patch submission or status. - HTTP: one
GET /api/patches/{patch_key}/source. - Result: the complete retained source under
data, including Patch key, Workflow-instance identity and code, applied version, exact authored source text, and source format. The 512 KiB result ceiling is atomic; no partial source is returned.
list_compaction_quarantine
- Inputs: optional
stateinquarantined | redrive-pending | redriving | recovered | redrive-failed; optional non-empty opaquecursor; optionallimit, integer1..=100, default50. - HTTP: one
GET /api/compaction/quarantine, forwarding supplied state and cursor plus the effective limit. - Result: the complete canonical page under
data, including ordereditems, nullablenext_cursor, Tenant identities, optional Workflow-instance identity and code, payload digest, formation source/reference, bounded failure evidence, attempt and observation fields, quarantine state, and redrive fields. RetainedCompactionEnvelopebytes are intentionally not exposed.
State-changing tools
The MCP facade never retries any state-changing tool. “Explicit retry” below always means a new caller-directed tool call after the caller applies the named rule.
register_workflow
- Annotations: state-changing, destructive, non-idempotent, open-world.
- Inputs: required
nickel_source, maximum 524,288 UTF-8 bytes; optional stringnamespace, with no additional facade character bound. There is no idempotency-key input. - HTTP and side effect: one
POST /api/workflows/registerwith exactly the supplied body fields. The API component validates and registers the Nickel Workflow definition. - Result: HTTP
200or202with the complete canonical registration response underdata, preservingInserted,Refreshed,NoOp, andBuildRequeuedoutcomes. - Retry rule: do not repeat after an ambiguous result. Reconcile through a canonical read before deciding whether a new registration request is appropriate; the facade cannot deduplicate it.
trigger_workflow
- Annotations: state-changing, destructive, idempotent, open-world.
- Inputs: required
workflow_id; required non-emptyidempotency_keythat is a valid HTTP header value; optional RFC 3339scheduled_at; optional JSON-objectinputswith no per-key schema; optionalname, at most 200 characters. - HTTP and side effect: one
POST /api/workflows/{workflow_id}/trigger. The key is forwarded unchanged only asIdempotency-Key; supplied scheduling, input, and name fields form the body. The API component creates or deduplicates the Trigger Signal. - Result: HTTP
200or202with canonicalsignal_id,deduplicated, and optionalscheduled_atunderdata. - Retry rule: an explicit retry must reuse the same idempotency key for the same logical trigger. Never use a new key to resolve ambiguity.
cancel_workflow_instance
- Annotations: state-changing, destructive, idempotent, open-world.
- Inputs: required
workflow_instance_id; required non-emptyidempotency_keythat is a valid HTTP header value; optionalnote, maximum 4,096 UTF-8 bytes. - HTTP and side effect: one
POST /api/workflows/instances/{workflow_instance_id}/cancel. The key is forwarded unchanged only asIdempotency-Key; the body contains only the optional note. The API component requests cancellation of the complete Workflow instance. - Result: HTTP
200or202with canonical Cancelsignal_idand applicableapplied,instances_matched, anddeduplicatedfields underdata. - Retry rule: an explicit retry must reuse the same idempotency key for the same logical Cancel.
cancel_task
- Annotations: state-changing, destructive, idempotent, open-world.
- Inputs: required
workflow_instance_id; required canonicaltask_id; required non-emptyidempotency_keythat is a valid HTTP header value; optionalnote, maximum 4,096 UTF-8 bytes. - HTTP and side effect: one
POST /api/workflows/instances/{workflow_instance_id}/tasks/{task_id}/cancel. The key is forwarded unchanged only asIdempotency-Key; the body contains only the optional note. The API component targets that Task within the named Workflow instance. - Result: HTTP
200or202with the canonical Cancel Signal identity and applicableapplied,instances_matched, anddeduplicatedfields underdata. - Retry rule: an explicit retry must reuse the same idempotency key for the same logical Task Cancel.
replay_workflow_instance
- Annotations: state-changing, destructive, idempotent, open-world.
- Inputs: required
source_workflow_instance_id; required non-emptyidempotency_key; optionalresume_from, an array of canonical Task UUIDs with no schema item-count bound; optionalname, at most 200 characters; optional JSON-objectinputscontaining fresh values for declared Event variables. - HTTP and side effect: one
POST /api/workflows/instances/{source_workflow_instance_id}/replay. The idempotency key is sent exactly once in the JSON body, not as a header. The body may contain the replay frontier, name, and fresh Event-variable inputs. It cannot contain replay seed state, archivedtickr-ctxscope, Task outputs, or arbitrary carried state. The API component and Conductor decide source terminality, chained-replay eligibility, Event-variable validity, and fireability. - Result: HTTP
200for a same-key replay or202for accepted replay, preserving complete canonical replay identity, code, status, source, frontier, name, and deduplication fields underdata. - Retry rule: an explicit retry must reuse the same idempotency key and the same logical replay body.
patch_workflow_instance
- Annotations: state-changing, destructive, non-idempotent, open-world.
- Inputs: required
workflow_instance_id; required non-empty authorednickel_source, maximum 524,288 UTF-8 bytes. The schema deliberately has no idempotency-key input. - HTTP and side effect: one
POST /api/workflows/instances/{workflow_instance_id}/patchwith exact source in the body. The API component submits it to the asynchronous Patch pipeline; the facade performs no preflight or follow-up read. - Result: HTTP
202with valid Patch identity, durable Patch key, andacceptedstatus underdata. Canonical HTTP409is aconflict, not successful data. - Retry rule: Patch is non-idempotent. Never resubmit automatically or blindly after ambiguity. If an identity or key was observed, reconcile with
get_patch_statusorget_patch_source; otherwise leave recovery to deliberate operator action.
redrive_compaction_quarantine
- Annotations: state-changing, destructive, idempotent, open-world.
- Inputs: required
quarantine_id; requiredidempotency_keyof1..=200characters that is also a valid HTTP header value. - HTTP and side effect: one
POST /api/compaction/quarantine/{quarantine_id}/redrive, with the key only inIdempotency-Keyand no request body. The API component re-stages work through the formation-selected Compaction path; the MCP facade receives no quarantine repository or formation-staging handle. - Result: HTTP
202for requested redrive or200for a same-key deduplicated result, preserving canonical redrive identity and lifecycle underdata. Competing-key conflict, not-found, and unavailable outcomes remain distinct. - Retry rule: an explicit retry must reuse the same idempotency key for the same quarantine record.
activate_workflow and deactivate_workflow
- Annotations: state-changing, non-destructive, idempotent, open-world.
- Inputs: each accepts only required
workflow_id. Neither accepts or invents an idempotency key. - HTTP and side effect:
activate_workflowsends onePOST /api/workflows/{workflow_id}/activate;deactivate_workflowsends onePOST /api/workflows/{workflow_id}/deactivate. Activation makes a Ready Workflow eligible for later admission. Deactivation prevents later Workflow-instance admission but does not cancel existing Workflow instances. - Result: HTTP
200with the complete canonical acknowledged status-version response underdata: application acknowledgementApplied | Superseded,created, effect identity, target status (ActiveorInactive), Workflow identity, positive Workflow version, and current version. - Retry rule: the facade never retries after ambiguity. Because status changes carry no idempotency key, read current Workflow status with
get_workflowbefore making another explicit status request.
Capability boundary
The operational surface has three distinct categories.
Canonical API operations
The API component's canonical HTTP operations remain authoritative. The MCP facade supplies typed, bounded access only to the twenty-five operations above; it does not turn every existing route into a tool or become a generic HTTP executor. Existing API operations outside this allowlist remain HTTP operations, not hidden MCP tools.
Wakeup submission and ByTag Cancel are intentionally unavailable. Wakeup needs a formation-neutral same-key idempotency law before it is safe across Tickr Lite and distributed formations. The current ByTag resolver can span Tenants; it needs a proven single-Tenant resolution boundary before it is safe through this MCP facade.
Local operator orchestration
Formation selection, setup, installation and migration work, startup and shutdown, fresh reset, endpoint and credential provisioning, Nickel preflight validation, mutation policy, identifier selection, and multi-call sequencing remain local operator responsibilities. The facade neither controls formations nor treats local orchestration as an agent-native fallback.
Unavailable agent-native families
Unsettled or unimplemented agent-native capability families remain unavailable. The registry does not expose Console dashboards, Activity collections, Tenant-wide Server event history, Health, dynamic Tenant selection, authentication, complete Log download, arbitrary Signal construction, generic discovery, TaskMessage, direct tickr-ctx, checkpoints, ShadowTask, Piloted, Advisor, Healer, resources, prompts, subscriptions, or notifications. MCP clients must not infer these capabilities from HTTP routes or local operator commands.
Formation behavior
Tickr Lite (lite-local) and the distributed all-nats and all-redis formations expose identical MCP tool names, input schemas, annotations, envelopes, and retry rules. The facade is formation-neutral because it speaks only to the selected formation's API component. It does not expose formation substrate, credentials, or control.
HTTP Commands and formation-level external Event ingress are different capabilities. All three admitted formations provide HTTP Commands used by these MCP tools. Distributed formations also provide external Event ingress; Tickr Lite does not. An MCP tool call remains an HTTP Command and never becomes, emulates, or configures formation-level external Event ingress.