Tools
Learn how to use built-in tools and create custom tools that extend your agent's capabilities.
Built-in Tools
Hybrid provides two main tool sets:
Blockchain Tools
import { blockchainTools } from "hybrid/tools"
// Available tools:
blockchainTools.getBalance // Check wallet balance
blockchainTools.getTransaction // Get transaction details
blockchainTools.sendTransaction // Send native tokens (requires privateKey)
blockchainTools.getBlock // Get block information
blockchainTools.getGasPrice // Get current gas price
blockchainTools.estimateGas // Estimate gas for transactionSupported chains: mainnet, sepolia, polygon, arbitrum, optimism, base
XMTP Tools
XMTP tools are automatically included when your agent starts listening for messages. These tools are available to your agent without needing to explicitly include them:
// Automatically available tools:
// getMessage // Get message by ID
// sendMessage // Send message to conversation
// sendReply // Send threaded reply
// sendReaction // Send emoji reactionUsing Built-in Tools
import { Agent } from "hybrid"
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
import { blockchainTools } from "hybrid/tools"
const agent = new Agent({
name: "Crypto Agent",
model: createOpenRouter({ apiKey: process.env.OPENROUTER_API_KEY })("openai/gpt-4"),
instructions: "You can check balances, send messages, and help with crypto tasks.",
// Add blockchain tools (XMTP tools are automatically included)
tools: blockchainTools,
// Optional: Configure runtime for blockchain tools
createRuntime: (runtime) => ({
privateKey: process.env.PRIVATE_KEY, // For sending transactions
rpcUrl: process.env.RPC_URL, // Optional custom RPC
defaultChain: "mainnet" // Optional default chain
})
})Creating Custom Tools
Custom tools use createTool with Zod schemas for type-safe validation.
Tool Structure
import { createTool } from "hybrid"
import { z } from "zod"
const myTool = createTool({
description: "Description for the AI model",
inputSchema: z.object({
param1: z.string().describe("Description of param1"),
param2: z.number().optional()
}),
outputSchema: z.object({
result: z.string(),
success: z.boolean()
}),
execute: async ({ input, runtime }) => {
// Tool implementation
return {
result: "...",
success: true
}
}
})Example: Weather Tool
import { createTool } from "hybrid"
import { z } from "zod"
const weatherTool = createTool({
description: "Get current weather for a location",
inputSchema: z.object({
location: z.string().describe("City or location name"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius")
}),
outputSchema: z.object({
location: z.string(),
temperature: z.number(),
condition: z.string(),
humidity: z.number()
}),
execute: async ({ input }) => {
const response = await fetch(
`https://api.weather.com/v1/current?location=${input.location}&units=${input.units}`,
{
headers: { 'Authorization': `Bearer ${process.env.WEATHER_API_KEY}` }
}
)
const data = await response.json()
return {
location: input.location,
temperature: data.temperature,
condition: data.condition,
humidity: data.humidity
}
}
})Example: Database Query Tool
import { createTool } from "hybrid"
import { z } from "zod"
const queryUserTool = createTool({
description: "Query user information from database",
inputSchema: z.object({
userId: z.string().describe("User ID to query"),
fields: z.array(z.string()).optional().describe("Fields to return")
}),
outputSchema: z.object({
user: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
createdAt: z.string()
}).nullable(),
error: z.string().optional()
}),
execute: async ({ input }) => {
try {
// Query your database
const user = await db.users.findOne({ id: input.userId })
if (!user) {
return { user: null, error: "User not found" }
}
return {
user: {
id: user.id,
name: user.name,
email: user.email,
createdAt: user.createdAt.toISOString()
}
}
} catch (error) {
return {
user: null,
error: error instanceof Error ? error.message : "Unknown error"
}
}
}
})Example: DeFi Protocol Tool
import { createTool } from "hybrid"
import { z } from "zod"
const getAaveRatesTool = createTool({
description: "Get current lending/borrowing rates from Aave",
inputSchema: z.object({
token: z.string().describe("Token symbol (e.g., USDC, DAI, ETH)"),
chain: z.enum(["mainnet", "polygon", "arbitrum"]).default("mainnet")
}),
outputSchema: z.object({
token: z.string(),
chain: z.string(),
depositAPY: z.number(),
borrowAPY: z.number(),
totalSupply: z.string(),
totalBorrow: z.string()
}),
execute: async ({ input }) => {
// Call Aave API or contract
const response = await fetch(
`https://api.aave.com/v3/rates/${input.chain}/${input.token}`
)
const data = await response.json()
return {
token: input.token,
chain: input.chain,
depositAPY: data.depositAPY,
borrowAPY: data.borrowAPY,
totalSupply: data.totalSupply,
totalBorrow: data.totalBorrow
}
}
})Using Runtime Context
Tools can access runtime context passed from the agent:
const myTool = createTool({
description: "Tool that uses runtime context",
inputSchema: z.object({ query: z.string() }),
outputSchema: z.object({ result: z.string() }),
execute: async ({ input, runtime }) => {
// Access custom runtime properties
const privateKey = (runtime as any).privateKey
const rpcUrl = (runtime as any).rpcUrl
const customConfig = (runtime as any).customConfig
// Use runtime values
return { result: "..." }
}
})Extending Runtime Type
For type safety, extend the runtime:
interface MyRuntimeExtension {
apiKey: string
customConfig: {
timeout: number
retries: number
}
}
const agent = new Agent<MyRuntimeExtension>({
name: "My Agent",
model: yourModel,
tools: { myTool },
createRuntime: (runtime) => ({
apiKey: process.env.MY_API_KEY!,
customConfig: {
timeout: 5000,
retries: 3
}
})
})Adding Tools to Agent
// Option 1: Spread blockchain tools with custom tools
// (XMTP tools are automatically included)
const agent = new Agent({
name: "My Agent",
model: yourModel,
tools: {
...blockchainTools,
weather: weatherTool,
queryUser: queryUserTool
}
})
// Option 2: Individual tools only
const agent = new Agent({
name: "My Agent",
model: yourModel,
tools: {
getBalance: blockchainTools.getBalance,
weather: weatherTool,
custom: myCustomTool
}
})Tool Best Practices
Schema Design
- Use descriptive field names
- Add
.describe()to help AI understand parameters - Set sensible defaults with
.default() - Validate input thoroughly with Zod
Error Handling
- Return errors in the output schema (don't throw)
- Provide helpful error messages
- Include success/error flags in output
outputSchema: z.object({
success: z.boolean(),
data: z.object({...}).optional(),
error: z.string().optional()
})Performance
- Keep tools focused and simple
- Avoid long-running operations
- Use timeouts for external API calls
- Cache results when appropriate
Security
- Validate all inputs with Zod
- Never expose sensitive data in outputs
- Use environment variables for API keys
- Sanitize user inputs before using in queries
Next Steps
- Learn about Blockchain Tools for detailed crypto functionality
- Explore XMTP Tools for messaging capabilities
- Check out Behaviors for message processing
- See Agent Configuration for customizing your agent