openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
103 lines (91 loc) • 3.92 kB
Markdown
---
summary: "The tool-only entry helper, its schemas, and generated manifest metadata"
title: "Plugin SDK defineToolPlugin helper"
sidebarTitle: "defineToolPlugin"
read_when:
- You are writing a plugin that only adds agent tools
- You need the defineToolPlugin signature and its manifest output
---
The entry helper for plugins that only add agent tools. Part of the
[Plugin entry points](/plugins/sdk-entrypoints) reference.
## `defineToolPlugin`
**Import:** `openclaw/plugin-sdk/tool-plugin`
For plugins that only add agent tools. Keeps the source small, infers config
and tool-parameter types from TypeBox schemas, wraps plain return values in
the OpenClaw tool-result format, and exposes static metadata that
`openclaw plugins build` writes into the plugin manifest (`contracts.tools`,
`configSchema`).
```typescript
import { Type } from "typebox";
import { defineToolPlugin } from "openclaw/plugin-sdk/tool-plugin";
export default defineToolPlugin({
id: "stock-quotes",
name: "Stock Quotes",
description: "Fetch stock quotes.",
configSchema: Type.Object({
apiKey: Type.Optional(Type.String({ description: "API key." })),
}),
tools: (tool) => [
tool({
name: "quote",
label: "Quote",
description: "Fetch a quote.",
parameters: Type.Object({
symbol: Type.String({ description: "Ticker symbol." }),
}),
outputSchema: Type.Object(
{
symbol: Type.String(),
hasKey: Type.Boolean(),
},
{ additionalProperties: false },
),
execute: async ({ symbol }, config) => ({ symbol, hasKey: Boolean(config.apiKey) }),
}),
],
});
```
- `configSchema` is optional; omitting it uses a strict empty object schema
(the generated manifest still includes `configSchema`).
- `execute` returns a plain string or JSON-serializable value; the helper
wraps it as a text tool result with `details` set to the original
(unstringified) return value.
- `outputSchema` optionally describes that original `details` value for Code
Mode and Tool Search. Catalog calls reject an invalid schema before execution
and validate the final value before returning it.
- For custom tool results, `openclaw/plugin-sdk/tool-results` exports
`textResult` and `jsonResult`.
- Tool names are static, so `openclaw plugins build` derives
`contracts.tools` from the declared tools without hand-duplicated names.
- Runtime loading stays strict: installed plugins still need
`openclaw.plugin.json` and `package.json` `openclaw.extensions`. OpenClaw
never executes plugin code to infer missing manifest data.
## Input-dependent output schemas
The existing `outputSchema` field supports action-specific Code Mode results
through the versioned `x-openclaw-input-discriminator` JSON Schema annotation.
Construct the union and mapping from the same local variants using standard
TypeBox APIs:
```typescript
const variants = Object.entries({
list: Type.Object({ items: Type.Array(Type.String()) }),
status: Type.Object({ ready: Type.Boolean() }),
});
const outputSchema = Type.Union(
variants.map(([, schema]) => schema),
{
"x-openclaw-input-discriminator": {
version: 1,
inputProperty: "action",
mapping: Object.fromEntries(variants.map(([value], index) => [value, index])),
},
},
);
```
Use the result as `outputSchema` in `defineToolPlugin` or `api.registerTool`.
Include success and non-throwing failure outcomes in each variant. Static metadata
preserves the annotation. Code Mode infers the selected return type; Tool Search
validates the actual and originally advertised operation results. Missing or
broad selectors retain a sound union. Reference-bearing schemas and declarations
that exceed existing bounds use the conservative umbrella contract. See
[Input-dependent outputs](/tools/code-mode/output#input-dependent-outputs) for
version, hook, and validation behavior.