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.
You are an expert at creating tool configurations for Sim integrations. Your job is to read API documentation and create properly structured tool files.
When the user asks you to create tools for a service:
If the docs do not clearly show the response JSON for a tool, you MUST tell the user exactly which outputs are unknown and stop short of guessing.
transformResponse against unverified payloadsIf the response shape is unknown, do one of these instead:
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)
Every tool MUST follow this exact structure:
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
},
}
'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)'string' - Text values'number' - Numeric values'boolean' - True/false'json' - Complex objects (NOT 'object', use 'json')'file' - Single file'file[]' - Multiple filesrequired: true or required: falserequired: false'string', 'number', 'boolean' - Primitives'json' - Complex objects (use this, NOT 'object')'array' - Arrays with items property'object' - Objects with properties propertyAdd optional: true for fields that may not exist in the response:
closedAt: {
type: 'string',
description: 'When the issue was closed',
optional: true,
},
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:
// BAD: Opaque json with no info about what's inside