Custom Tools¶
This guide explains how to create custom webhook-based tools that your agents can call.
What are Custom Tools?¶
Custom tools let you extend your agents' capabilities by defining webhook endpoints they can call. Unlike MCP servers (which require running a server), custom tools work with any HTTP endpoint you control.
Use cases: - Connect agents to internal APIs - Integrate with CRMs, ticketing systems, ERPs - Trigger automated workflows - Access private databases or services
Architecture¶
┌─────────────────────────────────────────────────────────────┐
│ Ag2Trust │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Agent │ │ Agent │ │ Agent │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ MCP Gateway │ │
│ │ (proxy) │ │
│ └──────┬──────┘ │
└───────────────────────────┼─────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Your │ │ Your │ │ Your │
│ Webhook │ │ Webhook │ │ Webhook │
│ (CRM) │ │ (ERP) │ │ (Custom)│
└─────────┘ └─────────┘ └─────────┘
Step 1: Create a Custom Tool¶
Via Dashboard¶
- Go to Tools in the sidebar
- Click Add Tool
- Fill in the tool details:
| Field | Description | Example |
|---|---|---|
| Tool Slug | Unique identifier (lowercase, underscores) | send_invoice |
| Display Name | Human-readable name | "Send Invoice" |
| Description | When agents should use this tool | "Sends an invoice to a customer via email. Use when customer requests billing." |
| Webhook URL | Your HTTPS endpoint | https://api.example.com/webhooks/invoice |
| Authentication | HMAC-SHA256 (recommended) or Bearer Token | HMAC |
| Parameter Schema | JSON Schema for parameters | See below |
Parameter Schema Example¶
{
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "The customer ID"
},
"amount": {
"type": "number",
"description": "Invoice amount in cents"
},
"description": {
"type": "string",
"description": "Line item description"
}
},
"required": ["customer_id", "amount"]
}
Save Your Secret
After creating an HMAC tool, the webhook secret is shown once. Save it immediately - you'll need it to verify signatures.
Step 2: Assign to Agent Types¶
Custom tools must be assigned to agent types before agents can use them:
- Go to Agents > Agent Types
- Edit the agent type
- Under Custom Tools, select the tools to enable
- Save changes
Step 3: Implement Your Webhook¶
Request Format¶
Ag2Trust sends POST requests to your webhook URL:
POST /webhooks/invoice HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
X-Request-ID: req_abc123
X-Tool-Slug: send_invoice
{
"customer_id": "cust_123",
"amount": 5000,
"description": "Monthly subscription"
}
Verify HMAC Signatures (Recommended)¶
For HMAC authentication, verify the signature before processing:
import hmac
import hashlib
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
# In your webhook handler
@app.post("/webhooks/invoice")
async def handle_invoice(request: Request):
body = await request.body()
signature = request.headers.get("X-Webhook-Signature", "")
if not verify_signature(body, signature, WEBHOOK_SECRET):
raise HTTPException(401, "Invalid signature")
data = json.loads(body)
# Process the request...
return {"success": True, "invoice_id": "inv_123"}
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(`sha256=${expected}`),
Buffer.from(signature)
);
}
// In your Express handler
app.post('/webhooks/invoice', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-webhook-signature'] || '';
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const data = JSON.parse(req.body);
// Process the request...
res.json({ success: true, invoice_id: 'inv_123' });
});
Response Format¶
Return a JSON response with the result:
The response is passed back to the agent as the tool result.
Error Handling¶
Return appropriate HTTP status codes:
| Status | Meaning | Agent Behavior |
|---|---|---|
| 200-299 | Success | Tool result returned to agent |
| 400 | Bad request | Error shown to agent |
| 401/403 | Auth failure | Error logged, tool may be disabled |
| 500+ | Server error | Counts toward circuit breaker |
Circuit Breaker¶
Custom tools have a built-in circuit breaker for reliability:
- 3 consecutive failures automatically disable the tool
- Status changes to "Circuit Breaker" in the dashboard
- Tool must be manually re-enabled after fixing the issue
To re-enable: 1. Fix the underlying issue with your webhook 2. Go to Tools in the dashboard 3. Click Enable on the affected tool
Security Features¶
SSRF Protection¶
Ag2Trust blocks webhooks to: - Private IP ranges (10.x, 172.16-31.x, 192.168.x) - Localhost (127.0.0.1, ::1) - Cloud metadata endpoints (169.254.169.254) - Link-local addresses
Secret Rotation¶
Rotate your HMAC secret periodically:
- Go to Tools in the dashboard
- Click Rotate Secret on the tool
- Update your webhook with the new secret
- The old secret stops working immediately
Update Webhook First
Consider updating your webhook to accept both old and new secrets during rotation to avoid downtime.
Credential Encryption¶
All secrets (HMAC keys, Bearer tokens) are: - Encrypted with AWS KMS (AES-256-GCM) - Never stored in plain text - Decrypted only when proxying requests
Example: CRM Integration¶
1. Create the Tool¶
Slug: lookup_customer
Display Name: Look Up Customer
Description: Retrieves customer information from the CRM. Use when you need customer details like contact info, purchase history, or account status.
Webhook URL: https://crm.example.com/api/webhook/lookup
Auth: HMAC-SHA256
Schema:
{
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer ID or email"
},
"fields": {
"type": "array",
"items": { "type": "string" },
"description": "Fields to retrieve (name, email, phone, purchases)"
}
},
"required": ["customer_id"]
}
2. Assign to Support Agent Type¶
Enable lookup_customer for the "Support Agent" agent type.
3. Implement Webhook¶
@app.post("/api/webhook/lookup")
async def lookup_customer(request: Request):
# Verify signature (see above)
data = await request.json()
customer = db.get_customer(data["customer_id"])
if not customer:
return {"found": False, "message": "Customer not found"}
fields = data.get("fields", ["name", "email"])
result = {k: getattr(customer, k) for k in fields if hasattr(customer, k)}
result["found"] = True
return result
4. Test with Agent¶
User: What's the status of customer C-12345?
Agent: Let me look up that customer for you.
[Agent calls: custom_lookup_customer(customer_id="C-12345")]
Agent: Customer C-12345 is John Smith (john@example.com).
Their account is active with 3 previous purchases totaling $450.
Tier Availability¶
| Tier | Custom Tools |
|---|---|
| Free | Not available |
| Starter | Unlimited |
| Pro | Unlimited |
| Enterprise | Unlimited |
Troubleshooting¶
"Tool disabled by circuit breaker"¶
Your webhook returned 3+ consecutive errors. Check: 1. Webhook URL is correct and reachable 2. Authentication is configured properly 3. Your server is returning valid JSON 4. No firewall blocking Ag2Trust IPs
"Invalid signature"¶
HMAC verification failing: 1. Verify you're using the correct secret 2. Ensure you're hashing the raw request body 3. Check the signature format: sha256={hex_digest}
"Connection refused"¶
- Webhook must be publicly accessible (not localhost)
- URL must use HTTPS
- Check firewall rules allow inbound connections
Best Practices¶
1. Use HMAC Authentication¶
HMAC-SHA256 signatures verify requests came from Ag2Trust: - Harder to forge than Bearer tokens - Secrets never sent over the wire - Each request has unique signature
2. Validate Input¶
Even with signature verification, validate parameters:
3. Return Helpful Responses¶
Agents work better with descriptive responses:
{
"success": true,
"invoice_id": "inv_123",
"message": "Invoice for $50.00 sent to john@example.com",
"due_date": "2025-02-15"
}
4. Log Request IDs¶
Include X-Request-ID in logs for debugging:
5. Handle Timeouts¶
Ag2Trust has a 30-second timeout. For long operations: - Return immediately with a job ID - Let the agent poll for status
Next Steps¶
- MCP Integration - For running MCP servers
- Agent Types - Configure capabilities
- Security Model - Security details