Ctrl K
ComunidadGuías…

specs/036-mcp-execute-tool.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Spec: MCP Execute Tool

Status: Implemented.

## Scope

This spec defines the `dagu_execute` MCP tool-call contract: input fields,
action contracts, step decisions, wait behavior, successful output, resource
links, and tool-level errors.

This spec does not define the MCP endpoint, authentication, API-key surfaces,
`dagu://` URI grammar, MCP resource reads, `dagu_read`, `dagu_change`,
prompts, subscriptions, REST response schemas, queue selection policy, run
execution semantics, storage internals, or MCP SDK internals.

Related specs: [020: MCP Server](020-mcp-server.md) defines the MCP endpoint,
authentication, and `dagu://` resource URI model used by this tool.
[021: MCP Read Tool](021-mcp-read-tool.md) defines the run detail model
reused by wait output and read before a step decision.
[031: Human Task](031-human-task.md) defines human-task completion and
push-back.

## Goal

`dagu_execute` gives MCP clients one way to start, enqueue, retry, and stop
DAG-runs, to decide steps that wait for a person, and optionally to wait for an
identified run's result inside the same tool call.

## Behavior

### Tool Identity

Rules:

- The tool name is `dagu_execute`.
- A successful call performs exactly the requested execution action. It must
  not create, update, delete, or rename DAG definitions or Wiki pages.
- Audit, metrics, logging, and transport side effects are allowed.

### Input

Tool input is a JSON object. Fields outside this table fail with
`invalid_tool_input`.

| Field | Type | Required rule | Meaning |
| --- | --- | --- | --- |
| `action` | string | Required. One of `start`, `enqueue`, `retry`, `stop`, `approve`, `reject`, `push_back`. | Execution action. |
| `targetType` | string | Optional. One of `dag`, `inline_spec`, `run`. | Target type. Defaults to `run` for `retry`, `stop`, `approve`, `reject`, and `push_back`, `inline_spec` when `spec` is present, otherwise `dag`. |
| `name` | string | Required. | DAG name, including the identity used for inline runs. |
| `spec` | string | Required for `targetType=inline_spec`; otherwise forbidden. | Inline DAG YAML document for `start` and `enqueue`. |
| `dagRunId` | string | Required for `retry`, `stop`, `approve`, `reject`, and `push_back`; optional override for `start` and `enqueue`. | DAG-run identifier. |
| `params` | string or object | Optional for `start` and `enqueue`. | Runtime parameters. An object is canonicalized to its compact JSON encoding. |
| `queue` | string | Optional for `enqueue`. | Queue override. |
| `singleton` | boolean | Optional for `start` and `enqueue`. | Reject duplicate running or queued DAG-runs. |
| `noReuse` | boolean | Optional for `start` and `enqueue`. | Execute eligible build steps without reusing prior materializations. |
| `profile` | string | Optional for `start` and `enqueue`. | Runtime profile for this run only. When absent, a stored DAG keeps its default profile resolution and an inline spec runs without a profile. |
| `labels` | array of strings | Optional for `start` and `enqueue`. | Additional labels, each `key=value` or key-only. |
| `stepName` | string | Optional for `retry`; required for `approve`, `reject`, and `push_back`. | Step to retry, or the waiting step to decide. |
| `includeDownstream` | boolean | Optional for `retry`. Requires `stepName`. | Retry the selected step and every reachable descendant. |
| `subDAGRunId` | string | Optional for `approve`, `reject`, and `push_back`. | Sub-DAG run of `dagRunId` that owns the waiting step. |
| `inputs` | object | Optional for `approve` and `push_back`. | Values for the waiting step. |
| `reason` | string | Optional for `reject`. | Reason recorded with the rejection. |
| `attemptId` | string | Optional for `approve`, `reject`, and `push_back`. | Attempt the decision is based on. |
| `wait` | boolean | Optional. | Wait for the identified run to reach a terminal state. |
| `waitTimeoutSeconds` | integer | Optional. Requires `wait`. From `1` through `300`; defaults to `60`. | Maximum wait duration. |

Rules:

- Unknown fields, non-null values of the wrong type, unknown `action` values,
  and unknown `targetType` values fail with `invalid_tool_input`.
- `null` field values are treated as absent.
- String values other than `spec` are trimmed of leading and trailing
  whitespace; a string that is empty after trimming is treated as absent.
- `targetType=run` is valid only for `retry`, `stop`, `approve`, `reject`, and
  `push_back`, and those actions require it.
- `params`, `singleton`, `noReuse`, `profile`, and `labels` are valid only for
  `start` and `enqueue`; `queue` is valid only for `enqueue`; `includeDownstream` is
  valid only for `retry`; `stepName` is valid only for `retry`, `approve`,
  `reject`, and `push_back`; `subDAGRunId` and `attemptId` are valid only for
  `approve`, `reject`, and `push_back`; `inputs` is valid only for `approve`
  and `push_back`; and `reason` is valid only for `reject`.
- `inputs` that is not a JSON object fails with `invalid_tool_input`.
- Supplying a field for an action that does not support it fails with
  `invalid_tool_input`, even when a boolean is `false` or an array is empty.

### Action contracts

| Action | Effect |
| --- | --- |
| `start` | Runs a stored DAG (`targetType=dag`) or an inline spec (`targetType=inline_spec`). |
| `enqueue` | Enqueues a stored DAG or inline spec for queue-based execution. |
| `retry` | Retries an existing DAG-run, optionally scoped to `stepName` and its downstream steps. |
| `stop` | Requests termination of an existing DAG-run. |
| `approve` | Approves a waiting approval gate, or completes a waiting human task. |
| `reject` | Rejects a waiting approval gate. |
| `push_back` | Pushes a waiting approval gate or human task back for re-execution. |

### Step decisions

`approve`, `reject`, and `push_back` decide one step that waits for a person:
an approval gate or a human task, as reported under `steps` by the Spec 021 run
detail model.

Rules:

- The decided step is the step named `stepName` in the run that `name` and
  `dagRunId` identify, or in its sub-DAG run `subDAGRunId` when given.
- A run or sub-DAG run that does not exist, or that has no step named
  `stepName`, fails with `resource_not_found`.
- When `attemptId` is given and differs from the decided run's current
  attempt, which the Spec 021 run detail reports as `attemptId`, the call fails
  with `conflict`, changes nothing, and reports the current attempt as
  `details.attemptId`. A client passes the `attemptId` it read, so a decision
  based on a read of an earlier attempt is refused.
- A step that is not waiting fails with `conflict`, changes nothing, and the
  message names the step's current status.
- A decision requires the same authorization as the equivalent REST operation;
  a caller without it fails with `unauthorized` and changes nothing.
- A decision that leaves the run ready to continue resumes it exactly as the
  equivalent REST operation does.

Approval gates:

| Action | Effect |
| --- | --- |
| `approve` | The step succeeds. `inputs` supplies approval input values, each a string. |
| `reject` | The step and the run are rejected, recording `reason` when given. |
| `push_back` | The step is pushed back for re-execution with `inputs` as feedback values, each a string. |

- A non-string `inputs` value fails with `invalid_tool_input` and changes
  nothing.
- An `approve` that omits a name the gate lists as required fails with
  `invalid_tool_input` and changes nothing.

Human tasks:

| Action | Effect |
| --- | --- |
| `approve` | Completes the task with `inputs` as its input object. |
| `push_back` | Pushes the task back with `inputs` as its feedback object. |
| `reject` | Fails with `invalid_tool_input` naming field `action`; human tasks cannot be rejected. Nothing changes. |

- `inputs` follows the typed JSON input rules of Spec 031 `--inputs-json`; an
  omitted `inputs` supplies `{}`.
- Input that the task's form or feedback form refuses fails with
  `invalid_tool_input` and changes nothing.

### Wait behavior

Rules:

- Wait applies after the requested action succeeds and only when a run is
  identified by `name` and a DAG-run ID.
- The call returns when the run reaches a terminal state, stops at a waiting
  checkpoint, or the timeout elapses, whichever comes first.
- A waiting checkpoint is a run with at least one step waiting on manual
  action. A run that reports the waiting status with no such step resumes on
  its own, and the wait continues.
- A run stopped at a waiting checkpoint makes no further progress without an
  operator; the output reports `completed=false`.
- On timeout the run keeps executing; the output reports `completed=false`.
- A wait failure after a successful action is reported inside the successful
  output, not as a tool error.

## Output

A successful result has a text content item describing the outcome and, when a
run is identified, resource-link content items for the run detail and run log
resources.

Structured output rules:

- Output always has `action`, `targetType`, `dagName`, `dagRunId`, and
  `references`.
- When a run is identified, output has `runUri` and `logsUri` with canonical
  `dagu://runs/...` URIs.
- Without `wait`, output for an identified run has `subscribe` guidance text.
- Step decision output also has `stepName`, and `subDAGRunId` when given.
- Without `wait`, step decision output has the `status` and `statusLabel` of
  the run that `name` and `dagRunId` identify, observed after the decision
  when that run could be read.
- With `wait`, output has `completed` boolean plus the last observed `status`
  and `statusLabel` when at least one poll succeeded.
- With `wait`, a completed run's output has `run` holding the Spec 021 run
  detail model, including per-step statuses and errors.
- With `wait`, a run stopped at a waiting checkpoint has `run` holding the same
  model, including each waiting step's human-task or approval prompt and, when
  the step declares one, its normalized form.
- With `wait`, a wait interruption or persistent poll failure is reported as
  `waitError` text alongside `completed=false`.

## Errors

Failed tool calls return an MCP tool result with `isError=true` and structured
output holding the error object:

```json
{
  "code": "invalid_tool_input",
  "message": "The dagRunId field is required.",
  "action": "retry",
  "targetType": "run",
  "dagName": "nightly-report",
  "field": "dagRunId"
}
```

`code` and `message` are required. Clients must branch on `code`, not parse
`message`. `action`, `targetType`, `dagName`, `dagRunId`, `field`, and
`details` are present when they apply, with `field` naming the single input
field that caused an input failure.

Common conditions map to codes as follows:

| Condition | Code |
| --- | --- |
| Authentication is required and the request is not authenticated. | `unauthenticated` |
| The accepted credential is not authorized for the requested execution. | `unauthorized` |
| Tool input is malformed, misses a required field, or uses unknown values. | `invalid_tool_input` |
| The named DAG, DAG-run, or runtime profile does not exist. | `resource_not_found` |
| A singleton run is already running or queued, a DAG with a `restart_policy` has an active run, the step to decide is not waiting, `attemptId` names another attempt, or the action otherwise conflicts with current run state. | `conflict` |
| The action target exists but cannot currently be acted on. | `resource_unavailable` |
| The server fails unexpectedly while handling the action. | `internal_error` |

## Examples

Start a stored DAG and wait up to 30 seconds for its result:

```json
{
  "action": "start",
  "name": "nightly-report",
  "params": {"TARGET": "orders"},
  "wait": true,
  "waitTimeoutSeconds": 30
}
```

Retry one step and its downstream steps:

```json
{
  "action": "retry",
  "name": "nightly-report",
  "dagRunId": "20260701T010000Z",
  "stepName": "load",
  "includeDownstream": true
}
```

Stop a run:

```json
{
  "action": "stop",
  "name": "nightly-report",
  "dagRunId": "20260701T010000Z"
}
```

Approve a waiting approval gate based on a read of attempt `att-2`, and wait
for the run to continue:

```json
{
  "action": "approve",
  "name": "nightly-report",
  "dagRunId": "20260701T010000Z",
  "stepName": "publish_gate",
  "inputs": {"ticket": "CHG-42"},
  "attemptId": "att-2",
  "wait": true
}
```