> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hedera.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create JavaScript Plugins

> Instructions for Creating, Publishing and Registering your Hedera Agent Kit Plugin

## Creating a Plugin

### Plugin Interface

Every plugin must implement the Plugin interface:

```typescript theme={null}
export interface Plugin {
  name: string;
  version?: string;
  description?: string;
  tools: (context: Context) => Tool[];
}
```

### Tool Interface

Each tool must implement the `Tool` interface:

```typescript theme={null}
export type Tool = {
  method: string;
  name: string;
  description: string;
  parameters: z.ZodObject<any, any>;
  execute: (client: Client, context: Context, params: any) => Promise<any>;
  // transactionToolOutputParser and untypedQueryOutputParser can be used. If required, define a custom parser
  outputParser?: (rawOutput: string) => { raw: any; humanMessage: string };
};
```

See [packages/core/src/shared/tools.ts](https://github.com/hashgraph/hedera-agent-kit-js/blob/main/packages/core/src/shared/tools.ts) for the full definition.

#### BaseTool (Recommended for v4)

<Note>
  **This is NOT a breaking change.** `BaseTool` is an abstract class that *implements* the `Tool` interface — plain object tools continue to work exactly as before. However, they do not support the hooks and policies system introduced in v4 (e.g., `HcsAuditTrailHook`, `MaxRecipientsPolicy`, `RejectToolPolicy`). To enable those features, migrate your tool to `BaseTool`.
</Note>

Extending `BaseTool` splits execution into a 7-stage lifecycle that hooks and policies tap into automatically — you never call them manually. For a detailed step-by-step guide on refactoring your tools, see the [Migration Guide](/solutions/ai/agent-kit/js/migration-guide#migrating-custom-tools-to-basetool-recommended-non-breaking).

<CodeGroup>
  ```typescript v3 (Functional Tool object) theme={null}
  import type { Tool } from '@/shared/tools';

  const tool = (context: Context): Tool => ({
    method: MY_TOOL,
    name: 'My Tool',
    description: myToolPrompt(context),
    parameters: myToolParameters(context),
    execute: myToolExecute, // all logic in one function; no hook/policy entry points
  });

  export default tool;
  ```

  ```typescript v4 (BaseTool class) theme={null}
  import { BaseTool } from '@/shared/tools'; // BaseTool implements Tool

  export class MyTool extends BaseTool {
    method = MY_TOOL;
    name = 'My Tool';
    description: string;
    parameters: ReturnType<typeof myToolParameters>;
    outputParser = myOutputParser; // optional

    constructor(context: Context) {
      super();
      this.description = myToolPrompt(context);
      this.parameters = myToolParameters(context);
    }

    // Stage 2: normalize/validate params
    async normalizeParams(params: any, context: Context, client: Client) {
      return await MyNormaliser.normalise(params, context, client);
    }

    // Stage 4: build tx or run query
    async coreAction(normalisedParams: any, _context: Context, _client: Client) {
      return MyBuilder.buildTx(normalisedParams);
    }

    // Stage 6: sign and submit (override shouldSecondaryAction() to skip for query-only tools)
    async secondaryAction(transaction: any, client: Client, context: Context) {
      return await handleTransaction(transaction, client, context, postProcess);
    }

    // Optional: custom error handling (BaseTool provides a sensible default)
    async handleError(error: unknown, _context: Context) {
      const msg = `Failed: ${error instanceof Error ? error.message : error}`;
      return { raw: { error: msg }, humanMessage: msg };
    }
  }

  const tool = (context: Context): BaseTool => new MyTool(context);
  export default tool;
  ```
</CodeGroup>

|                  | v3 (`Tool` object)              | v4 (`BaseTool` class)                                |
| ---------------- | ------------------------------- | ---------------------------------------------------- |
| Import           | `import type { Tool }`          | `import { BaseTool }`                                |
| Declaration      | Object literal                  | Class extending `BaseTool`                           |
| Lifecycle        | Single `execute()`              | `normalizeParams` → `coreAction` → `secondaryAction` |
| Hooks & policies | ✗ None                          | ✓ Automatic at stages 1, 3, 5, 7                     |
| Error handling   | Manual `try/catch` in `execute` | Override `handleError()`                             |
| Breaking change  | —                               | **No**                                               |

## Step-by-Step Guide

### Step 1: Create Plugin Directory Structure

```
  my-custom-plugin/
  ├── index.ts                    # Plugin definition and exports
  ├── tools/
  │   └── my-service/
  │       └── my-tool.ts         # Individual tool implementation
```

### Step 2: Implement Your Tool

Create your tool file (e.g., tools/my-service/my-tool.ts):

> This example uses Hedera Agent Kit v4 which extends the BaseTool class to enable policies & hooks

```typescript theme={null}
import { z } from "zod";
import { Context, BaseTool, handleTransaction } from "@hashgraph/hedera-agent-kit";
import { Client } from "@hiero-ledger/sdk";

// Define parameter schema
const myToolParameters = (context: Context = {}) =>
  z.object({
    requiredParam: z.string().describe("Description of required parameter"),
    optionalParam: z
      .string()
      .optional()
      .describe("Description of optional parameter"),
  });

// Create prompt function
const myToolPrompt = (context: Context = {}) => `
  This tool performs a specific operation.

  Parameters:
  - requiredParam (string, required): Description
  - optionalParam (string, optional): Description
`;

export const MY_TOOL = "my_tool";

export class MyCustomTool extends BaseTool {
  method = MY_TOOL;
  name = "My Custom Tool";
  description: string;
  parameters: ReturnType<typeof myToolParameters>;

  constructor(context: Context) {
    super();
    this.description = myToolPrompt(context);
    this.parameters = myToolParameters(context);
  }

  // Stage 2: normalize/validate params before core logic runs
  async normalizeParams(
    params: z.infer<ReturnType<typeof myToolParameters>>,
    _context: Context,
    _client: Client,
  ) {
    return params; // transform or enrich params here if needed
  }

  // Stage 4: build the transaction or run the query
  async coreAction(normalisedParams: any, _context: Context, _client: Client) {
    return await someHederaOperation(normalisedParams);
  }

  // Stage 6: sign and submit (omit or override shouldSecondaryAction() for query-only tools)
  async secondaryAction(result: any, client: Client, context: Context) {
    return await handleTransaction(result, client, context);
  }

  // Optional: override for tool-specific error messages
  async handleError(error: unknown, _context: Context) {
    const msg = `Operation failed: ${error instanceof Error ? error.message : error}`;
    return { raw: { error: msg }, humanMessage: msg };
  }
}

const tool = (context: Context): BaseTool => new MyCustomTool(context);
export default tool;
```

### Step 3: Create Plugin Definition

Create your plugin index file (index.ts):

```typescript theme={null}
  import { Context, Plugin } from '@hashgraph/hedera-agent-kit';
  import myTool, { MY_TOOL } from './tools/my-service/my-tool';

  export const myCustomPlugin: Plugin = {
    name: 'my-custom-plugin',
    version: '1.0.0',
    description: 'A plugin for custom functionality',
    tools: (context: Context) => {
      return [myTool(context)];
    },
  };

  export const myCustomPluginToolNames = {
    MY_TOOL,
  } as const;

  export default { myCustomPlugin, myCustomPluginToolNames };
```

### Best Practices

**Parameter Validation**

* Use Zod schemas for robust input validation
* Provide clear descriptions for all parameters
* Mark required vs optional parameters appropriately

**Tool Organization**

* Group related tools by service type
* Use consistent naming conventions
* Follow the established directory structure

**Transaction Handling**

* Use `handleTransaction()` to facilitate human-in-the-loop and autonomous execution flows
* Respect the AgentMode (`AUTONOMOUS` vs `RETURN_BYTES`)
* Implement proper transaction building patterns

### Tool Output Parsing

The Hedera Agent Kit tools return a structured JSON output. Each tool returns:

```typescript theme={null}
{
  raw: any;          // The raw data returned by the tool (e.g., transaction receipt, query result)
  humanMessage: string; // A human-readable message describing the result
}
```

This allows you to easily display a user-friendly message while still having access to the raw data for further processing.

### Using Your Custom Plugin

```typescript theme={null}
import { Client, PrivateKey } from "@hiero-ledger/sdk";
import { AgentMode } from "@hashgraph/hedera-agent-kit";
import { myCustomPlugin } from "./plugins/my-custom-plugin";
import { HederaLangchainToolkit } from "@hashgraph/hedera-agent-kit-langchain";
import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
import { ChatPromptTemplate } from "@langchain/core/prompts";

const client = Client.forTestnet().setOperator(
  process.env.ACCOUNT_ID!,
  PrivateKey.fromStringECDSA(process.env.PRIVATE_KEY!),
);

const toolkit = new HederaLangchainToolkit({
  client,
  configuration: {
    plugins: [myCustomPlugin],
    context: {
      mode: AgentMode.AUTONOMOUS,
    },
  },
});

const tools = toolkit.getTools();

const prompt = ChatPromptTemplate.fromMessages([
  ['system', 'You are a helpful assistant with access to Hedera blockchain tools'],
  ['human', '{input}'],
  ['placeholder', '{agent_scratchpad}'],
]);

const agent = createToolCallingAgent({ llm, tools, prompt });

const agentExecutor = new AgentExecutor({ agent, tools });

const response = await agentExecutor.invoke({ input: "what's my balance?" });

console.log(response?.output ?? response);
```

#### Examples and References

* See existing core plugins in typescript/src/plugins/core-\*-plugin/
* Follow the patterns established in tools like [transfer-hbar.ts](https://github.com/hashgraph/hedera-agent-kit-js/blob/main/packages/core/src/plugins/core-account-plugin/tools/account/transfer-hbar.ts)
* See [typescript/examples/langchain-v1/plugin-tool-calling-agent.ts](https://github.com/hashgraph/hedera-agent-kit-js/blob/main/examples/langchain-v1/plugin-tool-calling-agent.ts) for usage examples

## Publish and Register Your Plugin

To create a plugin to be used with the Hedera Agent Kit, you will need to create a plugin in your own repository, publish a npm package, and provide a description of the functionality included in that plugin, as well as the required and optional parameters.

Once you have a repository, published npm package, and a README with a description of the functionality included in that plugin in your plugin's repo, as well as the required and optional parameters, you can add it to the Hedera Agent Kit by forking and opening a Pull Request to:

1. Include the plugin as a bullet point under the **Available Third Party Plugin** section under the **Third Party Plugin** section in the [README.md in the hedera-agent-kit-js](https://github.com/hashgraph/hedera-agent-kit-js/blob/main/README.md#third-party-plugins). Include the name, a brief description, and a link to the repository with the README, as well the URL linked to the published npm package.

2. If you would like to include your plugin functionality in the Hedera plugin built for ElizaOS simply make a PR to [add your plugin name to the `plugins` array in the Hedera ElizaOS plugin](https://github.com/elizaos-plugins/plugin-hedera/blob/1.x/src/adapter-plugin/plugin.ts#L72) where the configuration is initiated. The hedera-agent-kit adaptor architecture means your plugin functionality will be usable with no additional configuration needed.

3. All commits for your plugin **must be [DCO signed](https://wiki.linuxfoundation.org/dco)**, have the names of the tools & core actions exposed by the plugin, and **point to the exact version of the npm packages**. To avoid having pull requests blocked in the future, always include a sign-off:

```Bonzo Plugin is a unified SDK to the Bonzo protocol, exposing the core actions (deposit, withdraw, repay, borrow) for decentralised lending and borrowing on Hedera: theme={null}
NPM: https://www.npmjs.com/package/@bonzofinancelabs/hak-bonzo-plugin
Github repository: https://github.com/Bonzo-Labs/bonzoPlugin
Version: @bonzofinancelabs/hak-bonzo-plugin@1.0.1
Status: Not validated by HAK team, v3-compatible release 
```

Feel free to also [reach out to the Hedera Agent Kit maintainers on Discord](https://hedera.com/discord) or another channel so we can test out your plugin, include it in our docs, and let our community know thorough marketing and community channels.

Please also reach out in the Hedera Discord in the Support > developer-help-desk channel create an Issue in this repository for help building, publishing, and promoting your plugin

## Plugin README Template

```markdown theme={null}
## Plugin Name

This plugin was built by <?> for the <project, platform, etc>. It was built to enable <who?> to <do what?>

<Include a description of your project and how it can be used with the Hedera Agent Kit.>

### Installation

'''bash
npm install <plugin-name>
'''

### Usage

'''javascript
import { myPlugin } from "<plugin-name>";
'''

'''typescript
import { AgentMode } from '@hashgraph/hedera-agent-kit';
import { myPlugin } from '<plugin-name>';
import { HederaLangchainToolkit } from '@hashgraph/hedera-agent-kit-langchain';

const hederaAgentToolkit = new HederaLangchainToolkit({
    client,
    configuration: {
        context: {
            mode: AgentMode.AUTONOMOUS,
        },
        plugins: [myPlugin],
    },
});

const tools = toolkit.getTools();
'''

### Functionality

Describe the different tools or individual pieces of functionality included in this plugin, and how to use them.

**Plugin Name**
_High level description of the plugin_

| Tool Name               | Description  | Usage                                                           |
| ----------------------- | ------------ | --------------------------------------------------------------- |
| `YOUR_PLUGIN_TOOL_NAME` | What it does | How to use. Include a list of parameters and their descriptions |

```

## Resources

* **GitHub**: [https://github.com/hashgraph/hedera-agent-kit-js](https://github.com/hashgraph/hedera-agent-kit-js)
* **Discord**: [https://hedera.com/discord](https://hedera.com/discord)

### Examples and References

* See existing core plugins in the [hedera agent kit examples](https://github.com/hashgraph/hedera-agent-kit-js/tree/main/examples)
* Follow the patterns established in tools like [transfer-hbar.ts](https://github.com/hashgraph/hedera-agent-kit-js/blob/main/packages/core/src/plugins/core-account-plugin/tools/account/transfer-hbar.ts)
* See [typescript/examples/langchain/tool-calling-agent.ts](https://github.com/hashgraph/hedera-agent-kit-js/blob/main/examples/langchain-v1/plugin-tool-calling-agent.ts) for usage examples
