Files
sim/.claude/rules/sim-integrations.md
Emir Karabeg 2daf34386e fix(copilot): ui/ux (#2891)
* feat(claude): added rules

* fix(copilot): chat loading; refactor(copilot): components, utils, hooks

* fix(copilot): options selection strikethrough

* fix(copilot): options render inside thinking

* fix(copilot): checkpoints, user-input; improvement(code): colors

* fix(copilot): scrolling, tool-call truncation, thinking ui

* fix(copilot): tool call spacing and shimmer/actions on previous messages

* improvement(copilot): queue

* addressed comments
2026-01-19 23:23:21 -08:00

5.8 KiB

paths
paths
apps/sim/tools/**
apps/sim/blocks/**
apps/sim/triggers/**

Adding Integrations

Overview

Adding a new integration typically requires:

  1. Tools - API operations (tools/{service}/)
  2. Block - UI component (blocks/blocks/{service}.ts)
  3. Icon - SVG icon (components/icons.tsx)
  4. Trigger (optional) - Webhooks/polling (triggers/{service}/)

Always look up the service's API docs first.

1. Tools (tools/{service}/)

tools/{service}/
├── index.ts           # Export all tools
├── types.ts           # Params/response types
├── {action}.ts        # Individual tool (e.g., send_message.ts)
└── ...

Tool file structure:

// tools/{service}/{action}.ts
import type { {Service}Params, {Service}Response } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'

export const {service}{Action}Tool: ToolConfig<{Service}Params, {Service}Response> = {
  id: '{service}_{action}',
  name: '{Service} {Action}',
  description: 'What this tool does',
  version: '1.0.0',
  oauth: { required: true, provider: '{service}' }, // if OAuth
  params: { /* param definitions */ },
  request: {
    url: '/api/tools/{service}/{action}',
    method: 'POST',
    headers: () => ({ 'Content-Type': 'application/json' }),
    body: (params) => ({ ...params }),
  },
  transformResponse: async (response) => {
    const data = await response.json()
    if (!data.success) throw new Error(data.error)
    return { success: true, output: data.output }
  },
  outputs: { /* output definitions */ },
}

Register in tools/registry.ts:

import { {service}{Action}Tool } from '@/tools/{service}'
// Add to registry object
{service}_{action}: {service}{Action}Tool,

2. Block (blocks/blocks/{service}.ts)

import { {Service}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { {Service}Response } from '@/tools/{service}/types'

export const {Service}Block: BlockConfig<{Service}Response> = {
  type: '{service}',
  name: '{Service}',
  description: 'Short description',
  longDescription: 'Detailed description',
  category: 'tools',
  bgColor: '#hexcolor',
  icon: {Service}Icon,
  subBlocks: [ /* see SubBlock Properties below */ ],
  tools: {
    access: ['{service}_{action}', ...],
    config: {
      tool: (params) => `{service}_${params.operation}`,
      params: (params) => ({ ...params }),
    },
  },
  inputs: { /* input definitions */ },
  outputs: { /* output definitions */ },
}

SubBlock Properties

{
  id: 'fieldName',           // Unique identifier
  title: 'Field Label',      // UI label
  type: 'short-input',       // See SubBlock Types below
  placeholder: 'Hint text',
  required: true,            // See Required below
  condition: { ... },        // See Condition below
  dependsOn: ['otherField'], // See DependsOn below
  mode: 'basic',             // 'basic' | 'advanced' | 'both' | 'trigger'
}

SubBlock Types: short-input, long-input, dropdown, code, switch, slider, oauth-input, channel-selector, user-selector, file-upload, etc.

condition - Show/hide based on another field

// Show when operation === 'send'
condition: { field: 'operation', value: 'send' }

// Show when operation is 'send' OR 'read'
condition: { field: 'operation', value: ['send', 'read'] }

// Show when operation !== 'send'
condition: { field: 'operation', value: 'send', not: true }

// Complex: NOT in list AND another condition
condition: {
  field: 'operation',
  value: ['list_channels', 'list_users'],
  not: true,
  and: { field: 'destinationType', value: 'dm', not: true }
}

required - Field validation

// Always required
required: true

// Conditionally required (same syntax as condition)
required: { field: 'operation', value: 'send' }

dependsOn - Clear field when dependencies change

// Clear when credential changes
dependsOn: ['credential']

// Clear when authMethod changes AND (credential OR botToken) changes
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] }

mode - When to show field

  • 'basic' - Only in basic mode (default UI)
  • 'advanced' - Only in advanced mode (manual input)
  • 'both' - Show in both modes (default)
  • 'trigger' - Only when block is used as trigger

Register in blocks/registry.ts:

import { {Service}Block } from '@/blocks/blocks/{service}'
// Add to registry object (alphabetically)
{service}: {Service}Block,

3. Icon (components/icons.tsx)

export function {Service}Icon(props: SVGProps<SVGSVGElement>) {
  return (
    <svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
      {/* SVG path from service's brand assets */}
    </svg>
  )
}

4. Trigger (triggers/{service}/) - Optional

triggers/{service}/
├── index.ts           # Export all triggers
├── webhook.ts         # Webhook handler
├── utils.ts           # Shared utilities
└── {event}.ts         # Specific event handlers

Register in triggers/registry.ts:

import { {service}WebhookTrigger } from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY
{service}_webhook: {service}WebhookTrigger,

Checklist

  • Look up API docs for the service
  • Create tools/{service}/types.ts with proper types
  • Create tool files for each operation
  • Create tools/{service}/index.ts barrel export
  • Register tools in tools/registry.ts
  • Add icon to components/icons.tsx
  • Create block in blocks/blocks/{service}.ts
  • Register block in blocks/registry.ts
  • (Optional) Create triggers in triggers/{service}/
  • (Optional) Register triggers in triggers/registry.ts