Creating Connectors¶
This guide covers how to create connectors for each execution model. For architecture details, see Connector Architecture.
Quick Start¶
The fastest way to create a connector:
- Navigate to Connectors in the sidebar
- Click New Connector
- Choose an execution model
- Fill in details and create
Execution Model Selection¶
| Model | When to Use | Complexity |
|---|---|---|
| Built-in | New instance of an existing registered type (e.g. a second Authifi tenant) | Low |
| Script | Custom integrations, user-authored logic | Medium |
| OAS-Derived | APIs with OpenAPI 3.x specs | Low |
| External | Remote services, separate processes | Low-Medium |
Note: Creating a new built-in connector type (i.e. writing the handler code) requires a server deployment. Creating a new instance of an existing built-in type is a low-complexity UI operation — see below.
Creating Built-in Connector Instances¶
Use this when you need another instance of a connector type that already exists in the registry (e.g. connecting a second Authifi tenant, or a separate LDAP directory).
Via the UI¶
- Navigate to Connectors and click New Connector
- Select Built-in as the execution model and click Next
- Choose the connector type from the dropdown (e.g.
authifi,http) - Enter a unique Name (e.g.
authifi-prod), a Description, and optionally a Category - If the type has connection config fields (API keys, URLs, etc.), fill them in — or leave them for later
- Click Next to review, then Create Connector
Via the API¶
POST /api/connectors
Content-Type: application/json
{
"name": "authifi-prod",
"type": "authifi",
"description": "Authifi connector for the production tenant",
"version": "1.0.0",
"executionModel": "built_in",
"configSchema": { ... },
"config": {
"baseUrl": "https://auth.example.com/_api",
"tenant": "prod",
"tenantId": "42",
"clientId": "my-client-id",
"clientSecret": "my-secret"
}
}
The type must match a registered built-in handler name. The config field contains connection secrets that are encrypted at rest. You can omit config and set it later via PUT /api/connectors/:id.
Creating a New Built-in Connector Type¶
When an existing built-in type does not cover your use case and a script connector is not a good fit (for example, you need tight integration with the server runtime, shared TypeScript types, or access to server-side services), define a new built-in type using the defineConnector() framework.
Scaffolding the shell¶
The fastest way to start is with the scaffolder, which writes a typed handler stub and registers it in BUILT_IN_DEFINITIONS for you:
| Flag | Purpose |
|---|---|
<name> |
Required, kebab-case (e.g. acme-crm). Becomes the connector name. |
--description, -d |
One-line description used in the generated stub (otherwise auto-generated). |
--force, -f |
Overwrite handlers/<name>.ts if it already exists. |
The scaffolder:
- Writes
packages/server/src/modules/connectors/handlers/<name>.tswith adefineConnector()block, a typed<Name>Connectioninterface, anerrorMapforConnectorHttpError, atestconnectivity command, and alistItemscommand that demonstratessucceed()/fail()and an in-codemockblock. - Inserts the import alphabetically into
handlers/index.tsand appends the new entry toBUILT_IN_DEFINITIONS. - Is idempotent: re-running with the same name leaves the registry untouched (and refuses to overwrite the handler file unless
--forceis passed).
After scaffolding, edit the generated file to replace the placeholder commands with your real ones, add a unit test under packages/server/test/unit/, and run pnpm typecheck && pnpm test:unit to verify.
Anatomy of a built-in connector¶
Every built-in connector lives under packages/server/src/modules/connectors/handlers/ and is written with the typed defineConnector() / defineCommand() / t.* builders:
// packages/server/src/modules/connectors/handlers/widget.ts
import { defineCommand, defineConnector, fail, succeed, t } from "../define/define-connector.js";
import { connectorHttpRequest, ConnectorHttpError } from "../clients/http-connector-client.js";
// Shape of the decrypted connection fields at runtime. Must line up with
// the `connectionConfig` entries below (same keys, matching runtime types).
interface WidgetConnection {
baseUrl: string;
apiKey: string;
}
// Hoist `params` to a const so `typeof listWidgetsParams` can be passed to
// `defineCommand` below — that's what gives the handler typed access to
// both `params` and `connection` (see "Connection typing" in Key points).
const listWidgetsParams = {
limit: t.number({ default: 50 }),
};
export const widgetConnector = defineConnector({
name: "widget",
version: "1.0.0",
description: "Widget platform connector.",
// Per-connector connection fields. Rendered as form inputs on the
// connector-instance page. Secrets are encrypted at rest.
connectionConfig: {
baseUrl: t.string({ required: true, description: "API base URL." }),
apiKey: t.secret({ required: true, description: "API key." }),
},
// Optional: map domain-specific exceptions into structured failure diagnostics.
errorMap: (error) =>
error instanceof ConnectorHttpError
? { error: `Widget API error (${error.status})`, diagnostics: { status: error.status } }
: null,
commands: {
listWidgets: defineCommand<typeof listWidgetsParams, WidgetConnection>({
description: "List widgets for the tenant.",
params: listWidgetsParams,
outputs: ["widgets", "total"],
async handler({ params, connection, logger }) {
// `params.limit` is typed `number | undefined` (inferred from the schema).
// `connection.baseUrl` / `connection.apiKey` are typed via `WidgetConnection`.
const response = await connectorHttpRequest(
`${connection.baseUrl}/widgets?limit=${params.limit ?? 50}`,
{ method: "GET", auth: { type: "bearer", token: connection.apiKey } },
logger,
);
if (!response.isSuccessful) {
return fail({
error: `Widget API returned ${response.status}`,
diagnostics: { status: response.status, body: response.body },
});
}
return succeed({
payload: { widgets: response.body, total: 0 },
variables: { widgetCount: Array.isArray(response.body) ? response.body.length : 0 },
});
},
}),
},
});
The
succeed()/fail()builders (exported from./define-connector.js) are the preferred way to construct the three-slot outcome. You can always return a plain object literal — the dispatcher accepts both — but the builders make the intent explicit and keep the two success/failure branches visually distinct.
Key points:
t.*builders (t.string,t.secret,t.number,t.boolean,t.date,t.select,t.raw) describe fields and carry compile-time type info. Pass{ required: true }to mark required fields — the dispatcher emits the legacyMissing required field(s): …errors for free.defineCommand()wraps each command. Its return type is erased for runtime use, but inside the callback TypeScript infersparamsfrom the command's schema, and you can thread aTConnectiontype through the generic signature.- Connection typing:
connectionis typed via the second generic parameter todefineCommand<TParams, TConnection>. If you omit it (e.g.defineCommand({ ... })),connectionfalls back toRecord<string, unknown>and every field read isunknown. The example above extractsparamsinto aconstsotypeof listWidgetsParamscan be passed as the first generic, letting you supplyWidgetConnectionas the second without losing param inference. - Handler args also include
context: ConnectorContext— the full{ config, connectorConfig, variables, stepId, runId }object. Usecontext.connectorConfigdirectly (with explicit narrowing /ascasts) when you prefer not to declare a dedicatedTConnectioninterface;authifiandgoogle-workspaceuse this pattern. handler()returns aCommandOutcome(sync orPromise<CommandOutcome>). See Handler return values below.connectorHttpRequest()fromclients/http-connector-client.tsis the preferred outbound HTTP helper. It enforces the SSRF URL policy, pins DNS against rebinding, and returns{ status, isSuccessful, body }(it throwsConnectorHttpErroronly on network failures and policy violations — not on non-2xx responses).
Handler return values¶
Every command handler resolves to a CommandOutcome (defined in define/types.ts). The dispatcher (define/dispatcher.ts::outcomeToResult) normalizes the outcome into a ConnectorResult ({ success, payload?, variables?, diagnostics?, error? }) before returning it to the caller. The contract has three explicit slots:
| Slot | When it's used | Scope in workflow variables | Typical contents |
|---|---|---|---|
payload |
success | Namespaced under the step's outputKey (when set): variables[outputKey] = payload. Also persisted to the task row for the run-detail UI. Consumed by entitlement mapping dot-paths and PAM session checkout. |
The command's domain result — list/lookup response body, the provisioned resource, etc. |
variables |
success | Flat-merged into the run's global variable bag via Object.assign(variables, result.variables). |
Curated fields you explicitly want downstream steps to read by name (e.g. userCount, nextPageToken). |
diagnostics |
failure | Never merged into the run-variable bag. Persisted to the task row so operators can inspect it on the run-detail UI. | Structured failure context: HTTP statusCode, responseBody, validation details, retry metadata. |
The accepted outcome shapes are:
| Shape | Normalized result | When to use |
|---|---|---|
succeed({ payload?, variables? }) |
{ success: true, payload, variables } |
Success. Use the succeed() builder (or a plain { payload, variables } object) for clarity. Omit fields you don't populate. |
fail({ error, diagnostics?, variables? }) |
{ success: false, error, diagnostics, variables } |
Recoverable failure with a human-readable message. Attach structured context via diagnostics. Failing calls MAY write variables (e.g. to set an error flag downstream). |
ConnectorResult ({ success, payload?, variables?, diagnostics?, error? }) |
Returned verbatim. | Full control for edge cases — e.g. success: false without an error string (uncommon but legal). Most handlers should prefer the builders. |
Non-object / undefined / no return |
{ success: true } |
Side-effect-only commands. Caveat: TypeScript's CommandOutcome type does not include void, so an async handler with no return statement fails type-check — write return succeed(); instead. |
Key semantics:
payloadvsvariables— both are populated on success; neither is automatic. Theoutputs: [...]array on the command definition is declarative metadata only — it never filters, validates, or copies between the two. Populate both slots explicitly.payloadis the slot consumers read by path ({{<step.outputKey>.id}}). Entitlement mapping (externalIdFrom,externalEmailFrom,attributesFrom) and PAM session checkout only readpayload.variablesis the slot that gets flat-merged into the shared run-variable bag, making its keys addressable by bare name ({{userCount}}) across every subsequent step.- Rule of thumb: put the domain result in
payload; copy the one or two fields you want globally addressable intovariables. diagnosticsis failure-only.outcomeToResultonly attachesdiagnosticsto failure results; it is ignored on success outcomes. Return diagnostic context (HTTP status, response body, validation details) so operators can see the root cause on the run-detail UI without it leaking into the variable bag.throwvsreturn fail(...): both surface assuccess: false, but only thrown errors flow througherrorMap. Throw the connector's own error class (e.g.AuthifiError,ConnectorHttpError) when you wanterrorMapto produce a typed{ error, diagnostics }payload; returnfail({ error: "..." })for ad-hoc failures that don't need translation. If noerrorMapmatch, the dispatcher falls back toerror.message(orString(error)for non-Errorthrows).- Failing with
variables: legal and supported.fail({ error: "...", variables: { ... } })(or the explicitConnectorResultshape) writes both the error envelope and the variable updates.
Registering the connector¶
Add the new module to the built-in list so it is auto-registered and auto-seeded:
// packages/server/src/modules/connectors/handlers/index.ts
import { loadBuiltInConnectors } from "../define/auto-discover.js";
import type { ConnectorDefinition } from "../define/types.js";
import { delayConnector } from "./delay.js";
import { httpConnector } from "./http.js";
import { widgetConnector } from "./widget.js"; // add
export const BUILT_IN_DEFINITIONS: readonly ConnectorDefinition[] = [
httpConnector,
delayConnector,
widgetConnector, // add
];
loadBuiltInConnectors(BUILT_IN_DEFINITIONS);
That single edit:
- Registers the handler with
registerConnector(vialoadBuiltInConnectors → registerDefinition), makingexecuteConnector("widget", …)work at runtime. - Emits a
ConnectorSeedon next boot (viatoConnectorSeed), creating the matchingconnector_typerow so the UI can render the connector-instance form.
No additional edits to routes.ts, registry.ts, or seed-connectors.ts are required.
Default-command connectors¶
Some built-in connectors treat their entire config as command params (no explicit command field in the step config). Set defaultCommand and the dispatcher will route to that command whenever config.command is absent:
export const widgetConnector = defineConnector({
name: "widget",
version: "1.0.0",
description: "Widget platform connector.",
defaultCommand: "listWidgets",
commands: {
listWidgets: defineCommand({
description: "List widgets.",
params: { limit: t.number() },
async handler({ params, connection }) {
/* real call */
},
}),
},
});
legacyCommandlessUI (reserved for http / delay)¶
A separate flag, legacyCommandlessUI: true, makes the connector emit configSchema: {} in its seed so the UI renders it via specialized step editors instead of the generic command picker. This is reserved for the migrated http and delay built-ins whose DB rows historically carried an empty schema:
export const delayConnector = defineConnector({
name: "delay",
version: "1.0.0",
description: "Wait for delayMs milliseconds.",
defaultCommand: "wait",
legacyCommandlessUI: true, // legacy built-ins only — see note below
commands: {
wait: defineCommand({
description: "Wait for delayMs milliseconds.",
params: { delayMs: t.number() },
async handler({ params }) {
await new Promise((resolve) => setTimeout(resolve, Number(params.delayMs) || 1000));
return succeed({ payload: { delayed: params.delayMs } });
},
}),
},
});
Do not set
legacyCommandlessUI: trueon new connectors. Even ones that usedefaultCommandshould leave it unset so the generic UI and schema-diff tooling can render their commands and connection config.
In-code mock scenarios¶
Per-command mock entries are invoked by runInCodeMock (the last hop after DB mockData/mockScript checks) and let tests or "mock connector" instances return canned outcomes without a real backend:
listWidgets: defineCommand({
description: "List widgets.",
params: { limit: t.number() },
mock: {
default: () => succeed({ payload: { widgets: [{ id: 1 }], total: 1 } }),
empty: () => succeed({ payload: { widgets: [], total: 0 } }),
},
async handler({ params, connection }) { /* real call */ },
}),
Testing¶
Write unit tests against the definition directly using executeDefinition — no Fastify server required:
import { executeDefinition } from "../../src/modules/connectors/define/dispatcher.js";
import { widgetConnector } from "../../src/modules/connectors/handlers/widget.js";
const ctx = {
config: { command: "listWidgets", limit: 10 },
connectorConfig: { baseUrl, apiKey },
variables: {},
stepId: "s",
runId: "r",
};
const result = await executeDefinition(widgetConnector, ctx);
expect(result.success).toBe(true);
See packages/server/test/unit/define-connector-framework.test.ts for more patterns (required-param errors, errorMap, runInCodeMock, commandless dispatch).
Versioning¶
Bump version on the definition whenever the configSchema changes. The server validates version bumps on PUT /api/connector-types/:id using the same rules for every connector type (built-in, script, OAS-derived); the authoritative implementation lives in packages/server/src/modules/connectors/schema-versioning.ts:
- Major (breaking):
- Removed command.
- Removed param from an existing command.
- New param added without a
default(callers may send old config that omits it). - Removed output from an existing command.
- Removed connection-config field.
- New connection-config field marked
required: true. - Minor:
- New command.
- New param with a
default. - New output.
- New optional connection-config field.
PUT /api/connector-types/:id compares old and new configSchema and rejects bumps that don't clear the suggested floor (e.g. patch bump on a breaking change). Pass autoBump: true in the request body to let the server compute the next version for you.
Creating Script Connectors¶
Script connectors run custom JavaScript in a sandboxed QuickJS environment.
Step 1: Define the connector¶
POST /api/connectors
Content-Type: application/json
{
"name": "my-crm",
"type": "my-crm",
"description": "Integration with My CRM",
"version": "1.0.0",
"executionModel": "script",
"category": "crm",
"tags": ["sales", "contacts"],
"configSchema": {
"connectionConfig": {
"baseUrl": { "type": "string", "required": true, "description": "CRM API base URL" },
"apiKey": { "type": "string", "required": true, "secret": true, "description": "API key" }
},
"commands": {
"listContacts": {
"params": ["limit", "offset"],
"description": "List contacts from the CRM",
"outputs": ["contacts", "total"],
"paramSchema": {
"limit": { "type": "number", "default": 50 },
"offset": { "type": "number", "default": 0 }
}
},
"createContact": {
"params": ["email", "firstName", "lastName"],
"description": "Create a new contact",
"outputs": ["contactId"]
},
"test": {
"params": [],
"description": "Test connectivity",
"outputs": []
}
}
},
"scriptSource": "... (see below)"
}
Step 2: Write the script¶
function listContacts() {
var url = floh.config.baseUrl + "/api/contacts";
url += "?limit=" + (floh.params.limit || 50);
url += "&offset=" + (floh.params.offset || 0);
var resp = floh.http.get(url, {
Authorization: "Bearer " + floh.config.apiKey,
});
if (resp.status >= 400) {
return { success: false, error: "API returned " + resp.status };
}
return {
success: true,
payload: { contacts: resp.body.data, total: resp.body.total },
variables: { contactCount: resp.body.total },
};
}
function createContact() {
var resp = floh.http.post(
floh.config.baseUrl + "/api/contacts",
JSON.stringify({
email: floh.params.email,
first_name: floh.params.firstName,
last_name: floh.params.lastName,
}),
{ Authorization: "Bearer " + floh.config.apiKey },
);
if (resp.status >= 400) {
return { success: false, error: "Failed to create contact: " + resp.status };
}
return { success: true, payload: { contactId: resp.body.id } };
}
function test() {
var resp = floh.http.get(floh.config.baseUrl + "/api/health", {
Authorization: "Bearer " + floh.config.apiKey,
});
return { success: resp.status < 400 };
}
Step 3: Test the connector¶
Sample Script Connector¶
A ready-made sample-script connector is seeded automatically on server startup. It provides a working reference implementation with five commands:
| Command | Description |
|---|---|
test |
Connectivity check — returns version and data count |
lookupEmployee |
Find an employee by id or email |
listEmployees |
List employees with optional department / limit |
createEmployee |
Simulated employee creation with validation |
echo |
Returns the full floh.params, floh.config, and floh.variables context |
The script source lives in packages/server/src/modules/connectors/seeds/sample-script-source.ts and demonstrates:
- Reading parameters from
floh.params - Accessing connector config via
floh.config - Reading workflow variables from
floh.variables - Structured logging with
floh.log.info/floh.log.warn/floh.log.debug - Returning
ConnectorResultwithpayload,variables, anddiagnostics - Input validation and error handling
Use it as a starting point for writing your own script connectors, or execute its commands directly to test the workflow engine's script connector path.
Script API Reference (floh.*)¶
All script connectors have access to the floh global object:
floh.http¶
| Method | Signature | Description |
|---|---|---|
get |
floh.http.get(url, headers?) |
HTTP GET request |
post |
floh.http.post(url, body?, headers?) |
HTTP POST request |
put |
floh.http.put(url, body?, headers?) |
HTTP PUT request |
delete |
floh.http.delete(url, headers?) |
HTTP DELETE request |
Note: PATCH is not currently exposed inside the script sandbox. Use
postwith anX-HTTP-Method-Override: PATCHheader if your target API accepts it, or model the operation as a built-in connector that callsconnectorHttpRequest(url, { method: "PATCH", … }).
Response format:
{
status: 200, // HTTP status code
body: { ... }, // Parsed JSON body
headers: { ... } // Response headers
}
All HTTP calls are proxied through the main thread. The sandbox has no direct network access.
floh.log¶
| Method | Description |
|---|---|
floh.log.trace(message) |
Trace-level log |
floh.log.debug(message) |
Debug-level log |
floh.log.info(message) |
Info-level log |
floh.log.warn(message) |
Warning-level log |
floh.log.error(message) |
Error-level log |
Logs are routed through the connector's ConnectorLogger and respect the configured logLevel.
floh.config¶
The connector's decrypted connection configuration. Contains the fields defined in connectionConfig:
floh.config.baseUrl; // "https://api.example.com"
floh.config.apiKey; // "sk-abc123..." (decrypted at runtime)
floh.params¶
The step parameters passed from the workflow. Contains the fields specified in the step's config:
floh.variables¶
The workflow run's current variable state:
Return Format¶
Every command function must return a ConnectorResult:
// Success — `payload` is the domain result; `variables` is flat-merged into the run-variable bag.
return {
success: true,
payload: { contacts: [...], total: 42 },
variables: { contactCount: 42 }
};
// Failure — `diagnostics` carries structured failure context for the run-detail UI.
return {
success: false,
error: "Connection refused",
diagnostics: { statusCode: 503, responseBody: { message: "upstream timeout" } }
};
Protocol 2.0 required. Script connectors must use
payload/variables/diagnostics. The sandbox rejects legacydata/outputVariableskeys with an explicit upgrade error at execution time.
Command Deprecation¶
When evolving a connector, mark old commands as deprecated before removing them:
{
"configSchema": {
"commands": {
"listUsers": {
"params": ["limit"],
"description": "List users (deprecated)",
"deprecated": true,
"deprecatedMessage": "Use listUsersV2 instead",
"addedInVersion": "1.0.0"
},
"listUsersV2": {
"params": ["limit", "cursor"],
"description": "List users with cursor pagination",
"addedInVersion": "2.0.0"
}
}
}
}
Deprecated commands continue to function normally but emit structured warning logs at runtime. Use GET /api/connector-types/:id/capabilities to inspect deprecation metadata.
Version Bump Requirements¶
PUT /api/connector-types/:id validates version bumps for every connector type (built-in, script, OAS-derived) using the rules in Versioning above. That section is the single source of truth — update it (and schema-versioning.ts) rather than restating the rules here.
Creating OAS-Derived Connectors¶
Via the UI¶
- Navigate to Connectors → Import from OpenAPI
- Paste the OpenAPI 3.x JSON spec
- Click Parse Spec to preview generated commands
- Select/deselect commands to include
- Review the generated script and connection config
- Click Create Connector
Via the API¶
POST /api/connectors/parse-oas
Content-Type: application/json
{
"spec": {
"openapi": "3.0.3",
"info": { "title": "My API", "version": "1.0.0" },
"paths": { ... },
"components": {
"securitySchemes": {
"bearerAuth": { "type": "http", "scheme": "bearer" }
}
}
}
}
The response contains a draft connector definition ready to be sent to POST /api/connectors.
Supported Security Schemes¶
| OAS Scheme | Generated Config Fields |
|---|---|
apiKey |
Named field with secret: true |
http/bearer |
bearerToken |
http/basic |
username, password |
oauth2 |
clientId, clientSecret, tokenUrl |
Creating External Connectors¶
External connectors communicate with Floh via a standardized HTTP protocol.
Protocol Endpoints¶
Your service must implement:
| Endpoint | Method | Description |
|---|---|---|
/execute |
POST | Execute a connector command |
/schema |
GET | Return the connector's schema (optional) |
/health |
GET | Return health status (optional) |
Execute Request¶
The request body includes a protocolVersion field. The current protocol is 2.0. Legacy 1.x responses are rejected by the server with an explicit upgrade error — there is no transparent compatibility shim.
{
"protocolVersion": "2.0",
"command": "listUsers",
"config": { "apiKey": "..." },
"params": { "limit": 50 },
"variables": { "project_id": "proj-1" },
"metadata": {
"stepId": "step-1",
"runId": "run-123",
"timeout": 30000
}
}
Execute Response¶
Protocol 2.0 has three explicit slots (same semantics as built-in and script connectors):
// Success
{
"success": true,
"payload": { "users": [ ... ] },
"variables": { "userCount": 10 }
}
// Failure
{
"success": false,
"error": "Upstream rejected the request",
"diagnostics": { "statusCode": 502, "responseBody": { "message": "bad gateway" } }
}
Breaking change from 1.x. Version 2.0 replaces
datawithpayload(success) ordiagnostics(failure), and replacesoutputVariableswithvariables. Responses that contain either legacy key are rejected up-front with a descriptive error pointing at this document; upgrade your external service to emit the new keys before rolling it out.
Registration¶
POST /api/connectors
Content-Type: application/json
{
"name": "my-external-service",
"type": "my-external-service",
"executionModel": "external",
"endpointUrl": "https://connector.example.com",
"version": "1.0.0",
"configSchema": { ... }
}
On-Premise Agent¶
For connectors that need to reach services inside a private network, use the on-premise agent. The agent establishes an outbound WebSocket connection to Floh, so no inbound ports need to be opened.
Preview / opt-in: The agent WebSocket transport is implemented in
packages/server/src/modules/connectors/agent-routes.tsbut is not yet wired intoroute-registry.ts, so/api/connectors/agents/wsis not reachable in default server builds. Tracked in #243.
Agent Registration¶
WebSocket: wss://floh.example.com/api/connectors/agents/ws
Registration message:
{
"agentId": "agent-prod-01",
"agentName": "Production Agent",
"connectorNames": ["internal-ldap", "sap-hr"],
"apiToken": "..."
}
Command Flow¶
Floh Server ──WebSocket──▶ Agent ──HTTP──▶ Internal Service
│
Agent ◀──HTTP──────────────────────────────────┘
│
Floh Server ◀──WebSocket──┘
The agent receives AgentCommand messages, executes them against internal services, and returns AgentResponse messages.
AI-Assisted Building¶
The Floh MCP server provides tools for AI-assisted connector development:
| MCP Tool | Description |
|---|---|
generate_connector_script |
Generate a JavaScript script from a description |
parse_openapi_spec |
Parse an OAS spec into a connector definition |
create_connector |
Create a connector via the API |
update_connector_script |
Update a connector's script |
test_connector |
Test a connector's connectivity |
execute_connector_command |
Execute a specific command |
Example AI Workflow¶
- Ask the AI: "Create a connector for the Acme CRM API"
- Provide the OpenAPI spec or describe the API endpoints
- The AI uses
parse_openapi_specorgenerate_connector_script - Review and refine the generated connector
- The AI uses
create_connectorandtest_connectorto deploy and verify