Help us improve
Share bugs, ideas, or general feedback.
From stackone-unified-connectors
Baseline skill for building unified/schema-based connectors that transform provider data into standardized schemas. Use alongside domain-specific schema skills (e.g., unified-hris-schema, unified-crm-schema) that define your organization's standard schemas. Use when user says "start unified build for [provider]", "build a schema-based connector", "map fields to schema", "test unified connector", or asks about field mapping, enum mapping, pagination configuration, or scope decisions. This skill provides implementation patterns; schema skills provide field definitions. Do NOT use for agentic/custom connectors (use stackone-cli), discovering existing connectors (use stackone-connectors), or building AI agents (use stackone-agents).
npx claudepluginhub stackonehq/agent-plugins --plugin stackone-unified-connectorsHow this skill is triggered — by the user, by Claude, or both
Slash command
/stackone-unified-connectors:stackone-unified-connectorsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build connectors that transform provider-specific data into standardized schemas with consistent field names, enum values, and pagination.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Executes ctx7 CLI to fetch up-to-date library documentation, manage AI coding skills (install/search/generate/remove/suggest), and configure Context7 MCP. Useful for current API refs, skill handling, or agent setup.
Share bugs, ideas, or general feedback.
Build connectors that transform provider-specific data into standardized schemas with consistent field names, enum values, and pagination.
This is a baseline skill that provides the core workflow and patterns for building unified connectors. It is designed to work alongside domain-specific schema skills that you create for your organization's specific use cases.
Recommended approach:
unified-hris-schema, unified-messaging-schema, unified-crm-schema)This separation allows you to maintain consistent schemas across all providers within a category while leveraging the shared technical patterns from this baseline skill.
Before building unified connectors:
stackone help <command> for command-specific details--debug before configuring mappingsThese principles apply to ALL unified connector work. Violations cause silent failures or broken mappings.
Every YAML configuration field uses camelCase, not snake_case:
# CORRECT
scopeDefinitions: fieldConfigs: targetFieldKey:
enumMapper: matchExpression: dataKey:
nextKey: pageSize: indexField:
stepFunction: functionName: dataSource:
# WRONG - causes validation errors or silent failures
scope_definitions: field_configs: target_field_key:
While config fields are camelCase, targetFieldKey values match YOUR schema (often snake_case):
fieldConfigs:
- targetFieldKey: first_name # YOUR schema field
expression: $.firstName # Provider's field
stepFunction:
functionName: map_fields
version: '2' # REQUIRED - omitting causes empty results
Pass fields directly in map_fields step parameters rather than action-level fieldConfigs. This avoids schema inference issues that cause build failures.
# RECOMMENDED - Inline fields
- stepId: map_data
stepFunction:
functionName: map_fields
version: '2'
parameters:
fields:
- targetFieldKey: email
expression: $.email # Direct reference, NO step prefix
type: string
dataSource: $.steps.get_data.output.data
| Location | Expression Format | Example |
|---|---|---|
Inline in parameters.fields | Direct field reference | $.email, $.work.department |
Action-level fieldConfigs | Step ID prefix required | $.get_employees.email |
The entire purpose of unified connectors is standardized output. Never suggest users handle mapping in application code.
Never assume response structure. Always run with --debug first:
stackone run --debug --connector <file> --credentials <file> --action-id <action>
Check for a domain-specific schema skill first.
Domain-specific schema skills (e.g., unified-hris-schema, unified-crm-schema) should define your organization's standard schema for that category. These skills complement this baseline skill by providing:
If schema skill exists:
If no schema skill exists:
What a schema skill should contain:
# Example: unified-hris-schema skill structure
# - Field definitions with types
# - Enum values (e.g., employment_status: active, inactive, terminated)
# - Required fields marked
# - Nested structures documented
Creating domain-specific schema skills prevents drift between providers and reduces repeated schema discussions.
Do not skip this step. Research ALL available endpoints before proceeding.
For each endpoint, document:
Present a comparison table and get explicit user approval before implementing:
| Option | Endpoint | Field Coverage | Permissions | Status |
|--------|----------|----------------|-------------|--------|
| A | GET /v2/employees | 70% | Narrow | Active |
| B | POST /reports | 100% | Moderate | Active |
| C | POST /v1/data | 100% | Broad | Deprecated |
Recommendation: Option B - Full coverage, not deprecated
Do not proceed without user selection.
Use scopeDefinitions (not scope_definitions):
scopeDefinitions:
employees:read:
description: Read employee data
employees:extended:read:
description: Extended employee data
includes: employees:read # Scope inheritance
Principles:
See references/scope-patterns.md for detailed patterns.
Use inline fields in map_fields parameters:
steps:
- stepId: map_data
stepFunction:
functionName: map_fields
version: '2'
parameters:
fields:
- targetFieldKey: id
expression: $.id
type: string
- targetFieldKey: email
expression: $.email
type: string
- targetFieldKey: department
expression: $.work.department # Nested field
type: string
- targetFieldKey: status
expression: $.status
type: enum
enumMapper:
matcher:
- matchExpression: '{{$.status == "Active"}}'
value: active
- matchExpression: '{{$.status == "Inactive"}}'
value: inactive
- matchExpression: '{{$.status == null}}'
value: unknown
dataSource: $.steps.get_data.output.data
- stepId: typecast_data
stepFunction:
functionName: typecast
version: '2'
parameters:
fields:
- targetFieldKey: id
type: string
- targetFieldKey: email
type: string
- targetFieldKey: department
type: string
- targetFieldKey: status
type: enum
dataSource: $.steps.map_data.output.data
result:
data: $.steps.typecast_data.output.data
See references/field-mapping-patterns.md for enum mapping, nested objects, and transformations.
For list endpoints, use cursor pagination with the request function:
cursor:
enabled: true
pageSize: 50
inputs:
- name: page_size
type: number
in: query
required: false
- name: cursor
type: string
in: query
required: false
steps:
- stepId: get_data
stepFunction:
functionName: request
parameters:
url: /items
method: get
args:
# Dual-condition pattern for defaults
- name: limit
value: $.inputs.page_size
in: query
condition: "{{present(inputs.page_size)}}"
- name: limit
value: 50
in: query
condition: "{{!present(inputs.page_size)}}"
- name: cursor
value: $.inputs.cursor
in: query
condition: "{{present(inputs.cursor)}}"
result:
data: $.steps.get_data.output.data
next: $.steps.get_data.output.data.meta.nextCursor
Important: Use request function (not paginated_request) when you need dynamic inputs like page_size. The paginated_request function can have issues with $.inputs.* resolving to undefined.
See references/pagination-patterns.md for detailed configuration.
stackone validate connectors/<provider>/<provider>.connector.s1.yaml
Phase 1: Raw Response
stackone run --debug --connector <file> --credentials <file> --action-id <action>
Phase 2: Field Mapping - Verify all fields use YOUR schema names, not provider names
Phase 3: Pagination - Test first page, next page, last page, empty results
Phase 4: Schema Completeness - All required fields present and populated
Create a coverage document listing:
User says: "start unified build for BambooHR"
Actions:
unified-hris-schema)unified-hris-schema skill for consistency across HRIS providers/v1/employees, /v1/employees/directory, custom reports--debug, verify field names match schemaResult: Working unified connector with standardized employee schema that matches other HRIS connectors.
User says: "build a unified messaging connector for Slack"
Actions:
unified-messaging-schema skill - none existsunified-messaging-schema skill so future messaging connectors (Teams, Discord) use the same schema."Result: New schema skill created, connector built, future messaging connectors will use same schema.
User says: "My unified connector returns provider field names instead of my schema"
Actions:
targetFieldKey uses YOUR schema names (not provider names)version: '2' is specified on map_fields and typecast--debug to see raw response structureResult: Fields correctly mapped to user's schema.
User says: "Pagination cursor isn't being passed correctly"
Actions:
--debug to see raw response structuredataKey path matches actual response (e.g., data.employees not just employees)nextKey path points to cursor valuepaginated_request with dynamic inputs - switch to request with dual-condition patternresult.next returns the cursor valueResult: Working pagination with correct cursor handling.
Cause: Missing or incorrect field mapping configuration.
Fix: Ensure targetFieldKey uses YOUR schema field names, not provider names. Verify map_fields step is present and dataSource is correct.
Cause: Missing version: '2' or wrong expression context.
Fix: Add version: '2' to map_fields and typecast. For inline fields, use direct references ($.email) without step prefix.
Cause: matchExpression doesn't match provider values (case-sensitive).
Fix: Check exact provider values with --debug. Use .toLowerCase() for case-insensitive matching. Always include null/unknown fallback.
Cause: Cursor not being sent or extracted correctly.
Fix: Verify iterator.key matches API's expected parameter name. Check nextKey path against raw response. Verify iterator.in is correct (query/body/headers).
Cause: Action-level fieldConfigs triggering unwanted schema inference.
Fix: Use inline fields in map_fields parameters instead of action-level fieldConfigs.
Cause: Using paginated_request which doesn't handle $.inputs.* well.
Fix: Use standard request function with dual-condition pattern for defaults.
| Resource | URL |
|---|---|
| CLI Package | https://www.npmjs.com/package/@stackone/cli |
| Connector Engine Docs | https://docs.stackone.com/guides/connector-engine |
| CLI Reference | https://docs.stackone.com/guides/connector-engine/cli-reference |
To maintain consistency across providers, create schema skills for each category you work with. A domain-specific schema skill should include:
Required content:
string, number, boolean, datetime_string, enum)Example skill structure:
# Unified HRIS Schema
## Employee Resource
### Required Fields
| Field | Type | Description |
|-------|------|-------------|
| id | string | Unique identifier |
| email | string | Primary email address |
| first_name | string | Employee first name |
| last_name | string | Employee last name |
| employment_status | enum | Current employment status |
### Enum: employment_status
| Value | Description |
|-------|-------------|
| active | Currently employed |
| inactive | On leave or suspended |
| terminated | No longer employed |
### Optional Fields
| Field | Type | Description |
|-------|------|-------------|
| department | string | Department name |
| hire_date | datetime_string | Date of hire |
Naming convention: unified-{category}-schema (e.g., unified-hris-schema, unified-crm-schema, unified-messaging-schema)