# Tool Schema Catalog Every model-facing tool a shipped plugin contributes to `ctx.tools`: the `name`, `description`, and JSON-Schema `parameters` the model receives via the system-prompt assembly. It complements the cordis [events](cordis-catalog/events.md) & [services](cordis-catalog/services.md) catalogs (the wiring a plugin listens to and calls) and [core-data-structures/](core-data-structures/core.md) (the types those signatures move) — this page is the *tools* the agent is offered. This file is GENERATED and verified fresh by `pnpm run verify-tool-catalog` (part of `doc-sync`) — do not edit it by hand. Unlike the cordis catalog (a pure source-AST pass), this generator BOOTS each tool plugin on a real context and reads `ctx.tools.schemas()`, because a tool schema is not statically knowable (runtime-spread enums, concatenated descriptions, config-driven names, raw-JSON-Schema MCP tools). A completeness guard globs `packages/*/tool-*` and fails if any package is missing from the generator's boot manifest, so a new tool cannot be silently undocumented. See [the tool-schema-catalog RFC](rfc/implemented/process/2026-07-02-tool-schema-catalog.md). Scope: shipped product tools under `packages/*/tool-*`, each booted with its DEFAULT config. The registered tool NAME can be a load-time config (e.g. `tool-subagent`'s `toolName`), so a deployment may surface a package under a different or additional name — a per-package note records those shipped aliases where they exist. The `examples/` demo tools (e.g. `echo`) are excluded, matching the cordis catalog's packages-only scope. ## Tool Package Map This table connects model-visible tool names to the plugin package and service seams behind them. Exact JSON Schemas follow in the package sections below. | Tool package | Model-visible names | Requires | Writes / affects | Shipped aliases | Deployment note | | --- | --- | --- | --- | --- | --- | | `@deepseek-ai/dsh-tool-ask-user` | `ask_user_question` | `ctx.tools`, `ctx.userInteraction` | `tool/call`, `tool/result after a UI/provider answers the question` | - | ask_user_question pauses the tool call until the active UI provider returns a human answer. | | `@deepseek-ai/dsh-tool-bash` | `bash` | `ctx.tools`, `ctx.bash`, `ctx.tasks at call time for run_in_background` | `tool/call`, `tool/result` | - | The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.tasks` runtime and is collected/stopped through the `task_*` tools from `@deepseek-ai/dsh-tool-tasks`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled. | | `@deepseek-ai/dsh-tool-fs` | `edit`, `read`, `write` | `ctx.tools`, `ctx.fs`, `ctx.systemPrompt` | `tool/call`, `fs/write-intent or fs/edit-intent for mutations`, `fs/observed after successful file operations`, `tool/result` | - | The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. | | `@deepseek-ai/dsh-tool-subagent` | `subagent` | `ctx.tools`, `ctx.subagents` | `tool/call`, `tool/result`, `child session events through the chosen provider` | `subagent`, `subagent_fork` | The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees `subagent_fork` (bound to the fork backend) with an identical schema — see `examples/coding-agent/cordis.yml` and `examples/acp-agent/cordis.yml`. | | `@deepseek-ai/dsh-tool-tasks` | `task_kill`, `task_list`, `task_output` | `ctx.tools`, `ctx.tasks`, `ctx.systemPrompt` | `tool/call`, `tool/result`, `context/message via agent.inject() for background completion notices` | - | The kind-agnostic background-task control surface: a background bash command and a background subagent are read, listed, and killed through the same three tools. Loading the plugin attaches the control surface that arms producers' `ctx.tasks.start()`. | | `@deepseek-ai/dsh-tool-todo` | `todo_write` | `ctx.tools`, `owning Agent session` | `tool/call`, `todo/write`, `tool/result` | - | todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan. | | `@deepseek-ai/dsh-tool-web` | `web_fetch`, `web_search` | `ctx.tools`, `ctx.web`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps. | ## `@deepseek-ai/dsh-tool-ask-user` ### `ask_user_question` Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding. Send one or more questions, each with a stable id that will be echoed in the answer. ```json { "type": "object", "properties": { "questions": { "type": "array", "description": "Questions to ask the user before continuing.", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Stable id for this question; echoed in the answer." }, "question": { "type": "string", "description": "The specific question to ask the user." }, "header": { "type": "string", "description": "Optional short heading for the question, such as \"Confirm\" or \"Choose Mode\"." }, "options": { "type": "array", "description": "Optional choices to show the user. If you recommend one, put it first and append \"(Recommended)\" to that label.", "items": { "type": "object", "properties": { "label": { "type": "string", "description": "Short user-facing option label." }, "description": { "type": "string", "description": "One sentence explaining the tradeoff or impact." } }, "required": [ "label" ] } }, "multi_select": { "type": "boolean", "description": "Whether the user may select more than one option. Defaults to false." } }, "required": [ "id", "question" ] } } }, "required": [ "questions" ] } ``` Source: [`packages/ui/tool-ask-user/src/index.ts`](../packages/ui/tool-ask-user/src/index.ts) ask_user_question pauses the tool call until the active UI provider returns a human answer. ## `@deepseek-ai/dsh-tool-bash` ### `bash` Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. ```json { "type": "object", "properties": { "command": { "type": "string", "description": "The bash command to execute." }, "description": { "type": "string", "description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"." }, "timeoutMs": { "type": "number", "description": "Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry." }, "workdir": { "type": "string", "description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it." }, "run_in_background": { "type": "boolean", "description": "Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies." } }, "required": [ "command", "description" ] } ``` Source: [`packages/bash/tool-bash/src/index.ts`](../packages/bash/tool-bash/src/index.ts) The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.tasks` runtime and is collected/stopped through the `task_*` tools from `@deepseek-ai/dsh-tool-tasks`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled. ## `@deepseek-ai/dsh-tool-fs` ### `edit` Edit an existing UTF-8 text file by replacing literal text. ```json { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to edit, resolved by the filesystem backend." }, "old_string": { "type": "string", "description": "Literal text to replace. Must match exactly." }, "new_string": { "type": "string", "description": "Literal replacement text. Use an empty string to delete the match." }, "replace_all": { "type": "boolean", "description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once." } }, "required": [ "file_path", "old_string", "new_string" ] } ``` Source: [`packages/fs/tool-fs/src/index.ts`](../packages/fs/tool-fs/src/index.ts) ### `read` Read a UTF-8 text file and return line-numbered content. ```json { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to read, resolved by the filesystem backend." }, "offset": { "type": "number", "description": "1-based first line to return. Defaults to 1." }, "limit": { "type": "number", "description": "Maximum number of lines to return. Defaults to 2000." } }, "required": [ "file_path" ] } ``` Source: [`packages/fs/tool-fs/src/index.ts`](../packages/fs/tool-fs/src/index.ts) ### `write` Create or fully replace a UTF-8 text file. ```json { "type": "object", "properties": { "file_path": { "type": "string", "description": "Path to write, resolved by the filesystem backend." }, "content": { "type": "string", "description": "Full UTF-8 text content to write." } }, "required": [ "file_path", "content" ] } ``` Source: [`packages/fs/tool-fs/src/index.ts`](../packages/fs/tool-fs/src/index.ts) The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. ## `@deepseek-ai/dsh-tool-subagent` ### `subagent` Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. Set `run_in_background: true` to get a task id immediately and keep working; collect the final answer with `task_output` (wait: true when you are blocked on it) and stop it with `task_kill`. ```json { "type": "object", "properties": { "description": { "type": "string", "description": "A short (3-5 word) description of the delegated task, for display." }, "prompt": { "type": "string", "description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs." }, "run_in_background": { "type": "boolean", "description": "Run the subagent as a background task and return a task id immediately (collect with task_output, stop with task_kill)." } }, "required": [ "description", "prompt" ] } ``` Source: [`packages/subagent/tool-subagent/src/index.ts`](../packages/subagent/tool-subagent/src/index.ts) The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees `subagent_fork` (bound to the fork backend) with an identical schema — see `examples/coding-agent/cordis.yml` and `examples/acp-agent/cordis.yml`. ## `@deepseek-ai/dsh-tool-tasks` ### `task_kill` Request cancellation of a running background task by task id. Returns immediately; the task settles as killed once its work actually stops. ```json { "type": "object", "properties": { "task_id": { "type": "string", "description": "Task id returned by the tool that started the background work." }, "reason": { "type": "string", "description": "Optional short reason, recorded in the log and forwarded to the task." } }, "required": [ "task_id" ] } ``` Source: [`packages/tasks/tool-tasks/src/index.ts`](../packages/tasks/tool-tasks/src/index.ts) ### `task_list` List your background tasks (running and finished) with their ids, kinds, and statuses. ```json { "type": "object", "properties": {} } ``` Source: [`packages/tasks/tool-tasks/src/index.ts`](../packages/tasks/tool-tasks/src/index.ts) ### `task_output` Read output/status from a background task (started by a tool with `run_in_background`). Stream tasks (bash) return only output produced since your previous task_output call; final-output tasks (subagent) return the final answer once the task finishes. Every response ends with a [status: ...] line. Non-blocking by default; set `wait: true` to block until the task finishes (bounded by a capped timeout) when you are genuinely blocked on its result. ```json { "type": "object", "properties": { "task_id": { "type": "string", "description": "Task id returned by the tool that started the background work." }, "wait": { "type": "boolean", "description": "Block until the task reaches a terminal status or the timeout expires. A timed-out wait returns [status: running] and leaves the task alive." }, "timeout_ms": { "type": "number", "description": "Max wait in milliseconds (only meaningful with wait: true). Defaults to the configured wait timeout; capped by the configured maximum." } }, "required": [ "task_id" ] } ``` Source: [`packages/tasks/tool-tasks/src/index.ts`](../packages/tasks/tool-tasks/src/index.ts) The kind-agnostic background-task control surface: a background bash command and a background subagent are read, listed, and killed through the same three tools. Loading the plugin attaches the control surface that arms producers' `ctx.tasks.start()`. ## `@deepseek-ai/dsh-tool-todo` ### `todo_write` Record and update a structured task list for the current work. Send the ENTIRE list every call — it REPLACES the previous list (there are no partial updates, no per-item edits). Use it to plan multi-step work and show progress: add one todo per concrete step before you start. Keep AT MOST ONE todo `in_progress` at a time; while work remains, exactly one active task should be `in_progress`. Mark a todo `completed` the moment it is done (do not batch completions), and allow no `in_progress` item only once all work is complete. Skip the list for trivial single-step tasks. Statuses: `pending` (not started), `in_progress` (being worked on now), `completed` (finished). ```json { "type": "object", "properties": { "todos": { "type": "array", "description": "The COMPLETE task list, replacing any previous list.", "items": { "type": "object", "properties": { "content": { "type": "string", "description": "What the task is — a short imperative line." }, "status": { "type": "string", "description": "pending (not started) | in_progress (now) | completed (done).", "enum": [ "pending", "in_progress", "completed" ] } }, "required": [ "content", "status" ] } } }, "required": [ "todos" ] } ``` Source: [`packages/todo/tool-todo/src/index.ts`](../packages/todo/tool-todo/src/index.ts) todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan. ## `@deepseek-ai/dsh-tool-web` ### `web_fetch` Fetch the content of a specific HTTP(S) URL and return it decoded to text. ```json { "type": "object", "properties": { "url": { "type": "string", "description": "The HTTP(S) URL to fetch." } }, "required": [ "url" ] } ``` Source: [`packages/web/tool-web/src/index.ts`](../packages/web/tool-web/src/index.ts) ### `web_search` Search the web for current information. Returns an optional summary answer and a list of source URLs. ```json { "type": "object", "properties": { "query": { "type": "string", "description": "The search query." } }, "required": [ "query" ] } ``` Source: [`packages/web/tool-web/src/index.ts`](../packages/web/tool-web/src/index.ts) web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps.