Files
deepseek-harness/docs/tool-catalog.md
2026-07-13 16:17:36 +08:00

31 KiB

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 & services catalogs (the wiring a plugin listens to and calls) and core-data-structures/ (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.

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-tools run_code ctx.tools, ctx.codeRuntime (execution time), ctx.systemPrompt tool/call, one tool/code-dispatch per bridged sub-call, tool/result - Owned by the tool registry as a reserved transport outside filterable capability layers under mode: code / mode: both (see the Code Mode RFC). Under code it is the registry's only wire contribution; the other visible capabilities are declared in a generated TypeScript SDK section, and a program calls them through serialized bindings that re-enter the complete guarded tool pipeline and link each nested execution to this outer result.
@deepseek-ai/dsh-tool-bash bash, bash_kill, bash_output ctx.tools, ctx.bash tool/call, tool/result, context/message via agent.inject() for background completion notices - The bash/bash_output/bash_kill tools are model-facing consumers of the bash executor seam.
@deepseek-ai/dsh-tool-cordis cordis_inspect, cordis_mount, cordis_unmount ctx.tools tool/call, tool/result, live plugin-tree mutations (mount/unmount) - Ships in examples/cordis-agent only (a deliberate opt-in — mounted code gets the real ctx, see docs/rfc/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). Plugins the model mounts may register ADDITIONAL model-visible tools at runtime; the request-header ToolsDelta logs those tool-set changes.
@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-fs-search glob, grep ctx.tools, ctx.bash, ctx.systemPrompt tool/call, tool/result - glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find rg, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments.
@deepseek-ai/dsh-tool-skill skill ctx.tools, ctx.skills tool/call, tool/result - -
@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-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-workflow workflow ctx.tools, ctx.workflows, ctx.systemPrompt, a calling Agent (exec.agent parents the script children) tool/call, tool/result - -
@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.

{
  "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

ask_user_question pauses the tool call until the active UI provider returns a human answer.

@deepseek-ai/dsh-tools

run_code

Execute a TypeScript program against the available tools. Write the BODY of an async function (erasable syntax only; top-level await and return work) and call tools as await tools.name(args) per the declarations in the system prompt. Only what you print or return comes back — curate it.

{
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "description": "The program: the body of an async TypeScript function."
    }
  },
  "required": [
    "code"
  ]
}

Source: packages/core/tools/src/code-mode.ts

Owned by the tool registry as a reserved transport outside filterable capability layers under mode: code / mode: both (see the Code Mode RFC). Under code it is the registry's only wire contribution; the other visible capabilities are declared in a generated TypeScript SDK section, and a program calls them through serialized bindings that re-enter the complete guarded tool pipeline and link each nested execution to this outer result.

@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]. Commands may run under a file sandbox; a blocked file operation is reported as [sandbox: file access denied under <mode> mode] — a policy denial, not a bug in the command; do not retry another way (a background task reports the same marker via bash_output once it has finished). 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; poll it with bash_output and stop it with bash_kill.

{
  "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. No timeout applies."
    }
  },
  "required": [
    "command",
    "description"
  ]
}

Source: packages/bash/tool-bash/src/index.ts

bash_kill

Ask the executor to kill a running background bash task by task id.

{
  "type": "object",
  "properties": {
    "task_id": {
      "type": "string",
      "description": "Task id returned by the bash tool."
    }
  },
  "required": [
    "task_id"
  ]
}

Source: packages/bash/tool-bash/src/index.ts

bash_output

Read new output from a background bash task started with bash + run_in_background. Returns only output produced since the previous bash_output call, plus the task status. Tasks keep running while you do other work; poll again later for more output.

{
  "type": "object",
  "properties": {
    "task_id": {
      "type": "string",
      "description": "Task id returned by the bash tool."
    }
  },
  "required": [
    "task_id"
  ]
}

Source: packages/bash/tool-bash/src/index.ts

The bash/bash_output/bash_kill tools are model-facing consumers of the bash executor seam.

@deepseek-ai/dsh-tool-cordis

cordis_inspect

Inspect the live cordis runtime that is running THIS agent. Read-only. Sections: services (every provided ctx service and the plugin fiber that owns it), plugins (a flat list of the loaded plugins with their lifecycle states), tools (the model-facing tools currently registered, i.e. what you can call), dynamic (plugins you mounted via cordis_mount: id, name, state, provided services, awaited services), api (method signatures AND argument/return type shapes for every LIVE service — read this before writing plugin code that calls a service), events (every harness event with its dispatch mode and exact signature — pick listener targets here). Omit what to get all six sections.

{
  "type": "object",
  "properties": {
    "what": {
      "type": "string",
      "description": "Limit the report to one section. Omit for all sections.",
      "enum": [
        "services",
        "plugins",
        "tools",
        "dynamic",
        "api",
        "events"
      ]
    }
  }
}

Source: packages/cordis/tool-cordis/src/index.ts

cordis_mount

Mount a NEW cordis plugin into the live runtime that is running THIS agent (self-modification). code runs as the body of an async JavaScript function in an isolated sandbox and MUST return a plugin. Two forms: FUNCTION form return (ctx) => { … } — declares no inject, so it can register tools, listen to events, and provide services, but reaching ANY service (e.g. ctx.bash) throws; use it only when you need no services. OBJECT form return { name?, inject: ['bash', 'llm', …], apply(ctx) { … } } — declares dependencies, and cordis activates the plugin only after the services exist; PREFER this form. You may reach ONLY the services you list in inject: an undeclared service throws even if it exists, because an undeclared dependency would not be cleaned up if its provider is unmounted. BEFORE calling a service from your code, read cordis_inspect what:"api" — it lists method signatures AND the type shapes of their arguments/returns (do not guess a field's type; e.g. a bash run's stdout is an object, not a string). Inside apply, use the standard cordis API: ctx.on(event, listener) to observe events (see cordis_inspect what:"events"), or call harness.registerTool(ctx, harness.defineTool({ name, description, parameters: { text: { type: 'string', required: true } }, async execute(args) { … } })) to give yourself a new tool — it becomes callable on your NEXT step. Tool parameters: each key IS a property — { type: 'string'|'number'|'boolean'|'object'|'array', required?: true, description?, enum?, items?, properties? }; a JSON-Schema-style { type: 'object', properties, required: […] } wrapper and type 'integer' are also accepted and normalized. A tool's execute MUST return an ARRAY of content blocks, e.g. return [{ type: 'text', text: someString }] — never a bare string. Mounts can COMPOSE: one plugin may ctx.provide('name', value) a service and another may declare inject: ['name'] to consume it — the consumer stays pending until the provider exists and returns to pending when the provider is unmounted. Everything registered inside apply is cleaned up automatically on unmount. Sandbox globals: console (tagged [cordis:<id>], writes through to the harness terminal), harness.defineTool, harness.registerTool, btoa, atob, TextEncoder, TextDecoder. Node APIs are DISABLED — do filesystem/network/timer work through the cordis services, never Node built-ins: require, setTimeout/setInterval, and fetch throw redirect errors; process and Buffer are undefined. Instead use inject: ['fs'] + ctx.fs for files, inject: ['web'] + ctx.web for HTTP, inject: ['bash'] + ctx.bash for processes, and inject: ['timer'] + ctx.setTimeout/ctx.setInterval for timing (fiber effects, auto-cleaned on unmount) — cordis_inspect what:"api" shows what THIS runtime provides. Write PLAIN JavaScript, not TypeScript (no as, no type annotations). Cautions: (1) waterfall events (e.g. tools/pre-execute) hand the listener a trailing next callback which MUST be called — returning without next() VETOES the call; prefer plain notification events unless you intend to intercept. (2) Never await something that only resolves after the current turn (your code runs INSIDE a tool call of that turn — it would deadlock). (3) Your ctx is a restricted façade: you can register tools, observe events, provide/consume services, and use timers, but framework internals (ctx.root, ctx.fiber, ctx.extend, ctx.plugin, …) are withheld. It is not a security boundary though — the services you inject (e.g. ctx.bash) reach the real runtime.

{
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "description": "Body of an async JS function; must `return` the plugin to mount."
    }
  },
  "required": [
    "code"
  ]
}

Source: packages/cordis/tool-cordis/src/index.ts

cordis_unmount

Dispose a plugin previously mounted with cordis_mount, by id. All its registrations (event listeners, tools, services) are cleaned up through the cordis effect lifecycle. Returns only after disposal has fully completed (quiescence, not just a request to stop).

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The dynamic mount id returned by cordis_mount (e.g. \"dyn-1\")."
    }
  },
  "required": [
    "id"
  ]
}

Source: packages/cordis/tool-cordis/src/index.ts

Ships in examples/cordis-agent only (a deliberate opt-in — mounted code gets the real ctx, see docs/rfc/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). Plugins the model mounts may register ADDITIONAL model-visible tools at runtime; the request-header ToolsDelta logs those tool-set changes.

@deepseek-ai/dsh-tool-fs

edit

Edit an existing UTF-8 text file by replacing literal text.

{
  "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

read

Read a UTF-8 text file and return line-numbered content.

{
  "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

write

Create or fully replace a UTF-8 text file.

{
  "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

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.

glob

Find files whose paths match a glob pattern. Returns matching paths sorted by modification time, including hidden and ignored files (VCS metadata directories are excluded). Returns the first 100 paths inline; a capped result reports where the complete list was saved.

{
  "type": "object",
  "properties": {
    "pattern": {
      "type": "string",
      "description": "Glob pattern to match file paths against (e.g. \"**/*.ts\", \"src/**/*.test.js\")."
    },
    "path": {
      "type": "string",
      "description": "Directory to search in. Defaults to the session workspace; a relative path resolves against it."
    }
  },
  "required": [
    "pattern"
  ]
}

Source: packages/fs/tool-fs-search/src/index.ts

grep

Search file contents with a ripgrep regular expression. Returns matching lines with line numbers, grouped by file. Returns the first 250 matches inline; a capped result reports where the complete match list was saved. Use read on a matched file for surrounding context.

{
  "type": "object",
  "properties": {
    "pattern": {
      "type": "string",
      "description": "Regular expression to search for (ripgrep syntax)."
    },
    "path": {
      "type": "string",
      "description": "File or directory to search. Defaults to the session workspace; a relative path resolves against it."
    },
    "include": {
      "type": "string",
      "description": "One glob filter for which files to search (e.g. \"*.ts\", \"*.{js,jsx}\"). Not a list; negation is not supported."
    }
  },
  "required": [
    "pattern"
  ]
}

Source: packages/fs/tool-fs-search/src/index.ts

glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find rg, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments.

@deepseek-ai/dsh-tool-skill

skill

Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The exact skill name from the available skills list."
    }
  },
  "required": [
    "name"
  ]
}

Source: packages/skill/tool-skill/src/index.ts

@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.

{
  "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."
    }
  },
  "required": [
    "description",
    "prompt"
  ]
}

Source: 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-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).

{
  "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

todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan.

@deepseek-ai/dsh-tool-workflow

workflow

Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.

The workflow's identity rides the meta parameter as JSON: required name (short kebab-case) and description strings, optional whenToUse string and phases array ({title, detail?, model?}). The script parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO export const meta statement — meta is a parameter, not code), running with top-level await; end with return <value> — the value must be JSON-serializable and is this tool's result.

Script-body hooks:

  • agent(prompt, opts?): Promise<any> — run one subagent to completion. Without opts.schema it resolves to the child's final text; with opts.schema (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const — no oneOf/pattern/format/numeric bounds) it resolves to the validated object. Resolves null when the child fails (filter with .filter(Boolean)). Other opts: label (display), phase (progress group), model (override). Anything else (effort/isolation/agentType) is rejected loudly.
  • pipeline(items, ...stages): Promise<any[]> — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives (prev, item, index). An ordinary stage throw drops that ITEM to null and skips its remaining stages.
  • parallel(thunks): Promise<any[]> — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to null.
  • phase(title) — start a progress phase; log(message) — narrate progress; args — the tool call's args input, verbatim.

Misused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item null.

Constraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them. The run executes in the foreground: this call returns when the whole script finishes.

{
  "type": "object",
  "properties": {
    "script": {
      "type": "string",
      "description": "The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`)."
    },
    "meta": {
      "type": "object",
      "description": "The workflow identity block (plain JSON — never code).",
      "properties": {
        "name": {
          "type": "string",
          "description": "Short kebab-case workflow name."
        },
        "description": {
          "type": "string",
          "description": "One-line description of what the workflow does."
        },
        "whenToUse": {
          "type": "string",
          "description": "Optional guidance on when this workflow applies."
        },
        "phases": {
          "type": "array",
          "description": "Optional phase declarations matched by phase() calls.",
          "items": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string",
                "description": "The phase title phase() calls match by exact string."
              },
              "detail": {
                "type": "string",
                "description": "Optional one-line description of the phase."
              },
              "model": {
                "type": "string",
                "description": "Optional model override this phase is expected to use."
              }
            },
            "required": [
              "title"
            ]
          }
        }
      },
      "required": [
        "name",
        "description"
      ]
    },
    "args": {
      "type": "object",
      "description": "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]})."
    }
  },
  "required": [
    "script",
    "meta"
  ]
}

Source: packages/workflow/tool-workflow/src/index.ts

@deepseek-ai/dsh-tool-web

web_fetch

Fetch the content of a specific HTTP(S) URL and return it decoded to text.

{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "description": "The HTTP(S) URL to fetch."
    }
  },
  "required": [
    "url"
  ]
}

Source: packages/web/tool-web/src/index.ts

Search the web for current information. Returns an optional summary answer and a list of source URLs.

{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "The search query."
    }
  },
  "required": [
    "query"
  ]
}

Source: 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.