Claude-skill-registry agent-tool
Define tools for the support agent. Use when adding new capabilities like refund processing, license transfer, knowledge lookup, or any agent action.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/agent-tool" ~/.claude/skills/majiayu000-claude-skill-registry-agent-tool && rm -rf "$T"
manifest:
skills/data/agent-tool/SKILL.mdsource content
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 |
|---|---|
| Agent definition with tool bindings |
| Individual tool implementations |
| Tool factory with success/error wrapping |
| App config lookup (5-min TTL cache) |
| IntegrationClient for SDK calls |
Reference Docs
For full details, see:
docs/support-app-prd/64-agent-tools.md