---
name: add-tools
description: Create or update Sim tool configurations from service API docs, including typed params, request mapping, response transforms, outputs, and registry entries. Use when working in `apps/sim/tools/{service}/` or fixing tool definitions for an integration.
---

# Add Tools Skill

You are an expert at creating tool configurations for Sim integrations. Your job is to read API documentation and create properly structured tool files.

## Your Task

When the user asks you to create tools for a service:
1. Use Context7 or WebFetch to read the service's API documentation
2. Create the tools directory structure
3. Generate properly typed tool configurations

## Directory Structure

Create files in `apps/sim/tools/{service}/`:
```
tools/{service}/
├── index.ts      # Barrel export
├── types.ts      # Parameter & response types
└── {action}.ts   # Individual tool files (one per operation)
```

## Tool Configuration Structure

Every tool MUST follow this exact structure:

```typescript
import type { {ServiceName}{Action}Params } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'

interface {ServiceName}{Action}Response {
  success: boolean
  output: {
    // Define output structure here
  }
}

export const {serviceName}{Action}Tool: ToolConfig<
  {ServiceName}{Action}Params,
  {ServiceName}{Action}Response
> = {
  id: '{service}_{action}',           // snake_case, matches tool name
  name: '{Service} {Action}',         // Human readable
  description: 'Brief description',   // One sentence
  version: '1.0.0',

  // OAuth config (if service uses OAuth)
  oauth: {
    required: true,
    provider: '{service}',            // Must match OAuth provider ID
  },

  params: {
    // Hidden params (system-injected, only use hidden for oauth accessToken)
    accessToken: {
      type: 'string',
      required: true,
      visibility: 'hidden',
      description: 'OAuth access token',
    },
    // User-only params (credentials, api key, IDs user must provide)
    someId: {
      type: 'string',
      required: true,
      visibility: 'user-only',
      description: 'The ID of the resource',
    },
    // User-or-LLM params (everything else, can be provided by user OR computed by LLM)
    query: {
      type: 'string',
      required: false,                // Use false for optional
      visibility: 'user-or-llm',
      description: 'Search query',
    },
  },

  request: {
    url: (params) => `https://api.service.com/v1/resource/${params.id}`,
    method: 'POST',
    headers: (params) => ({
      Authorization: `Bearer ${params.accessToken}`,
      'Content-Type': 'application/json',
    }),
    body: (params) => ({
      // Request body - only for POST/PUT/PATCH
      // Trim ID fields to prevent copy-paste whitespace errors:
      // userId: params.userId?.trim(),
    }),
  },

  transformResponse: async (response: Response) => {
    const data = await response.json()
    return {
      success: true,
      output: {
        // Map API response to output
        // Use ?? null for nullable fields
        // Use ?? [] for optional arrays
      },
    }
  },

  outputs: {
    // Define each output field
  },
}
```

## Critical Rules for Parameters

### Visibility Options
- `'hidden'` - System-injected (OAuth tokens, internal params). User never sees.
- `'user-only'` - User must provide (credentials, api keys, account-specific IDs)
- `'user-or-llm'` - User provides OR LLM can compute (search queries, content, filters, most fall into this category)

### Parameter Types
- `'string'` - Text values
- `'number'` - Numeric values
- `'boolean'` - True/false
- `'json'` - Complex objects (NOT 'object', use 'json')
- `'file'` - Single file
- `'file[]'` - Multiple files

### Required vs Optional
- Always explicitly set `required: true` or `required: false`
- Optional params should have `required: false`

## Critical Rules for Outputs

### Output Types
- `'string'`, `'number'`, `'boolean'` - Primitives
- `'json'` - Complex objects (use this, NOT 'object')
- `'array'` - Arrays with `items` property
- `'object'` - Objects with `properties` property

### Optional Outputs
Add `optional: true` for fields that may not exist in the response:
```typescript
closedAt: {
  type: 'string',
  description: 'When the issue was closed',
  optional: true,
},
```

### Typed JSON Outputs

When using `type: 'json'` and you know the object shape in advance, **always define the inner structure** using `properties` so downstream consumers know what fields are available:

```typescript
// BAD: Opaque json with no info about what's inside
metadata: {
  type: 'json',
  description: 'Response metadata',
},

// GOOD: Define the known properties
metadata: {
  type: 'json',
  description: 'Response metadata',
  properties: {
    id: { type: 'string', description: 'Unique ID' },
    status: { type: 'string', description: 'Current status' },
    count: { type: 'number', description: 'Total count' },
  },
},
```

For arrays of objects, define the item structure:
```typescript
items: {
  type: 'array',
  description: 'List of items',
  items: {
    type: 'object',
    properties: {
      id: { type: 'string', description: 'Item ID' },
      name: { type: 'string', description: 'Item name' },
    },
  },
},
```

Only use bare `type: 'json'` without `properties` when the shape is truly dynamic or unknown.

## Critical Rules for transformResponse

### Handle Nullable Fields
ALWAYS use `?? null` for fields that may be undefined:
```typescript
transformResponse: async (response: Response) => {
  const data = await response.json()
  return {
    success: true,
    output: {
      id: data.id,
      title: data.title,
      body: data.body ?? null,           // May be undefined
      assignee: data.assignee ?? null,   // May be undefined
      labels: data.labels ?? [],         // Default to empty array
      closedAt: data.closed_at ?? null,  // May be undefined
    },
  }
}
```

### Never Output Raw JSON Dumps
DON'T do this:
```typescript
output: {
  data: data,  // BAD - raw JSON dump
}
```

DO this instead - extract meaningful fields:
```typescript
output: {
  id: data.id,
  name: data.name,
  status: data.status,
  metadata: {
    createdAt: data.created_at,
    updatedAt: data.updated_at,
  },
}
```

## Types File Pattern

Create `types.ts` with interfaces for all params and responses:

```typescript
import type { ToolResponse } from '@/tools/types'

// Parameter interfaces
export interface {Service}{Action}Params {
  accessToken: string
  requiredField: string
  optionalField?: string
}

// Response interfaces (extend ToolResponse)
export interface {Service}{Action}Response extends ToolResponse {
  output: {
    field1: string
    field2: number
    optionalField?: string | null
  }
}
```

## Index.ts Barrel Export Pattern

```typescript
// Export all tools
export { serviceTool1 } from './{action1}'
export { serviceTool2 } from './{action2}'

// Export types
export * from './types'
```

## Registering Tools

After creating tools, remind the user to:
1. Import tools in `apps/sim/tools/registry.ts`
2. Add to the `tools` object with snake_case keys:
```typescript
import { serviceActionTool } from '@/tools/{service}'

export const tools = {
  // ... existing tools ...
  {service}_{action}: serviceActionTool,
}
```

## V2 Tool Pattern

If creating V2 tools (API-aligned outputs), use `_v2` suffix:
- Tool ID: `{service}_{action}_v2`
- Variable name: `{action}V2Tool`
- Version: `'2.0.0'`
- Outputs: Flat, API-aligned (no content/metadata wrapper)

## Naming Convention

All tool IDs MUST use `snake_case`: `{service}_{action}` (e.g., `x_create_tweet`, `slack_send_message`). Never use camelCase or PascalCase for tool IDs.

## Checklist Before Finishing

- [ ] All tool IDs use snake_case
- [ ] All params have explicit `required: true` or `required: false`
- [ ] All params have appropriate `visibility`
- [ ] All nullable response fields use `?? null`
- [ ] All optional outputs have `optional: true`
- [ ] No raw JSON dumps in outputs
- [ ] Types file has all interfaces
- [ ] Index.ts exports all tools

## Final Validation (Required)

After creating all tools, you MUST validate every tool before finishing:

1. **Read every tool file** you created — do not skip any
2. **Cross-reference with the API docs** to verify:
   - All required params are marked `required: true`
   - All optional params are marked `required: false`
   - Param types match the API (string, number, boolean, json)
   - Request URL, method, headers, and body match the API spec
   - `transformResponse` extracts the correct fields from the API response
   - All output fields match what the API actually returns
   - No fields are missing from outputs that the API provides
   - No extra fields are defined in outputs that the API doesn't return
3. **Verify consistency** across tools:
   - Shared types in `types.ts` match all tools that use them
   - Tool IDs in the barrel export match the tool file definitions
   - Error handling is consistent (error checks, meaningful messages)
