fix(worker): make list_tasks/batch_get_tasks lean by default

list_tasks on a list of ~100 verbosely-described tasks could return
390k+ characters in one call, blowing past the caller's token limit.
Both tools now default to lean TaskRefDto references (no
Description/Result) and take an includeDescription flag to opt back
into the full TaskDto payload — same flag-alongside-nullable-payload
idiom already used by BatchGetTaskResult/TaskConfigResult. get_task is
unchanged.
This commit is contained in:
mika kuns
2026-08-06 11:25:32 +02:00
parent 86f962ee7d
commit f8c48e2ed7
5 changed files with 151 additions and 22 deletions
+14 -2
View File
@@ -1,8 +1,8 @@
# External MCP tool surface
> **Explore-note — verify before trusting.** Distilled map of a subsystem, not authoritative.
> Last verified against commit `bdee731` (2026-08-05).
> Drift check: `git log --oneline bdee731..HEAD -- src/ClaudeDo.Worker/External`
> Last verified against commit `86f962e` (2026-08-06).
> Drift check: `git log --oneline 86f962e..HEAD -- src/ClaudeDo.Worker/External`
> Stable structure only (no line numbers). See docs/explore-notes/README.md.
Covers `src/ClaudeDo.Worker/External/` — the always-on MCP tools ClaudeDo exposes to general
@@ -27,6 +27,11 @@ session internals, or app-settings writes. Auth via an optional `X-ClaudeDo-Key`
the resulting config so the caller can see which fields were set vs. cleared to null).
- *Read* tools that may have nothing to return use an explicit `Found` / `Available` flag
alongside the nullable payload (`TaskConfigResult`, `BatchGetTaskResult`, `TaskLogResult`).
- The same flag-alongside-nullable-payload idiom also covers "which of two shapes did you
get": `ListTasks`/`BatchGetTasks` take `includeDescription` (default `false`) and return
`ListTasksResult`/`BatchGetTaskResult`, where exactly one of the lean (`TaskRefDto`) and
full (`TaskDto`, incl. Description/Result) fields is populated per the flag — keeps a
list of verbosely-described tasks from blowing past the response size limit by default.
3. `ExternalMcpExceptionFilter.Wrap` is registered as a call-tool filter so
`InvalidOperationException` / `ArgumentException` messages survive as `McpException` —
otherwise the SDK's catch-all replaces any non-`McpException` with a generic
@@ -62,6 +67,11 @@ Daily prep: `GetDailyPrepCandidates`, `SetMyDay`.
## Per-tool behaviour worth knowing
**`ListTasks`** — `includeDescription=false` (default) returns lean `TaskRefDto` references in
`tasks` (`tasksFull` null); `includeDescription=true` returns full `TaskDto`s (incl.
Description/Result) in `tasksFull` instead (`tasks` null). Filtering by `createdBy`/`status`
happens before the lean/full projection either way.
**`UpdateTaskStatus`** accepts `Idle` / `Queued` / `Cancelled` / `Done` only.
- `Cancelled` goes through `TaskStateService.CancelAsync(..., allowFromIdle: true)` — the
**only** caller that opts into cancelling from `Idle`. `PlanningChainCoordinator` relies on
@@ -102,6 +112,8 @@ directory is shared with other concurrent sessions.
**Sequential**, because the scoped `DbContext` is not thread-safe. Merge/review stay
single-task. Every tool returns a per-item result array (`{ id/index, ok, error?, … }`) — a
failing item never aborts the rest — and rejects batches over **100 items**.
`BatchGetTasks` mirrors `ListTasks`'s `includeDescription` flag (default `false`): a found item's
`BatchGetTaskResult` carries `task` (lean) or `taskFull` (full), never both.
**`GetTaskLog`** — latest run's log, tail-capped at 256 KB.