Claude-skill-registry connection-schema-design

Connection profile and state schema design rules and core profile selection

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
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"
manifest: skills/data/connection-schema-design/SKILL.md
source content

Connection 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

ProfileWhen to UseContains
tokenProfile.ymlAPI Key, Bearer Token, PATtoken
oauthClientProfile.ymlOAuth2 Client CredentialsclientId, clientSecret
oauthTokenProfile.ymlOAuth2 with RefreshclientId, clientSecret
basicConnection.ymlUsername/Password, Email/Passwordusername, 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

StateWhen to UseContains
tokenConnectionState.ymlSimple token authaccessToken + expiresIn
oauthTokenState.ymlOAuth with refreshaccessToken + 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:

  1. Read product documentation authentication section
  2. Look for optional parameters (region, environment, mfaCode, etc.)
  3. Include ALL mentioned parameters (even if optional)
  4. 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:

  1. Does connect() method return this field?
  2. Do operations use this field?
  3. 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

  1. Always extend core profiles - Never create custom from scratch
  2. Always extend baseConnectionState - Server needs expiresIn for cronjobs
  3. expiresIn in seconds - Not timestamps, not milliseconds
  4. Use allOf for extensions - Add custom fields via composition
  5. Receive data from @credential-manager - Don't guess auth method

Connection schemas are YAML schemas - design them like api.yml schemas!