Building Connectors¶
A connector is defined by its descriptor -- a YAML or JSON document that declares the external system's API operations, authentication requirements, rate limits, and capabilities. The platform validates the descriptor and registers it in the connector catalog.
Connector Descriptor Structure¶
A descriptor has the following top-level sections:
graph TD
CD["Connector Descriptor"] --> Meta["metadata<br/>name, slug, version, publisher"]
CD --> Auth["authentication<br/>auth profiles + credential fields"]
CD --> Chains["credential_chains<br/>multi-step auth flows (optional)"]
CD --> Ops["operations<br/>API endpoints + parameters"]
CD --> Cap["capabilities<br/>sync modes, schema discovery"]
CD --> RL["rate_limits<br/>per-account/user/token limits"]
CD --> SLA["sla<br/>availability, latency targets"]
CD --> MCP["mcp_config<br/>MCP tool generation (optional)"]
CD --> DB["database_config<br/>adapter settings (optional)"]
Metadata¶
Every connector starts with metadata that identifies it in the catalog:
metadata:
name: "Jira Cloud"
slug: "jira-cloud"
version: "1.2.0"
description: "Connect to Atlassian Jira Cloud for issue tracking and project management."
summary: "Jira Cloud connector for issue CRUD, search, and project management"
publisher: "Acme Corp"
documentation_url: "https://developer.atlassian.com/cloud/jira/platform/rest/v3/"
categories:
- saas
tags:
- project-management
- issue-tracking
- atlassian
| Field | Required | Rules |
|---|---|---|
name |
Yes | 3-200 characters |
slug |
Yes | 3-100 characters, lowercase alphanumeric with hyphens |
version |
No | Defaults to 0.1.0 |
description |
Yes | 10-2000 characters |
summary |
No | Up to 300 characters; short label for listings |
publisher |
Yes | 2-200 characters |
documentation_url |
No | URL to external API documentation |
categories |
No | One or more of: database, file_storage, saas, messaging, analytics, custom |
tags |
No | Free-form string labels for search and filtering |
Authentication Profiles¶
Define how users authenticate with the external system. A connector can support multiple auth methods (e.g., both API key and OAuth2).
Supported types: api_key, basic, oauth2_authorization_code, oauth2_client_credentials, service_account, none.
Authentication Profile Fields¶
| Field | Required | Description |
|---|---|---|
type |
Yes | Auth type (see supported types above) |
description |
No | Human-readable description (up to 500 chars) |
docs_url |
No | Link to external auth documentation |
fields |
No | List of credential fields the user must supply |
oauth |
No | OAuth2 settings (required for oauth2_* types) |
test_endpoint |
No | Endpoint called to validate credentials on instance creation |
credential_chain_id |
No | References a credential_chains entry for multi-step auth |
Credential Field Properties¶
Each entry in fields has:
| Field | Required | Description |
|---|---|---|
key |
Yes | Internal identifier (1-100 chars) |
label |
Yes | Display name shown in the UI (1-200 chars) |
field_type |
No | One of: string, secret, integer, boolean, password, json. Defaults to string |
required |
No | Whether the field must be supplied. Defaults to true |
secret |
No | Encrypts the value at rest. Defaults to false |
description |
No | Help text (up to 500 chars) |
example |
No | Example value shown in the UI (up to 500 chars) |
authentication:
- type: api_key
description: "Jira API token (generated in Atlassian account settings)"
docs_url: "https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/"
fields:
- key: email
label: "Email Address"
field_type: string
required: true
description: "Atlassian account email"
example: "user@company.com"
- key: api_token
label: "API Token"
field_type: secret
required: true
secret: true
description: "Jira API token"
test_endpoint:
url: "https://{domain}.atlassian.net/rest/api/3/myself"
method: GET
expected_status: 200
authentication:
- type: oauth2_authorization_code
description: "OAuth2 with user consent for Jira Cloud"
oauth:
authorize_url: "https://auth.atlassian.com/authorize"
token_url: "https://auth.atlassian.com/oauth/token"
scopes:
- name: "read:jira-work"
description: "Read Jira project and issue data"
- name: "write:jira-work"
description: "Create and update Jira issues"
default_scopes:
- "read:jira-work"
authentication:
- type: oauth2_client_credentials
description: "Machine-to-machine OAuth2 flow"
fields:
- key: client_id
label: "Client ID"
field_type: string
required: true
- key: client_secret
label: "Client Secret"
field_type: secret
required: true
secret: true
oauth:
token_url: "https://auth.example.com/oauth/token"
scopes:
- name: "api:read"
description: "Read access"
default_scopes:
- "api:read"
authentication:
- type: basic
description: "Username and password for Jira Server"
fields:
- key: username
label: "Username"
field_type: string
required: true
- key: password
label: "Password"
field_type: password
required: true
secret: true
test_endpoint:
url: "https://{base_url}/rest/api/2/myself"
method: GET
expected_status: 200
Credential field types
Use field_type: secret, field_type: password, or field_type: json for sensitive values. These fields are encrypted at rest and never returned in API responses.
test_endpoint¶
The optional test_endpoint on an authentication profile is called when a user creates a connector instance to verify their credentials are valid.
| Field | Required | Description |
|---|---|---|
url |
Yes | URL to call (supports {variable} substitution) |
method |
No | HTTP method, defaults to GET |
expected_status |
No | Expected HTTP status code, defaults to 200 |
headers |
No | Additional headers as a key/value map |
Credential Chains¶
For systems with multi-step authentication, define a credential chain and reference it from the auth profile:
credential_chains:
- chain_id: securonix_oauth
description: "Multi-step OAuth for Securonix SIEM"
initial_credentials_required:
- username
- password
- base_url
- gateway_url
steps:
- step_name: ws_token
endpoint: "{base_url}/Snypr/ws/token/generate"
method: GET
headers:
username: "{username}"
password: "{password}"
validity: "365"
extract:
ws_token: "$.token"
ttl_seconds: 31536000
- step_name: oauth_token
endpoint: "{gateway_url}/api/v2/oauth/token"
method: POST
headers:
wstoken: "{ws_token}"
extract:
bearer_token: "$.access_token"
expires_in: "$.expires_in"
final_credential_name: bearer_token
authentication:
- type: api_key
description: "Securonix bearer token via multi-step OAuth"
credential_chain_id: securonix_oauth
The platform executes each step in order, using JSONPath to extract intermediate credentials and caching them based on TTL.
Credential Chain Fields¶
| Field | Required | Description |
|---|---|---|
chain_id |
No | Identifier referenced by credential_chain_id in an auth profile (1-100 chars) |
description |
Yes | Human-readable description (1-500 chars) |
initial_credentials_required |
Yes | List of field keys the user must provide before the chain runs |
steps |
Yes | Ordered list of auth steps (minimum 1) |
final_credential_name |
Yes | Which extracted key from the last step is used as the credential |
Credential Chain Step Fields¶
| Field | Required | Description |
|---|---|---|
step_name |
Yes | Step identifier (1-100 chars) |
endpoint |
Yes | URL template with {variable} substitution |
method |
No | HTTP method, defaults to POST |
headers |
No | Header key/value map with {variable} substitution |
body |
No | Body key/value map with {variable} substitution |
query_params |
No | Query param key/value map with {variable} substitution |
extract |
Yes | JSONPath expressions mapping response fields to credential names |
ttl_seconds |
No | How long to cache the extracted credential |
cache_key |
No | Template string used as the cache key |
Defining Operations¶
Operations are the core of a connector. Each operation maps to one API endpoint on the external system.
operations:
- operation_id: list_issues
endpoint: "https://{domain}.atlassian.net/rest/api/3/search"
method: GET
summary: "Search Jira issues using JQL"
description: "Returns issues matching a JQL query with pagination support"
auth_type_ref: api_key
auth_headers:
Authorization: "Basic {base64({email}:{api_token})}"
parameters:
- name: jql
location: query
description: "JQL query string"
required: true
field_type: string
example: "project = PROJ AND status = Open"
- name: maxResults
location: query
description: "Maximum results per page"
required: false
field_type: integer
default_value: 50
- name: startAt
location: query
description: "Pagination offset"
required: false
field_type: integer
default_value: 0
response_schema:
type: object
required: ["issues", "total"]
properties:
issues:
type: array
total:
type: integer
tags:
- read
- search
- operation_id: create_issue
endpoint: "https://{domain}.atlassian.net/rest/api/3/issue"
method: POST
summary: "Create a new Jira issue"
auth_type_ref: api_key
auth_headers:
Authorization: "Basic {base64({email}:{api_token})}"
parameters:
- name: project_key
location: body
description: "Project key (e.g., PROJ)"
required: true
field_type: string
- name: summary
location: body
description: "Issue summary/title"
required: true
field_type: string
- name: description
location: body
description: "Issue description"
required: false
field_type: string
- name: issue_type
location: body
description: "Issue type (Bug, Story, Task, etc.)"
required: true
field_type: string
tags:
- write
- create
Operation Fields¶
| Field | Required | Description |
|---|---|---|
operation_id |
Yes | Unique identifier (lowercase, alphanumeric, underscores). Pattern: ^[a-z0-9_]+$ |
endpoint |
Yes | URL path or full URL. Supports {variable} substitution |
method |
No | HTTP method (defaults to GET) |
summary |
Yes | Brief description (1-200 chars) |
auth_type_ref |
Yes | References an authentication profile defined in the descriptor |
auth_headers |
No | Header templates using {credential_field} substitution (e.g., Authorization: "Bearer {token}") |
credential_field |
No | Which extracted credential to inject (e.g., bearer_token from a credential chain) |
parameters |
No | List of input parameters |
response_schema |
No | JSON Schema for response validation |
rate_limit_scope |
No | References a rate_limits scope by name for per-operation enforcement |
tags |
No | Categorization tags (e.g., read, write, admin) |
Parameter Fields¶
| Field | Required | Description |
|---|---|---|
name |
Yes | Parameter identifier (1-100 chars) |
location |
Yes | Where the parameter is sent (see table below) |
description |
No | Human-readable description (up to 500 chars) |
required |
No | Defaults to false |
field_type |
No | One of: string, integer, boolean, secret, password, json. Defaults to string |
default_value |
No | Default value used when the parameter is not supplied |
example |
No | Example value (up to 200 chars) |
Parameter Locations¶
Parameters can appear in four locations:
| Location | Description | Example |
|---|---|---|
query |
URL query string | ?jql=project=PROJ |
path |
URL path segment | /issues/{issue_id} |
header |
HTTP request header | X-Custom-Header: value |
body |
Request body (JSON) | {"summary": "Bug fix"} |
Rate Limits¶
Declare the external system's rate limits so the platform can enforce them:
rate_limits:
- scope: per_account
limit: 100
interval_seconds: 60
burst: 20
note: "Jira Cloud standard rate limit"
- scope: per_user
limit: 50
interval_seconds: 60
| Field | Required | Description |
|---|---|---|
scope |
Yes | One of: per_account, per_user, per_token, per_connection |
limit |
Yes | Maximum number of requests allowed in the interval |
interval_seconds |
Yes | Rolling window duration in seconds |
burst |
No | Additional requests allowed above the limit in a burst |
note |
No | Human-readable explanation (up to 300 chars) |
SLA Metrics¶
Define the expected service level:
sla:
availability_percentage: 99.9
latency_ms_p95: 2000
error_budget_percentage: 0.1
data_freshness_minutes_p95: 5
rto_minutes: 60
rpo_minutes: 15
| Field | Required | Description |
|---|---|---|
availability_percentage |
Yes | Target uptime (0-100) |
latency_ms_p95 |
No | p95 API response latency in milliseconds |
error_budget_percentage |
No | Acceptable error rate (0-100) |
data_freshness_minutes_p95 |
No | p95 data freshness in minutes |
rto_minutes |
No | Recovery Time Objective in minutes |
rpo_minutes |
No | Recovery Point Objective in minutes |
Capabilities¶
Declare what the connector can do:
capabilities:
sync_modes:
- full_refresh
- incremental
- streaming
supports_schema_discovery: true
supports_incremental_bookmark: true
supports_change_data_capture: false
supports_custom_sql: false
supports_file_ingestion: false
max_parallel_tasks: 5
| Field | Required | Description |
|---|---|---|
sync_modes |
No | List of supported modes: full_refresh, incremental, streaming |
supports_schema_discovery |
No | Whether the connector can enumerate its schema. Defaults to true |
supports_incremental_bookmark |
No | Whether incremental syncs use a bookmark cursor. Defaults to false |
supports_change_data_capture |
No | Whether CDC is available. Defaults to false |
supports_custom_sql |
No | Whether arbitrary SQL can be executed. Defaults to false |
supports_file_ingestion |
No | Whether the connector can ingest files. Defaults to false |
max_parallel_tasks |
No | Maximum concurrent sync tasks |
MCP Configuration¶
Expose connector operations as MCP tools so AI agents can call them directly:
mcp_config:
enabled: true
server_name: "jira-mcp"
server_description: "Jira Cloud MCP server for issue management"
route_mappings:
- operation_id: list_issues
mcp_tool_name: "search_issues"
mcp_tool_description: "Search Jira issues using a JQL query"
parameter_mappings:
jql: "jql_query"
- operation_id: create_issue
mcp_tool_name: "create_issue"
mcp_tool_description: "Create a new issue in a Jira project"
| Field | Required | Description |
|---|---|---|
enabled |
No | Defaults to false |
server_name |
Yes | MCP server identifier (1-100 chars) |
server_description |
Yes | Human-readable description (1-500 chars) |
route_mappings |
No | List of operation → MCP tool mappings |
Each route_mappings entry:
| Field | Required | Description |
|---|---|---|
operation_id |
Yes | Must reference an operation defined in this descriptor |
mcp_tool_name |
Yes | Name exposed to MCP clients (must be unique) |
mcp_tool_description |
Yes | Description of the tool for the LLM (1-500 chars) |
parameter_mappings |
No | Map connector parameter names to MCP parameter names |
Database Connectors¶
For database-category connectors, add database-specific configuration:
database_config:
adapter: postgres
default_schema: public
tables: [] # empty = all tables accessible
| Field | Required | Description |
|---|---|---|
adapter |
No | Database adapter. Defaults to postgres |
default_schema |
No | Default schema to use. Defaults to public |
tables |
No | List of tables to expose. Empty list means all accessible tables |
Supported adapters: postgres, mysql, and others via the connector adapter registry.
Validation¶
Before registering a connector, validate the descriptor:
Coming Soon
The Python SDK for local development is not yet publicly available.
from flow_sdk.cli_client import CLIClient
client = CLIClient(config)
result = client.connectors.validate(descriptor={
"metadata": {"name": "My Connector", "slug": "my-connector", ...},
"authentication": [...],
"operations": [...],
...
})
if result.valid:
print("Descriptor is valid")
else:
for error in result.errors:
print(f"Error: {error}")
The validator checks:
- All required fields are present with valid types
- Authentication profiles have no duplicates
- Operations reference valid authentication profiles
- Operation IDs are unique
- MCP route mappings (if configured) reference existing operations and have unique tool names
- Credential chain steps are properly sequenced
Registering the Connector¶
Once validated, register the connector in the catalog:
Coming Soon
The Python SDK for local development is not yet publicly available.
connector = client.connectors.create({
"descriptor": descriptor,
"status": "draft", # draft, published, or archived
"visibility": "organization", # private, workspace, organization, marketplace
})
print(f"Registered: {connector.id} ({connector.slug} v{connector.version})")
Version management
Update the version field in metadata when changing operations or auth profiles. The platform tracks connector versions and instances can be pinned to specific versions.