Runnable

MCP reference

MCP tools

All successful tools return the same data as JSON text and structuredContent. Read tokens expose five tools, operator tokens add three mutations, and the secrets scope adds two more.

Response envelope

Clients should prefer structuredContent when available and fall back to parsing the text block.

successful CallToolResultJSON
{
  "content": [{ "type": "text", "text": "{ ...pretty JSON... }" }],
  "structuredContent": { "...": "tool-specific data" }
}
failed CallToolResultJSON
{
  "isError": true,
  "content": [{ "type": "text", "text": "Run not found" }]
}

list_runs

List recent runs in this token’s organization, newest first.

readread onlyidempotentStart here for run discovery.
InputTypeRequiredContract
repositorystringNoExact owner/repository full name, 1–255 characters.
statusenumNoqueued, in_progress, completed, success, failure, cancelled, skipped, timed_out, or infrastructure_failure.
limitintegerNo1–100; default 30.
example inputJSON
{ "repository": "acme/api", "status": "failure", "limit": 10 }
output shapeJSON
{
  "runs": [{
    "id": "run_uuid", "runNumber": 42, "runName": "CI",
    "attempt": 1, "status": "completed", "conclusion": "failure",
    "trigger": "pull_request", "sourceSha": "...", "sourceRef": "...",
    "headBranch": "fix/cache", "actor": "octocat",
    "createdAt": "...", "startedAt": "...", "completedAt": "...",
    "workflowId": "workflow_uuid", "workflow": "CI",
    "repositoryId": "repository_uuid", "repository": "acme/api"
  }],
  "count": 1
}

get_run

Get one run with its repository, workflow, jobs, current attempts, and step results.

readread onlyidempotentUse returned job IDs with get_job_logs.
InputTypeRequiredContract
runIdstringYesNon-empty Runnable run ID.
example inputJSON
{ "runId": "run_uuid" }
output shapeJSON
{
  "run": {
    "id": "...", "runNumber": 42, "runName": "CI", "attempt": 1,
    "status": "completed", "conclusion": "failure", "trigger": "push",
    "sourceSha": "...", "sourceRef": "refs/heads/main", "headBranch": "main",
    "actor": "octocat", "createdAt": "...", "startedAt": "...", "completedAt": "...",
    "workflow": { "id": "...", "name": "CI", "path": ".runnable/workflows/ci.yml" },
    "repository": { "id": "...", "fullName": "acme/api", "provider": "github" }
  },
  "jobs": [{
    "id": "job_uuid", "key": "test", "name": "Test", "needs": [], "matrix": null,
    "status": "completed", "conclusion": "failure", "currentAttempt": 1,
    "queuedAt": "...", "startedAt": "...", "completedAt": "...",
    "attempt": {
      "id": "attempt_uuid", "status": "completed", "conclusion": "failure",
      "lastHeartbeatAt": "...",
      "steps": [{ "id": "...", "key": "test", "position": 2, "name": "npm test",
        "status": "completed", "conclusion": "failure", "exitCode": 1,
        "startedAt": "...", "completedAt": "..." }]
    }
  }]
}

get_job_logs

Read a line- and byte-bounded masked tail from one job attempt. The operation is audit logged.

readread onlyidempotentIncrease bounds only when needed.
InputTypeRequiredContract
jobIdstringYesNon-empty job ID from get_run.
attemptpositive integerNoDefaults to the job’s current attempt.
tailLinesintegerNo1–2,000; default 400.
maxBytesintegerNo1,024–200,000; default 100,000.
input and outputJSONC
// input
{ "jobId": "job_uuid", "attempt": 1, "tailLines": 200, "maxBytes": 50000 }

// output
{
  "job": { "id": "job_uuid", "name": "Test", "status": "completed", "conclusion": "failure" },
  "attempt": 1,
  "logs": "...masked log tail...",
  "returnedBytes": 18432,
  "truncated": true
}

truncated is true when earlier chunks or lines/bytes were omitted. Expired log chunks are excluded and cannot be recovered by increasing the bounds.

wait_for_run

Long-poll one run without consuming runner compute or relying on an in-memory MCP session.

readread onlyidempotentRepeat with the returned cursor.
InputTypeRequiredContract
runIdstringYesNon-empty run ID.
afterCursorstringNoCursor from the previous call. The server returns early if state changed.
timeoutSecondsintegerNo1–25; default 20.
input and outputJSONC
// first input
{ "runId": "run_uuid", "timeoutSeconds": 20 }

// output
{
  "run": { "id": "run_uuid", "status": "in_progress", "conclusion": null,
    "startedAt": "...", "completedAt": null },
  "jobs": [{ "id": "job_uuid", "name": "Test", "status": "in_progress",
    "conclusion": null, "currentAttempt": 1, "startedAt": "...", "completedAt": null }],
  "cursor": "JgQX6pEo_s9...",
  "terminal": false,
  "timedOut": true
}

// next input
{ "runId": "run_uuid", "afterCursor": "JgQX6pEo_s9...", "timeoutSeconds": 20 }

A terminal run returns immediately. timedOut: true means the bounded wait elapsed without a state change; it is not a workflow timeout.

list_workflows

List definition IDs available to the organization, optionally for one exact repository.

readread onlyidempotentUse the ID with dispatch_workflow.
InputTypeRequiredContract
repositorystringNoExact full name, 1–255 characters.
limitintegerNo1–100; default 50.
input and outputJSONC
// input
{ "repository": "acme/api", "limit": 20 }

// output
{
  "workflows": [{
    "id": "workflow_uuid", "name": "CI", "path": ".runnable/workflows/ci.yml",
    "status": "active", "diagnostics": [], "lastSourceSha": "...",
    "repositoryId": "repository_uuid", "repository": "acme/api",
    "defaultBranch": "main", "updatedAt": "..."
  }],
  "count": 1
}

rerun_run

Queue another run for the same workflow snapshot and source revision.

operatorwritesnon-idempotentNot idempotent; each accepted call creates a run.
InputTypeRequiredContract
runIdstringYesSource run ID.
failedOnlybooleanNoDefault true. False queues a complete rerun.
input and outputJSONC
// input
{ "runId": "source_run_uuid", "failedOnly": true }

// output
{ "runId": "new_run_uuid", "sourceRunId": "source_run_uuid",
  "failedOnly": true, "orchestration": { "started": true } }

A rerun does not pick up new commits

Dispatch the workflow after a source/configuration fix. Rerun only when repeating the same revision and snapshot is intentional.

cancel_run

Cancel all unfinished work in a run. A terminal run returns safely without mutation.

operatorwritesdestructiveidempotentIdempotent.
InputTypeRequiredContract
runIdstringYesRun to cancel.
input and outputJSONC
// input
{ "runId": "run_uuid" }

// active output
{ "runId": "run_uuid", "cancelled": true, "alreadyTerminal": false }

// terminal output
{ "runId": "run_uuid", "cancelled": false, "alreadyTerminal": true }

dispatch_workflow

Start a workflow that declares workflow_dispatch, then wait on the returned run ID.

operatorwritesnon-idempotentNot idempotent; validate inputs first.
InputTypeRequiredContract
workflowIdstringYesID from list_workflows.
refstringYesProvider-accepted Git ref/revision, 1–255 characters.
inputsobjectNoString-keyed values; default {}. Validated against workflow_dispatch definitions.
input and outputJSONC
// input
{ "workflowId": "workflow_uuid", "ref": "refs/heads/main",
  "inputs": { "environment": "staging", "dry-run": false } }

// output
{ "runId": "run_uuid", "workflowId": "workflow_uuid",
  "ref": "refs/heads/main", "orchestration": { "started": true } }

list_secrets

List secret names and scopes. Values are never returned by any tool.

operatorread onlyidempotentRequires mcp:secrets, listing included.
InputTypeRequiredContract
repositorystringNoExact full name. Organization-wide secrets are always included, because they apply to every repository.
input and outputJSONC
// input
{ "repository": "acme/api" }

// output — names and scopes only, never values
{ "secrets": [
    { "id": "secret_uuid", "name": "DATABASE_URL", "scope": "organization",
      "repositoryId": null, "environmentId": null, "updatedAt": "2026-08-14T09:12:00.000Z" }
  ] }

set_secret

Create a secret, or replace the value of one that already exists, for use as ${{ secrets.NAME }}.

operatorwritesdestructiveidempotentRequires mcp:secrets. Destructive: a replaced value cannot be recovered.
InputTypeRequiredContract
namestringYesUpper-cased. Letters, digits, and underscores; must not start with a digit.
valuestringYes1–64,000 characters. Encrypted with the organization's workspace key and never returned.
scopestringNoorganization (default), repository, or environment.
repositoryIduuidConditionalRequired for repository and environment scope.
environmentIduuidConditionalRequired for environment scope.
input and outputJSONC
// input
{ "name": "DATABASE_URL", "value": "postgresql://…", "scope": "organization" }

// output — acknowledges the write, echoes nothing of the value
{ "id": "secret_uuid", "name": "DATABASE_URL", "scope": "organization",
  "repositoryId": null, "environmentId": null, "created": true }

Writes are recorded as mcp.secret.created or mcp.secret.updated audit events, attributed to the token rather than a person. Because no tool reads a value back, call list_secrets first when you need to know whether you are creating or overwriting.

Tool errors and recovery

Input-schema violations are rejected by MCP validation. Service errors use isError and a text message.

Message or symptomRecovery
Run not foundCall list_runs in the same token organization and use its ID.
Job not found / Job attempt not foundRefresh get_run; use the current job ID and a returned attempt number.
Workflow not foundCall list_workflows; definitions can be replaced after source discovery.
Workflow not found or invalid / dispatch input errorInspect workflow status/diagnostics and supply every required typed input.
Run watch cancelledClient aborted the request; call wait_for_run again with the last good cursor.
Tool does not appearThe credential is read-only; issue a separately approved operator token if needed.
NextTroubleshootingResolve workflow, checkout, deployment, log, billing, and MCP incidents.