You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
serviceId: '{service}', // Must match OAuth provider
119
+
serviceId: '{service}', // Must match OAuth provider service key
120
+
requiredScopes: getScopesForService('{service}'), // Import from @/lib/oauth/utils
119
121
placeholder: 'Select account',
120
122
required: true,
121
123
}
122
124
```
123
125
126
+
**Scopes:** Always use `getScopesForService(serviceId)` from `@/lib/oauth/utils` for `requiredScopes`. Never hardcode scope arrays — the single source of truth is `OAUTH_PROVIDERS` in `lib/oauth/oauth.ts`.
127
+
128
+
**Scope descriptions:** When adding a new OAuth provider, also add human-readable descriptions for all scopes in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`.
129
+
124
130
### Selectors (with dynamic options)
125
131
```typescript
126
132
// Channel selector (Slack, Discord, etc.)
@@ -532,6 +538,41 @@ outputs: {
532
538
}
533
539
```
534
540
541
+
### Typed JSON Outputs
542
+
543
+
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:
544
+
545
+
```typescript
546
+
outputs: {
547
+
// BAD: Opaque json with no info about what's inside
548
+
plan: { type: 'json', description: 'Zone plan information' },
549
+
550
+
// GOOD: Describe the known fields in the description
551
+
plan: {
552
+
type: 'json',
553
+
description: 'Zone plan information (id, name, price, currency, frequency, is_subscribed)',
554
+
},
555
+
556
+
// BEST: Use nested output definition when the shape is stable and well-known
mode: 'advanced', // Rarely used, hide from basic view
759
+
}
760
+
```
761
+
762
+
## WandConfig for Complex Inputs
763
+
764
+
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.
765
+
766
+
```typescript
767
+
// Timestamps - use generationType: 'timestamp' to inject current date context
768
+
{
769
+
id: 'startTime',
770
+
title: 'Start Time',
771
+
type: 'short-input',
772
+
mode: 'advanced',
773
+
wandConfig: {
774
+
enabled: true,
775
+
prompt: 'Generate an ISO 8601 timestamp based on the user description. Return ONLY the timestamp string.',
776
+
generationType: 'timestamp',
777
+
},
778
+
}
779
+
780
+
// Comma-separated lists - simple prompt without generationType
781
+
{
782
+
id: 'mediaIds',
783
+
title: 'Media IDs',
784
+
type: 'short-input',
785
+
mode: 'advanced',
786
+
wandConfig: {
787
+
enabled: true,
788
+
prompt: 'Generate a comma-separated list of media IDs. Return ONLY the comma-separated values.',
789
+
},
790
+
}
791
+
```
792
+
793
+
## Naming Convention
794
+
795
+
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.
796
+
698
797
## Checklist Before Finishing
699
798
700
799
-[ ] All subBlocks have `id`, `title` (except switch), and `type`
701
800
-[ ] Conditions use correct syntax (field, value, not, and)
702
801
-[ ] DependsOn set for fields that need other values
703
802
-[ ] Required fields marked correctly (boolean or condition)
704
-
-[ ] OAuth inputs have correct `serviceId`
705
-
-[ ] Tools.access lists all tool IDs
706
-
-[ ] Tools.config.tool returns correct tool ID
803
+
-[ ] OAuth inputs have correct `serviceId` and `requiredScopes: getScopesForService(serviceId)`
804
+
-[ ] Scope descriptions added to `SCOPE_DESCRIPTIONS` in `lib/oauth/utils.ts` for any new scopes
805
+
-[ ] Tools.access lists all tool IDs (snake_case)
806
+
-[ ] Tools.config.tool returns correct tool ID (snake_case)
- Set `optional: true` for outputs that may not exist
104
104
- 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
@@ -408,7 +411,7 @@ If creating V2 versions (API-aligned outputs):
408
411
### Block
409
412
-[ ] Created `blocks/blocks/{service}.ts`
410
413
-[ ] Defined operation dropdown with all operations
411
-
-[ ] Added credential field (oauth-input or short-input)
414
+
-[ ] Added credential field with `requiredScopes: getScopesForService('{service}')`
412
415
-[ ] Added conditional fields per operation
413
416
-[ ] Set up dependsOn for cascading selectors
414
417
-[ ] Configured tools.access with all tool IDs
@@ -418,6 +421,12 @@ If creating V2 versions (API-aligned outputs):
418
421
-[ ] If triggers: set `triggers.enabled` and `triggers.available`
419
422
-[ ] If triggers: spread trigger subBlocks with `getTrigger()`
420
423
424
+
### OAuth Scopes (if OAuth service)
425
+
-[ ] Defined scopes in `lib/oauth/oauth.ts` under `OAUTH_PROVIDERS`
426
+
-[ ] Added scope descriptions in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`
427
+
-[ ] Used `getCanonicalScopesForProvider()` in `auth.ts` (never hardcode)
428
+
-[ ] Used `getScopesForService()` in block `requiredScopes` (never hardcode)
429
+
421
430
### Icon
422
431
-[ ] Asked user to provide SVG
423
432
-[ ] Added icon to `components/icons.tsx`
@@ -436,6 +445,12 @@ If creating V2 versions (API-aligned outputs):
436
445
-[ ] Ran `bun run scripts/generate-docs.ts`
437
446
-[ ] Verified docs file created
438
447
448
+
### Final Validation (Required)
449
+
-[ ] Read every tool file and cross-referenced inputs/outputs against the API docs
450
+
-[ ] Verified block subBlocks cover all required tool params with correct conditions
451
+
-[ ] Verified block outputs match what the tools actually return
452
+
-[ ] Verified `tools.config.params` correctly maps and coerces all param types
453
+
439
454
## Example Command
440
455
441
456
When the user asks to add an integration:
@@ -685,13 +700,61 @@ return NextResponse.json({
685
700
|`isUserFile`|`@/lib/core/utils/user-file`| Type guard for UserFile objects |
686
701
|`FileInputSchema`|`@/lib/uploads/utils/file-schemas`| Zod schema for file validation |
687
702
703
+
### Advanced Mode for Optional Fields
704
+
705
+
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.
706
+
707
+
### WandConfig for Complex Inputs
708
+
709
+
Use `wandConfig` for fields that are hard to fill out manually:
710
+
-**Timestamps**: Use `generationType: 'timestamp'` to inject current date context into the AI prompt
711
+
-**JSON arrays**: Use `generationType: 'json-object'` for structured data
712
+
-**Complex queries**: Use a descriptive prompt explaining the expected format
713
+
714
+
```typescript
715
+
{
716
+
id: 'startTime',
717
+
title: 'Start Time',
718
+
type: 'short-input',
719
+
mode: 'advanced',
720
+
wandConfig: {
721
+
enabled: true,
722
+
prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.',
723
+
generationType: 'timestamp',
724
+
},
725
+
}
726
+
```
727
+
728
+
### OAuth Scopes (Centralized System)
729
+
730
+
Scopes are maintained in a single source of truth and reused everywhere:
731
+
732
+
1.**Define scopes** in `lib/oauth/oauth.ts` under `OAUTH_PROVIDERS[provider].services[service].scopes`
733
+
2.**Add descriptions** in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts` for the OAuth modal UI
734
+
3.**Reference in auth.ts** using `getCanonicalScopesForProvider(providerId)` from `@/lib/oauth/utils`
735
+
4.**Reference in blocks** using `getScopesForService(serviceId)` from `@/lib/oauth/utils`
736
+
737
+
**Never hardcode scope arrays** in `auth.ts` or block `requiredScopes`. Always import from the centralized source.
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`
750
+
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
692
751
3.**Block type is snake_case** - `type: 'stripe'`, not `type: 'Stripe'`
693
752
4.**Alphabetical ordering** - Keep imports and registry entries alphabetically sorted
694
753
5.**Required can be conditional** - Use `required: { field: 'op', value: 'create' }` instead of always true
695
754
6.**DependsOn clears options** - When a dependency changes, selector options are refetched
696
755
7.**Never pass Buffer directly to fetch** - Convert to `new Uint8Array(buffer)` for TypeScript compatibility
Copy file name to clipboardExpand all lines: .claude/commands/add-tools.md
+40-3Lines changed: 40 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -147,9 +147,18 @@ closedAt: {
147
147
},
148
148
```
149
149
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
+
152
154
```typescript
155
+
// BAD: Opaque json with no info about what's inside
Only use bare `type: 'json'` without `properties` when the shape is truly dynamic or unknown.
189
+
176
190
## Critical Rules for transformResponse
177
191
178
192
### Handle Nullable Fields
@@ -272,13 +286,36 @@ If creating V2 tools (API-aligned outputs), use `_v2` suffix:
272
286
- Version: `'2.0.0'`
273
287
- Outputs: Flat, API-aligned (no content/metadata wrapper)
274
288
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
+
275
293
## Checklist Before Finishing
276
294
295
+
-[ ] All tool IDs use snake_case
277
296
-[ ] All params have explicit `required: true` or `required: false`
278
297
-[ ] All params have appropriate `visibility`
279
298
-[ ] All nullable response fields use `?? null`
280
299
-[ ] All optional outputs have `optional: true`
281
300
-[ ] No raw JSON dumps in outputs
282
301
-[ ] Types file has all interfaces
283
302
-[ ] 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 (error checks, meaningful messages)
0 commit comments