Integrations
Custom Tools
Custom Tools
Custom tools extend your agents' capabilities by allowing them to interact with external APIs, databases, and services. This guide covers creating, testing, and deploying custom tools in HIVE Protocol.
Overview
┌─────────────────────────────────────────────────────────────────┐
│ Custom Tools Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Agent Custom Tool │
│ ┌──────────┐ ┌─────────────────┐ │
│ │ │ "I need │ │ │
│ │ Decides │ weather │ Weather Tool │ │
│ │ to use │ ─────────▶ │ - Validate │ │
│ │ tool │ │ - Call API │ │
│ │ │ │ - Format │ │
│ └──────────┘ └────────┬────────┘ │
│ ▲ │ │
│ │ ▼ │
│ │ ┌─────────────────┐ │
│ │ Result │ External API │ │
│ └───────────────────│ (weather.com) │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Tool Types
Built-in Tools
HIVE includes several built-in tools:
| Tool | Description | Use Case |
|---|---|---|
| Web Search | Search the internet | Research, fact-checking |
| Code Executor | Run code snippets | Data analysis, calculations |
| File Reader | Read uploaded files | Document analysis |
| Calculator | Mathematical operations | Numerical tasks |
Custom Tools
Create your own tools for:
- External API integrations
- Database queries
- Internal service calls
- Specialized computations
- Third-party service access
Creating Custom Tools
Tool Definition Structure
Every tool requires:
interface ToolDefinition {
name: string; // Unique identifier
description: string; // What the tool does (shown to AI)
parameters: { // JSON Schema for inputs
type: 'object';
properties: Record<string, ParameterSchema>;
required: string[];
};
execute: (params: any) => Promise<any>; // Implementation
}Example: Weather API Tool
const weatherTool = {
name: 'get_current_weather',
description: 'Get the current weather conditions for a specific location. Returns temperature, conditions, humidity, and wind speed.',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'City name or address (e.g., "San Francisco, CA")'
},
units: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: 'Temperature units',
default: 'fahrenheit'
}
},
required: ['location']
},
execute: async ({ location, units = 'fahrenheit' }) => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=${API_KEY}&q=${encodeURIComponent(location)}`
);
if (!response.ok) {
throw new Error(`Weather API error: ${response.statusText}`);
}
const data = await response.json();
return {
location: data.location.name,
country: data.location.country,
temperature: units === 'celsius'
? data.current.temp_c
: data.current.temp_f,
units: units,
conditions: data.current.condition.text,
humidity: data.current.humidity,
wind_mph: data.current.wind_mph
};
}
};Example: Database Query Tool
const databaseQueryTool = {
name: 'query_database',
description: 'Execute a read-only SQL query against the analytics database. Only SELECT queries are allowed.',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'SQL SELECT query to execute'
},
limit: {
type: 'number',
description: 'Maximum rows to return',
default: 100
}
},
required: ['query']
},
execute: async ({ query, limit = 100 }) => {
// Security: Only allow SELECT queries
const normalizedQuery = query.trim().toLowerCase();
if (!normalizedQuery.startsWith('select')) {
throw new Error('Only SELECT queries are allowed');
}
// Add limit if not present
const limitedQuery = query.includes('LIMIT')
? query
: `${query} LIMIT ${limit}`;
const { data, error } = await supabase
.rpc('execute_readonly_query', { sql: limitedQuery });
if (error) throw new Error(`Database error: ${error.message}`);
return {
rows: data,
count: data.length,
query: limitedQuery
};
}
};Example: Slack Notification Tool
const slackNotifyTool = {
name: 'send_slack_message',
description: 'Send a message to a Slack channel. Use for notifications and alerts.',
parameters: {
type: 'object',
properties: {
channel: {
type: 'string',
description: 'Slack channel name (without #)',
enum: ['general', 'alerts', 'team-updates']
},
message: {
type: 'string',
description: 'Message content to send'
},
priority: {
type: 'string',
enum: ['low', 'normal', 'high'],
description: 'Message priority level',
default: 'normal'
}
},
required: ['channel', 'message']
},
execute: async ({ channel, message, priority = 'normal' }) => {
const emoji = {
low: ':information_source:',
normal: ':speech_balloon:',
high: ':rotating_light:'
};
const response = await fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
channel: `#${channel}`,
text: `${emoji[priority]} ${message}`,
unfurl_links: false
})
});
if (!response.ok) {
throw new Error('Failed to send Slack message');
}
return {
success: true,
channel,
timestamp: new Date().toISOString()
};
}
};Adding Tools via Dashboard
Step-by-Step Creation
- Navigate to Tools in the sidebar
- Click Create Tool
- Fill in the tool details:
┌─────────────────────────────────────────────────────────────────┐
│ Create Custom Tool │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Basic Information │
│ ────────────────────────────────────────────────────────────── │
│ │
│ Name (unique identifier) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ get_stock_price │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Display Name │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Stock Price Lookup │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Description (for AI) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Get real-time stock price and basic info for a ticker │ │
│ │ symbol. Returns current price, change, and volume. │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Parameters │
│ ────────────────────────────────────────────────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Name: symbol │ │
│ │ Type: string │ │
│ │ Required: Yes │ │
│ │ Description: Stock ticker symbol (e.g., AAPL, GOOGL) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ [+ Add Parameter] │
│ │
│ Endpoint Configuration │
│ ────────────────────────────────────────────────────────────── │
│ │
│ Type: [HTTP Request v] │
│ │
│ URL Template │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ https://api.stocks.com/v1/quote/{{symbol}} │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Method: [GET v] │
│ │
│ Headers │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Authorization: Bearer {{STOCK_API_KEY}} │ │
│ │ Content-Type: application/json │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [Cancel] [Create Tool] │
└─────────────────────────────────────────────────────────────────┘Tool Configuration Options
| Option | Description | Example |
|---|---|---|
| HTTP Request | Call external REST API | Weather API, stock prices |
| JavaScript | Custom code execution | Data transformation |
| Database | Query internal database | Analytics queries |
| Webhook | Call your own endpoint | Custom business logic |
Testing Tools
Built-in Test Interface
Test your tools before deploying:
┌─────────────────────────────────────────────────────────────────┐
│ Test Tool: get_stock_price │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Input Parameters │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "symbol": "AAPL" │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ [Run Test] │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ Result (234ms) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "symbol": "AAPL", │ │
│ │ "price": 178.42, │ │
│ │ "change": +2.35, │ │
│ │ "change_percent": +1.34, │ │
│ │ "volume": 45234567, │ │
│ │ "last_updated": "2024-01-15T14:30:00Z" │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Status: SUCCESS │
│ │
└─────────────────────────────────────────────────────────────────┘Test with Different Inputs
// Test cases for your tool
const testCases = [
{ input: { symbol: 'AAPL' }, expectSuccess: true },
{ input: { symbol: 'INVALID' }, expectSuccess: false },
{ input: { symbol: '' }, expectSuccess: false },
{ input: {}, expectSuccess: false }
];
for (const testCase of testCases) {
try {
const result = await tool.execute(testCase.input);
console.log(`Test passed: ${JSON.stringify(testCase.input)}`);
} catch (error) {
if (!testCase.expectSuccess) {
console.log(`Test passed (expected error): ${error.message}`);
} else {
console.error(`Test failed: ${error.message}`);
}
}
}Assigning Tools to Agents
Per-Agent Tool Assignment
┌─────────────────────────────────────────────────────────────────┐
│ Agent: Data Analyst │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Available Tools │
│ │
│ Built-in Tools │
│ [x] Web Search │
│ [x] Calculator │
│ [ ] Code Executor │
│ [x] File Reader │
│ │
│ Custom Tools │
│ [x] get_stock_price │
│ [x] query_database │
│ [ ] send_slack_message │
│ [x] get_weather │
│ │
│ Tool Permissions │
│ ────────────────────────────────────────────────────────────── │
│ [ ] Require approval before tool execution │
│ [x] Log all tool executions │
│ [ ] Limit tool calls per message (Max: ___) │
│ │
└─────────────────────────────────────────────────────────────────┘Tool Access in System Prompt
Agent: Data Analyst
Model: gpt-4o
System Prompt: |
You are a data analyst with access to the following tools:
- get_stock_price: Look up real-time stock prices
- query_database: Query the analytics database
- get_weather: Get current weather conditions
Use these tools when needed to answer questions accurately.
Always verify data before making conclusions.
Tools:
- get_stock_price
- query_database
- get_weatherSecurity Best Practices
Input Validation
Always validate and sanitize inputs:
execute: async ({ query }) => {
// Validate input type
if (typeof query !== 'string') {
throw new Error('Query must be a string');
}
// Sanitize input
const sanitized = query.replace(/[;'"\\]/g, '');
// Validate against allowed patterns
if (!sanitized.match(/^SELECT/i)) {
throw new Error('Only SELECT queries allowed');
}
// Length limits
if (sanitized.length > 1000) {
throw new Error('Query too long');
}
// Execute safely
return executeQuery(sanitized);
}Secret Management
Never hardcode secrets:
// BAD - Don't do this
const API_KEY = 'sk-1234567890';
// GOOD - Use environment variables
const API_KEY = process.env.EXTERNAL_API_KEY;
// BETTER - Use HIVE's secret storage
const API_KEY = await getSecret('external_api_key');Rate Limiting
Implement rate limits on tools:
const rateLimiter = new Map();
execute: async (params) => {
const key = `tool_${userId}`;
const now = Date.now();
const windowMs = 60000; // 1 minute
const maxCalls = 10;
const calls = rateLimiter.get(key) || [];
const recentCalls = calls.filter(t => now - t < windowMs);
if (recentCalls.length >= maxCalls) {
throw new Error('Rate limit exceeded. Try again later.');
}
recentCalls.push(now);
rateLimiter.set(key, recentCalls);
// Execute tool
return performAction(params);
}Error Handling
Return safe error messages:
execute: async (params) => {
try {
return await callExternalApi(params);
} catch (error) {
// Log detailed error internally
console.error('Tool error:', error);
// Return safe message to agent
throw new Error('Unable to complete request. Please try again.');
}
}Advanced Patterns
Chained Tool Calls
Tools that call other tools:
const analyzeCompanyTool = {
name: 'analyze_company',
description: 'Get comprehensive company analysis',
parameters: {
type: 'object',
properties: {
symbol: { type: 'string' }
},
required: ['symbol']
},
execute: async ({ symbol }, { callTool }) => {
// Call multiple tools and combine results
const [stock, news, financials] = await Promise.all([
callTool('get_stock_price', { symbol }),
callTool('get_company_news', { symbol }),
callTool('get_financials', { symbol })
]);
return {
symbol,
stock,
news: news.slice(0, 5),
financials: {
revenue: financials.revenue,
profit: financials.profit
}
};
}
};Streaming Results
For long-running tools:
const longRunningTool = {
name: 'process_large_dataset',
streaming: true,
execute: async function* ({ dataset_id }) {
const chunks = await getDatasetChunks(dataset_id);
for (let i = 0; i < chunks.length; i++) {
yield {
progress: (i + 1) / chunks.length,
status: `Processing chunk ${i + 1} of ${chunks.length}`
};
await processChunk(chunks[i]);
}
yield {
progress: 1,
status: 'Complete',
result: await getFinalResult()
};
}
};Conditional Tool Access
const restrictedTool = {
name: 'admin_action',
access: {
roles: ['admin'],
conditions: async (user, context) => {
// Check additional conditions
return user.verified && context.swarm.is_internal;
}
},
execute: async (params) => {
// Admin-only functionality
}
};Tool Monitoring
Execution Logs
View tool execution history:
┌─────────────────────────────────────────────────────────────────┐
│ Tool Executions - Last 24 Hours │
├─────────────────────────────────────────────────────────────────┤
│ │
│ get_stock_price │
│ ├─ Executions: 156 │
│ ├─ Success Rate: 98.7% │
│ ├─ Avg Latency: 234ms │
│ └─ Errors: 2 (rate limit) │
│ │
│ query_database │
│ ├─ Executions: 89 │
│ ├─ Success Rate: 100% │
│ ├─ Avg Latency: 45ms │
│ └─ Errors: 0 │
│ │
│ send_slack_message │
│ ├─ Executions: 34 │
│ ├─ Success Rate: 97.1% │
│ ├─ Avg Latency: 189ms │
│ └─ Errors: 1 (channel not found) │
│ │
└─────────────────────────────────────────────────────────────────┘Setting Up Alerts
// Configure tool monitoring alerts
const toolAlerts = {
'get_stock_price': {
errorThreshold: 5, // Alert after 5 errors
latencyThreshold: 1000, // Alert if > 1 second
notifyChannel: 'alerts'
}
};Related Documentation
- [Available Integrations](/docs/integrations/available-integrations): Integration overview
- [Webhooks](/docs/integrations/webhooks): Event notifications
- [Agents](/docs/agents/creating-agents): Agent configuration
- [Tools API](/docs/api/tools-api): Tools API reference