Claude-skill-registry connection-schema-design
Connection profile and state schema design rules and core profile selection
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/connection-schema-design" ~/.claude/skills/majiayu000-claude-skill-registry-connection-schema-design && rm -rf "$T"
skills/data/connection-schema-design/SKILL.mdConnection Profile & State Design Rules
Rules for designing connectionProfile.yml and connectionState.yml schemas
🚨 CRITICAL RULES
Rule 1: Always Extend Core Profiles
NEVER create custom profiles from scratch - ALWAYS extend core profiles
# ❌ WRONG - Custom profile type: object properties: apiKey: type: string # ✅ CORRECT - Extend core profile $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml'
Rule 2: connectionState MUST Extend baseConnectionState
🚨 MANDATORY: connectionState.yml MUST extend baseConnectionState.yml for expiresIn
# ✅ CORRECT - Extends base with expiresIn allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml' - type: object properties: accessToken: type: string # ❌ WRONG - Missing baseConnectionState type: object properties: accessToken: type: string # Missing expiresIn!
WHY: Server uses expiresIn to schedule automatic token refresh cronjobs
Rule 3: expiresIn Must Be in Seconds
# ✅ CORRECT expiresIn: type: integer description: Token expiry time in SECONDS from now # ❌ WRONG expiresAt: type: string # Don't use expiresAt, convert to expiresIn
Calculation:
expiresIn = Math.floor((expiresAt - now) / 1000)
Core Profile Selection
Available Core Profiles
| Profile | When to Use | Contains |
|---|---|---|
| tokenProfile.yml | API Key, Bearer Token, PAT | token |
| oauthClientProfile.yml | OAuth2 Client Credentials | clientId, clientSecret |
| oauthTokenProfile.yml | OAuth2 with Refresh | clientId, clientSecret |
| basicConnection.yml | Username/Password, Email/Password | username, password |
Path:
./node_modules/@zerobias-org/types-core/schema/{profileName}.yml
Selection Decision Tree
@credential-manager provides authMethodType: IF authMethodType == "bearer-token" OR "api-key" → EXTEND tokenProfile.yml IF authMethodType == "oauth2-client-credentials" → EXTEND oauthClientProfile.yml IF authMethodType == "oauth2-authorization-code" → EXTEND oauthTokenProfile.yml IF authMethodType == "basic-auth" → EXTEND basicConnection.yml
Available Core States
| State | When to Use | Contains |
|---|---|---|
| tokenConnectionState.yml | Simple token auth | accessToken + expiresIn |
| oauthTokenState.yml | OAuth with refresh | accessToken + refreshToken + expiresIn |
State Selection
IF requiresRefresh == false → EXTEND tokenConnectionState.yml IF requiresRefresh == true → EXTEND oauthTokenState.yml
Common Patterns
Pattern 1: Simple API Token
# connectionProfile.yml $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml' # connectionState.yml $ref: './node_modules/@zerobias-org/types-core/schema/tokenConnectionState.yml'
Pattern 2: OAuth Client Credentials
# connectionProfile.yml $ref: './node_modules/@zerobias-org/types-core/schema/oauthClientProfile.yml' # connectionState.yml (no refresh) $ref: './node_modules/@zerobias-org/types-core/schema/tokenConnectionState.yml'
Pattern 3: OAuth with Refresh Token
# connectionProfile.yml $ref: './node_modules/@zerobias-org/types-core/schema/oauthTokenProfile.yml' # connectionState.yml $ref: './node_modules/@zerobias-org/types-core/schema/oauthTokenState.yml'
Pattern 4: Custom Fields (Extend Core)
# connectionProfile.yml - Add organizationId allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml' - type: object required: [organizationId] properties: organizationId: type: string description: Organization identifier for API access # connectionState.yml - Add extra state allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/tokenConnectionState.yml' - type: object properties: organizationName: type: string description: Cached organization name
Connection Profile Scope
What Belongs in connectionProfile
ONLY connection parameters - minimal set needed to CONNECT
# ✅ GOOD - Connection parameters - token / apiKey / clientId / clientSecret - baseUrl (if API has multiple environments) - organizationId (if required to authenticate) # ❌ BAD - Operation parameters - limit (pagination parameter) - projectId (operation scope, not connection scope) - fields (query parameter)
Rule: If you can connect WITHOUT it, it's an operation parameter (not profile)
Check for Additional Optional Parameters
Always review API docs for environment-specific optional parameters:
# Example: Service has optional region parameter allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml' - type: object properties: region: type: string description: Optional region for multi-region deployments enum: [us-east-1, eu-west-1, ap-southeast-1] # region is optional, but include it if API docs mention it
Don't omit parameters that some environments might need!
🚨 CRITICAL: Check Parent Schema First
Before adding ANY property, check what the parent schema provides:
# Check what tokenProfile.yml provides cat node_modules/@zerobias-org/types-core/schema/tokenProfile.yml # Check what basicConnection.yml provides cat node_modules/@zerobias-org/types-core/schema/basicConnection.yml # Check what baseConnectionState.yml provides cat node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml
Avoid Semantic Duplication
# ❌ WRONG - basicConnection already has uri allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/basicConnection.yml' - type: object properties: url: # Duplicate! basicConnection has uri type: string # ✅ CORRECT - Use parent's uri, or extend if truly different allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/basicConnection.yml' # uri is already provided by basicConnection
Semantic duplicates to avoid:
- url / uri / baseUrl
- token / accessToken
- username / user / userName
- password / pass / pwd
🚨 CRITICAL: ALWAYS Check Product Documentation
Before finalizing connectionProfile, check product docs for ALL auth parameters:
# Example: Missing mfaCode from product docs allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/basicConnection.yml' - type: object properties: mfaCode: # Found in product docs! type: string description: Multi-factor authentication code (optional)
Don't assume - verify:
- Read product documentation authentication section
- Look for optional parameters (region, environment, mfaCode, etc.)
- Include ALL mentioned parameters (even if optional)
- Don't omit parameters some environments might need
🚨 CRITICAL: Connection Scope MUST NOT Limit Operations
NEVER add organization/project/workspace IDs to connection:
# ❌ WRONG - Limits connection to single organization connectionState: allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml' - type: object properties: accessToken: type: string organizationId: # NO! Limits scope type: string # ✅ CORRECT - Connection works across all organizations connectionState: allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/baseConnectionState.yml' - type: object properties: accessToken: type: string # organizationId is operation parameter, NOT connection parameter
WHY: Connection should be reusable across all scopes. Use operation parameters for scope.
Scope identifiers belong in operation parameters, NOT connection:
- ❌ organizationId in connection
- ❌ workspaceId in connection
- ❌ projectId in connection
- ❌ teamId in connection
- ✅ These are operation parameters (use in API paths)
🚨 CRITICAL: Only Add Used Fields (for connectionState)
For connectionState - don't add fields "just in case" - only add what's actually USED:
# ❌ WRONG - identityId not used anywhere connectionState: properties: accessToken: string identityId: string # Where is this used? Nowhere! # ✅ CORRECT - Only fields that are actually used connectionState: properties: accessToken: string # If connect() doesn't return identityId, don't add it
Ask:
- Does connect() method return this field?
- Do operations use this field?
- Is it needed for refresh/reconnect? If NO to all → don't add it
For connectionProfile - include environment-optional fields:
# ✅ CORRECT - Include optional fields that some environments need allOf: - $ref: './node_modules/@zerobias-org/types-core/schema/tokenProfile.yml' - type: object properties: region: type: string description: Optional region (needed in some deployments) mfaCode: type: string description: Multi-factor authentication code (optional) # These aren't required, but include them if product docs mention them
connectionProfile vs connectionState:
- connectionProfile: Include optional fields that might be needed in some environments (region, mfaCode, etc.)
- connectionState: Only fields actually used by connect() or operations
Validation Checklist
Before finalizing connection schemas:
- Checked parent schema - no semantic duplicates (url/uri, token/accessToken)
- Checked product docs thoroughly - all auth parameters included (mfaCode, region, etc.)
- connectionProfile extends a core profile (NOT custom)
- connectionState extends baseConnectionState (includes expiresIn)
- expiresIn is in seconds (integer), not expiresAt timestamp
- If refresh capability: state extends oauthTokenState
- If simple token: state extends tokenConnectionState
- Custom fields use allOf to extend core
- All required fields marked as required
- Checked API docs for optional connection parameters (region, environment, etc.)
- Profile contains ONLY connection parameters (not operation parameters)
- NO scope limitation - no organizationId/projectId/workspaceId in connection
- Only used fields - every state field is actually used by connect() or operations
- Minimal set needed to connect (don't add operation scope parameters)
Common Mistakes
❌ Creating Custom Profile
# DON'T DO THIS type: object properties: apiKey: string baseUrl: string
Fix: Extend tokenProfile.yml
❌ Missing baseConnectionState
# DON'T DO THIS type: object properties: accessToken: string
Fix: Extend baseConnectionState or tokenConnectionState
❌ Using expiresAt Instead of expiresIn
# DON'T DO THIS expiresAt: type: string format: date-time
Fix: Convert to expiresIn (seconds) in connect() method
❌ No expiresIn for Refresh Tokens
# DON'T DO THIS properties: accessToken: string refreshToken: string # Missing expiresIn!
Fix: MUST extend baseConnectionState for expiresIn
Remember
- Always extend core profiles - Never create custom from scratch
- Always extend baseConnectionState - Server needs expiresIn for cronjobs
- expiresIn in seconds - Not timestamps, not milliseconds
- Use allOf for extensions - Add custom fields via composition
- Receive data from @credential-manager - Don't guess auth method
Connection schemas are YAML schemas - design them like api.yml schemas!