Tool Creation
SkillAI & modelsCreate custom tools with defineTool() or tool(), register them with the agent, and configure required-tools gates and per-tool call budgets.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Tool Creation skill
What this skill tells your AI
The instructions your AI receives, as published by tylerjrbuell/reactive-agents-ts in apps/docs/skills/tool-creation/SKILL.md and read by ahel’s review.
Agent objective
Produce a ToolDefinition object + handler, registered with the builder via .withTools({ tools: [...] }). Every field in the definition must be present and typed correctly.
When to load this skill
- Adding a custom tool (API call, database query, file operation, etc.)
- Restricting which built-in tools an agent can use
- Forcing the agent to call a specific tool before completing
- Setting per-tool approval requirements or risk levels
Implementation baseline
import { ReactiveAgents } from "@reactive-agents/runtime";
import { tool } from "@reactive-agents/tools"; // simple factory — no Effect knowledge needed
const lookupUser = tool("lookup-user", "Look up a user record by ID", async (args) => {
const { userId } = args as { userId: string };
const user = await db.findUser(userId);
return { id: user.id, name: user.name, email: user.email };
});
const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withReasoning({ defaultStrategy: "adaptive" })
.withTools({
tools: [lookupUser],
allowedTools: ["lookup-user", "checkpoint"], // restrict to only these
})
.withRequiredTools({ tools: ["lookup-user"], maxRetries: 2 })
.build();
Key patterns
Two tool factories
// Factory 1: tool() — simple async, no Effect knowledge needed
import { tool } from "@reactive-agents/tools";
const myTool = tool("my-tool", "Description of what the tool does", async (args) => {
const { param } = args as { param: string };
return { result: await doSomething(param) };
});
// Factory 2: defineTool() — Effect-native, Schema-inferred typed args
import { defineTool } from "@reactive-agents/tools";
import { Schema } from "effect";
const myTool = defineTool({
name: "my-tool",
description: "Description of what the tool does",
parameters: Schema.Struct({ param: Schema.String }),
handler: ({ param }) =>
Effect.tryPromise(() => doSomething(param)),
});
Full ToolDefinition shape (when using low-level API)
import type { ToolDefinition } from "@reactive-agents/tools";
const myTool: ToolDefinition = {
name: "send-email", // unique identifier — no spaces
description: "Send an email to a recipient",
parameters: [
{ name: "to", type: "string", description: "Recipient email", required: true },
{ name: "subject", type: "string", description: "Email subject", required: true },
{ name: "body", type: "string", description: "Email body text", required: false },
],
returnType: "object", // "string" | "object" | "array" | "boolean" | "number"
riskLevel: "medium", // "low" | "medium" | "high" | "critical"
timeoutMs: 10_000, // default varies — set explicitly for network tools
requiresApproval: false, // true = agent pauses for human approval before calling
source: "function", // "function" for custom tools (not "builtin" or "mcp")
category: "http", // "search"|"file"|"code"|"http"|"data"|"custom"|"system"
isCacheable: false, // optional — enable for idempotent tools
cacheTtlMs: 60_000, // optional — cache duration
};
Built-in tools available
Enable selectively with allowedTools:
Standard tools (enabled by .withTools() with no args):
web-search, http-get, file-read, file-write, code-execute
Meta-tools (registered by kernel, available in .withTools()):
checkpoint, recall, find, brief, pulse, context-status, final-answer
Opt-in tools (not auto-enabled — must be registered manually):
shell-execute — see shell-execution-sandbox skill for registration pattern
// Only allow read-only tools
.withTools({ allowedTools: ["web-search", "http-get", "file-read", "checkpoint"] })
// Add custom tools alongside built-ins
.withTools({
tools: [myCustomTool],
allowedTools: ["web-search", "my-custom-tool"],
})
Requiring tool calls before completion
// Agent must call web-search before final-answer is accepted
.withRequiredTools({ tools: ["web-search"], maxRetries: 3 })
// Framework decides which tools are required based on task content
.withRequiredTools({ adaptive: true })
Builder API reference
| Method | Key params | Notes |
|---|---|---|
.withTools(opts?) | { tools?, allowedTools?, adaptive?, resultCompression? } | No args = all built-ins |
.withRequiredTools(cfg) | { tools?: string[], adaptive?: boolean, maxRetries?: number } | Forces tool calls |
Pitfalls
- Tool
namemust be unique — registering a tool with a name that matches a built-in silently overrides the built-in timeoutMsdefault is often too short for network/API tools — always set it explicitlywithRequiredToolswithoutwithToolsdoes nothing — tools must be enabled firstsource: "builtin"is reserved for internal tools; use"function"for all custom toolsdefineTool()handlers must returnEffect<unknown, ToolExecutionError>— wrap non-Effect async code withEffect.tryPromise()requiresApproval: trueon a tool pauses execution until a human responds — requires an approval callback configured elsewhere
Signals
- GitHub stars
- 27
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
tool-creation- Source
- github.com/tylerjrbuell/reactive-agents-ts