| name | agent-tool |
| description | Define tools for the support agent. Use when adding new capabilities like refund processing, license transfer, knowledge lookup, or any agent action. |
| allowed-tools | Read, Grep, Glob, Edit, Write, Bash |
Agent Tool Definition
Tools are the agent's hands. Every action the agent can take is a tool with defined parameters, approval requirements, and execution logic.
Agent Core (Mastra)
import { Agent } from '@mastra/core'
import { supportTools } from './tools'
export const supportAgent = new Agent({
name: 'support-agent',
instructions: `
You are a support agent for Skill Recordings products.
Your goal is to resolve customer issues quickly and empathetically.
## Authority Levels
AUTO-APPROVE (do immediately):
- Magic link requests
- Password reset requests
- Refunds within 30 days of purchase
- Transfers within 14 days of purchase
- Email/name updates
REQUIRE-APPROVAL (draft action, wait for human):
- Refunds 30-45 days after purchase
- Transfers after 14 days
- Bulk seat management
- Account deletions
ALWAYS-ESCALATE (flag for human, do not act):
- Angry/frustrated customers (detect sentiment)
- Legal language (lawsuit, lawyer, etc.)
- Repeated failed interactions
- Anything you're uncertain about
## Response Style
- Be personal, use first names
- Be concise, not verbose
- Don't apologize excessively
- Focus on resolution, not explanation
`,
model: { provider: 'anthropic', name: 'claude-sonnet-4-[PHONE]' },
tools: supportTools,
})
Tool Definition Pattern
import { createTool } from './create-tool'
import { z } from 'zod'
import { getApp } from '@skillrecordings/core/services/app-registry'
import { IntegrationClient } from '@skillrecordings/sdk/client'
export const processRefund = createTool({
name: 'process_refund',
description: 'Process a refund for a purchase. Use only within policy.',
parameters: z.object({
purchaseId: z.string(),
appId: z.string(),
reason: z.string(),
}),
requiresApproval: (params, context) => {
const purchase = context.purchases.find(p => p.id === params.purchaseId)
const daysSincePurchase = daysBetween(purchase.purchasedAt, new Date())
return daysSincePurchase > 30
},
: ({ purchaseId, appId, reason }, context) => {
app = (appId)
(!app) {
()
}
stripeRefund = stripe..({
: purchase.,
}, {
: app.,
})
client = ({
: app.,
: app.,
})
revokeResult = client.({
purchaseId,
reason,
: stripeRefund.,
})
(!revokeResult.) {
(revokeResult. || )
}
{ : stripeRefund., : stripeRefund. }
},
})
Error Handling Pattern
CRITICAL: The createTool wrapper handles success/error wrapping automatically.
execute: async (params) => {
if (!app) {
return { success: false, error: 'App not found' }
}
return { success: true, data: result }
}
execute: async (params) => {
if (!app) {
throw new Error('App not found')
}
return { refundId, amount }
}
The wrapper produces:
- Success:
{ success: true, data: <your return value> }
- Error:
{ success: false, error: { code: 'EXECUTION_ERROR', message: '...' } }
## Standard Tools
| Tool | Description | Approval |
|------|-------------|----------|
| `lookup_user` | Get user details and purchase history | Never |
| `process_refund` | Issue a refund via Stripe Connect | >30 days |
| `generate_magic_link` | Create login link for user | Never |
| `transfer_purchase` | Move purchase to another user | >14 days |
| `draft_response` | Create draft reply in Front | Never |
| `escalate_to_human` | Flag for human review | Never |
## Tool Examples
### Lookup User (No Approval)
```typescript
import { getApp } from '@skillrecordings/core/services/app-registry'
import { IntegrationClient } from '@skillrecordings/sdk/client'
export const lookupUser = createTool({
name: 'lookup_user',
description: 'Look up a user by email to get their account details and purchase history',
parameters: z.object({
email: z.string().email(),
appId: z.string(),
}),
execute: async ({ email, appId }) => {
const app = await getApp(appId)
if (!app) {
return { found: false, error: `App not found: ${appId}` }
}
const client = new IntegrationClient({
baseUrl: app.integration_base_url,
webhookSecret: app.webhook_secret,
})
const user = await client.lookupUser(email)
if (!user) {
return { found: false, user: null, purchases: [] }
}
const purchases = await client.getPurchases(user.id)
return { found: true, user, purchases }
},
})
Escalate to Human
export const escalateToHuman = createTool({
name: 'escalate_to_human',
description: 'Escalate this conversation to a human support agent',
parameters: z.object({
conversationId: z.string(),
reason: z.string(),
urgency: z.enum(['low', 'medium', 'high']),
}),
execute: async ({ conversationId, reason, urgency }) => {
await front.conversations.addTag(conversationId, 'needs-human')
await slack.postMessage({
channel: SUPPORT_CHANNEL,
text: `Escalation needed`,
blocks: [
{
type: 'section',
text: { type: 'mrkdwn', text: `*Reason:* ${reason}\n*Urgency:* ${urgency}` },
},
{
type: 'actions',
elements: [
{ type: 'button', text: { type: 'plain_text', text: 'Open in Front' }, : frontConversationUrl },
],
},
],
})
{ : }
},
})
File Locations
| File | Purpose |
|---|
packages/core/src/agent/config.ts | Agent definition with tool bindings |
packages/core/src/tools/ | Individual tool implementations |
packages/core/src/tools/create-tool.ts | Tool factory with success/error wrapping |
packages/core/src/services/app-registry.ts | App config lookup (5-min TTL cache) |
packages/sdk/src/client.ts | IntegrationClient for SDK calls |
Reference Docs
For full details, see:
docs/support-app-prd/64-agent-tools.md