Skip to main content
skill CX Codex

Hedera Policy Creation — Claude Code skill: how to install

Guides developers in creating blocking validation rules for Hedera Agent Kit tools. Useful when setting transaction thresholds, recipient allowlists, or execution limits.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add hedera-dev/hedera-skills --skill hedera-policy-creation -a claude-code
npx skills add hedera-dev/hedera-skills --skill agent-kit-policy -a claude-code
Open on GitHub

What Hedera Policy Creation does

This skill guides the creation of blocking validation rules that abort tool execution in Hedera Agent Kit by throwing errors. Rules inherit from AbstractPolicy, extending the core hook system. Developers override specific lifecycle stage checks such as shouldBlockPreToolExecution for raw inputs, shouldBlockPostParamsNormalization for normalized arguments, or shouldBlockPostCoreAction before transaction submission.

Policies run within the Context.hooks array in registration order. The first rule returning true terminates execution and returns the error to the LLM agent. Because the toolkit dispatches checks across every tool call, each handler must filter methods against relevantTools explicitly.

Tools that implement the Tool interface directly without extending BaseTool bypass policies entirely. For non-blocking tasks like event logging or context enrichment, the kit requires observation hooks instead.

What it helps with

  • block Hedera tool execution when specific safety conditions fail
  • cap recipient counts for token transfers and airdrops
  • enforce maximum transaction amount thresholds for HBAR
  • restrict agent tool calls to business hours

Example prompts for the agent

  • "Write a Hedera Agent Kit policy class that blocks transfers exceeding a threshold"
  • "Create an AbstractPolicy rule to restrict recipients to an account allowlist"
  • "How do I implement rate limiting for Hedera Agent Kit tools using policies?"

How to install Hedera Policy Creation

1Skills CLI (Vercel)

npx skills add hedera-dev/hedera-skills --skill hedera-policy-creation -a claude-code
npx skills add hedera-dev/hedera-skills --skill agent-kit-policy -a claude-code

2Manually: put the files in place

~/.claude/skills/agent-kit-policy/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/agent-kit-policy/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about Hedera Policy Creation

How to install Hedera Policy Creation in Claude Code?

Run the command npx skills add hedera-dev/hedera-skills --skill hedera-policy-creation -a claude-code.

What does Hedera Policy Creation do?

It provides patterns and architecture rules for building blocking validation policies that halt Hedera Agent Kit tools before transactions execute.

What is the difference between a policy and a hook in Hedera Agent Kit?

Hooks handle passive observation, telemetry, and context enrichment without stopping execution. Policies extend hooks specifically to abort tool runs by throwing errors.

Source text

SKILL.md · Apache-2.0
---
name: Hedera Policy Creation
description: This skill should be used when the user asks to "create a hedera policy", "block a tool", "limit recipients", "extend AbstractPolicy", "reject a tool", "enforce a threshold", "rate limit a tool", "block transfers over X", "allowlist tools", or needs guidance on writing blocking validation rules for Hedera Agent Kit tools. Policies block tool execution by throwing — for non-blocking observation use the hook skill instead.
version: 1.0.0
---

# Creating Hedera Agent Kit Policies

Policies are blocking validation rules layered on the `BaseTool` lifecycle. A policy checks the call at one or more stages and **throws** to abort execution — the error propagates back to the LLM agent. Use them to enforce thresholds, allowlists/blocklists, rate limits, business hours, compliance rules, etc.

> Policies fire **only** for tools that extend `BaseTool`. A tool that implements the `Tool` interface directly without extending `BaseTool` bypasses policies entirely.

## Policy vs. hook

| Goal | Use |
|---|---|
| Stop a call when a condition holds | **Policy** (this skill) |
| Reject specific tool methods | **Policy** |
| Threshold / rate-limit / allowlist / blocklist | **Policy** |
| Log, audit, enrich context | **Hook** (see `agent-kit-hook` skill) |

`AbstractPolicy` extends `AbstractHook`, so policies live in the same `Context.hooks` array and run through the same lifecycle.

Built-in policies live in `@hashgraph/hedera-agent-kit/policies`:
- `RejectToolPolicy(relevantTools: string[])` — blocks the listed tool methods unconditionally
- `MaxRecipientsPolicy(maxRecipients, additionalTools?, customStrategies?)` — caps recipient count for transfer/airdrop tools

## How blocking actually works

You **never** override `preToolExecutionHook` or any of the other `*Hook` methods on a policy — those are wired up by the base class to call your `shouldBlock...` method and throw if it returns `true`.

Override one or more of these instead:

```typescript
abstract class AbstractPolicy extends AbstractHook {
  abstract name: string;
  abstract description?: string;
  abstract relevantTools: string[];

  protected shouldBlockPreToolExecution(params, method): boolean | Promise<boolean>;
  protected shouldBlockPostParamsNormalization(params, method): boolean | Promise<boolean>;
  protected shouldBlockPostCoreAction(params, method): boolean | Promise<boolean>;
  protected shouldBlockPostSecondaryAction(params, method): boolean | Promise<boolean>;
}
```

Defaults all return `false` (allow). Returning `true` causes the base class to throw a generic block error. Throwing your own error from `shouldBlock...` lets you control the message.

> **Don't override `preToolExecutionHook` etc.** — those are `@internal` on `AbstractPolicy`. They drive the block flow. If you override them, your policy will never block.

## Picking the stage

| Stage | What's available | Good for |
|---|---|---|
| `shouldBlockPreToolExecution` | `rawParams` only | allowlist/blocklist by tool method, time-based, rate limits |
| `shouldBlockPostParamsNormalization` | `+ normalisedParams` | thresholds on resolved values, recipient counts |
| `shouldBlockPostCoreAction` | `+ coreActionResult` | inspect the built `Transaction` before submission |
| `shouldBlockPostSecondaryAction` | `+ toolResult` | very rare — call already executed; usually too late to "block" |

Most policies live at stage 1 (cheapest) or stage 3 (after defaults are filled in).

## Skeleton

```typescript
import {
  AbstractPolicy,
  PreToolExecutionParams,
  PostParamsNormalizationParams,
} from '@hashgraph/hedera-agent-kit';

export class MyPolicy extends AbstractPolicy {
  name = 'My Policy';
  description = 'What this policy blocks, in one sentence';
  relevantTools = ['transfer_hbar', 'transfer_fungible_token'];

  // Stage 1 — block by raw input or method
  protected shouldBlockPreToolExecution(
    params: PreToolExecutionParams,
    method: string,
  ): boolean {
    if (!this.relevantTools.includes(method)) return false;
    // throw new Error('<custom message>') for tailored errors
    return /* condition */ false;
  }

  // Stage 3 — block on resolved/normalised values
  protected shouldBlockPostParamsNormalization(
    params: PostParamsNormalizationParams,
    method: string,
  ): boolean {
    if (!this.relevantTools.includes(method)) return false;
    return /* condition */ false;
  }
}
```

`relevantTools` is informational unless you check it — the kit dispatches your policy for every tool call. Always filter by `method` first thing in the method.

## Registering policies

Policies are `AbstractHook` instances. They go in the same `Context.hooks` array as observation hooks:

```typescript
import { AgentMode } from '@hashgraph/hedera-agent-kit';
import { allCorePlugins } from '@hashgraph/hedera-agent-kit/plugins';
import { RejectToolPolicy, MaxRecipientsPolicy } from '@hashgraph/hedera-agent-kit/policies';
import { HederaLangchainToolkit } from '@hashgraph/hedera-agent-kit-langchain';
import { MyPolicy } from './my-policy';

const toolkit = new HederaLangchainToolkit({
  client,
  configuration: {
    plugins: allCorePlugins,
    context: {
      mode: AgentMode.AUTONOMOUS,
      hooks: [
        new RejectToolPolicy(['delete_account', 'freeze_token']),
        new MaxRecipientsPolicy(5),
        new MyPolicy(),
      ],
    },
  },
});
```

Policies run in array order at each stage — the first one to return `true` (or throw) wins.

## Common patterns

### Threshold

```typescript
protected shouldBlockPostParamsNormalization(
  params: PostParamsNormalizationParams,
  method: string,
): boolean {
  if (!this.relevantTools.includes(method)) return false;
  const p = params.normalisedParams as { amount?: number };
  return (p.amount ?? 0) > this.maxAmount;
}
```

### Allowlist / Blocklist

```typescript
protected shouldBlockPreToolExecution(
  params: PreToolExecutionParams,
  method: string,
): boolean {
  if (!this.relevantTools.includes(method)) return false;
  const accountId = (params.rawParams as { accountId?: string }).accountId;
  return accountId !== undefined && this.blockedAccounts.has(accountId);
}
```

### Time-based

```typescript
protected shouldBlockPreToolExecution(
  _params: PreToolExecutionParams,
  method: string,
): boolean {
  if (!this.relevantTools.includes(method)) return false;
  const hour = new Date().getUTCHours();
  return hour < 9 || hour > 17;  // outside business hours
}
```

### Rate limiting (uses `Context` as scratchpad)

```typescript
protected shouldBlockPreToolExecution(
  params: PreToolExecutionParams,
  method: string,
): boolean {
  if (!this.relevantTools.includes(method)) return false;
  const counters = ((params.context as any).__rateCounts ??= {} as Record<string, number>);
  counters[method] = (counters[method] ?? 0) + 1;
  return counters[method] > this.maxCallsPerSession;
}
```

### Strategy map (pluggable for custom tools)

When a single policy needs to support tools the kit doesn't know about, accept a `Record<string, (params) => SomeMetric>` and validate that every tool added via `additionalTools` has a strategy. This is the pattern of `MaxRecipientsPolicy`:

```typescript
constructor(
  private maxRecipients: number,
  additionalTools: string[] = [],
  private customStrategies: Record<string, (p: any) => number> = {},
) {
  super();
  this.relevantTools = [...DEFAULT_TOOLS, ...additionalTools];
  for (const tool of additionalTools) {
    if (!this.customStrategies[tool]) {
      throw new Error(`Custom tool "${tool}" requires a strategy function`);
    }
  }
}
```

## Custom error messages

The default block error is generic. To control the message, **throw** from your `shouldBlock...` method instead of returning `true`:

```typescript
protected shouldBlockPostParamsNormalization(
  params: PostParamsNormalizationParams,
  method: string,
): boolean {
  if (!this.relevantTools.includes(method)) return false;
  const p = params.normalisedParams as { amount?: number };
  if ((p.amount ?? 0) > this.maxAmount) {
    throw new Error(
      `[max-amount-policy] Refusing ${method}: amount ${p.amount} exceeds limit ${this.maxAmount}`,
    );
  }
  return false;
}
```

The agent receives the thrown message verbatim — make it informative.

## Best practices

1. **Filter by `relevantTools`** at the top of every `shouldBlock...` method
2. **Pick the earliest stage** that has the data you need — stage 1 is cheapest
3. **Throw with a clear message** when blocking; the LLM sees this
4. **Don't override `*Hook` methods** — only `shouldBlock...`
5. **Don't do I/O on the hot path** unless you must — policies run synchronously in the lifecycle
6. **Document `relevantTools` defaults** so consumers understand what your policy guards
7. **Tag the error message** with `[<policy-name>]` for easy log correlation

## Resources

- **`references/policy-interface.md`** — full `AbstractPolicy` contract, when each `shouldBlock` fires
- **`examples/max-amount-policy/`** — threshold policy that blocks transfers above a cap

## Common imports

```typescript
import {
  AbstractPolicy,
  Context,
  PreToolExecutionParams,
  PostParamsNormalizationParams,
  PostCoreActionParams,
  PostSecondaryActionParams,
} from '@hashgraph/hedera-agent-kit';

// Built-in implementations live in the /policies subpath
import { RejectToolPolicy, MaxRecipientsPolicy } from '@hashgraph/hedera-agent-kit/policies';
```

© hedera-dev · Apache-2.0 · hedera-dev/hedera-skills

Similar skills

All skills for Agent setup: memory, context, orchestration →

From the same set19

hedera-consensus-service A skill that teaches your agent Hedera Consensus Service in JavaScript: create topics, submit messages, subscribe to the stream via mirror node. CX Codex +1 547installs Project Scaffolding This skill builds CLAUDE.md and a .claude workspace for a project. Use it when starting an AI coding workflow or preparing a new codebase. CX Codex +1 536installs Session Management Keeps a work log and a tech debt tracker across agent sessions, so a restart still shows what got done and what is pending. CX Codex +1 538installs Quality Gates This skill wires up automatic code checks: lint and typecheck after every edit, a local CI run before push, and a five-stage deploy checklist. CX Codex +1 536installs Hedera Agent Kit v4 Migration Walks a project from [email protected] to v4: swaps packages, rewrites imports and ports custom tools to BaseTool. CX Codex +1 484installs agent-kit-plugin This skill guides the creation of Hedera Agent Kit plugins and tools. Use it when adding token, account, consensus, smart contract, or custom Hedera functionality. CX Codex +1 463installs create-harness-spec Turns a Hedera feature idea into a runnable hedera-harness recipe: PRD, spec file and validators. Useful when you need a spec you can actually run. CX Codex +1 465installs harness-spec-anatomy Defines what a hedera-harness recipe is made of: the files, the v3 defaults, the run stages and the blind evaluate-checklist rule. For anyone authoring or auditing recipes. CX Codex +1 464installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor