Agent skill

Add Integration

by simstudioai in simstudioai/sim

Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, resolved-secret/model-input safety, and integration conventions.

Apache-2.0Auto-check passedDevelopment

Install Add Integration

skills CLI
$ npx skills add simstudioai/sim --skill add-integration -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install simstudioai/sim add-integration --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/simstudioai/sim.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/add-integration .claude/skills/add-integration && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
add-integration
GitHub stars
30k
Token cost
~7.5k tokens
SKILL.md length
2,988 words
Files
2
Skills in repo
40
Repo updated
First seen
Licence
Apache-2.0

At a glance

Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, resolved-secret/model-input safety, and integration conventions.

  • Works in 8 steps: Research the API → Create Tools → Create Block → …
  • Introducing a new service under apps/sim/tools
  • SKILL.md covers Overview, Step 1: Research the API, Step 2: Create Tools and Step 3: Create Block, plus 7 more sections
  • Calls bun; reaches w3.org and service.com

What it does

Add Integration is an agent skill from simstudioai/sim. Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, resolved-secret/model-input safety, and integration conventions. Use when introducing a new service under apps/sim/tools, apps/sim/blocks, and apps/sim/triggers.

Its SKILL.md is about 7.5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files (for example `agents/openai.yaml`).

It sits in Development, covering Technical documentation. The repository describes itself as: Sim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders. The licence is Apache-2.0.

When your agent uses it

  • Introducing a new service under apps/sim/tools
  • Apps/sim/blocks
  • Apps/sim/triggers

Example prompts

  • “/add-integration”

Workflow steps

8 steps, taken from the step headings in SKILL.md.

  1. Research the API
  2. Create Tools
  3. Create Block
  4. Add Icon
  5. Create Triggers (Optional)
  6. Register Everything
  7. Configure Deployment Availability
  8. Generate and Validate the Catalog

What it can do on your machine

Read from SKILL.md and the folder at commit 546d4e7. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • bun

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • w3.org
    • service.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Add Integration loads about 7.5k tokens when it runs. Until then it costs about 73 tokens; SKILL.md has 2,988 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~73
When it runs · the whole SKILL.md, loaded when a task matches
~7.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from simstudioai/sim at commit 546d4e7, republished under its Apache-2.0 licence (© simstudioai). 2,988 words, ~7,461 tokens.

Download SKILL.mdSave it as .claude/skills/add-integration/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
add-integration
description
Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, resolved-secret/model-input safety, and integration conventions. Use when introducing a new service under `apps/sim/tools`, `apps/sim/blocks`, and `apps/sim/triggers`.
argument-hint
<service-name> [api-docs-url]

Add Integration Skill

You are an expert at adding complete integrations to Sim. This skill orchestrates the full process of adding a new service integration.

Overview

Adding an integration involves these steps in order:

  1. Research - Read the service's API documentation
  2. Create Tools - Build tool configurations for each API operation
  3. Create Block - Build the block UI configuration
  4. Add Icon - Add the service's brand icon
  5. Create Triggers (optional) - If the service supports webhooks
  6. Register - Register tools, block, and triggers in their registries
  7. Configure Deployment Availability - Wire OAuth client and service-account metadata
  8. Generate and Validate the Catalog - Regenerate docs/catalog artifacts and run drift checks

Step 1: Research the API

Before writing any code:

  1. Use Context7 to find official documentation: mcp__context7__resolve-library-id, then fetch with mcp__context7__query-docs
  2. Or use WebFetch to read API docs directly
  3. Identify:
    • Authentication method (OAuth, API Key, both)
    • Available operations (CRUD, search, etc.)
    • Required vs optional parameters
    • Response structures
Hard Rule: No Guessed Response Schemas

If the official docs do not clearly show the response JSON shape for an endpoint, you MUST stop and tell the user exactly which outputs are unknown.

  • Do NOT guess response field names
  • Do NOT infer nested JSON paths from related endpoints
  • Do NOT invent output properties just because they seem likely
  • Do NOT implement transformResponse against unverified payload shapes

If response schemas are missing or incomplete, do one of the following before proceeding:

  1. Ask the user for sample responses
  2. Ask the user for test credentials so you can verify the live payload
  3. Reduce the scope to only endpoints whose response shapes are documented
  4. Leave the tool unimplemented and explicitly report why

Step 2: Create Tools

Directory Structure
apps/sim/tools/{service}/
├── index.ts          # Barrel exports
├── types.ts          # TypeScript interfaces
├── {action1}.ts      # Tool for action 1
├── {action2}.ts      # Tool for action 2
└── ...
Key Patterns

Choose the tool boundary before writing the declaration:

  • Use InternalToolConfig.operation for same-process Sim/provider work. Put the handler under apps/sim/lib/internal/{service}/execute-tool.ts and register every ID in apps/sim/lib/internal/tool-operations/registry.server.ts.
  • Use ToolConfig.request only for an absolute external HTTP(S) provider endpoint.

Never point a tool at /api/..., construct an absolute URL back to Sim, declare request.internal, add a directExecution property (it fails bun run check:tool-request-boundary), or add an API route merely to reuse code, normalize files, or authorize resources. A real external/browser route and an in-process tool may share the same operation, but neither calls the other. Follow the full transport and handler rules in the add-tools skill.

types.ts:

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

export interface {Service}{Action}Params {
  accessToken: string      // For OAuth services
  // OR
  apiKey: string          // For API key services

  requiredParam: string
  optionalParam?: string
}

export interface {Service}{Action}Response extends ToolResponse {
  output: {
    // Define output structure
  }
}

Declare one response interface per tool, imported by that tool's ToolConfig<Params, Response> (or InternalToolConfig for in-process work). Never add an umbrella {Service}Response union: nothing imports it.

Tool file pattern: an external provider API uses ToolConfig with request (absolute https:// URL, headers, body, transformResponse); same-process Sim work uses InternalToolConfig with operation. Both full templates, param visibility rules, and output typing live in .agents/skills/add-tools/SKILL.md — read it before writing the first tool.

Critical Rules
  • visibility: 'hidden' for OAuth tokens
  • visibility: 'user-only' for API keys and user credentials
  • visibility: 'user-or-llm' for operation parameters
  • Always use ?? null for nullable API response fields
  • Always use ?? [] for optional array fields
  • Set optional: true for outputs that may not exist
  • Never output raw JSON dumps - extract meaningful fields
  • When using type: 'json' and you know the object shape, define properties with the inner fields so downstream consumers know the structure. Only use bare type: 'json' when the shape is truly dynamic
Resolved Secrets at Model and Persistence Boundaries

Classify every request field (ordinary provider input / AI-consumed text / opaque model bytes / Sim-durable storage) before implementing the tool and apply the shared projection or provenance mechanism only where a concrete Sim {{...}} resolution path reaches a later model or log boundary. Full rules and the required tests are in .agents/skills/add-tools/SKILL.md → "Resolved Secrets and Provenance Boundaries".

Step 3: Create Block

File Location

apps/sim/blocks/blocks/{service}.ts

Follow .agents/skills/add-block/SKILL.md for the block structure, subBlock types, condition/dependsOn/required/mode syntax, outputs, canvasPresentation sentences, and the {Service}BlockMeta export (minimum 7 templates, plus url and skills). Every block declares canvasPresentation; bun run apps/sim/scripts/check-canvas-sentences.ts --block={service} must pass (CI runs check:canvas-sentences --require-coverage).

Three rules that are easy to get wrong when copying from existing blocks:

  • Every remote selectorKey must use the unified server selector path. Apply the add-selector skill: add browser-safe metadata to apps/sim/lib/selectors/manifest.ts, reuse or extract a server-only provider listing primitive, and add a credential- and destination-bound server attachment. Do not add a client provider fetcher, a provider-specific query key, browser token acquisition, or a selector-only API route. The shared context builder sends only active dependsOn values and preserves exact {{KEY}} environment references for server-side resolution.
  • Basic/advanced pairs use a canonicalParamId; its constraints are in .claude/rules/sim-integrations.md and the add-block skill → canonicalParamId Pattern.
  • Every text-entry subBlock (short-input, long-input, code) and every selector declares a placeholder; an empty box tells the user nothing. Secrets read Enter your {thing} (e.g. Enter your API key), free text names what to type (Enter branch name), and formatted values show the shape (2023-01-01T00:00:00Z, 1 to 1000). An optional field with a server-side default names that default (Defaults to the database region). Dropdowns, switches, and oauth-input do not need one.

Step 4: Add Icon

File Location

apps/sim/components/icons.tsx

Pattern
typescript
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 paths from user-provided SVG */}
    </svg>
  )
}
Getting Icons

Do not search for icons yourself. At the end of implementation, ask the user to paste the service's SVG (usually on its brand/press kit page).

Once the user provides the SVG:

  1. Extract the SVG paths/content
  2. Create a React component that spreads props
  3. Ensure viewBox is preserved from the original SVG
Theme-safety (bare rendering) — REQUIRED

The icon renders both inside its colored bgColor tile AND "bare" (no tile) on a neutral page — e.g. the home Suggested actions list — in both light and dark mode. A monochrome logo whose paths hardcode a single near-white or near-black fill is invisible bare on the matching background (white-on-white in light mode, black-on-black in dark mode).

Rules when adding the SVG:

  • Monochrome logos (a single white or black mark): draw the shape with fill='currentColor', not fill='#fff' / fill='#000000'. It then inherits white inside dark tiles, near-black inside light tiles (via getTileIconColorClass), and the theme-aware var(--text-icon) bare — legible everywhere. Do NOT set iconColor for these.
  • Multi-color brand logos (their own vivid fills): keep the hardcoded fills. They read on any background. Only set iconColor (a vivid brand hex, never a near-black/near-white tile color) if the bare icon should adopt a brand tint.
  • A large white shape with a tiny vivid accent (e.g. a logo where the body is the white negative space) still vanishes bare — convert the body to currentColor.

Verify with bun run check:bare-icons (also runs in CI). It flags purely monochrome hazards; for partial-accent logos, eyeball the suggested-actions list in both light and dark mode.

Step 5: Create Triggers (Optional)

If the service supports webhooks or needs polling, follow .agents/skills/add-trigger/SKILL.md (directory layout, buildTriggerSubBlocks, provider handler, polling handler); then wire triggers.enabled / triggers.available into the block and spread each trigger's getTrigger(id).subBlocks after the tool subBlocks.

Step 6: Register Everything

Tools Registry (apps/sim/tools/registry.ts)
typescript
// Add import (alphabetically)
import {
  {service}Action1Tool,
  {service}Action2Tool,
} from '@/tools/{service}'

// Add to tools object (alphabetically)
export const tools: Record<string, ExecutableToolConfig> = {
  // ... existing tools ...
  {service}_action1: {service}Action1Tool,
  {service}_action2: {service}Action2Tool,
}

Then regenerate the generated tool metadata and commit it:

bash
bun run tool-metadata:generate

Client code reads params/outputs from these artifacts rather than importing the registry, so a tool you add, change or remove is invisible to the UI until they are regenerated, and CI fails on stale ones. See .agents/skills/tool-registry-boundary/SKILL.md.

Block Registry (apps/sim/blocks/registry-maps.ts)

The data maps (BLOCK_REGISTRY + BLOCK_META_REGISTRY) live in registry-maps.ts; registry.ts holds only the accessor functions. Add the import and an entry to each map alphabetically:

typescript
// Add import (alphabetically)
import { {Service}Block, {Service}BlockMeta } from '@/blocks/blocks/{service}'

// Add to the config map (alphabetically)
export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
  // ... existing blocks ...
  {service}: {Service}Block,
}

// Add to the catalog-meta map (alphabetically)
export const BLOCK_META_REGISTRY: Record<string, BlockMeta> = {
  // ... existing metas ...
  {service}: {Service}BlockMeta,
}
Trigger Registry (apps/sim/triggers/registry.ts) - If triggers exist
typescript
// Add import (alphabetically)
import {
  {service}EventATrigger,
  {service}EventBTrigger,
  {service}WebhookTrigger,
} from '@/triggers/{service}'

// Add to TRIGGER_REGISTRY (alphabetically)
export const TRIGGER_REGISTRY: TriggerRegistry = {
  // ... existing triggers ...
  {service}_event_a: {service}EventATrigger,
  {service}_event_b: {service}EventBTrigger,
  {service}_webhook: {service}WebhookTrigger,
}

Step 7: Configure Deployment Availability

Do this for every visible OAuth integration. API-key and unauthenticated integrations do not need an OAuth client capability.

The block's oauth-input.serviceId is the canonical link between the generated integration catalog, the OAuth service configuration, deployment availability, and the setup CLI.

  1. Ensure the block has exactly one distinct OAuth serviceId and that it matches the canonical service entry in apps/sim/lib/oauth/oauth.ts.
  2. Confirm resolveOAuthClientCapabilityId(serviceId) resolves to the intended provider entry in OAUTH_CLIENT_CAPABILITIES in packages/deployment-config/src/env-capabilities.ts. Google and Microsoft service IDs deliberately share provider-level capabilities.
  3. For a new OAuth provider, add the required client fields to OAUTH_CLIENT_CAPABILITIES, add every referenced field to the env schema in apps/sim/lib/core/config/env.ts, and add the matching text or secret entries to OAUTH_CLIENT_SETUP_FIELDS in packages/sim-setup/src/capability-config.ts. Do not create integration-specific setup logic or infer secret fields from naming; the CLI mapping is exhaustively checked against the runtime fields.
  4. If the canonical OAuth service has serviceAccountProviderId, run bun run deployment-config:generate to refresh packages/deployment-config/src/service-account-providers.generated.ts; never hand-edit the generated provider-ID map. In packages/deployment-config/src/service-account-metadata.ts, use:
    • no deploymentRequirement when the service-account path works independently of OAuth client fields;
    • 'oauth-client' when it requires the same deployment OAuth client fields;
    • 'preview-gated' when availability is controlled by the service-account preview block.

Never add a permissive fallback for missing capability metadata. A visible OAuth integration without a resolvable capability must fail validation.

Step 8: Generate and Validate the Catalog

Run bun run tool-metadata:generate, bun run scripts/generate-docs.ts, bun run deployment-config:generate, then bun run check:audits (see the validate-integration skill → Regenerate Derived Artifacts for the full list and what each check verifies).

The docs generator creates apps/docs/content/docs/integrations/{service}.mdx — one page per service carrying the block's Actions and, if it has one, its Triggers section. Never hand-edit generated pages; the only editable region is the {/* MANUAL-CONTENT */} block (see scripts/README.md).

Every generated integration page carries a hand-written intro directly under <BlockInfoCard />. The generator preserves it across regenerations, so write it once after the first generate:

mdx
{/* MANUAL-CONTENT-START:intro */}
[{Service}](https://service.com/) is {one sentence on what the service is}.

With the {Service} block, you can:

- **{Capability}**: {what the operations in this group do}
- **{Capability}**: {...}

{How to connect: which credential to create and where, if it is not OAuth.}

In Sim, the {Service} block lets your agents {concrete workflow uses}.
{/* MANUAL-CONTENT-END */}

Group the bullets by what the user gets done, not one bullet per tool. Only describe operations the block actually ships. Follow .claude/rules/constitution.md for voice. Re-run bun run scripts/generate-docs.ts afterwards and confirm the section survived unchanged.

The docs generator refreshes packages/deployment-config/src/integrations.json, and the deployment config generator projects service-account provider IDs from that catalog plus the canonical OAuth registry. The checks compare both committed projections with their sources. Review the generated diff and keep only intentional changes.

V2 Integration Pattern

If creating V2 versions (API-aligned outputs):

  1. V2 Tools - Add _v2 suffix, version 2.0.0, flat outputs

  2. V2 Block - Add _v2 type, use createVersionedToolSelector

  3. V1 Block - Add (Legacy) to name, set hideFromToolbar: true, and add sunset: { status: 'legacy', replacedBy: '{service}_v2' } — check-block-registry fails a legacy block with no replacedBy, and the amber legacy badge plus its click-to-upgrade action read from that field.

    Only add replacedBy once the target is GA. The same check also fails when the target is unregistered, itself sunset, or still preview: true. If v2 is preview-gated, leave v1 alone until GA and drop preview in the same commit that adds the sunset — splitting them breaks the build in between.

  4. Registry - Register both versions

typescript
// In registry
{service}: {Service}Block,        // V1 (legacy, hidden)
{service}_v2: {Service}V2Block,   // V2 (visible)

Complete Checklist

Show full SKILL.md (1,264 more words)Show less
Tools
  • Created tools/{service}/ directory
  • Created types.ts with all interfaces
  • Created tool file for each operation
  • Chose exactly one boundary per tool: registered InternalToolConfig.operation or absolute external HTTP(S) ToolConfig.request
  • No tool points to /api/..., constructs a URL back to Sim, declares request.internal or a directExecution property (fails bun run check:tool-request-boundary), or has an HTTP fallback for an in-process operation
  • All params have correct visibility
  • All nullable fields use ?? null
  • All optional outputs have optional: true
  • Created index.ts barrel export
  • Registered all tools in tools/registry.ts
  • Ran bun run tool-metadata:generate and committed the regenerated artifacts
  • Classified every model-visible, opaque, Sim-durable, and internal-execution request field
  • Added shared model-input projection or private provenance only where required; ordinary external resource locators and control inputs retain their request semantics
  • Confirmed ordinary third-party tool results are not generically sanitized
  • Added provenance compatibility and fail-closed boundary tests where applicable
  • bun run check:tool-request-boundary passes
  • Internal-operation registry completeness test passes for every operation-backed tool
Block
  • Created blocks/blocks/{service}.ts
  • Set integrationType to the correct IntegrationType enum value
  • {Service}BlockMeta.tags lists every applicable IntegrationTag (tags live on the meta, not the block)
  • Defined operation dropdown with all operations
  • Added credential field with requiredScopes: getScopesForService('{service}')
  • Added conditional fields per operation
  • Every short-input, long-input, code, and selector subBlock has a placeholder
  • Set up dependsOn for cascading selectors
  • Every remote selectorKey exists in the shared manifest and has one server attachment with trusted credential provider binding and a fixed, credential-bound, or explicitly reviewed user-controlled destination policy
  • No selector provider logic, credential resolution, or provider route call runs in the browser
  • Configured tools.access with all tool IDs
  • Configured tools.config.tool selector
  • Defined outputs matching tool outputs
  • Registered block + meta in blocks/registry-maps.ts (BLOCK_REGISTRY / BLOCK_META_REGISTRY)
  • If triggers: set triggers.enabled and triggers.available
  • If triggers: spread trigger subBlocks with getTrigger()
  • Exported {Service}BlockMeta with at least 7 templates
  • canvasPresentation.sentences covers every operation; bun run apps/sim/scripts/check-canvas-sentences.ts --block={service} passes
  • {Service}BlockMeta also sets url (verified external homepage) and skills (grounded in tools.access, sourced from real use cases) — see add-block → BlockMeta
OAuth Scopes (if OAuth service)
  • Defined scopes in lib/oauth/oauth.ts under OAUTH_PROVIDERS
  • Added scope descriptions in SCOPE_DESCRIPTIONS within lib/oauth/utils.ts
  • Used getCanonicalScopesForProvider() in lib/auth/connectors/providers.ts (never hardcode)
  • Used getScopesForService() in block requiredScopes (never hardcode)
Deployment Availability (if OAuth service)
  • Block declares exactly one distinct oauth-input.serviceId
  • resolveOAuthClientCapabilityId(serviceId) resolves to the intended OAUTH_CLIENT_CAPABILITIES entry
  • Every new OAuth capability field exists in apps/sim/lib/core/config/env.ts
  • Runtime OAuth fields live in OAUTH_CLIENT_CAPABILITIES; matching CLI input modes live in the exhaustively checked OAUTH_CLIENT_SETUP_FIELDS
  • If serviceAccountProviderId is configured, SERVICE_ACCOUNT_METADATA_BY_OAUTH_SERVICE_ID has the matching projection and deployment requirement
Icon
  • Asked user to provide SVG
  • Added icon to components/icons.tsx
  • Icon spreads props correctly
  • Monochrome marks use fill='currentColor' (not hardcoded white/black) so the icon renders bare in light AND dark mode — verified with bun run check:bare-icons
Triggers (if service supports webhooks)
  • Created triggers/{service}/ directory
  • Created utils.ts with options, instructions, and extra fields helpers
  • Primary trigger uses includeDropdown: true
  • Secondary triggers do NOT have includeDropdown
  • All triggers use buildTriggerSubBlocks helper
  • Created index.ts barrel export
  • Registered all triggers in triggers/registry.ts
Docs and deployment metadata
  • Ran bun run scripts/generate-docs.ts
  • Ran bun run deployment-config:generate for OAuth or service-account changes
  • Verified docs file created
  • Wrote the {/* MANUAL-CONTENT-START:intro */} section under <BlockInfoCard /> and confirmed it survives a regenerate
  • Reviewed and committed the generated packages/deployment-config/src/integrations.json change
  • bun run integration-catalog:check passes
  • bun run docs:check passes — CI fails on stale generated docs, so commit the full generator output, including catch-up regeneration for pages another PR left stale (never revert it as "unrelated drift")
  • bun run deployment-config:check passes
Final Validation (Required)
  • Read every tool file and cross-referenced inputs/outputs against the API docs
  • Verified block subBlocks cover all required tool params with correct conditions
  • Verified block outputs match what the tools actually return
  • Verified tools.config.params correctly maps and coerces all param types
  • Verified every tool output and transformResponse path against documented or live-verified JSON responses
  • If any response schema remained unknown, explicitly told the user instead of guessing
  • {Service}BlockMeta exported with at least 7 templates, each having icon, title, prompt, modules, category, and tags

File Handling

When your integration handles file uploads or downloads, follow these patterns to work with UserFile objects consistently.

What is a UserFile?

UserFile (apps/sim/executor/types.ts) is the standard file representation in Sim — id, name, an access url (not guaranteed presigned — remoteUrl is the short-lived signed one, set only for providers that fetch by URL), size, MIME type, storage key, and optional inline base64 / provider file handles. Read file bytes through the documented upload helpers, never by fetching url directly. Read the interface rather than relying on a copy here.

File Input Pattern (Uploads)

File authorization, normalization, storage reads, provider upload, and response mapping belong in a registered in-process operation. Do not create an internal API route for file tools.

1. Block SubBlocks for File Input

Use the basic/advanced mode pattern:

typescript
// Basic mode: File upload UI
{
  id: 'uploadFile',
  title: 'File',
  type: 'file-upload',
  canonicalParamId: 'file',  // Maps to 'file' param
  placeholder: 'Upload file',
  mode: 'basic',
  multiple: false,
  required: true,
  condition: { field: 'operation', value: 'upload' },
},
// Advanced mode: Reference from previous block
{
  id: 'fileRef',
  title: 'File',
  type: 'short-input',
  canonicalParamId: 'file',  // Same canonical param
  placeholder: 'Reference file (e.g., {{file_block.output}})',
  mode: 'advanced',
  required: true,
  condition: { field: 'operation', value: 'upload' },
},

Critical: canonicalParamId must NOT match the id of a subblock outside its canonical group.

2. Normalize File Input in Block Config

tools.config.tool selects the tool before variable resolution and must not mutate or coerce input. Use tools.config.params, which runs after variable resolution, to normalize all file variants:

typescript
import { normalizeFileInput } from '@/blocks/utils'

tools: {
  config: {
    tool: (params) => `{service}_${params.operation}`,
    params: (params) => {
      // Serialization collapses the basic/advanced pair into the canonical `file` key.
      const normalizedFile = normalizeFileInput(params.file, { single: true })
      return normalizedFile ? { file: normalizedFile } : {}
    },
  },
}
3. Define and register the in-process operation
typescript
export const {service}UploadTool: InternalToolConfig<Params, Response> = {
  id: '{service}_upload',
  // ...
  params: {
    file: { type: 'file', required: false, visibility: 'user-or-llm' },
  },
  operation: {
    input: (params) => ({
      accessToken: params.accessToken,
      file: params.file,
    }),
  },
}

Implement apps/sim/lib/internal/{service}/execute-tool.ts and keep the file/provider work in typed operations beside it. The handler validates request.input, derives storage authority only from trusted request.context, authorizes every stored file before reading bytes, forwards request.signal, enforces declared and actual byte caps, and returns the canonical tool response. Register {service}_upload in apps/sim/lib/internal/tool-operations/registry.server.ts; the sweep in apps/sim/tools/request-transport.test.ts fails a forgotten registration (registry.server.test.ts checks registered ids are canonical with loadable handlers). For anything more, run the test-audit gate. There is no HTTP fallback.

File Output Pattern (Downloads)

Declare a file / file[] output on the tool. For a raw binary endpoint, set request.responseType: 'binary' and return output.file = { name, mimeType, data: buffer, size } from transformResponse(response, params?, context?). The executor's FileToolProcessor stores it and replaces it with a UserFile; tools never call it.

In an operation handler, return createInternalToolFileResult / createInternalToolFilesResult from lib/internal/tool-operations/file-result.ts — never base64 JSON. See the add-tools skill → File Downloads and Generated Files.

Key Helpers Reference
HelperLocationPurpose
normalizeFileInput@/blocks/utilsNormalize file params in block config
processFilesToUserFiles@/lib/uploads/utils/file-utilsConvert raw inputs to UserFile[]
downloadFileFromStorage@/lib/uploads/utils/file-utils.serverGet file Buffer from UserFile
FileToolProcessor@/executor/utils/file-tool-processorExecutor-side; stores declared file outputs (not called by tools)
isUserFile@/lib/core/utils/user-fileType guard for UserFile objects
FileInputSchema@/lib/uploads/utils/file-schemasZod schema for file validation
Advanced Mode for Optional Fields

Optional fields that are rarely used should be set to mode: 'advanced' so they don't clutter the basic UI. Examples: pagination tokens, time range filters, sort order, max results, reply settings.

WandConfig for Complex Inputs

Use wandConfig for fields that are hard to fill out manually:

  • Timestamps: Use generationType: 'timestamp' to inject current date context into the AI prompt
  • JSON arrays: Use generationType: 'json-object' for structured data
  • Complex queries: Use a descriptive prompt explaining the expected format
typescript
{
  id: 'startTime',
  title: 'Start Time',
  type: 'short-input',
  mode: 'advanced',
  wandConfig: {
    enabled: true,
    prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.',
    generationType: 'timestamp',
  },
}
OAuth Scopes (Centralized System)

Scopes are maintained in a single source of truth and reused everywhere:

  1. Define scopes in lib/oauth/oauth.ts under OAUTH_PROVIDERS[provider].services[service].scopes
  2. Add descriptions in SCOPE_DESCRIPTIONS within lib/oauth/utils.ts for the OAuth modal UI
  3. Reference in lib/auth/connectors/providers.ts (buildConnectorProviders) using getCanonicalScopesForProvider(providerId) from @/lib/oauth/utils
  4. Reference in blocks using getScopesForService(serviceId) from @/lib/oauth/utils

Never hardcode scope arrays in the Better Auth connector providers or block requiredScopes. Always import from the centralized source.

typescript
// In lib/auth/connectors/providers.ts (Better Auth connector providers)
scopes: getCanonicalScopesForProvider('{service}'),

// In block credential sub-block
requiredScopes: getScopesForService('{service}'),
Common Gotchas
  1. OAuth serviceId must match - The serviceId in oauth-input must match the OAuth provider configuration
  2. DependsOn clears options - When an active dependency changes, the shared selector facade refetches with an opaque query revision; dependency values and references never enter query keys
  3. Never pass Buffer directly to fetch - Convert to new Uint8Array(buffer) for TypeScript compatibility
  4. Legacy fileContent params - Only an existing tool that already accepted base64 fileContent keeps that hidden param; new tools take file only

© simstudioai, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file in .agents/skills/add-integration of simstudioai/sim.

  • SKILL.md
  • agents/openai.yaml

Open the folder on GitHubat commit 546d4e7

Compare with similar skills

Add Integration next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Add Integration compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Add Integration this skillsimstudioai/sim30k—~7.5kAutomated safety check: PassApache-2.0
Meilisearch PHP PHPDoc Writermeilisearch/meilisearch-php757—~569Automated safety check: PassMIT
DDNS Provider DevelopmentNewFuture/DDNS4.7k—~558Automated safety check: PassMIT
Human Writingkataras/jwt212—~1.9kAutomated safety check: PassMIT
API Generatinghuangjia2019/claude-code-engineering1.1k1 repos~426Automated safety check: PassNone
Nacos API Doc Updatenacos-group/nacos-group.github.io115—~3.3kAutomated safety check: PassApache-2.0

Similar skills

  • Meilisearch PHP PHPDoc Writer

    meilisearch/meilisearch-php

    Documents public methods of the meilisearch-php SDK with compact PHPDoc, including @see links, @since tags and the experimental-feature notice.

    757 GitHub stars~569 tokensUpdated yesterday
    DevelopmentAuto-check passed
  • Adds or changes a DNS provider in the DDNS project while keeping its code, schemas, tests and Chinese and English docs consistent.

    4.7k GitHub stars~558 tokensUpdated 2 days ago
    DevelopmentAuto-check passed
  • Human Writing

    kataras/jwt

    A skill your agent uses when writing or editing any prose in this repository - the book's preface, its chapters and epilogue, the brand kit notes, the agent skill documents, the changelog, and the…

    212 GitHub stars~1.9k tokensUpdated 25 days ago
    DevelopmentAuto-check passed
  • API Generating

    huangjia2019/claude-code-engineering

    Generate API endpoint documentation from Express route files.

    1.1k GitHub starsUsed in 1 repo~426 tokens
    DevelopmentAuto-check passed
  • Nacos API Doc Update

    nacos-group/nacos-group.github.io

    Updates Nacos API documentation from Swagger api.json. An agent skill from nacos-group/nacos-group.github.io.

    115 GitHub stars~3.3k tokensUpdated 14 days ago
    DevelopmentAuto-check passed
  • Mark Task Executed

    ethereum-optimism/superchain-ops

    Mark one or more superchain-ops tasks as EXECUTED by updating each task README's Status line to link the on-chain execution transaction, then open a PR.

    100 GitHub stars~679 tokensUpdated 3 days ago
    DevelopmentAuto-check passed

More from simstudioai/sim

All 40 skills in this repo
  • Sim Helm

    simstudioai/sim

    Install, upgrade, and operate the Sim Helm chart on Kubernetes.

    30k GitHub stars~2.2k tokensUpdated today
    Auto-check passed
  • Add Column Type

    simstudioai/sim

    Add a new table column type to Sim — registry entry, icon, storage shape, coercion, and the behavioral hooks the grid and API read.

    30k GitHub stars~2.9k tokensUpdated today
    Auto-check passed
  • Add Enrichment

    simstudioai/sim

    Add a code-defined table enrichment (registry entry) under apps/sim/enrichments/ backed by an ordered provider cascade, ensuring every provider tool it calls has hosted-key support.

    30k GitHub stars~2.2k tokensUpdated today
    Auto-check passed
  • Add Hosted Key

    simstudioai/sim

    Add hosted API key support to a tool so Sim provides the key (metered and billed to the workspace) when a user has not brought their own.

    30k GitHub stars~3.4k tokensUpdated today
    Auto-check passed
  • Add Managed CLI

    simstudioai/sim

    Add or upgrade a curated, immutable managed CLI for Sim Function sandboxes, including client-safe catalog metadata, a pinned server-only installation recipe, checksum and executable verification…

    30k GitHub stars~2.4k tokensUpdated today
    Auto-check passed
  • Add Selector

    simstudioai/sim

    Add or update a Sim dynamic selector using the shared manifest, server attachment, and selectors.execute path.

    30k GitHub stars~1.7k tokensUpdated today
    Auto-check passed

Questions about Add Integration

What does Add Integration do?

Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, resolved-secret/model-input safety, and integration conventions. Add Integration is an agent skill from simstudioai/sim. Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, resolved-secret/model-input safety, and integration conventions.

When should I use Add Integration?

Add Integration fits situations like: introducing a new service under apps/sim/tools; apps/sim/blocks; apps/sim/triggers.

How do I install Add Integration in Claude Code?

Run `npx skills add simstudioai/sim --skill add-integration -a claude-code`. Or copy the skill folder (.agents/skills/add-integration in simstudioai/sim) into .claude/skills/add-integration in your project. Claude Code loads it when a task matches its description.

How do I install Add Integration in Codex?

Run `npx skills add simstudioai/sim --skill add-integration -a codex`. Or copy the skill folder (.agents/skills/add-integration in simstudioai/sim) into .agents/skills/add-integration in your project. Codex loads it when a task matches its description.

Can I use Add Integration in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add simstudioai/sim --skill add-integration -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/add-integration, .gemini/skills/add-integration, .github/skills/add-integration and .opencode/skills/add-integration in your project.

What does Add Integration need to run?

Going by SKILL.md and its folder, Add Integration needs the command-line tools its instructions call (bun).

Does Add Integration access the network?

SKILL.md names 2 domains. In commands or code: w3.org and service.com; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.

Is Add Integration safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Add Integration use?

Add Integration is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Add Integration use?

About 7.5k tokens (SKILL.md is roughly 30k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Add Integration?

Skills that share tags, products or a category with Add Integration: Meilisearch PHP PHPDoc Writer (meilisearch/meilisearch-php, 757 stars), DDNS Provider Development (NewFuture/DDNS, 4.7k stars), Human Writing (kataras/jwt, 212 stars) and API Generating (huangjia2019/claude-code-engineering, 1.1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Add Integration?

simstudioai (a GitHub organization) maintains it in simstudioai/sim, which has 29,792 GitHub stars. The repository holds 40 skills in this directory. The repository was last updated on October 8, 2026.

Source: simstudioai/sim on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.