agent-tool
SKILL.md
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.',
// Zod schema for parameters
parameters: z.object({
purchaseId: z.string(),
appId: z.string(),
reason: z.string(),
}),
// Dynamic approval requirement based on context
requiresApproval: (params, context) => {
const purchase = context.purchases.find(p => p.id === params.purchaseId)
const daysSincePurchase = daysBetween(purchase.purchasedAt, new Date())
return daysSincePurchase > 30 // Auto-approve within 30 days
},
// Execution logic - THROW errors, don't return {success: false}
execute: async ({ purchaseId, appId, reason }, context) => {
// Get app config from registry (5-min TTL cache)
const app = await getApp(appId)
if (!app) {
throw new Error(`App not found: ${appId}`) // ← THROW, don't return error
}
// Process via Stripe Connect
const stripeRefund = await stripe.refunds.create({
charge: purchase.stripeChargeId,
}, {
stripeAccount: app.stripe_account_id,
})
// Revoke access via IntegrationClient (signed request)
const client = new IntegrationClient({
baseUrl: app.integration_base_url,
webhookSecret: app.webhook_secret,
})
const revokeResult = await client.revokeAccess({
purchaseId,
reason,
refundId: stripeRefund.id,
})
if (!revokeResult.success) {
throw new Error(revokeResult.message || 'Failed to revoke access')
}
// Return data only - wrapper adds {success: true, data: ...}
return { refundId: stripeRefund.id, amountRefunded: stripeRefund.amount }
},
})
Error Handling Pattern
CRITICAL: The createTool wrapper handles success/error wrapping automatically.
// ❌ WRONG - Don't return success/error objects
execute: async (params) => {
if (!app) {
return { success: false, error: 'App not found' } // BAD
}
return { success: true, data: result } // BAD
}
// ✅ CORRECT - Throw errors, return plain data
execute: async (params) => {
if (!app) {
throw new Error('App not found') // GOOD - wrapper catches this
}
return { refundId, amount } // GOOD - wrapper wraps as {success: true, data: ...}
}
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' }, url: frontConversationUrl },
],
},
],
})
return { escalated: true }
},
})
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
Weekly Installs
2
Repository
skillrecordings/supportFirst Seen
Feb 28, 2026
Security Audits
Installed on
gemini-cli2
opencode2
codebuddy2
github-copilot2
codex2
kimi-cli2