
OpenClaw Tool Plugins 实战指南用 defineToolPlugin 构建纯工具型插件【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw本文是 OpenClaw 插件开发系列中面向纯工具型插件的完整实战指南。核心主题是defineToolPlugin—— 一个只负责为 Agent 提供可调用工具Tools的插件定义方式不涉及 channel、model provider、hook、service 或 setup 后端。你将掌握从openclaw plugins init脚手架、编写 TypeBox 类型化工具、工厂工具factory、输出契约outputSchema到构建、校验、CI 集成、本地安装、ClawHub 发布与排障的完整闭环。对于 provider、channel、hook、service 或多能力混合型插件请以 Building plugins、Channel Plugins、Provider Plugins 为起点而非本文。什么是 Tool PluginTool Plugin 是 OpenClaw 插件体系中最轻量的一类它只向 Agent 暴露可被模型调用的工具除此之外不包含任何其他能力面。它的关键优势在于——defineToolPlugin会静态生成插件清单元数据manifest metadataOpenClaw 无需加载插件运行时代码就能发现插件提供的工具列表。这一点在源码中得到直接印证defineToolPlugin会为入口对象附加一个非枚举的元数据符号toolPluginMetadataSymbolSymbol.for(openclaw.plugin-sdk.tool-plugin.metadata)并暴露getToolPluginMetadata()读取器见 src/plugin-sdk/tool-plugin.ts。静态元数据包含插件 id、name、description、activation、configSchema 以及每个工具的 name/label/description/parameters/outputSchema/optional见 ToolPluginMetadata 类型。Requirements环境要求构建 Tool Plugin 需要满足以下前提Node 24.16 或 Node 26.1。TypeScript ESM 包输出type: module。typebox必须放在dependencies不能只放devDependencies因为生成的插件在运行时会 import 它。openclaw 2026.5.17这是第一个导出openclaw/plugin-sdk/tool-plugin子路径的版本。包根目录需包含dist/、openclaw.plugin.json与package.json。Quickstart五分钟跑通一个工具插件openclaw plugins init stock-quotes --name Stock Quotes cd stock-quotes npm install npm run plugin:build npm run plugin:validate npm testopenclaw plugins init会为你生成以下脚手架文件文件用途src/index.tsdefineToolPlugin入口自带一个echo工具src/index.test.ts元数据测试断言工具列表tsconfig.jsonNodeNext 模式TypeScript 输出到dist/vitest.config.ts针对src/**/*.test.ts的 Vitest 配置package.json脚本、运行时依赖、openclaw.extensions: [./dist/index.js]openclaw.plugin.json为初始工具生成的清单元数据其中npm run plugin:build会先执行npm run buildtsc 编译再执行openclaw plugins build --entry ./dist/index.jsnpm run plugin:validate则会重新构建并执行openclaw plugins validate --entry ./dist/index.js。校验成功时输出Plugin stock-quotes is valid.上述脚本组合同样出现在 CLI 实现中plugins-authoring-command.ts内部生成的脚手架package.json就包含plugin:build与plugin:validate两个脚本见 src/cli/plugins-authoring-command.ts。openclaw plugins init id选项Flag默认值作用--directory pathid输出目录--name nameTitle-casedid显示名称--type typetool脚手架类型tool或provider--force关闭覆盖已存在的输出目录编写一个工具defineToolPlugin 核心用法defineToolPlugin接收三个要素插件身份id/name/description、可选的 config schema、以及一个静态工具列表。参数类型与配置类型会从 TypeBox schema 中自动推断StaticTConfigSchema见 ToolPluginConfig 类型。import { Type } from typebox; import { defineToolPlugin } from openclaw/plugin-sdk/tool-plugin; export default defineToolPlugin({ id: stock-quotes, name: Stock Quotes, description: Fetch stock quote snapshots., configSchema: Type.Object({ apiKey: Type.Optional(Type.String({ description: Quote API key. })), baseUrl: Type.Optional(Type.String({ description: Quote API base URL. })), }), tools: (tool) [ tool({ name: stock_quote, label: Stock Quote, description: Fetch a stock quote snapshot., parameters: Type.Object({ symbol: Type.String({ description: Ticker symbol, for example OPEN. }), }), outputSchema: Type.Object( { symbol: Type.String(), configured: Type.Boolean(), baseUrl: Type.String(), }, { additionalProperties: false }, ), async execute({ symbol }, config, context) { context.signal?.throwIfAborted(); return { symbol: symbol.toUpperCase(), configured: Boolean(config.apiKey), baseUrl: config.baseUrl ?? https://api.example.com, }; }, }), ], });工具名是稳定 API。请选择唯一、小写、足够具体避免与核心工具或其他插件冲突的名称。工具名会在清单中作为contracts.tools契约被记录因此改名意味着需要重新生成元数据。在源码层面声明式工具的execute会被包装为运行时签名execute(toolCallId, params, signal, onUpdate)同时注入api、signal、toolCallId、onUpdate到ToolPluginExecutionContext上下文见 src/plugin-sdk/tool-plugin.ts。这解释了为什么你的execute(params, config, context)能拿到context.signal做中断检查。可选工具与工厂工具optional需要用户显式白名单当工具需要用户显式 allowlist 后才发送给模型时设置optional: true。openclaw plugins build会把匹配的toolMetadata.tool.optional写入清单OpenClaw 无需加载插件运行时即可感知该工具是可选的。tool({ name: workflow_run, description: Run an external workflow., parameters: Type.Object({ goal: Type.String() }), optional: true, execute: ({ goal }) ({ queued: true, goal }), });factory运行时才知道怎么建的工具当工具需要运行时工具上下文才能被创建时使用factory——例如针对某次运行选择退出、检查沙箱状态、绑定运行时辅助函数。元数据保持静态具体工具在运行时构建。tool({ name: local_workflow, description: Run a local workflow outside sandboxed sessions., parameters: Type.Object({ goal: Type.String() }), optional: true, factory({ api, toolContext }) { if (toolContext.sandboxed) { return null; } return createLocalWorkflowTool(api); }, });factory 可以返回一个核心AgentTool、一组工具数组或返回null/undefined选择退出如上例。注意factory 返回的具体工具使用核心运行时签名execute(toolCallId, params, signal?, onUpdate?)toolCallId 在第一个参数——这与声明式execute(params, config, context)的参数顺序恰好相反且与 Building Plugins 中api.registerTool的示例一致。若从 factory 工具的第一个参数读取params你拿到的其实是 toolCallId 字符串。源码中factory分支通过api.registerTool((toolContext) tool.factory?.({ api, config, toolContext }), opts)注册见 src/plugin-sdk/tool-plugin.ts而声明式工具则走完整的参数校验 结果包装路径。factory 上下文ToolPluginFactoryContextTConfig包含api插件运行时 API、config解析后的插件配置与toolContext含沙箱/能力信息见 src/plugin-sdk/tool-plugin.ts。factory 中的消息发送能力factory 可使用toolContext.delivery?.send({ text, mediaUrl })向当前会话发送出站消息。宿主负责选择目的地、账号、线程与本地媒体策略插件不能重定向该辅助函数且保留的副本在会话回合结束后失效。对于由 Gateway 传输层持有 delivery 的 channel该辅助函数不可用。factory 工具的运行时细节具体工具可提供prepareArguments(args)在 schema 校验前规范化输入。原生 agent 循环还支持executionMode: sequential当工具调用必须逐个串行执行时使用。这些运行时属性、schemas 与展示元数据都在工具组装时取自当前 factory 上下文参数准备与执行使用同一实例当所属插件 registry 被回收后保留的工具停止工作。在具体 factory 工具上设置hideFromChannelProgress: true可让其瞬时活动不进入 channel 进度草稿生命周期事件与最终工具结果仍正常流转。OpenClaw 在规范化其 schema 时会保留当前 factory 的该标志省略或为false则保持正常进度行为。参见 Progress drafts。注意factory 仍需在声明时固定工具名。当插件需要动态计算工具名或要把工具与 hooks、services、providers、commands 组合时请直接使用definePluginEntry。返回值约定defineToolPlugin会把普通返回值包装进 OpenClaw 工具结果格式见 wrapToolPluginResult 实现返回字符串模型看到的就是这段精确文本。返回JSON 兼容值模型看到格式化 JSON同时 OpenClaw 在details中保留原始值。tool({ name: echo_text, description: Echo input text., parameters: Type.Object({ input: Type.String(), }), execute: ({ input }) input, });tool({ name: echo_json, description: Echo input as structured JSON., parameters: Type.Object({ input: Type.String(), }), execute: ({ input }) ({ input, length: input.length }), });需要自定义AgentToolResult或复用已有api.registerTool实现时请改用 factory 工具。测试用例也验证了这套包装逻辑plugin-sdk/tool-plugin.test.ts中返回对象被包装为result.content的格式化 JSON 文本同时result.details保留原始对象见 src/plugin-sdk/tool-plugin.test.ts字符串返回则直接作为文本内容。输出契约Output Contracts让模型一次完成调用与变换当工具返回稳定的 JSON 兼容数据时添加outputSchema。它描述的是存放在AgentToolResult.details中的原始值而非content中的格式化文本tool({ name: shipment_list, description: List shipments., parameters: Type.Object({ buyer: Type.Optional(Type.String()), }), outputSchema: Type.Array( Type.Object( { id: Type.String(), buyer: Type.String(), paid: Type.Boolean(), tons: Type.Number(), }, { additionalProperties: false }, ), ), execute: ({ buyer }) listShipments(buyer), });Code Mode 与 Tool Search 会把该 schema 转换为有界的 TypeScript 风格输出提示让模型能在一次程序中完成调用并变换已知结果而不必再花一个模型回合去观察结果形状。关键语义与约束OpenClaw 在执行目录调用之前编译 schema并在工具 hooks 之后、经 bridge 返回前校验最终details值。无效的 schema 无法运行工具结果不匹配会使已完成调用失败。请包含所有不会抛异常的返回值变体包括结构化错误变体结果不稳定时干脆省略 schema。不要把密钥或敏感值写进 schema description——受信任的输出元数据可能对模型可见。在对象层使用{ additionalProperties: false }以获得完整紧凑的输出提示开放或截断的 schema 仍可通过 callable catalog 句柄的describe()获取但不会被宣传为完整的 quick-index 契约。factory 工具在返回的具体AnyAgentTool上声明outputSchema静态的tool({ factory })声明不接受独立的 output schema因为它可能与运行时工具漂移。结果分级保留字段OpenClaw 还会从details对调用结果分级因此status、ok、success、error、timedOut、exitCode是保留字段名。当status为blocked、denied、invalid、cancelled或其他失败值时即便execute正常返回调用也会被标记为失败除非显式设置ok或success为true。使用这些名字的领域数据应放在包装键下例如{ card }而不是details顶层。配置ConfigurationconfigSchema是可选的。省略时OpenClaw 应用严格的空对象 schemaType.Object({}, { additionalProperties: false })但生成的清单仍包含configSchema字段见 EMPTY_TOOL_PLUGIN_CONFIG_SCHEMA。export default defineToolPlugin({ id: no-config-tools, name: No Config Tools, description: Adds tools that do not need configuration., tools: () [], });提供configSchema后execute的第二个参数会按其类型推断const configSchema Type.Object({ apiKey: Type.String(), }); export default defineToolPlugin({ id: configured-tools, name: Configured Tools, description: Adds configured tools., configSchema, tools: (tool) [ tool({ name: configured_ping, description: Check whether configuration is available., parameters: Type.Object({}), execute: (_params, config) ({ hasKey: config.apiKey.length 0 }), }), ], });OpenClaw 从 Gateway 配置中该插件的条目读取插件配置。不要把密钥硬编码进源码或文档示例——按插件的安全模型使用配置、环境变量或 SecretRefs。测试中通过captured.api.pluginConfig { apiKey: test-key }注入配置并断言config.apiKey的类型与值见 src/plugin-sdk/tool-plugin.test.ts。生成的元数据Generated MetadataOpenClaw 必须在导入插件运行时代码之前读取插件清单。defineToolPlugin为此暴露静态元数据openclaw plugins build把它写入包内。修改插件 id、name、description、config schema、activation 或工具名后务必重新运行生成器npm run build openclaw plugins build --entry ./dist/index.js一个单工具插件生成的清单示例{ id: stock-quotes, name: Stock Quotes, description: Fetch stock quote snapshots., version: 0.1.0, configSchema: { type: object, additionalProperties: false, properties: {} }, activation: { onStartup: true }, contracts: { tools: [stock_quote] } }contracts.tools是关键发现契约它告诉 OpenClaw 每个工具归属于哪个插件无需加载所有已安装插件的运行时。清单过期会导致工具从发现结果中消失或注册错误被归咎于错误的插件。包元数据Package Metadataopenclaw plugins build还会把package.json对齐到所选运行时入口{ type: module, files: [dist, openclaw.plugin.json, README.md], dependencies: { typebox: ^1.1.38 }, peerDependencies: { openclaw: 2026.5.17 }, openclaw: { extensions: [./dist/index.js] } }发布编译后的 JavaScript./dist/index.js而不是 TypeScript 源码入口——源码入口只适用于工作区本地开发。在 CI 中校验plugins build --check在生成元数据过期时失败但不改写文件npm run build openclaw plugins build --entry ./dist/index.js --check openclaw plugins validate --entry ./dist/index.js npm testOpenClaw SDK 的兼容性字段带有 TypeScriptdeprecated注解编辑器会以迁移警告的形式呈现。要在 CI 中强制执行请启用类型感知规则如typescript-eslint/no-deprecated。Oxlint 不是类型感知的无法强制这些注解因此生成的plugins init脚手架不包含弃用 lint 配置。plugins validate检查项openclaw.plugin.json存在并通过常规清单加载器。当前入口导出defineToolPlugin元数据。生成的清单字段与入口元数据匹配。contracts.tools与声明的工具名匹配。package.json的openclaw.extensions指向所选运行时入口。CLI 源码中generated metadata is stale. Run openclaw plugins build. 与 Generated plugin metadata is out of date. 等错误信息正是这些检查的失败分支见 src/cli/plugins-authoring-command.ts。本地安装与检查从另一个 OpenClaw checkout 或已安装的 CLI 中安装包路径openclaw plugins install ./stock-quotes openclaw plugins inspect stock-quotes --runtime进行打包冒烟测试时先 pack 再安装 tarballnpm pack openclaw plugins install npm-pack:./openclaw-plugin-stock-quotes-0.1.0.tgz openclaw plugins inspect stock-quotes --runtime --json安装会自动应用到正在运行的本地 Gateway若 Gateway 已停止则先启动它。然后让 Agent 使用该工具。如果工具不可见在改代码之前先检查插件运行时与生效的工具目录见下方 Troubleshooting。后续对源码或清单的修改请使用 plugin Reload。发布到 ClawHub包就绪后通过 ClawHub 发布。clawhub package publish接受三类来源本地文件夹、GitHub 仓库owner/repo[ref]或 tarball URL。clawhub package publish ./stock-quotes --dry-run clawhub package publish ./stock-quotes使用显式 ClawHub locator 安装openclaw plugins install clawhub:your-org/stock-quotes裸 npm 包规格会从 npm 安装但ClawHub 是 OpenClaw 插件的首选发现与分发渠道。owner 范围与发布审核参见 ClawHub publishing。Troubleshooting 排障指南plugin entry not found: ./dist/index.js所选入口文件不存在。运行npm run build然后重跑openclaw plugins build --entry ./dist/index.js或openclaw plugins validate --entry ./dist/index.js。plugin entry does not expose defineToolPlugin metadata入口没有导出defineToolPlugin创建的值。确认模块的默认导出是defineToolPlugin(...)的结果或用--entry传入正确的入口。openclaw.plugin.json generated metadata is stale清单与入口元数据不再匹配。运行npm run build openclaw plugins build --entry ./dist/index.js并提交openclaw.plugin.json与package.json的改动。package.json openclaw.extensions must include ./dist/index.js包元数据指向了不同的运行时入口。运行openclaw plugins build --entry ./dist/index.js让生成器把包元数据对齐到你要发布的入口。Cannot find package typebox构建出的插件在运行时 import 了typebox。把它保留在dependencies重新安装、重新构建并重新校验。安装后工具不出现按顺序检查openclaw plugins inspect plugin-id --runtimeopenclaw plugins validate --root plugin-root --entry ./dist/index.jsopenclaw.plugin.json的contracts.tools包含期望的工具名。package.json的openclaw.extensions: [./dist/index.js]。安装报告运行时应用成功若源码编辑后或修复激活失败后运行openclaw plugins reload plugin-id。延伸阅读Building pluginsPlugin SDK overviewPlugin entry pointsPlugin SDK subpathsPlugin manifestPlugins CLIClawHub publishing如果你需要 provider、channel、hook、service 或多能力混合型插件请回到 Building plugins、Channel Plugins、Provider Plugins 阅读对应指南。【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考