Skip to content

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:

  1. Navigate to Connectors in the sidebar
  2. Click New Connector
  3. Choose an execution model
  4. 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

  1. Navigate to Connectors and click New Connector
  2. Select Built-in as the execution model and click Next
  3. Choose the connector type from the dropdown (e.g. authifi, http)
  4. Enter a unique Name (e.g. authifi-prod), a Description, and optionally a Category
  5. If the type has connection config fields (API keys, URLs, etc.), fill them in — or leave them for later
  6. 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:

pnpm new-connector <name> [--description "One-line description"] [--force]
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:

  1. Writes packages/server/src/modules/connectors/handlers/<name>.ts with a defineConnector() block, a typed <Name>Connection interface, an errorMap for ConnectorHttpError, a test connectivity command, and a listItems command that demonstrates succeed() / fail() and an in-code mock block.
  2. Inserts the import alphabetically into handlers/index.ts and appends the new entry to BUILT_IN_DEFINITIONS.
  3. Is idempotent: re-running with the same name leaves the registry untouched (and refuses to overwrite the handler file unless --force is 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 legacy Missing required field(s): … errors for free.
  • defineCommand() wraps each command. Its return type is erased for runtime use, but inside the callback TypeScript infers params from the command's schema, and you can thread a TConnection type through the generic signature.
  • Connection typing: connection is typed via the second generic parameter to defineCommand<TParams, TConnection>. If you omit it (e.g. defineCommand({ ... })), connection falls back to Record<string, unknown> and every field read is unknown. The example above extracts params into a const so typeof listWidgetsParams can be passed as the first generic, letting you supply WidgetConnection as the second without losing param inference.
  • Handler args also include context: ConnectorContext — the full { config, connectorConfig, variables, stepId, runId } object. Use context.connectorConfig directly (with explicit narrowing / as casts) when you prefer not to declare a dedicated TConnection interface; authifi and google-workspace use this pattern.
  • handler() returns a CommandOutcome (sync or Promise<CommandOutcome>). See Handler return values below.
  • connectorHttpRequest() from clients/http-connector-client.ts is the preferred outbound HTTP helper. It enforces the SSRF URL policy, pins DNS against rebinding, and returns { status, isSuccessful, body } (it throws ConnectorHttpError only 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:

  • payload vs variables — both are populated on success; neither is automatic. The outputs: [...] array on the command definition is declarative metadata only — it never filters, validates, or copies between the two. Populate both slots explicitly.
  • payload is the slot consumers read by path ({{<step.outputKey>.id}}). Entitlement mapping (externalIdFrom, externalEmailFrom, attributesFrom) and PAM session checkout only read payload.
  • variables is 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 into variables.
  • diagnostics is failure-only. outcomeToResult only attaches diagnostics to 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.
  • throw vs return fail(...): both surface as success: false, but only thrown errors flow through errorMap. Throw the connector's own error class (e.g. AuthifiError, ConnectorHttpError) when you want errorMap to produce a typed { error, diagnostics } payload; return fail({ error: "..." }) for ad-hoc failures that don't need translation. If no errorMap match, the dispatcher falls back to error.message (or String(error) for non-Error throws).
  • Failing with variables: legal and supported. fail({ error: "...", variables: { ... } }) (or the explicit ConnectorResult shape) 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:

  1. Registers the handler with registerConnector (via loadBuiltInConnectors → registerDefinition), making executeConnector("widget", …) work at runtime.
  2. Emits a ConnectorSeed on next boot (via toConnectorSeed), creating the matching connector_type row 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: true on new connectors. Even ones that use defaultCommand should 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

POST /api/connectors/:id/test

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 ConnectorResult with payload, variables, and diagnostics
  • 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 post with an X-HTTP-Method-Override: PATCH header if your target API accepts it, or model the operation as a built-in connector that calls connectorHttpRequest(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.params.command; // "listContacts"
floh.params.limit; // 50
floh.params.userId; // "user-123"

floh.variables

The workflow run's current variable state:

floh.variables.initiated_by; // "user-456"
floh.variables.project_id; // "proj-789"

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 legacy data / outputVariables keys 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

  1. Navigate to Connectors → Import from OpenAPI
  2. Paste the OpenAPI 3.x JSON spec
  3. Click Parse Spec to preview generated commands
  4. Select/deselect commands to include
  5. Review the generated script and connection config
  6. 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 data with payload (success) or diagnostics (failure), and replaces outputVariables with variables. 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.ts but is not yet wired into route-registry.ts, so /api/connectors/agents/ws is 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

  1. Ask the AI: "Create a connector for the Acme CRM API"
  2. Provide the OpenAPI spec or describe the API endpoints
  3. The AI uses parse_openapi_spec or generate_connector_script
  4. Review and refine the generated connector
  5. The AI uses create_connector and test_connector to deploy and verify