validate-integration
GitHub验证 Sim 集成(工具、区块、注册表及配置)是否符合 API 文档和执行规范。通过交叉比对代码与官方文档,按严重程度报告并修复问题,确保集成正确且完整。
Trigger Scenarios
Install
npx skills add simstudioai/sim --skill validate-integration -g -y
SKILL.md
Frontmatter
{
"name": "validate-integration",
"description": "Validate an existing Sim integration (tools, block, registry, and resolved-secret\/model-input boundaries) against the service's API docs and Sim execution conventions",
"argument-hint": "<service-name> [api-docs-url]"
}
Validate Integration Skill
You are an expert auditor for Sim integrations. Your job is to thoroughly validate that an existing integration is correct, complete, and follows all conventions.
Your Task
When the user asks you to validate an integration:
- Read the service's API documentation (via WebFetch or Context7)
- Read every tool, the block, and registry entries
- Cross-reference everything against the API docs and Sim conventions
- Report all issues found, grouped by severity (critical, warning, suggestion)
- Fix all issues after reporting them
Step 1: Gather All Files
Read every file for the integration — do not skip any:
apps/sim/tools/{service}/ # All tool files, types.ts, index.ts
apps/sim/blocks/blocks/{service}.ts # Block definition
apps/sim/tools/registry.ts # Tool registry entries for this service
apps/sim/blocks/registry-maps.ts # Block + meta registry entry (BLOCK_REGISTRY / BLOCK_META_REGISTRY)
apps/sim/components/icons.tsx # Icon definition
apps/sim/lib/auth/auth.ts # OAuth config — should use getCanonicalScopesForProvider()
apps/sim/lib/oauth/oauth.ts # OAuth provider config — single source of truth for scopes
apps/sim/lib/oauth/utils.ts # Scope utilities, SCOPE_DESCRIPTIONS for modal UI
packages/deployment-config/src/env-capabilities.ts # OAuth client runtime capability source of truth
apps/sim/lib/core/config/env.ts # Runtime env schema for capability fields
packages/sim-setup/src/capability-config.ts # Exhaustive CLI input-mode mapping for OAuth fields
packages/deployment-config/src/integrations.json # Generated client-safe integration catalog
packages/deployment-config/src/service-account-providers.generated.ts # Generated provider-ID facts
packages/deployment-config/src/service-account-metadata.ts # Handwritten deployment policy
Step 2: Pull API Documentation
Fetch the official API docs for the service. This is the source of truth for:
- Endpoint URLs, HTTP methods, and auth headers
- Required vs optional parameters
- Parameter types and allowed values
- Response shapes and field names
- Pagination patterns (which param name, which response field)
- Rate limits and error formats
Hard Rule: No Guessed Response Schemas
If the official docs do not clearly show the response JSON shape for an endpoint, you MUST tell the user instead of guessing.
- Do NOT assume field names from nearby endpoints
- Do NOT infer nested JSON paths without evidence
- Do NOT treat "likely" fields as confirmed outputs
- Do NOT accept implementation guesses as valid just because they are defensive
If a response schema is unknown, the validation must explicitly call that out and require:
- sample responses from the user,
- live test credentials for verification, or
- trimming the tool/block down to only documented fields.
Step 3: Validate Tools
For every tool file, check:
Tool ID and Naming
- Tool ID uses
snake_case:{service}_{action}(e.g.,x_create_tweet,slack_send_message) - Tool
nameis human-readable (e.g.,'X Create Tweet') - Tool
descriptionis a concise one-liner describing what it does - Tool
versionis set ('1.0.0'or'2.0.0'for V2)
Params
- All required API params are marked
required: true - All optional API params are marked
required: false - Every param has explicit
required: trueorrequired: false— never omitted - Param types match the API (
'string','number','boolean','json') - Visibility is correct:
'hidden'— ONLY for OAuth access tokens and system-injected params'user-only'— for API keys, credentials, and account-specific IDs the user must provide'user-or-llm'— for everything else (search queries, content, filters, IDs that could come from other blocks)
- Every param has a
descriptionthat explains what it does
Request
- URL matches the API endpoint exactly (correct base URL, path segments, path params)
- HTTP method matches the API spec (GET, POST, PUT, PATCH, DELETE)
- Headers include correct auth pattern:
- OAuth:
Authorization: Bearer ${params.accessToken} - API Key: correct header name and format per the service's docs
- OAuth:
-
Content-Typeheader is set for POST/PUT/PATCH requests - Body sends all required fields and only includes optional fields when provided
- For GET requests with query params: URL is constructed correctly with query string
- ID fields in URL paths are
.trim()-ed to prevent copy-paste whitespace errors - Path params use template literals correctly:
`https://api.service.com/v1/${params.id.trim()}`
Response / transformResponse
- Correctly parses the API response (
await response.json()) - Extracts the right fields from the response structure (e.g.,
data.datavsdatavsdata.results) - All nullable fields use
?? null - All optional arrays use
?? [] - Error cases are handled: checks for missing/empty data and returns meaningful error
- Does NOT do raw JSON dumps — extracts meaningful, individual fields
- Every extracted field is backed by official docs or live-verified sample payloads
Outputs
- All output fields match what the API actually returns
- No fields are missing that the API provides and users would commonly need
- No phantom fields defined that the API doesn't return
-
optional: trueis set on fields that may not exist in all responses - When using
type: 'json'and the shape is known,propertiesdefines the inner fields (tool outputs only — block outputs do not supportproperties) - When using
type: 'array',itemsdefines the item structure withproperties(tool outputs only) - Field descriptions are accurate and helpful
Types (types.ts)
- Has param interfaces for every tool (e.g.,
XCreateTweetParams) - Has response interfaces for every tool (extending
ToolResponse) - Optional params use
?in the interface (e.g.,replyTo?: string) - Field names in types match actual API field names
- Shared response types are properly reused (e.g.,
XTweetResponseshared across tweet tools)
Barrel Export (index.ts)
- Every tool is exported
- All types are re-exported (
export * from './types') - No orphaned exports (tools that don't exist)
Tool Registry (tools/registry.ts)
- Every tool is imported and registered
- Registry keys use snake_case and match tool IDs exactly
- Entries are in alphabetical order within the file
Resolved-Secret Provenance and Model Input
For every request field, determine whether it is ordinary API input, model-visible text/structured content, opaque model input, or a value persisted into Sim-owned durable storage.
Treat model-input provenance as opt-in. Require official documentation or an unambiguous local execution path proving that the exact field reaches an AI model. If the evidence is ambiguous, leave the integration unchanged; do not infer a model boundary merely from natural-language, search, extraction, or "AI-powered" marketing terminology.
- AI-consumed text/structured fields use
request.modelInputwithmode: 'project'and a minimal exact selector; nested/JSON-string adapters preserve shape throughapplyProjected - Ordinary external URLs, domains, resource IDs, and control fields retain normal request semantics unless the exact field is proven model-visible; an AI-backed provider or later model processing of the referenced resource is not sufficient evidence
- Serialized content proven to be sent directly to an external model is selected by
request.modelInput, projected before the existing formatter parses it, and has deterministic formatter behavior when a whole-value placeholder is invalid for the serialized grammar - Actual inline/raw AI-consumed bytes owned by an authenticated internal route use
privateProvenance(ormode: 'private-provenance'), and the route validatesvalidateOpaqueModelInputProvenancebefore model egress; storage keys, paths, signed URLs, and ordinary remote URLs are not treated as byte provenance, while tracked stored bytes are authorized independently at the owning model-egress boundary - Persisted workspace-file contents are checked with the shared provenance guard only when
their bytes or decoded content cross into a model/tool-result boundary; ordinary file APIs
remain unchanged. Unsupported secret-bearing file paths are rejected at
file_write - Sim-owned durable writes and internal execution handoffs that can enter workflows/models use
field-scoped
request.secretProvenance; authenticated receivers validate the exact selection and scope, strip private metadata, and persist, import, or propagate it at the owning boundary - Private provenance is never attached to external URLs or
directExecution; proven model-visible external fields use projection, while other external inputs remain unchanged - No tool performs raw secret plaintext/source substitution or serializes plaintext provenance
- No
transformResponseor tool-local helper blanket-sanitizes ordinary third-party results; only execution-scoped, activated Sim provenance is projected at shared model/log boundaries - Private headers/envelopes are produced and stripped by the shared tool executor, never hand-rolled or returned as functional output
- Every added provenance hook has a concrete Sim
{{...}}resolution path and a later persistence/model/log crossing; there is no generic handling for arbitrary filenames, metadata, provider results, or API payloads - Diagnostic projection is applied only to values carrying execution-scoped provenance; ordinary provider responses, filenames, URLs, and errors are unchanged
- Tests cover named
{{NAME}}projection, unproven identical public text, nested and serialized shape handling, unchanged ordinary external inputs, malformed/incomplete metadata, headerless legacy requests, metadata stripping, and durable legacy/stale/scope cases when applicable
Treat a missing or bypassed model, durable, or internal-execution provenance boundary as critical. Do not fix it with a tool-specific string replacer or by sanitizing every provider result; repair the shared request, authenticated internal-route, persistence, or re-entry boundary that owns the data.
Step 4: Validate Block
Block ↔ Tool Alignment (CRITICAL)
This is the most important validation — the block must be perfectly aligned with every tool it references.
For each tool in tools.access:
- The operation dropdown has an option whose ID matches the tool ID (or the
tools.config.toolfunction correctly maps to it) - Every required tool param (except
accessToken) has a corresponding subBlock input that is:- Shown when that operation is selected (correct
condition) - Marked as
required: true(or conditionally required)
- Shown when that operation is selected (correct
- Every optional tool param has a corresponding subBlock input (or is intentionally omitted if truly never needed)
- SubBlock
idvalues are unique across the entire block — no duplicates even across different conditions - The
tools.config.toolfunction returns the correct tool ID for every possible operation value - The
tools.config.paramsfunction correctly maps subBlock IDs to tool param names when they differ
SubBlocks
- Operation dropdown lists ALL tool operations available in
tools.access - Dropdown option labels are human-readable and descriptive
- Conditions use correct syntax:
- Single value:
{ field: 'operation', value: 'x_create_tweet' } - Multiple values (OR):
{ field: 'operation', value: ['x_create_tweet', 'x_delete_tweet'] } - Negation:
{ field: 'operation', value: 'delete', not: true } - Compound:
{ field: 'op', value: 'send', and: { field: 'type', value: 'dm' } }
- Single value:
- Condition arrays include ALL operations that use that field — none missing
-
dependsOnis set for fields that need other values (selectors depending on credential, cascading dropdowns) - SubBlock types match tool param types:
- Enum/fixed options →
dropdown - Free text →
short-input - Long text/content →
long-input - True/false →
dropdownwith Yes/No options (notswitchunless purely UI toggle) - Credentials →
oauth-inputwith correctserviceId
- Enum/fixed options →
- Dropdown
value: () => 'default'is set for dropdowns with a sensible default
Advanced Mode
- Optional, rarely-used fields are set to
mode: 'advanced':- Pagination tokens / next tokens
- Time range filters (start/end time)
- Sort order / direction options
- Max results / per page limits
- Reply settings / threading options
- Rarely used IDs (reply-to, quote-tweet, etc.)
- Exclude filters
- Required fields are NEVER set to
mode: 'advanced' - Fields that users fill in most of the time are NOT set to
mode: 'advanced'
WandConfig
- Timestamp fields have
wandConfigwithgenerationType: 'timestamp' - Comma-separated list fields have
wandConfigwith a descriptive prompt - Complex filter/query fields have
wandConfigwith format examples in the prompt - All
wandConfigprompts end with "Return ONLY the [format] - no explanations, no extra text." -
wandConfig.placeholderdescribes what to type in natural language
Tools Config
-
tools.accesslists every tool ID the block can use — none missing -
tools.config.toolreturns the correct tool ID for each operation - Type coercions are in
tools.config.params(runs at execution time), NOT intools.config.tool(runs at serialization time before variable resolution) -
tools.config.paramshandles:Number()conversion for numeric params that come as strings from inputsBoolean/ string-to-boolean conversion for toggle params- Empty string →
undefinedconversion for optional dropdown values - Any subBlock ID → tool param name remapping
- No
Number(),JSON.parse(), or other coercions intools.config.tool— these would destroy dynamic references like<Block.output>
Block Outputs
- Outputs cover the key fields returned by ALL tools (not just one operation)
- Output types are correct (
'string','number','boolean','json') -
type: 'json'outputs describe inner fields in the description string:'User profile (id, name, username, bio)'or'[{address, status, type}]'for arrays - Do NOT add a
properties: {...}field on block outputs. Block-levelOutputFieldDefinition(from@sim/workflow-types/blocks) only accepts{ type, description?, condition?, hiddenFromDisplay? }. Nestedpropertiesis a tool-level construct (OutputProperty) — adding it to a block output will fail TypeScript at build time - No opaque
type: 'json'with vague descriptions like'Response data' - Outputs that only appear for certain operations use
conditionif supported, or document which operations return them
Block Metadata
-
typeis snake_case (e.g.,'x','cloudflare') -
nameis human-readable (e.g.,'X','Cloudflare') -
descriptionis a concise one-liner -
longDescriptionprovides detail for docs -
docsLinkpoints to'https://docs.sim.ai/integrations/{service}' -
categoryis'tools' -
bgColoruses the service's brand color hex -
iconreferences the correct icon component from@/components/icons -
authModeis set correctly (AuthMode.OAuthorAuthMode.ApiKey) - Block + meta are registered in
blocks/registry-maps.ts(BLOCK_REGISTRY/BLOCK_META_REGISTRY) alphabetically
BlockMeta
-
{Service}BlockMetais exported in the same file as the block - Has at least 7 templates, each with
icon,title,prompt,modules,category, andtags - Prompts describe concrete use cases, not generic descriptions of what the service does
-
alsoIntegrationsis set on any template whose prompt references another service -
skillspresent (3–5 mainstream, 2–3 niche), each grounded intools.access— flag any skill implying an unsupported action - Each skill is real, not hallucinated — web-search and confirm it maps to a popular use case attested online (vendor use-case pages, official docs describing the workflow, reputable "top automations" articles); rewrite/remove any you cannot source
- Each skill has a kebab-case
name(≤64 chars, unique), a one-linedescription, and markdowncontentwith# Title+## Steps+ an output/guidance section
Block Inputs
-
inputssection lists all subBlock params that the block accepts - Input types match the subBlock types
- When using
canonicalParamId, inputs list the canonical ID (not the raw subBlock IDs)
Step 5: Validate OAuth Scopes (if OAuth service)
Scopes are centralized — the single source of truth is OAUTH_PROVIDERS in lib/oauth/oauth.ts.
- Scopes defined in
lib/oauth/oauth.tsunderOAUTH_PROVIDERS[provider].services[service].scopes -
auth.tsusesgetCanonicalScopesForProvider(providerId)— NOT a hardcoded array - Block
requiredScopesusesgetScopesForService(serviceId)— NOT a hardcoded array - No hardcoded scope arrays in
auth.tsor block files (should all use utility functions) - Each scope has a human-readable description in
SCOPE_DESCRIPTIONSwithinlib/oauth/utils.ts - No excess scopes that aren't needed by any tool
Step 6: Validate Deployment Availability (if OAuth service)
The deployment UI and setup CLI do not infer OAuth client fields from scopes. They resolve the
block's generated oauthServiceId through the shared deployment capability catalog.
- The visible integration block has exactly one distinct
oauth-input.serviceId -
resolveOAuthClientCapabilityId(serviceId)returns the intended provider capability - The resolved provider exists in
OAUTH_CLIENT_CAPABILITIES - Every field listed by that capability exists in
apps/sim/lib/core/config/env.ts - Every capability field has the correct
textorsecretentry inOAUTH_CLIENT_SETUP_FIELDS; no CLI naming heuristic is required - Shared Google/Microsoft service IDs resolve to their provider capability rather than duplicate entries
-
npx sim-setup add integration <capabilityId>is the command emitted by availability; the CLI has only the exhaustive input-mode projection, not a second runtime provider definition - If the canonical OAuth service declares
serviceAccountProviderId, the generatedSERVICE_ACCOUNT_PROVIDER_BY_OAUTH_SERVICE_ID[serviceId]has the same provider ID - The service-account
deploymentRequirementmatches how that credential actually works: omitted for an independent path,'oauth-client'when it needs the OAuth client fields, or'preview-gated'when controlled by a preview block
Treat a missing capability as critical: runtime availability intentionally throws instead of silently exposing an unusable integration.
Step 7: Validate Pagination Consistency
If any tools support pagination:
- Pagination param names match the API docs (e.g.,
pagination_tokenvsnext_tokenvscursor) - Different API endpoints that use different pagination param names have separate subBlocks in the block
- Pagination response fields (
nextToken,cursor, etc.) are included in tool outputs - Pagination subBlocks are set to
mode: 'advanced'
Step 8: Validate Memory Load Safety
If any tool lists, searches, exports, imports, downloads, uploads, paginates, batches, transforms arrays, or reads file/HTTP bodies, read .agents/skills/memory-load-check/SKILL.md and apply it to the integration.
- List/search tools expose API limits and do not auto-fetch every page into memory
- Transform logic does not build unbounded arrays, maps, sets, or
Promise.allfan-outs - File and HTTP body reads use explicit byte caps or existing stream-limit helpers
- Large result payloads are summarized, paginated, referenced, or capped rather than raw-dumped
- Pagination and download tests cover caps, early stop behavior, or partial-result preservation when relevant
Step 9: Validate Error Handling
-
transformResponsechecks for error conditions before accessing data - Error responses include meaningful messages (not just generic "failed")
- HTTP error status codes are handled (check
response.okor status codes)
Step 10: Report and Fix
Report Format
Group findings by severity:
Critical (will cause runtime errors or incorrect behavior):
- Wrong endpoint URL or HTTP method
- Missing required params or wrong
requiredflag - Incorrect response field mapping (accessing wrong path in response)
- Missing error handling that would cause crashes
- Tool ID mismatch between tool file, registry, and block
tools.access - OAuth scopes missing in
auth.tsthat tools need - OAuth integration
serviceIdmissing from the deployment capability catalog - Capability references an env field absent from the runtime env schema
- Service-account metadata disagrees with the canonical OAuth service configuration
tools.config.toolreturning wrong tool ID for an operation- Type coercions in
tools.config.toolinstead oftools.config.params - Proven model-visible request fields bypass the shared projection or private-provenance boundary
- Opaque model input is downloaded or sent before provenance and workspace-file checks
- A Sim-owned durable sink or internal execution handoff drops encrypted provenance or breaks
legacy headerless/
NULLdata - A tool substitutes secret plaintext into source, leaks private metadata, or generically sanitizes unrelated third-party results
Warning (follows conventions incorrectly or has usability issues):
- Optional field not set to
mode: 'advanced' - Missing
wandConfigon timestamp/complex fields - Wrong
visibilityon params (e.g.,'hidden'instead of'user-or-llm') - Missing
optional: trueon nullable outputs - Opaque
type: 'json'without property descriptions - Missing
.trim()on ID fields in request URLs - Missing
?? nullon nullable response fields - Block condition array missing an operation that uses that field
- Hardcoded scope arrays instead of using
getScopesForService()/getCanonicalScopesForProvider() - Missing scope description in
SCOPE_DESCRIPTIONSwithinlib/oauth/utils.ts
Suggestion (minor improvements):
- Better description text
- Inconsistent naming across tools
- Missing
longDescriptionordocsLink - Pagination fields that could benefit from
wandConfig
Fix All Issues
After reporting, fix every critical and warning issue. Apply suggestions where they don't add unnecessary complexity.
Regenerate Derived Artifacts
Several files are generated from tool and block definitions. Editing a tool or block WITHOUT regenerating them fails CI, so run these before pushing:
bun run tool-metadata:generate # repo root — apps/sim/tools/generated/*
bun run scripts/generate-docs.ts # docs .mdx + deployment-config/integrations.json + docs icons
bun run deployment-config:generate # canonical OAuth registry + catalog → provider-ID facts
bun run integration-catalog:check # registry ↔ committed deployment metadata drift
bun run docs:check # committed docs ↔ what the generator renders today
bun run deployment-config:check # OAuth registry/catalog ↔ provider-ID fact drift
tool-metadata:generate— required whenever a tool'soutputs,params, or descriptions change. CI enforces this withbun run tool-metadata:check, which fails with "Generated tool metadata is stale". This is the easiest gate to miss, because nothing in the tool file hints that a generated artifact mirrors it.generate-docs— required whenever block metadata changes (bgColor,name,description, operations, outputs). Regenerates the integration.mdx,packages/deployment-config/src/integrations.json, and the docs copy ofcomponents/icons.tsx.deployment-config:generate— required for OAuth or service-account changes. Regenerates provider-ID facts from the canonical OAuth registry and integration catalog; special deployment requirements remain handwritten policy.integration-catalog:check— loads the executable block registry, derives visible integration deployment fields, and compares them with the committed catalog. It catches missing/unexpected entries and stale auth/service IDs without loading the executable registry in client code.docs:check— check mode ofgenerate-docs.ts: renders every generated docs artifact in memory and fails listing any committed file that differs. Runs in CI viacheck:audits.
Always diff the regen output before committing — but commit all of it. These generators rewrite
every file they own, so they also true up drift that accumulated on the base branch (pages whose
source changed without a regen). That catch-up is correct output, not a regression: docs:check
fails CI on any page left stale, so reverting swept-in hunks with git checkout -- reintroduces the
failure. Review the diff to confirm each hunk is explained by a real source change (yours or an
upstream PR that skipped regeneration), and investigate anything that looks like content loss — a
page losing a section usually means its source block moved or a generator input broke, not that the
hunk should be reverted.
If an icon changed, apps/sim/components/icons.tsx is the source of truth and apps/docs/components/icons.tsx is its generated mirror — they must end up byte-identical for that component.
Validation Output
After fixing, confirm:
bun run lintpasses with no fixes needed- TypeScript compiles clean (no type errors) — check the error list is empty for the files you touched; pre-existing unrelated errors in a worktree usually mean workspace packages resolve to the main checkout
- The integration's tests pass, and any test you added actually fails without its fix (revert it once and watch it go red)
- Derived artifacts regenerated and their diffs reviewed (see above)
bun run integration-catalog:checkpassesbun run docs:checkpasses- For OAuth or service-account changes,
bun run deployment-config:checkpasses - For OAuth or service-account changes,
bun run --cwd apps/sim test lib/integrations/availability.server.test.tspasses - Re-read all modified files to verify fixes are correct
- Any remaining unknown response schemas were explicitly reported to the user instead of guessed
Checklist Summary
- Read ALL tool files, block, types, index, and registries
- Pulled and read official API documentation
- Validated every tool's ID, params, request, response, outputs, and types against API docs
- Validated block ↔ tool alignment (every tool param has a subBlock, every condition is correct)
- Validated advanced mode on optional/rarely-used fields
- Validated wandConfig on timestamps and complex inputs
- Validated tools.config mapping, tool selector, and type coercions
- Validated block outputs match what tools return, with typed JSON where possible
- Validated OAuth scopes use centralized utilities (getScopesForService, getCanonicalScopesForProvider) — no hardcoded arrays
- Validated scope descriptions exist in
SCOPE_DESCRIPTIONSwithinlib/oauth/utils.tsfor all scopes - Validated OAuth
serviceIdresolves to the intendedOAUTH_CLIENT_CAPABILITIESentry and all capability fields exist in the env schema - Validated service-account projection and deployment requirement against the canonical OAuth service config
- Regenerated deployment config when block/OAuth metadata changed and ran both catalog checks
- Validated pagination consistency across tools and block
- Validated memory load safety using
.agents/skills/memory-load-check/SKILL.mdwhen tools list/search/download/import/export/batch data - Validated error handling (error checks, meaningful messages)
- Validated registry entries (tools and block, alphabetical, correct imports)
- Validated model-visible/opaque inputs and Sim-durable/internal-execution provenance at their owning boundaries
- Confirmed legacy persisted data keeps working and tracked invalid provenance fails closed
- Confirmed ordinary third-party results remain unchanged absent activated Sim provenance
- Validated
{Service}BlockMetaexported with at least 7 templates - Reported all issues grouped by severity
- Fixed all critical and warning issues
- Ran
bun run tool-metadata:generateif any tool outputs/params changed, and confirmedbun run tool-metadata:checkpasses - Ran
bun run generate-docsif any block metadata changed, and committed the full generated diff — including stale-page catch-up for other integrations (bun run docs:checkfails CI on reverted generator output) - Ran
bun run lintafter fixes - Verified TypeScript compiles clean
- Verified added tests fail without their fix
Version History
- ceda457 Current 2026-08-20 15:29


