Ctrl K
ComunidadGuías…

specs/021-mcp-read-tool.md

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

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

Status: Implemented.

## Scope

This spec defines the `dagu_read` MCP tool-call contract: input fields,
addressing modes, target contracts, successful output, resource links,
read-only effects, and tool-level errors.

This spec does not define the MCP endpoint, authentication, API-key surfaces,
`dagu://` URI grammar, MCP resource reads, `dagu_change`, `dagu_execute`,
prompts, subscriptions, REST response schemas, Web UI behavior, 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.

The `data` object returned by this tool is part of the `dagu_read` contract.
REST API response schemas may be used by an implementation, but they are not
the MCP tool output contract.

## Goal

`dagu_read` gives MCP clients one read-only way to inspect Dagu state and
built-in MCP reference content while preserving the resource URI model from
Spec 020.

## Behavior

### Tool Identity

Rules:

- The tool name is `dagu_read`.
- The tool is read-only from the Dagu domain perspective.
- A successful call must not create, update, delete, start, enqueue, retry, or
  stop DAGs or DAG runs.
- 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 | Allowed in mode | Required rule | Meaning |
| --- | --- | --- | --- | --- |
| `target` | string | Target mode only. | Required for target mode. | Case-sensitive read target literal. |
| `name` | string | Target mode only. | Required for `dag`, `dag_spec`, `run`, `run_logs`, `step_log`, `foreach_items`, and `foreach_item`; optional for `reference`; forbidden for the remaining targets. | DAG name or reference topic name. |
| `dagRunId` | string | Target mode only. | Required for `run`, `run_logs`, `step_log`, `foreach_items`, and `foreach_item`; forbidden for all other targets. | DAG-run identifier. |
| `subRunId` | string | Target mode only. | Optional for `run`, `step_log`, `foreach_items`, and `foreach_item`; forbidden for all other targets. | Child DAG-run identifier addressed under the root run selected by `name` and `dagRunId`. |
| `stepName` | string | Target mode only. | Required for `step_log`, `foreach_items`, and `foreach_item`; forbidden for all other targets. | Step name inside the DAG-run. |
| `item` | string | Target mode only. | Required for `foreach_item`; optional for `step_log`, where it requires `bodyStepName`; forbidden for all other targets. | Foreach item path: the item index, or for an item of a nested foreach the `foreachParent` a body step reports followed by `.` and the index. |
| `bodyStepName` | string | Target mode only. | Optional for `step_log`, where it requires `item`; forbidden for all other targets. | Body step name or ID inside the foreach item. |
| `query` | string | Target mode only. | Optional only for `dags`, `wiki`, `runs`, `run_logs`, `step_log`, and `foreach_items`; forbidden for all other targets. | URL query string without a leading `?`. |
| `workspace` | string | Target mode only. | Required for `wiki_page`, where `all` is not allowed; optional for `wiki`, `wiki_search`, and `dag_search`, defaulting to `all`; forbidden for all other targets. | Workspace selector. |
| `path` | string | Target mode only. | Required for `wiki_page` and `workbook`; forbidden for all other targets. | Wiki page path without the `.md` extension, or for `workbook` a file path on the server. |
| `password` | string | Target mode only. | Optional for `workbook`; forbidden for all other targets. | Password of a protected workbook, kept exactly as supplied. It is not recorded in the audit log. |
| `search` | string | Target mode only. | Required for `wiki_search` and `dag_search`; forbidden for all other targets. | Search text. |
| `prefix` | string | Target mode only. | Optional for `wiki` and `wiki_search`; forbidden for all other targets. | Wiki page path prefix. |
| `cursor` | string | Target mode only. | Optional for `wiki_search` and `dag_search`; forbidden for all other targets. | Opaque continuation cursor returned by the same search target. |
| `limit` | integer | Target mode only. | Optional for `wiki_search` and `dag_search`, from `1` through `50`; forbidden for all other targets. | Maximum number of search results. |
| `uri` | string | URI mode only. | Required for URI mode. | Direct `dagu://` resource URI. Query parameters for URI mode live inside this value. |

Supported fields other than `limit`, when present and not `null`, must be
strings, and `limit` must be an integer. `null` is treated as absent. String
field values other than `password` are trimmed of leading and trailing
whitespace. The trimmed value is the effective value used for mode selection,
resource lookup, returned `target`, returned `uri`, and error fields. A string
that is empty after trimming is treated as absent. A forbidden field fails
when its trimmed value is non-empty. `password` is kept exactly as supplied,
including leading and trailing spaces and a value that is only spaces. An
empty `password` and `null` are absent, and a forbidden `password` fails when
the supplied value is non-empty. Supported target names are case-sensitive.

The `docs`, `doc`, and `doc_search` target values are deprecated aliases that
resolve to `wiki`, `wiki_page`, and `wiki_search`. They are accepted but not
advertised.

### Addressing modes

| Mode | Required fields | Forbidden fields | Read identity | Resolved output `target` |
| --- | --- | --- | --- | --- |
| Target mode | `target` | `uri` | Reads one named target using target-specific fields. | The supplied `target` value. |
| URI mode | `uri` | Every target-mode field. | Reads the MCP resource identified by `uri`. URI query parameters live inside `uri`. | Derived from the URI family and path. |

Rules:

- A request with neither addressing mode fails with `invalid_tool_input`.
- A request with fields from both addressing modes fails with
  `invalid_tool_input`.
- In target mode, `target=reference` defaults `name` to `authoring` when
  `name` is absent.
- In URI mode, output `target` is derived as follows:
  - `dagu://reference` resolves to `references`.
  - `dagu://reference/{topic}` resolves to `reference`.
  - `dagu://dags` resolves to `dags`.
  - `dagu://dags/{name}/spec` resolves to `dag_spec`.
  - `dagu://wiki` and `dagu://wiki/{workspace}` resolve to `wiki`.
  - `dagu://wiki/{workspace}/{path}` resolves to `wiki_page`.
  - `dagu://runs` resolves to `runs`.
  - `dagu://runs/{name}/{dagRunId}` resolves to `run`.
  - `dagu://runs/{name}/{dagRunId}/logs` resolves to `run_logs`.
  - `dagu://runs/{name}/{dagRunId}/steps/{stepName}/logs` resolves to
    `step_log`.
  - `dagu://runs/{name}/{dagRunId}/sub/{subRunId}` resolves to `run`.
  - `dagu://runs/{name}/{dagRunId}/sub/{subRunId}/steps/{stepName}/logs`
    resolves to `step_log`.
  - `dagu://runs/{name}/{dagRunId}/steps/{stepName}/foreach` resolves to
    `foreach_items`.
  - `dagu://runs/{name}/{dagRunId}/steps/{stepName}/foreach/{item}` resolves
    to `foreach_item`.
  - `dagu://runs/{name}/{dagRunId}/steps/{stepName}/foreach/{item}/steps/{bodyStepName}/logs`
    resolves to `step_log` with `item` and `bodyStepName`.
  - The three foreach shapes under `dagu://runs/{name}/{dagRunId}/sub/{subRunId}`
    resolve the same way with `subRunId`.
  - Query parameters do not affect target derivation.

### Target contracts

| Target | Required fields | Optional fields | Output `uri` | Minimum `data` contract |
| --- | --- | --- | --- | --- |
| `references` | None. | None. | Omitted in target mode; `dagu://reference` in URI mode. | Reference collection model. |
| `reference` | None. | `name`. | `dagu://reference/{name}` after applying the default topic when needed. | `data.mimeType` is `text/markdown`; `data.text` is a string. |
| `dags` | None. | `query`. | Omitted in target mode; `dagu://dags` plus query in URI mode. | DAG collection model. |
| `dag` | `name`. | None. | Omitted. Spec 020 does not define a DAG-details resource URI. | DAG detail model. |
| `dag_spec` | `name`. | None. | `dagu://dags/{name}/spec`. | DAG spec model. |
| `dag_search` | `search`. | `workspace`, `cursor`, `limit`. | Omitted. | DAG search model. |
| `wiki` | None. | `workspace`, `query`, `prefix`. | `dagu://wiki` or `dagu://wiki/{workspace}`, plus query when present. | Wiki collection model. |
| `wiki_page` | `workspace`, `path`. | None. | `dagu://wiki/{workspace}/{path}`. | Wiki page model. |
| `wiki_search` | `search`. | `workspace`, `prefix`, `cursor`, `limit`. | Omitted. | Wiki search model. |
| `workbook` | `path`. | `password`. | Omitted. | Workbook model. The output also carries `path` as supplied. |
| `runs` | None. | `query`. | Omitted in target mode; `dagu://runs` plus query in URI mode. | Run collection model. |
| `run` | `name`, `dagRunId`. | `subRunId`. | `dagu://runs/{name}/{dagRunId}` or its `/sub/{subRunId}` child URI. | Run detail model. |
| `run_logs` | `name`, `dagRunId`. | `query`. | `dagu://runs/{name}/{dagRunId}/logs`, with the supplied query appended when present. | Run-log model. |
| `step_log` | `name`, `dagRunId`, `stepName`. | `subRunId`, `query`, `item` with `bodyStepName`. | The root or child step-log URI, or the foreach step-log URI when `item` is supplied, with the supplied query appended when present. | Step-log model. |
| `foreach_items` | `name`, `dagRunId`, `stepName`. | `subRunId`, `query`. | The root or child foreach-items URI, with the supplied query appended when present. | Foreach items model. |
| `foreach_item` | `name`, `dagRunId`, `stepName`, `item`. | `subRunId`. | The root or child foreach-item URI. | Foreach item model. |

Minimum `data` models:

| Model | Required shape |
| --- | --- |
| Reference collection | `data.items` is an array. Each item has `name` string, `uri` string, and `mimeType` string. The array includes `authoring`, `tools`, `read-tool`, `change-tool`, `execute-tool`, and `notifications`. |
| Reference topic | `data.mimeType` is `text/markdown`; `data.text` is the Markdown content string. |
| DAG collection | `data.items` is an array. Each item has `name` string, `uri` string, and `suspended` boolean. `uri` is the canonical `dagu://dags/{name}/spec` URI for that DAG. `data.pagination` is an object describing the returned page. The collection includes DAGs defined under `paths.alt_dags_dir` in addition to those discovered under `paths.dags_dir`. |
| DAG detail | `data.name` is the DAG name string. `data.specUri` is the canonical `dagu://dags/{name}/spec` URI. `data.suspended` is a boolean. When the DAG has run at least once, `data.latestRun` is an object with `dagRunId` string, `status` number, and `statusLabel` string. |
| DAG spec | `data.name` is the DAG name string. `data.mimeType` is `application/yaml`. `data.spec` is the YAML document string. `data.errors` is an array of strings. |
| Run collection | `data.items` is an array. Each item has `name` string, `dagRunId` string, `uri` string, `status` number, and `statusLabel` string. `uri` is the canonical `dagu://runs/{name}/{dagRunId}` URI. An item carries `startedAt` and `finishedAt` strings once those timestamps are recorded. When another page is available, `data.nextCursor` is an opaque cursor string. |
| Run detail | `data.name` is the resolved DAG name string. `data.dagRunId` is the resolved DAG-run ID string. `data.uri` is the canonical root or child run URI. Root runs include the canonical `data.logsUri`. `data.attemptId` is the string identifier of the run's current attempt once one is recorded; each retry of the run, including a resume after manual action, records a new attempt. `data.status` is a number. `data.statusLabel` is a string. The run carries `startedAt` and `finishedAt` strings once those timestamps are recorded. `data.steps` is an array with one entry per step in execution order; each entry has `name` string, `status` number, `statusLabel` string, and a root- or child-addressed `logUri` string, and a failed step carries its `error` string when one was recorded. A `foreach` step carries `foreachUri`, the canonical foreach-items URI for that step. A step waiting on an open human task carries `humanTask` with its resolved `prompt` string and, when the task declares a form, its normalized `form` object. A step waiting on an approval gate carries `approval` with the gate's `prompt` string, `input` array, and `required` array, each present when the gate declares it. `data.humanTaskResumePending` is `true` when accepted human-task input is durable but the run still needs its retry queued. |
| Run logs | `data.schedulerLog` is an object with `content` string, `lineCount` number, `totalLines` number, and `hasMore` boolean. `data.stepLogs` is an array. Each step-log item has `stepName` string, `status` number, `statusLabel` string, `hasStdout` boolean, and `hasStderr` boolean. |
| Step log | `data.stdoutContent` and `data.stderrContent` are strings holding the selected log lines; a stream excluded by the `stream` parameter is empty. `data.lineCount`, `data.totalLines`, and `data.hasMore` describe the returned stream, following stdout unless only stderr was requested. |
| Foreach items | `data.total` is the number of items the step expanded to. `data.counts` is an object with `notStarted`, `running`, `succeeded`, `failed`, and `aborted` numbers. `data.items` is one page of items sorted failed first, then running, then the rest in index order; each has `item` string, `index` number, `key` string, `status` number, and `statusLabel` string, and carries `error`, `startedAt`, and `finishedAt` strings once recorded. |
| Foreach item | `data.item`, `data.key`, `data.statusLabel` are strings, `data.index` and `data.status` numbers, and `data.error` a string when the body run failed. `data.steps` is an array in execution order; each entry has `name` string, `status` number, `statusLabel` string, `hasStdout` and `hasStderr` booleans, carries `error`, `startedAt`, and `finishedAt` strings once recorded, and for a body step that is itself a foreach carries `foreachParent`, the `parent` query value that lists its items. A body step that kept an agent session, such as the operations of a browser step, carries it as `agentSession`, with the run's secrets masked, and a body step that called models carries `modelUsage`; both have the shape the run's own steps have in the HTTP API. |
| DAG search | `data.results` is an array. Each result has `name` string, `uri` string set to the canonical `dagu://dags/{name}/spec` URI, `matches` array of line-level snippets, and `hasMoreMatches` boolean. `data.hasMore` is a boolean, and `data.nextCursor` is an opaque cursor string when another page is available. |
| Workbook | `data.path` is the resolved file path string and `data.date_system` is a string, `1900` or `1904`. `data.sheets` is an array with one entry per sheet, each with `name` string, `hidden` boolean `true` for a hidden or very hidden sheet, `used_range` string, `range` string, `header_row` number (0 for an empty sheet), `headers` array of strings, `types` object mapping header to `string`, `number`, `integer`, `boolean`, `date`, or `datetime`, `row_count` number, `hidden_rows` number of data rows hidden by a filter or by hand when there are any, `columns` array profiling each column in header order, `profile_truncated` boolean `true` when the table holds more data rows than the profile reads, `tables` array of `{name, range}`, and, for a sheet with a header row, `sample` array of up to five typed rows keyed by header, each carrying `_row`. Each `columns` entry has `name` string, `type` string, and `filled`, `blank`, and `distinct` numbers, and may carry `values` array of strings, `min` and `max`, `odd` number, and `odd_cells` array of `{cell, text}`; Spec 077 defines the profile. `data.named_ranges` is an array of `{name, refers_to, scope}` and `data.warnings` an array of strings. The path is any workbook the server process can read, the same trust as DAG authoring, and the audit record carries it as `workbook_path`. A path that is not `.xlsx` or `.xlsm` is `invalid_tool_input` on `path`, a missing file is `resource_not_found`, and a file another program holds or that is not a workbook is `resource_unavailable`. A protected workbook needs `password`; a missing or wrong password is `invalid_tool_input` on `password`, with `<name>: workbook password is missing or incorrect`, and a workbook whose encryption is not supported is `resource_unavailable`. The password is not recorded in the audit log. |
| Wiki collection | `data.pagination` is an object describing the returned page. Tree mode returns `data.tree`, and flat mode returns `data.items`; entries carry `id` strings and canonical `dagu://wiki/{workspace}/{path}` URIs for pages. |
| Wiki page | `data.id` is the page path string. `data.content` is the Markdown string. `data.mimeType` is `text/markdown`. `data.uri` is the canonical `dagu://wiki/{workspace}/{path}` URI. |
| Wiki search | `data.results` is an array. Each result has `id` string, `uri` string, `matches` array of snippets, and `hasMoreMatches` boolean. `data.hasMore` is a boolean, and `data.nextCursor` is an opaque cursor string when another page is available. |

Every `startedAt` and `finishedAt` value in these models is an RFC 3339 string with millisecond precision, for example `2026-10-27T08:02:02.158Z`. A run that an older version stored can have second precision, with no fraction.

Implementations may add fields inside `data`. Conformance tests must not require
absence of additional fields.

### Query handling

Rules:

- The `query` field is allowed only in target mode for `dags`, `wiki`,
  `runs`, `run_logs`, `step_log`, and `foreach_items`.
- The `query` field must not start with `?`.
- The same parameter names are allowed in URI-mode collection and log URIs.
- Query parameter order is not normative.

The effective query string is parsed as URL query data. An empty effective
query string is treated as absent. Invalid percent-encoding, unknown
parameters, repeated parameters not marked repeatable, empty values, and values
outside the table below fail with `invalid_tool_input` in target mode and
`invalid_resource_uri` in URI mode.

| Target | Parameter | Value contract |
| --- | --- | --- |
| `dags` | `page` | Integer greater than or equal to `1`. |
| `dags` | `perPage` | Integer from `1` through `200`. |
| `dags` | `name` | Non-empty string. |
| `dags` | `labels` | Comma-separated non-empty label strings. |
| `dags` | `sort` | One of `name` or `nextRun`. |
| `dags` | `order` | One of `asc` or `desc`. |
| `runs` | `name` | Non-empty string. |
| `runs` | `dagRunId` | Non-empty string. The value `latest` is allowed. |
| `runs` | `status` | Repeatable integer enum value from `0` through `8`. |
| `runs` | `fromDate` | Unix timestamp in seconds. |
| `runs` | `toDate` | Unix timestamp in seconds. |
| `runs` | `limit` | Integer from `1` through `500`. |
| `runs` | `cursor` | Non-empty opaque string. |
| `runs` | `labels` | Comma-separated non-empty label strings. |
| `wiki` | `page` | Integer greater than or equal to `1`. |
| `wiki` | `perPage` | Integer from `1` through `200`. |
| `wiki` | `flat` | One of `true` or `false`. |
| `wiki` | `sort` | One of `name`, `type`, or `mtime`. |
| `wiki` | `order` | One of `asc` or `desc`. |
| `wiki` | `prefix` | Valid Wiki page path prefix. |
| `run_logs` | `tail` | Integer from `1` through `10000`. |
| `step_log` | `tail` | Integer from `1` through `10000`. |
| `step_log` | `head` | Integer from `1` through `10000`. |
| `step_log` | `offset` | Integer greater than or equal to `1`. |
| `step_log` | `limit` | Integer from `1` through `10000`. |
| `step_log` | `stream` | One of `stdout` or `stderr`. |
| `foreach_items` | `parent` | A `foreachParent` value reported by a `foreach_item` body step. |
| `foreach_items` | `status` | One of `not_started`, `running`, `succeeded`, `failed`, or `aborted`. |
| `foreach_items` | `page` | Integer greater than or equal to `1`. |
| `foreach_items` | `perPage` | Integer from `1` through `500`. |

For `step_log`, at most one of `tail`, `head`, and `offset` may be supplied.
`limit` may be combined only with `offset`; `limit` alone reads from the
beginning of the log.

### Output

A successful MCP tool result has:

- A first text content item containing the target-specific success text described below.
- Structured output with this exact top-level envelope:

```json
{
  "target": "run_logs",
  "uri": "dagu://runs/nightly-report/20260701T010000Z/logs?tail=100",
  "data": {},
  "references": [
    "dagu://reference/authoring",
    "dagu://reference/tools",
    "dagu://reference/notifications"
  ]
}
```

Rules:

- `target` is the resolved read target.
- `uri` is present when the read has a canonical `dagu://` resource URI. URI
  mode always returns the validated resource URI, including collection URIs.
- `data` contains the read result and must satisfy the target contract above.
- `references` contains exactly `dagu://reference/authoring`,
  `dagu://reference/tools`, and `dagu://reference/notifications`; order is not
  normative.
- For `dags`, the success text is `Dagu read completed.`, followed by a blank
  line and indented JSON containing the same value as `data`.
- For every other target, the success text is exactly `Dagu read completed.`.
- `step_log`, `foreach_items`, and `foreach_item` results additionally echo
  `name`, `dagRunId`, and `stepName` at the top level, plus `subRunId`,
  `item`, and `bodyStepName` when supplied; Wiki results echo `workspace`,
  `path`, and `prefix` when supplied.
- A result with `uri` has exactly two content items: `content[0]` is a text
  content item with the target-specific success text, and `content[1]` is a
  resource-link content item for that URI.
- A result without `uri` has exactly one content item: `content[0]` is a text
  content item with the target-specific success text.
- URI path segments in returned URIs follow Spec 020 escaping.

Resource-link content items have MCP content type `resource_link`. The required
fields are `uri`, `name`, and `mimeType`. Optional MCP resource-link fields,
such as `title` and `description`, may be present.

| URI shape | Resource-link `name` | Resource-link `mimeType` |
| --- | --- | --- |
| `dagu://reference` | `dagu_references` | `application/json` |
| `dagu://reference/{topic}` | `dagu_reference` | `text/markdown` |
| `dagu://dags` | `dags` | `application/json` |
| `dagu://dags/{name}/spec` | `dag_spec` | `application/yaml` |
| `dagu://runs` | `dag_runs` | `application/json` |
| `dagu://runs/{name}/{dagRunId}` | `dag_run` | `application/json` |
| `dagu://runs/{name}/{dagRunId}/logs` | `dag_run_logs` | `application/json` |
| `dagu://runs/{name}/{dagRunId}/steps/{stepName}/logs` | `dag_run_step_log` | `application/json` |
| `dagu://runs/{name}/{dagRunId}/sub/{subRunId}` | `sub_dag_run` | `application/json` |
| `dagu://runs/{name}/{dagRunId}/sub/{subRunId}/steps/{stepName}/logs` | `sub_dag_run_step_log` | `application/json` |
| `dagu://runs/{name}/{dagRunId}/steps/{stepName}/foreach` | `dag_run_foreach_items` | `application/json` |
| `dagu://runs/{name}/{dagRunId}/steps/{stepName}/foreach/{item}` | `dag_run_foreach_item` | `application/json` |
| `dagu://runs/{name}/{dagRunId}/steps/{stepName}/foreach/{item}/steps/{bodyStepName}/logs` | `dag_run_foreach_step_log` | `application/json` |
| The three foreach shapes under `dagu://runs/{name}/{dagRunId}/sub/{subRunId}` | `sub_dag_run_foreach_items`, `sub_dag_run_foreach_item`, `sub_dag_run_foreach_step_log` | `application/json` |
| `dagu://wiki` and `dagu://wiki/{workspace}` | `wiki` | `application/json` |
| `dagu://wiki/{workspace}/{path}` | `wiki_page` | `text/markdown` |

## Errors

Failed tool calls return an MCP tool result with `isError=true`. When the
transport supports structured output, the structured output is the error object.
Text content may include the error message as a fallback.

The error object has this shape:

```json
{
  "code": "invalid_tool_input",
  "message": "The dagRunId field is required for target run_logs.",
  "target": "run_logs",
  "field": "dagRunId",
  "uri": "dagu://runs/nightly-report/20260701T010000Z/logs",
  "details": {}
}
```

`code` and `message` are required. Clients must branch on `code`, not parse
`message`.

Optional field rules:

| Field | Required when |
| --- | --- |
| `target` | A valid target has been resolved, or an unsupported target value was supplied. |
| `field` | Exactly one input field caused the failure. For an unknown field, this is the unknown field name. |
| `uri` | URI mode supplied a non-empty `uri`, or target mode resolved a singular resource URI before lookup failed. |
| `details` | More than one field caused the failure, or the server needs to expose structured validation details. A `resource_not_found` failure for a DAG name may carry close existing names as `details.didYouMean`. |

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 read. | `unauthorized` |
| Tool input is not a JSON object. | `invalid_tool_input` |
| Tool input contains an unknown field. | `invalid_tool_input` |
| A supported field has a non-string, non-null value. | `invalid_tool_input` |
| The request provides neither `target` nor `uri` after trimming. | `invalid_tool_input` |
| The request combines URI mode with any non-empty target-mode field. | `invalid_tool_input` |
| A required target-mode field is absent after trimming. | `invalid_tool_input` |
| A field is supplied for a target that forbids it. | `invalid_tool_input` |
| `target` is present but is not a supported case-sensitive read target. | `unsupported_read_target` |
| The `query` field is supplied in URI mode, supplied for a target that does not allow it, starts with `?`, contains malformed URL query encoding, or contains unsupported parameters. | `invalid_tool_input` |
| `uri` is malformed, uses the wrong scheme, has an unsupported path shape, or contains malformed query parameters. | `invalid_resource_uri` |
| `uri` identifies a resource family that this tool cannot read. | `unsupported_resource` |
| The named DAG, DAG run, log stream, or reference topic does not exist. | `resource_not_found` |
| The resource exists but cannot currently be read. | `resource_unavailable` |
| The server fails unexpectedly while handling the read. | `internal_error` |

## Examples

URI mode direct reference read:

```json
{
  "uri": "dagu://reference/authoring"
}
```

Target mode default reference:

```json
{
  "target": "reference"
}
```

Target mode DAG spec:

```json
{
  "target": "dag_spec",
  "name": "nightly-report"
}
```

Target mode run logs with query:

```json
{
  "target": "run_logs",
  "name": "nightly-report",
  "dagRunId": "20260701T010000Z",
  "query": "tail=100"
}
```

Invalid mixed-mode request:

```json
{
  "uri": "dagu://reference/authoring",
  "target": "reference"
}
```

Expected error code:

```json
{
  "code": "invalid_tool_input"
}
```