Claude-skill-registry integration-generator
Generate new OAuth integration providers for Dafthunk with backend providers, type definitions, frontend configurations, and integration nodes
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/integration-generator" ~/.claude/skills/majiayu000-claude-skill-registry-integration-generator && rm -rf "$T"
skills/data/integration-generator/SKILL.mdIntegration Generator
Generate OAuth integration providers for Dafthunk: research provider APIs, create backend OAuth provider, add type definitions, configure frontend, and optionally create integration nodes.
Step 1: Research Provider and Register OAuth App
Research the OAuth flow:
- Use WebSearch/WebFetch to find official OAuth documentation
- Look for "OAuth 2.0", "Developer Apps", or "API Authentication" sections
- Identify authorization, token, and user info endpoints
- Note required scopes and token format (refresh support, expiration)
- Check token response structure and user info fields
Register OAuth application:
- Create a developer account on the provider's platform
- Register a new OAuth application/client
- Set redirect URI to:
(dev)http://localhost:3001/oauth/{provider-id}/connect - Note the generated Client ID and Client Secret
- Configure required scopes/permissions
Present for confirmation:
Based on {Provider} OAuth documentation: **Provider**: {Provider Name} **ID**: `{provider-id}` (kebab-case) **Endpoints**: - Authorization: https://... - Token: https://... - User Info: https://... **Scopes**: scope1, scope2 **Token Refresh**: Supported/Not supported **Token Expiration**: {expires_in seconds} / Never expires **OAuth App Registered**: - Client ID: {id} - Client Secret: {secret} - Redirect URI: http://localhost:3001/oauth/{provider-id}/connect Ready to implement?
Step 2: Add Backend Types
File:
apps/api/src/oauth/types.ts
/** * {Provider} OAuth token response */ export interface {Provider}Token { access_token: string; refresh_token?: string; expires_in?: number; token_type: string; scope?: string; } /** * {Provider} user information */ export interface {Provider}User { id: string | number; email?: string; name?: string; // Add provider-specific fields }
Step 3: Create Provider Class
File:
apps/api/src/oauth/providers/{Provider}Provider.ts
import { OAuthProvider } from "../OAuthProvider"; import type { {Provider}Token, {Provider}User } from "../types"; export class {Provider}Provider extends OAuthProvider<{Provider}Token, {Provider}User> { readonly name = "{provider-id}"; readonly displayName = "{Provider Name}"; readonly authorizationEndpoint = "https://..."; readonly tokenEndpoint = "https://..."; readonly userInfoEndpoint = "https://..."; readonly scopes = ["scope1", "scope2"]; readonly refreshEnabled = true; // false if no refresh support readonly refreshEndpoint = "https://..."; // only if refreshEnabled protected formatIntegrationName(user: {Provider}User): string { return user.name || user.email || "User"; } protected formatUserMetadata(user: {Provider}User): Record<string, any> { return { userId: user.id, email: user.email, name: user.name, }; } protected extractAccessToken(token: {Provider}Token): string { return token.access_token; } protected extractRefreshToken(token: {Provider}Token): string | undefined { return token.refresh_token; } protected extractExpiresAt(token: {Provider}Token): Date | undefined { return token.expires_in ? new Date(Date.now() + token.expires_in * 1000) : undefined; } }
Step 4: Register Backend Provider
File:
apps/api/src/oauth/registry.ts
import { {Provider}Provider } from "./providers/{Provider}Provider"; const providers = { // ... existing providers "{provider-id}": new {Provider}Provider(), } as const;
Step 5: Update Shared Types
File:
packages/types/src/integration.ts
Add to
IntegrationProvider union and OAUTH_PROVIDERS array:
export type IntegrationProvider = | "existing-provider" | "{provider-id}" | "other-provider"; export const OAUTH_PROVIDERS: OAuthProvider[] = [ // ... existing providers { id: "{provider-id}", name: "{Provider Name}", description: "Connect your {Provider} account to...", supportsOAuth: true, oauthEndpoint: "/oauth/{provider-id}/connect", }, ];
Step 6: Create Frontend Provider
File:
apps/web/src/integrations/providers/{provider-id}.ts
import {IconName} from "lucide-react/icons/{icon-name}"; import type { ProviderConfig } from "../types"; export const {provider}Provider: ProviderConfig = { id: "{provider-id}", name: "{Provider Name}", description: "Connect your {Provider} account...", icon: {IconName}, supportsOAuth: true, oauthEndpoint: "/oauth/{provider-id}/connect", successMessage: "{Provider} integration connected successfully", };
Step 7: Register Frontend Provider
File:
apps/web/src/integrations/providers/index.ts
import { {provider}Provider } from "./{provider-id}"; export const PROVIDER_REGISTRY: Record<IntegrationProvider, ProviderConfig> = { // ... existing providers "{provider-id}": {provider}Provider, };
Step 8: Configure Environment
File:
apps/api/.dev.vars
INTEGRATION_{PROVIDER}_CLIENT_ID=your_client_id INTEGRATION_{PROVIDER}_CLIENT_SECRET=your_client_secret
Step 9: Update Integration Routes
File:
apps/api/src/routes/integrations.ts
Add provider availability check:
if ( env.INTEGRATION_{PROVIDER}_CLIENT_ID && env.INTEGRATION_{PROVIDER}_CLIENT_SECRET ) { availableProviders.push("{provider-id}"); }
Step 10: Test OAuth Flow
pnpm typecheck pnpm dev
Navigate to integrations page, click "Connect {Provider}", complete OAuth flow, verify integration appears in list.
Optional: Create Integration Nodes
File:
apps/api/src/nodes/{provider-id}/{action}-{provider-id}-node.ts
import { NodeExecution, NodeType } from "@dafthunk/types"; import { ExecutableNode, NodeContext } from "../types"; export class {Action}{Provider}Node extends ExecutableNode { public static readonly nodeType: NodeType = { id: "{action}-{provider-id}", name: "{Action} ({Provider})", type: "{action}-{provider-id}", description: "{Action description}", tags: ["{Category}", "{Provider}"], icon: "icon-name", inputs: [ { name: "integrationId", type: "string", description: "{Provider} integration to use", hidden: true, required: true, }, // Add action-specific inputs ], outputs: [ // Add action-specific outputs ], }; async execute(context: NodeContext): Promise<NodeExecution> { try { const { integrationId } = context.inputs; // Get integration (auto-refreshes token) const integration = await context.getIntegration(integrationId); // Call provider API const response = await fetch("https://api.{provider}.com/...", { method: "POST", headers: { Authorization: `Bearer ${integration.token}`, "Content-Type": "application/json", }, body: JSON.stringify({ /* request */ }), }); if (!response.ok) { return this.createErrorResult(`API call failed: ${await response.text()}`); } const data = await response.json(); return this.createSuccessResult({ /* outputs */ }); } catch (error) { return this.createErrorResult( error instanceof Error ? error.message : "Unknown error" ); } } }
Register in
apps/api/src/nodes/cloudflare-node-registry.ts:
import { {Action}{Provider}Node } from "./{provider-id}/{action}-{provider-id}-node"; this.registerImplementation({Action}{Provider}Node);
Optional: Create Frontend Widget
File:
apps/web/src/components/workflow/widgets/integration-selector.tsx
export const {provider}Widget = createIntegrationWidget("{provider-id}", [ "{action1}-{provider-id}", "{action2}-{provider-id}", ]);
Register in
apps/web/src/components/workflow/widgets/index.ts:
import { {provider}Widget } from "./integration-selector"; export const widgetRegistry = { ...{provider}Widget, };
Common Patterns
HTTP Basic Auth (e.g., Reddit):
protected getTokenHeaders(clientId: string, clientSecret: string): Record<string, string> { const credentials = btoa(`${clientId}:${clientSecret}`); return { Authorization: `Basic ${credentials}`, "Content-Type": "application/x-www-form-urlencoded", }; }
No Token Expiration (e.g., GitHub):
readonly refreshEnabled = false; protected extractExpiresAt(token: {Provider}Token): Date | undefined { return undefined; }
Custom Auth Parameters (e.g., Google):
protected customizeAuthUrl(url: URL): void { url.searchParams.set("access_type", "offline"); }
Summary
After completion, list:
- Files created (backend types, provider, frontend config)
- Environment variables required
- OAuth flow test results
- Optional nodes/widgets created