DeepSeek Harness Development First Tool: defineTool

Tools are functions used by an Agent to get things done. After the model sees the tool definition, it decides whether to call it.

In this chapter, we will use defineTool to write our first tool, greet, and have the model actually call it.


What is a tool?

A tool is a clearly described function that includes a name, description, parameters, and output format.

When generating a response, the model can make calls based on this information.

In dsh, tools are registered to the tool registry via ctx.tools.register.


The Complete Structure of defineTool

defineTool is a DSL for defining tools. It takes an object that describes all the information about the tool.

FieldsFunctionDescription
nameTool nameThe model uses it to initiate calls.
descriptionTool descriptionTells the model what this tool does.
parametersinput parameter schemaTyped definition: defineTool infers and validates args from it.
output.schemaReturn value schemaDeclares the canonical value type returned by execute.
output.renderResult formattingConverts canonical values into model-oriented content.
executeTool implementationActually executes the logic and returns canonical values.

Create a greet tool.

Replace scratch-plugin/src/my-plugin.ts with the following content.

Example

// File path: scratch-plugin/src/my-plugin.ts
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'greet-tool'
// Requires tools service: prerequisite for registering tools.
export const inject = ['tools']

export function apply(ctx: Context) {
  // Register a tool named greet
  ctx.tools.register(defineTool({
    // Tool name: the model will invoke the tool by this name
    name: 'greet',
    // Tool description: tell the model when to use it
    description: 'Greet someone by name.',
    // Input parameter schema: defineTool will infer and validate args
    parameters: {
      name: { type: 'string', required: true, description: 'The name to greet' },
    },
    // Output definition
    output: {
      // Normalize value type: return value of execute
      schema: { type: 'string' },
      // render: convert canonical values into model-oriented content
      render: (_args, value) => [{ type: 'text', text: value }],
    },
    // Tool implementation: actually execute logic
    async execute(args) {
      // Return a canonical value; here it is a string
      return `Hello, ${args.name}!`
    },
  }))
}

inject makes Cordis wait for the tool registry to be ready.

defineTool derives and validates args based on parameters.

execute returns the canonical value declared by output.schema.

output.render then converts the canonical value into model-facing content.


Run and invoke

If the development command is not running, restart it:

pnpm dsh web --patch ./scratch-plugin/cordis.yml

Open http://127.0.0.1:3080, enter:

Use the greet tool to greet EXAMPLE.

The model can call greet and receive the tool result Hello, EXAMPLE!.

工具注册到执行的完整流程


Key point: the schema automatically flows into the prompt.

The tool's name, description, parameters, and output are automatically assembled into the model prompt.

The model "knows" such a tool exists and will call it at the right time.

You don't need to hand-write function signatures for the model. The schema is the interface the model sees.

Canonical value(canonical value) is the value returned by execute and declared in output.schema.

It is decoupled from "model-facing content": the same canonical value can be turned into different formats through different render functions.

Tip: to see more complex tool examples (nested schemas, background work, policy hooks), refer to the official tool writing guide.


Summary and self-test

defineTool describes the tool with a schema, ctx.tools.register registers it, and the model calls it directly after seeing the schema.

1. What does defineTool's execute return?

2. What is the purpose of output.render?

3. How does the model know to call greet?

other extensions