关于 Hedera Plugin Creation
Hedera Plugin Creation 是面向开发者的 Skill,指导为 Hedera Agent Kit 构建自定义插件,涵盖工具定义、生命周期实现和工具包注册,包括 MCP 工具包。
主要功能
- 01插件结构与导出方式
- 02BaseTool 生命周期指南
- 03Zod 参数验证
- 04查询与变更工具模式
- 05工具包插件注册
- 06参考模式与示例
适用场景
- 01构建自定义 Hedera 代币工具
- 02实现只读 Hedera 查询
- 03向 Agent 工具包注册插件
MCP 相关
介绍如何将 Hedera Agent Kit 插件注册到 `@hashgraph/hedera-agent-kit-mcp`。插件通过所选工具包向 AI Agent 提供工具。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ hedera-dev/ hedera-skills --skill hedera-plugin-creation安装后在 Claude Code 中输入/hedera-plugin-creation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/simple-plugin/index.tsexamples/simple-plugin/tools/greeting.tsexamples/token-plugin/index.tsexamples/token-plugin/tools/create-token.tsget-token-info.tsreferences/plugin-interface.mderror-handling.mdprompt-patterns.mdzod-schema-patterns.md详细介绍
创建 Hedera Agent Kit 插件
本技能提供创建自定义插件的指导,用于扩展 Hedera Agent Kit。插件支持添加与 Hedera 网络交互的新工具,包括代币操作、账户管理、共识服务、智能合约和自定义集成。
快速入门
通过以下 5 个步骤创建 Hedera 插件:
- 安装依赖项:使用
@hashgraph/hedera-agent-kit和@hiero-ledger/sdk搭建 TypeScript 项目 - 创建插件结构:创建包含插件定义的
index.ts和tools/目录 - 定义工具:每个工具都是一个扩展
BaseTool的类 — 这样工具即可参与钩子/策略生命周期 - 正确导出:导出插件对象和工具名称常量
- 向 Agent 注册:导入插件,并将其传入工具包的
configuration.plugins数组
插件接口
每个 Hedera 插件都实现 @hashgraph/hedera-agent-kit 中的此接口:
import { Plugin } from '@hashgraph/hedera-agent-kit';
export interface Plugin {
name: string; // Unique kebab-case identifier
version?: string; // Semantic version (e.g., "1.0.0")
description?: string; // Brief explanation of plugin purpose
tools: (context: Context) => Tool[]; // Factory returning tools
}
tools 函数接收一个包含网络配置的 Context 对象,并返回一个 Tool 对象数组。BaseTool 本身实现了 Tool,因此 BaseTool 子类实例是有效的返回值。
BaseTool
工具以扩展 BaseTool 的类来编写。BaseTool 实现了 Tool,并运行一个包含 7 个阶段的生命周期:
[1] preToolExecutionHook
[2] normalizeParams ← your logic
[3] postParamsNormalizationHook
[4] coreAction ← your logic
[5] postCoreActionHook
[6] secondaryAction ← your logic (skipped via shouldSecondaryAction for queries)
[7] postToolExecutionHook
在 Agent Kit 上注册的钩子和策略会在阶段 1、3、5 和 7 自动触发 — 无需手动调用。
子类必须提供:
| 成员 | 类型 | 说明 |
|---|---|---|
method | string | 以 _tool 为后缀的蛇形命名标识符 |
name | string | 面向用户的显示名称 |
description | string | 面向大模型的描述(通常在构造函数中由提示词函数生成) |
parameters | z.ZodObject | 用于输入验证的 Zod 架构 |
normalizeParams(params, context, client) | async | 阶段 2 — 解析默认值、验证格式 |
coreAction(normalised, context, client) | async | 阶段 4 — 构建交易或执行查询 |
secondaryAction(request, client, context) | async | 阶段 6 — 签名/提交交易(查询操作无需执行) |
可选重写项:outputParser、shouldSecondaryAction(仅查询工具返回 false)、handleError,以及四个钩子方法。
工具类型
变更类工具 — 会更改状态的操作(代币创建/铸造/转账、账户更新、主题提交)。coreAction 构建 Transaction,secondaryAction 调用 handleTransaction()。使用 transactionToolOutputParser。
查询工具 — 只读操作(代币信息、余额、镜像节点获取)。coreAction 直接返回结果。重写 shouldSecondaryAction 并使其返回 false。使用 untypedQueryOutputParser。
文件结构模式
所有 Hedera 插件都遵循以下结构:
my-hedera-plugin/
├── index.ts # Plugin definition and exports
└── tools/
└── category/ # Group related tools
├── create-something.ts
└── get-something.ts
创建工具
步骤 1:定义工具常量
export const MY_TOOL_NAME = 'my_tool_name_tool';
常量使用带 _TOOL 后缀的 UPPER_SNAKE_CASE 命名。其值应采用小写蛇形命名。
步骤 2:创建提示词函数
const myToolPrompt = (context: Context = {}) => {
return `This tool does X on Hedera.
Parameters:
- param1 (str, required): Description of param1
- param2 (int, optional): Description of param2, defaults to 0`;
};
步骤 3:使用 Zod 定义参数
import { z } from 'zod';
const myToolParameters = (_context: Context = {}) => {
return z.object({
param1: z.string().describe('Description of param1'),
param2: z.number().optional().describe('Description of param2'),
});
};
type MyToolParams = z.infer<ReturnType<typeof myToolParameters>>;
常见的 Hedera 参数模式请参阅 references/zod-schema-patterns.md。
步骤 4:实现 BaseTool 子类(变更类示例)
import { Client, Status } from '@hiero-ledger/sdk';
import {
BaseTool,
Context,
handleTransaction,
RawTransactionResponse,
transactionToolOutputParser,
} from '@hashgraph/hedera-agent-kit';
const postProcess = (response: RawTransactionResponse) =>
`Operation completed.
Transaction ID: ${response.transactionId}`;
export class MyTool extends BaseTool<MyToolParams, MyToolParams> {
method = MY_TOOL_NAME;
name = 'My Tool Display Name';
description: string;
parameters: ReturnType<typeof myToolParameters>;
outputParser = transactionToolOutputParser;
constructor(context: Context) {
super();
this.description = myToolPrompt(context);
this.parameters = myToolParameters(context);
}
// Stage 2 — resolve defaults
async normalizeParams(params: MyToolParams) {
return params;
}
// Stage 4 — build the transaction (do not sign here)
async coreAction(params: MyToolParams, _context: Context, _client: Client) {
// return new SomeTransaction()...;
return undefined as any;
}
// Stage 6 — sign and submit
async secondaryAction(transaction: any, client: Client, context: Context) {
return handleTransaction(transaction, client, context, postProcess);
}
async handleError(error: unknown) {
const message = 'Failed to execute' + (error instanceof Error ? `: ${error.message}` : '');
console.error(`[${MY_TOOL_NAME}]`, message);
return { raw: { status: Status.InvalidTransaction, error: message }, humanMessage: message };
}
}
步骤 5:导出工厂函数
const tool = (context: Context): BaseTool => new MyTool(context);
export default tool;
工厂模式使插件的 tools(context) 函数能够为每个请求实例化一个绑定了相应上下文的新工具。
查询工具变体 — 跳过阶段 6
对于只读工具,重写 shouldSecondaryAction,使生命周期在 coreAction 后停止,并让 secondaryAction 原样传递结果:
export class MyQueryTool extends BaseTool<MyToolParams, MyToolParams> {
// ... method/name/description/parameters as above ...
outputParser = untypedQueryOutputParser;
async normalizeParams(params: MyToolParams) {
return params;
}
async coreAction(params: MyToolParams, _context: Context, client: Client) {
return await someHederaQuery(params, client);
}
async shouldSecondaryAction() {
return false;
}
async secondaryAction(result: any) {
return result;
}
}
创建插件索引
import { Context, Plugin } from '@hashgraph/hedera-agent-kit';
import myTool, { MY_TOOL_NAME } from './tools/category/my-tool';
export const myPlugin: Plugin = {
name: 'my-hedera-plugin',
version: '1.0.0',
description: 'A plugin for custom Hedera operations',
tools: (context: Context) => {
return [
myTool(context),
];
},
};
export const myPluginToolNames = {
MY_TOOL_NAME,
} as const;
export default { myPlugin, myPluginToolNames };
后处理结果
secondaryAction 通常会委托给 handleTransaction,后者使用原始交易响应调用 postProcess 函数:
const postProcess = (response: RawTransactionResponse) => {
if (response.scheduleId) {
return `Scheduled transaction created.
Transaction ID: ${response.transactionId}
Schedule ID: ${response.scheduleId.toString()}`;
}
return `Operation completed.
Transaction ID: ${response.transactionId}`;
};
RETURN_BYTES 模式 — raw.bytes 是 Uint8Array
当工具以 RETURN_BYTES 模式运行时,ResponseParserService 返回的 raw.bytes 在 Node.js 和浏览器环境中始终是普通的 Uint8Array。直接将其传递给 Transaction.fromBytes() 即可,无需手动转换。
const bytes = toolCall.parsedData.raw.bytes;
const tx = Transaction.fromBytes(bytes);
命名约定
| 元素 | 约定 | 示例 |
|---|---|---|
| 插件名称 | kebab-case | my-token-plugin |
| 插件变量 | camelCase | myTokenPlugin |
| 工具类 | PascalCase + Tool | CreateTokenTool |
| 工具常量 | UPPER_SNAKE_CASE + _TOOL | CREATE_TOKEN_TOOL |
| 工具方法值 | snake_case + _tool | create_token_tool |
| 工具文件 | kebab-case | create-token.ts |
| 工具名称导出项 | camelCase + ToolNames | myTokenPluginToolNames |
常见导入
// From @hashgraph/hedera-agent-kit (core)
import {
Context,
Plugin,
BaseTool,
handleTransaction,
RawTransactionResponse,
transactionToolOutputParser,
untypedQueryOutputParser,
} from '@hashgraph/hedera-agent-kit';
// Built-in plugins live in the /plugins subpath
import { coreTokenPlugin, coreAccountPlugin } from '@hashgraph/hedera-agent-kit/plugins';
// Hedera SDK is a peer dependency
import { Client, Status } from '@hiero-ledger/sdk';
// For parameter validation
import { z } from 'zod';
其他资源
参考文件
如需了解详细模式和技巧,请参阅:
references/plugin-interface.md- 插件接口,以及BaseTool生命周期的深入介绍references/zod-schema-patterns.md- Hedera 参数的常用 Zod schemareferences/prompt-patterns.md- 工具描述的提示词生成模式references/error-handling.md- 错误处理和输出解析模式
示例文件
examples/ 中的可运行示例:
examples/simple-plugin/- 最简BaseTool查询(入门模板)examples/token-plugin/- 修改操作(CreateTokenTool)和查询(GetTokenInfoTool)的BaseTool子类
最佳实践
- 每个工具对应一个文件,文件名使用工具名称的 kebab-case
- 将相关工具分组到
tools/下的类别目录中 - 使用稳定的类名:
<DoesWhat>Tool(例如,CreateTokenTool) - 在
normalizeParams中解析默认值,而不是在coreAction中——确保构建步骤保持纯粹 - 在
coreAction中构建,不要提交——提交操作应放在secondaryAction中,以便钩子做出响应 - 重写
handleError,使日志带有工具方法标记 - 使用 Zod 的
.describe()进行验证——每个字段都要验证;描述可为 Agent 提供指引 - 导出工具名称常量,以便使用方通过编程方式引用工具
向 Toolkit 注册插件
插件会显式传递给你选择的 toolkit。Toolkit 位于专用软件包中——请选择与你的框架匹配的软件包:
| 框架 | Toolkit 软件包 |
|---|---|
| LangChain | @hashgraph/hedera-agent-kit-langchain |
| Vercel AI SDK | @hashgraph/hedera-agent-kit-ai-sdk |
| ElizaOS | @hashgraph/hedera-agent-kit-elizaos |
| MCP | @hashgraph/hedera-agent-kit-mcp |
plugins数组为空意味着没有任何工具。 显式列出所需的每个插件,或使用/plugins子路径中的allCorePlugins一次性加载所有内置核心插件。
import { Client, PrivateKey } from '@hiero-ledger/sdk';
import { AgentMode } from '@hashgraph/hedera-agent-kit';
import { coreTokenPlugin, coreAccountPlugin } from '@hashgraph/hedera-agent-kit/plugins';
import { HederaLangchainToolkit } from '@hashgraph/hedera-agent-kit-langchain';
import { myPlugin } from './my-hedera-plugin';
const client = Client.forTestnet().setOperator(
process.env.ACCOUNT_ID!,
PrivateKey.fromStringECDSA(process.env.PRIVATE_KEY!),
);
const toolkit = new HederaLangchainToolkit({
client,
configuration: {
plugins: [coreTokenPlugin, coreAccountPlugin, myPlugin],
context: { mode: AgentMode.AUTONOMOUS },
},
});
const tools = toolkit.getTools();
若要一次性加载所有内置核心插件,请导入 allCorePlugins:
import { allCorePlugins } from '@hashgraph/hedera-agent-kit/plugins';
// ...
plugins: [...allCorePlugins, myPlugin],
工作流概要
- 创建插件目录,其中包含
index.ts和tools/子目录 - 为每个工具在单独的文件中编写一个
BaseTool子类和工厂函数 - 在
index.ts中导出工具及其工具名称常量 - 将插件传递到 toolkit 的
configuration.plugins数组中 - 工具即可供 AI Agent 使用
完整的可运行示例请参阅 examples/ 目录。