Skip to content

Commit 71ecf6c

Browse files
authored
improvement(x): align OAuth scopes, add scope descriptions, and set optional fields to advanced mode (#3372)
* improvement(x): align OAuth scopes, add scope descriptions, and set optional fields to advanced mode * improvement(skills): add typed JSON outputs guidance to add-tools, add-block, and add-integration skills * improvement(skills): add final validation steps to add-tools, add-block, and add-integration skills * fix(skills): correct misleading JSON array comment in wandConfig example * feat(skills): add validate-integration skill for auditing tools, blocks, and registry against API docs * improvement(skills): expand validate-integration with full block-tool alignment, OAuth scopes, pagination, and error handling checks
1 parent e9e5ba2 commit 71ecf6c

File tree

7 files changed

+512
-9
lines changed

7 files changed

+512
-9
lines changed

.claude/commands/add-block.md

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,41 @@ outputs: {
532532
}
533533
```
534534

535+
### Typed JSON Outputs
536+
537+
When using `type: 'json'` and you know the object shape in advance, **describe the inner fields in the description** so downstream blocks know what properties are available. For well-known, stable objects, use nested output definitions instead:
538+
539+
```typescript
540+
outputs: {
541+
// BAD: Opaque json with no info about what's inside
542+
plan: { type: 'json', description: 'Zone plan information' },
543+
544+
// GOOD: Describe the known fields in the description
545+
plan: {
546+
type: 'json',
547+
description: 'Zone plan information (id, name, price, currency, frequency, is_subscribed)',
548+
},
549+
550+
// BEST: Use nested output definition when the shape is stable and well-known
551+
plan: {
552+
id: { type: 'string', description: 'Plan identifier' },
553+
name: { type: 'string', description: 'Plan name' },
554+
price: { type: 'number', description: 'Plan price' },
555+
currency: { type: 'string', description: 'Price currency' },
556+
},
557+
}
558+
```
559+
560+
Use the nested pattern when:
561+
- The object has a small, stable set of fields (< 10)
562+
- Downstream blocks will commonly access specific properties
563+
- The API response shape is well-documented and unlikely to change
564+
565+
Use `type: 'json'` with a descriptive string when:
566+
- The object has many fields or a dynamic shape
567+
- It represents a list/array of items
568+
- The shape varies by operation
569+
535570
## V2 Block Pattern
536571

537572
When creating V2 blocks (alongside legacy V1):
@@ -695,16 +730,87 @@ Please provide the SVG and I'll convert it to a React component.
695730
You can usually find this in the service's brand/press kit page, or copy it from their website.
696731
```
697732

733+
## Advanced Mode for Optional Fields
734+
735+
Optional fields that are rarely used should be set to `mode: 'advanced'` so they don't clutter the basic UI. This includes:
736+
- Pagination tokens
737+
- Time range filters (start/end time)
738+
- Sort order options
739+
- Reply settings
740+
- Rarely used IDs (e.g., reply-to tweet ID, quote tweet ID)
741+
- Max results / limits
742+
743+
```typescript
744+
{
745+
id: 'startTime',
746+
title: 'Start Time',
747+
type: 'short-input',
748+
placeholder: 'ISO 8601 timestamp',
749+
condition: { field: 'operation', value: ['search', 'list'] },
750+
mode: 'advanced', // Rarely used, hide from basic view
751+
}
752+
```
753+
754+
## WandConfig for Complex Inputs
755+
756+
Use `wandConfig` for fields that are hard to fill out manually, such as timestamps, comma-separated lists, and complex query strings. This gives users an AI-assisted input experience.
757+
758+
```typescript
759+
// Timestamps - use generationType: 'timestamp' to inject current date context
760+
{
761+
id: 'startTime',
762+
title: 'Start Time',
763+
type: 'short-input',
764+
mode: 'advanced',
765+
wandConfig: {
766+
enabled: true,
767+
prompt: 'Generate an ISO 8601 timestamp based on the user description. Return ONLY the timestamp string.',
768+
generationType: 'timestamp',
769+
},
770+
}
771+
772+
// Comma-separated lists - simple prompt without generationType
773+
{
774+
id: 'mediaIds',
775+
title: 'Media IDs',
776+
type: 'short-input',
777+
mode: 'advanced',
778+
wandConfig: {
779+
enabled: true,
780+
prompt: 'Generate a comma-separated list of media IDs. Return ONLY the comma-separated values.',
781+
},
782+
}
783+
```
784+
785+
## Naming Convention
786+
787+
All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MUST use `snake_case` (e.g., `x_create_tweet`, `slack_send_message`). Never use camelCase or PascalCase.
788+
698789
## Checklist Before Finishing
699790

700791
- [ ] All subBlocks have `id`, `title` (except switch), and `type`
701792
- [ ] Conditions use correct syntax (field, value, not, and)
702793
- [ ] DependsOn set for fields that need other values
703794
- [ ] Required fields marked correctly (boolean or condition)
704795
- [ ] OAuth inputs have correct `serviceId`
705-
- [ ] Tools.access lists all tool IDs
706-
- [ ] Tools.config.tool returns correct tool ID
796+
- [ ] Tools.access lists all tool IDs (snake_case)
797+
- [ ] Tools.config.tool returns correct tool ID (snake_case)
707798
- [ ] Outputs match tool outputs
708799
- [ ] Block registered in registry.ts
709800
- [ ] If icon missing: asked user to provide SVG
710801
- [ ] If triggers exist: `triggers` config set, trigger subBlocks spread
802+
- [ ] Optional/rarely-used fields set to `mode: 'advanced'`
803+
- [ ] Timestamps and complex inputs have `wandConfig` enabled
804+
805+
## Final Validation (Required)
806+
807+
After creating the block, you MUST validate it against every tool it references:
808+
809+
1. **Read every tool definition** that appears in `tools.access` — do not skip any
810+
2. **For each tool, verify the block has correct:**
811+
- SubBlock inputs that cover all required tool params (with correct `condition` to show for that operation)
812+
- SubBlock input types that match the tool param types (e.g., dropdown for enums, short-input for strings)
813+
- `tools.config.params` correctly maps subBlock IDs to tool param names (if they differ)
814+
- Type coercions in `tools.config.params` for any params that need conversion (Number(), Boolean(), JSON.parse())
815+
3. **Verify block outputs** cover the key fields returned by all tools
816+
4. **Verify conditions** — each subBlock should only show for the operations that actually use it

.claude/commands/add-integration.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const {service}{Action}Tool: ToolConfig<Params, Response> = {
102102
- Always use `?? []` for optional array fields
103103
- Set `optional: true` for outputs that may not exist
104104
- Never output raw JSON dumps - extract meaningful fields
105+
- 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
105106

106107
## Step 3: Create Block
107108

@@ -436,6 +437,12 @@ If creating V2 versions (API-aligned outputs):
436437
- [ ] Ran `bun run scripts/generate-docs.ts`
437438
- [ ] Verified docs file created
438439

440+
### Final Validation (Required)
441+
- [ ] Read every tool file and cross-referenced inputs/outputs against the API docs
442+
- [ ] Verified block subBlocks cover all required tool params with correct conditions
443+
- [ ] Verified block outputs match what the tools actually return
444+
- [ ] Verified `tools.config.params` correctly maps and coerces all param types
445+
439446
## Example Command
440447

441448
When the user asks to add an integration:
@@ -685,13 +692,40 @@ return NextResponse.json({
685692
| `isUserFile` | `@/lib/core/utils/user-file` | Type guard for UserFile objects |
686693
| `FileInputSchema` | `@/lib/uploads/utils/file-schemas` | Zod schema for file validation |
687694

695+
### Advanced Mode for Optional Fields
696+
697+
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.
698+
699+
### WandConfig for Complex Inputs
700+
701+
Use `wandConfig` for fields that are hard to fill out manually:
702+
- **Timestamps**: Use `generationType: 'timestamp'` to inject current date context into the AI prompt
703+
- **JSON arrays**: Use `generationType: 'json-object'` for structured data
704+
- **Complex queries**: Use a descriptive prompt explaining the expected format
705+
706+
```typescript
707+
{
708+
id: 'startTime',
709+
title: 'Start Time',
710+
type: 'short-input',
711+
mode: 'advanced',
712+
wandConfig: {
713+
enabled: true,
714+
prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.',
715+
generationType: 'timestamp',
716+
},
717+
}
718+
```
719+
688720
### Common Gotchas
689721

690722
1. **OAuth serviceId must match** - The `serviceId` in oauth-input must match the OAuth provider configuration
691-
2. **Tool IDs are snake_case** - `stripe_create_payment`, not `stripeCreatePayment`
723+
2. **All tool IDs MUST be snake_case** - `stripe_create_payment`, not `stripeCreatePayment`. This applies to tool `id` fields, registry keys, `tools.access` arrays, and `tools.config.tool` return values
692724
3. **Block type is snake_case** - `type: 'stripe'`, not `type: 'Stripe'`
693725
4. **Alphabetical ordering** - Keep imports and registry entries alphabetically sorted
694726
5. **Required can be conditional** - Use `required: { field: 'op', value: 'create' }` instead of always true
695727
6. **DependsOn clears options** - When a dependency changes, selector options are refetched
696728
7. **Never pass Buffer directly to fetch** - Convert to `new Uint8Array(buffer)` for TypeScript compatibility
697729
8. **Always handle legacy file params** - Keep hidden `fileContent` params for backwards compatibility
730+
9. **Optional fields use advanced mode** - Set `mode: 'advanced'` on rarely-used optional fields
731+
10. **Complex inputs need wandConfig** - Timestamps, JSON arrays, and other hard-to-type values should have `wandConfig` enabled

.claude/commands/add-tools.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,18 @@ closedAt: {
147147
},
148148
```
149149

150-
### Nested Properties
151-
For complex outputs, define nested structure:
150+
### Typed JSON Outputs
151+
152+
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:
153+
152154
```typescript
155+
// BAD: Opaque json with no info about what's inside
156+
metadata: {
157+
type: 'json',
158+
description: 'Response metadata',
159+
},
160+
161+
// GOOD: Define the known properties
153162
metadata: {
154163
type: 'json',
155164
description: 'Response metadata',
@@ -159,7 +168,10 @@ metadata: {
159168
count: { type: 'number', description: 'Total count' },
160169
},
161170
},
171+
```
162172

173+
For arrays of objects, define the item structure:
174+
```typescript
163175
items: {
164176
type: 'array',
165177
description: 'List of items',
@@ -173,6 +185,8 @@ items: {
173185
},
174186
```
175187

188+
Only use bare `type: 'json'` without `properties` when the shape is truly dynamic or unknown.
189+
176190
## Critical Rules for transformResponse
177191

178192
### Handle Nullable Fields
@@ -272,13 +286,36 @@ If creating V2 tools (API-aligned outputs), use `_v2` suffix:
272286
- Version: `'2.0.0'`
273287
- Outputs: Flat, API-aligned (no content/metadata wrapper)
274288

289+
## Naming Convention
290+
291+
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.
292+
275293
## Checklist Before Finishing
276294

295+
- [ ] All tool IDs use snake_case
277296
- [ ] All params have explicit `required: true` or `required: false`
278297
- [ ] All params have appropriate `visibility`
279298
- [ ] All nullable response fields use `?? null`
280299
- [ ] All optional outputs have `optional: true`
281300
- [ ] No raw JSON dumps in outputs
282301
- [ ] Types file has all interfaces
283302
- [ ] Index.ts exports all tools
284-
- [ ] Tool IDs use snake_case
303+
304+
## Final Validation (Required)
305+
306+
After creating all tools, you MUST validate every tool before finishing:
307+
308+
1. **Read every tool file** you created — do not skip any
309+
2. **Cross-reference with the API docs** to verify:
310+
- All required params are marked `required: true`
311+
- All optional params are marked `required: false`
312+
- Param types match the API (string, number, boolean, json)
313+
- Request URL, method, headers, and body match the API spec
314+
- `transformResponse` extracts the correct fields from the API response
315+
- All output fields match what the API actually returns
316+
- No fields are missing from outputs that the API provides
317+
- No extra fields are defined in outputs that the API doesn't return
318+
3. **Verify consistency** across tools:
319+
- Shared types in `types.ts` match all tools that use them
320+
- Tool IDs in the barrel export match the tool file definitions
321+
- Error handling is consistent (logger imports, error checks)

0 commit comments

Comments
 (0)