DeepSeek Harness tool cards: presentCall in a DSH plugin
A DSH plugin tool's output travels two paths: output.render returns the model-visible content, while "cards" are a separate concern declared through pure presentation projections and the optional presentCall / presentResult (source). This article covers what each method returns, how to choose among the five card kinds, and the three hard rules that break things when violated; the defineTool and output.schema contract is in the tool execute contract, and settings-page cards (host half + browser half) are in plugin UI development, so neither is repeated here.
presentCall in a DSH plugin: the three card kinds a pending card can return
presentCall(args) returns a ToolCallView for the pending phase (the PENDING card), covering the period before the tool produces a result (source). Three kinds are available:
generic(default) —{ card: 'generic', title, kind?, rawInput?, content?, locations? }. Setkindfor an icon (read/search/ …); setlocations: [{ path, line? }]to mark the files the tool touches so capable editors can follow or jump.terminal—{ card: 'terminal', title, description?, cwd? }. The call is itself a shell command:titleis the command anddescriptionrenders above the terminal card (astool-bashdoes).diff—{ card: 'diff', title, diffs, locations? }.diffs: [{ path, oldText, newText }]renders an inline diff card (oldText: nullfor a new file, as intool-fswrite/edit).
Note there is no search: discovery results exist only after execute, so a discovery call's pending state can only stay a generic card.
presentResult in a DSH plugin: completed cards and five card details
presentResult(args, { content, isError, meta? }) returns the completed card, and the search and web details are the easiest to miss (source).
generic— provides an optional title and content.terminal— provides raw output and optional exit metadata; each UI renders its own view or a fallback based on capability.diff— provides the applied hunks, usually derived byoutput.presentationMetaand carried through persistedresult.metaso replay can reproduce them. Change-making tools keep the diff result because the completed view replaces the pending card.search— discovery results rebuilt from persistedresult.meta: matches grouped by file (shape: 'matches', grep) or a flat path list (shape: 'paths', glob), plustruncated/totalso the UI never presents a truncated result as complete. This view carries no result text (a UI without asearchcard falls back to the raw result content), and there is nosearchcall view (tool-fs-searchgrep/glob).web— a completed web retrieval distinguished bykind: 'search' | 'fetch'(structured search sources or a fetch summary), derived fromresult.meta; it carries no body copy, so a UI withoutwebcapability falls back to the raw result content (tool-webweb_search/web_fetch).
The three hard rules for DSH plugin tool cards
The official reference singles out the rules that "break things when violated," and all three must hold (source).
- Pure functions — these methods run both during live streaming and during session-log replay, so they must be pure functions of
args(plus result): no I/O, no reading session state, no clocks or randomness. diffs derive from args only (writeusesoldText: nullbecause the call-time presenter has no prior file contents); session context comes from the UI adapter, not the tool. If you find yourself fetching old file contents or the working directory insidepresentCall, stop: that belongs to persisted result metadata or the adapter, not the presenter. - UI formats do not enter the model result — fenced
consoleblocks, diffs, and relativized paths should not enter the canonical value or Native content just to serve the UI.output.renderowns model-visible natural language;presentationMetaand the card presenters own replayable UI state; theterminalresult view carries raw output and the adapter adds fallback formatting as needed. defineToolsoft-validates the presentation path — malformed or legacy-log arguments make the wrapper returnundefined(the generic fallback) instead of throwing, because presentation must never crash replay.
One more architectural discipline: neutral vocabulary lives in dsh-tools, and tools never import UI or transport types; host and client runtimes map each card to their own view. To compare reference implementations, find tool plugins on the DSH Plugin Hub (dsh-tool-fs for generic / diff, dsh-tool-bash for terminal); for the output.render and canonical-value contract see the tool execute contract, and for how tools are consumed inside UI plugins see plugin UI development.
FAQ
In a DSH plugin, output.render returns the model-visible content, while tool cards are a separate concern: cards are declared through pure presentation projections and the optional presentCall / presentResult, and they serve the human interface only. A tool without UI presentation methods falls back to a generic card (title = tool name, raw args as input), so the two should be designed together.
In a DSH plugin, presentCall(args) returns a ToolCallView for the pending phase (the PENDING card), and presentResult(args, { content, isError, meta? }) returns the completed card. Both return a render intent tagged with a card field, chosen among generic, terminal, diff, search, and web to match the tool's behavior.
A DSH plugin's discovery results exist only after execute, so the search card has no pending state: it is rebuilt from persisted result.meta, with grep using shape: 'matches' grouped by file and glob using shape: 'paths' as a flat path list, plus truncated / total so the UI never presents a truncated result as complete. The pending state of a discovery call therefore stays a generic card, and there is no search call view.
DSH plugin card methods must be pure functions of args (plus result), because they run both during live streaming and during session-log replay — no I/O, no reading session state, no clocks or randomness. diffs derive from args only (write uses oldText: null because the call-time presenter has no prior file contents); session context comes from the UI adapter. If you find yourself fetching old file contents or the working directory inside presentCall, stop: that belongs to persisted result metadata or the adapter, not the presenter.
No. DSH plugin defineTool soft-validates the presentation path: malformed or legacy-log arguments make the wrapper return undefined (the generic fallback) instead of throwing, because presentation must never crash replay. Conversely, a tool's UI formats — fenced console blocks, diffs, relativized paths — should not enter the canonical value or Native content just to serve the UI.
Related Terms
- tool card
- A tool card is the pure presentation projection of a tool call in DeepSeek Harness, designed alongside the canonical value via the optional presentCall / presentResult methods and kept separate from output.render, which serves model-visible content; a tool without presentation methods falls back to a generic card.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/adding-a-tool
- render intent
- A render intent is the card-tagged object returned by presentCall / presentResult, using generic, terminal, diff, search, or web to describe which card kind the call should render as; host and client runtimes map each card to their own view.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/adding-a-tool
- PENDING card (ToolCallView)
- The PENDING card is the pending-phase view returned by presentCall, covering the period before the tool produces a result; it may only be generic, terminal, or diff, because discovery results exist only after execute, so there is no search call view.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/adding-a-tool
- result.meta
- result.meta is the replayable metadata the core persists on tool/result, usually derived by output.presentationMeta; the search and web cards rebuild discovery results from it, and the diff card uses it to reproduce applied hunks during replay.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/adding-a-tool