Skip to content

Commit 05c4bc1

Browse files
waleedlatif1claude
andcommitted
fix(mcp): tighten OAuth probe signal and clear stale popup interval
- probe: only classify as OAuth on resource_metadata or scope params. Bare `Bearer error="invalid_token"` is generic and used by API-key servers, so it must not auto-flip the auth type to OAuth. - popup hook: clear any existing close-watcher interval before overwriting when startOauthForServer is invoked twice for the same serverId. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5be7f79 commit 05c4bc1

10 files changed

Lines changed: 2314 additions & 1 deletion

File tree

.agents/skills/source-command-add-connector/SKILL.md

Lines changed: 534 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
---
2+
name: "source-command-add-tools"
3+
description: "Create tool configurations for a Sim integration by reading API docs"
4+
---
5+
6+
# source-command-add-tools
7+
8+
Use this skill when the user asks to run the migrated source command `add-tools`.
9+
10+
## Command Template
11+
12+
# Add Tools Skill
13+
14+
You are an expert at creating tool configurations for Sim integrations. Your job is to read API documentation and create properly structured tool files.
15+
16+
## Your Task
17+
18+
When the user asks you to create tools for a service:
19+
1. Use Context7 or WebFetch to read the service's API documentation
20+
2. Create the tools directory structure
21+
3. Generate properly typed tool configurations
22+
23+
## Directory Structure
24+
25+
Create files in `apps/sim/tools/{service}/`:
26+
```
27+
tools/{service}/
28+
├── index.ts # Barrel export
29+
├── types.ts # Parameter & response types
30+
└── {action}.ts # Individual tool files (one per operation)
31+
```
32+
33+
## Tool Configuration Structure
34+
35+
Every tool MUST follow this exact structure:
36+
37+
```typescript
38+
import type { {ServiceName}{Action}Params } from '@/tools/{service}/types'
39+
import type { ToolConfig } from '@/tools/types'
40+
41+
interface {ServiceName}{Action}Response {
42+
success: boolean
43+
output: {
44+
// Define output structure here
45+
}
46+
}
47+
48+
export const {serviceName}{Action}Tool: ToolConfig<
49+
{ServiceName}{Action}Params,
50+
{ServiceName}{Action}Response
51+
> = {
52+
id: '{service}_{action}', // snake_case, matches tool name
53+
name: '{Service} {Action}', // Human readable
54+
description: 'Brief description', // One sentence
55+
version: '1.0.0',
56+
57+
// OAuth config (if service uses OAuth)
58+
oauth: {
59+
required: true,
60+
provider: '{service}', // Must match OAuth provider ID
61+
},
62+
63+
params: {
64+
// Hidden params (system-injected, only use hidden for oauth accessToken)
65+
accessToken: {
66+
type: 'string',
67+
required: true,
68+
visibility: 'hidden',
69+
description: 'OAuth access token',
70+
},
71+
// User-only params (credentials, api key, IDs user must provide)
72+
someId: {
73+
type: 'string',
74+
required: true,
75+
visibility: 'user-only',
76+
description: 'The ID of the resource',
77+
},
78+
// User-or-LLM params (everything else, can be provided by user OR computed by LLM)
79+
query: {
80+
type: 'string',
81+
required: false, // Use false for optional
82+
visibility: 'user-or-llm',
83+
description: 'Search query',
84+
},
85+
},
86+
87+
request: {
88+
url: (params) => `https://api.service.com/v1/resource/${params.id}`,
89+
method: 'POST',
90+
headers: (params) => ({
91+
Authorization: `Bearer ${params.accessToken}`,
92+
'Content-Type': 'application/json',
93+
}),
94+
body: (params) => ({
95+
// Request body - only for POST/PUT/PATCH
96+
// Trim ID fields to prevent copy-paste whitespace errors:
97+
// userId: params.userId?.trim(),
98+
}),
99+
},
100+
101+
transformResponse: async (response: Response) => {
102+
const data = await response.json()
103+
return {
104+
success: true,
105+
output: {
106+
// Map API response to output
107+
// Use ?? null for nullable fields
108+
// Use ?? [] for optional arrays
109+
},
110+
}
111+
},
112+
113+
outputs: {
114+
// Define each output field
115+
},
116+
}
117+
```
118+
119+
## Critical Rules for Parameters
120+
121+
### Visibility Options
122+
- `'hidden'` - System-injected (OAuth tokens, internal params). User never sees.
123+
- `'user-only'` - User must provide (credentials, api keys, account-specific IDs)
124+
- `'user-or-llm'` - User provides OR LLM can compute (search queries, content, filters, most fall into this category)
125+
126+
### Parameter Types
127+
- `'string'` - Text values
128+
- `'number'` - Numeric values
129+
- `'boolean'` - True/false
130+
- `'json'` - Complex objects (NOT 'object', use 'json')
131+
- `'file'` - Single file
132+
- `'file[]'` - Multiple files
133+
134+
### Required vs Optional
135+
- Always explicitly set `required: true` or `required: false`
136+
- Optional params should have `required: false`
137+
138+
## Critical Rules for Outputs
139+
140+
### Output Types
141+
- `'string'`, `'number'`, `'boolean'` - Primitives
142+
- `'json'` - Complex objects (use this, NOT 'object')
143+
- `'array'` - Arrays with `items` property
144+
- `'object'` - Objects with `properties` property
145+
146+
### Optional Outputs
147+
Add `optional: true` for fields that may not exist in the response:
148+
```typescript
149+
closedAt: {
150+
type: 'string',
151+
description: 'When the issue was closed',
152+
optional: true,
153+
},
154+
```
155+
156+
### Typed JSON Outputs
157+
158+
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:
159+
160+
```typescript
161+
// BAD: Opaque json with no info about what's inside
162+
metadata: {
163+
type: 'json',
164+
description: 'Response metadata',
165+
},
166+
167+
// GOOD: Define the known properties
168+
metadata: {
169+
type: 'json',
170+
description: 'Response metadata',
171+
properties: {
172+
id: { type: 'string', description: 'Unique ID' },
173+
status: { type: 'string', description: 'Current status' },
174+
count: { type: 'number', description: 'Total count' },
175+
},
176+
},
177+
```
178+
179+
For arrays of objects, define the item structure:
180+
```typescript
181+
items: {
182+
type: 'array',
183+
description: 'List of items',
184+
items: {
185+
type: 'object',
186+
properties: {
187+
id: { type: 'string', description: 'Item ID' },
188+
name: { type: 'string', description: 'Item name' },
189+
},
190+
},
191+
},
192+
```
193+
194+
Only use bare `type: 'json'` without `properties` when the shape is truly dynamic or unknown.
195+
196+
## Critical Rules for transformResponse
197+
198+
### Handle Nullable Fields
199+
ALWAYS use `?? null` for fields that may be undefined:
200+
```typescript
201+
transformResponse: async (response: Response) => {
202+
const data = await response.json()
203+
return {
204+
success: true,
205+
output: {
206+
id: data.id,
207+
title: data.title,
208+
body: data.body ?? null, // May be undefined
209+
assignee: data.assignee ?? null, // May be undefined
210+
labels: data.labels ?? [], // Default to empty array
211+
closedAt: data.closed_at ?? null, // May be undefined
212+
},
213+
}
214+
}
215+
```
216+
217+
### Never Output Raw JSON Dumps
218+
DON'T do this:
219+
```typescript
220+
output: {
221+
data: data, // BAD - raw JSON dump
222+
}
223+
```
224+
225+
DO this instead - extract meaningful fields:
226+
```typescript
227+
output: {
228+
id: data.id,
229+
name: data.name,
230+
status: data.status,
231+
metadata: {
232+
createdAt: data.created_at,
233+
updatedAt: data.updated_at,
234+
},
235+
}
236+
```
237+
238+
## Types File Pattern
239+
240+
Create `types.ts` with interfaces for all params and responses:
241+
242+
```typescript
243+
import type { ToolResponse } from '@/tools/types'
244+
245+
// Parameter interfaces
246+
export interface {Service}{Action}Params {
247+
accessToken: string
248+
requiredField: string
249+
optionalField?: string
250+
}
251+
252+
// Response interfaces (extend ToolResponse)
253+
export interface {Service}{Action}Response extends ToolResponse {
254+
output: {
255+
field1: string
256+
field2: number
257+
optionalField?: string | null
258+
}
259+
}
260+
```
261+
262+
## Index.ts Barrel Export Pattern
263+
264+
```typescript
265+
// Export all tools
266+
export { serviceTool1 } from './{action1}'
267+
export { serviceTool2 } from './{action2}'
268+
269+
// Export types
270+
export * from './types'
271+
```
272+
273+
## Registering Tools
274+
275+
After creating tools, remind the user to:
276+
1. Import tools in `apps/sim/tools/registry.ts`
277+
2. Add to the `tools` object with snake_case keys:
278+
```typescript
279+
import { serviceActionTool } from '@/tools/{service}'
280+
281+
export const tools = {
282+
// ... existing tools ...
283+
{service}_{action}: serviceActionTool,
284+
}
285+
```
286+
287+
## V2 Tool Pattern
288+
289+
If creating V2 tools (API-aligned outputs), use `_v2` suffix:
290+
- Tool ID: `{service}_{action}_v2`
291+
- Variable name: `{action}V2Tool`
292+
- Version: `'2.0.0'`
293+
- Outputs: Flat, API-aligned (no content/metadata wrapper)
294+
295+
## Naming Convention
296+
297+
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.
298+
299+
## Checklist Before Finishing
300+
301+
- [ ] All tool IDs use snake_case
302+
- [ ] All params have explicit `required: true` or `required: false`
303+
- [ ] All params have appropriate `visibility`
304+
- [ ] All nullable response fields use `?? null`
305+
- [ ] All optional outputs have `optional: true`
306+
- [ ] No raw JSON dumps in outputs
307+
- [ ] Types file has all interfaces
308+
- [ ] Index.ts exports all tools
309+
310+
## Final Validation (Required)
311+
312+
After creating all tools, you MUST validate every tool before finishing:
313+
314+
1. **Read every tool file** you created — do not skip any
315+
2. **Cross-reference with the API docs** to verify:
316+
- All required params are marked `required: true`
317+
- All optional params are marked `required: false`
318+
- Param types match the API (string, number, boolean, json)
319+
- Request URL, method, headers, and body match the API spec
320+
- `transformResponse` extracts the correct fields from the API response
321+
- All output fields match what the API actually returns
322+
- No fields are missing from outputs that the API provides
323+
- No extra fields are defined in outputs that the API doesn't return
324+
3. **Verify consistency** across tools:
325+
- Shared types in `types.ts` match all tools that use them
326+
- Tool IDs in the barrel export match the tool file definitions
327+
- Error handling is consistent (error checks, meaningful messages)

0 commit comments

Comments
 (0)