Fibric. Docs fibric.io →
v1.0.0 ยท stable
Build

Connector SDK

The Connector SDK is one shape for everything that plugs into Fibric: SaaS APIs, hardware gateways, and AI operators all ship as connectors, declared with defineConnector(), built from tool() definitions, authenticated by declaration. You write the honest call to the target system; the runtime supplies per-tenant credentials, rate-limited HTTP, idempotency, and tracing, and the governed executor stands in front of every side effect. This page is the map of the Build section.

Philosophy

Two design decisions explain everything else in the SDK.

Capability, then connector. Operators never name vendors. An operator requires orders.hold; configuration binds that capability to whichever connector provides it. Your connector's job is to provide capabilities well, and the reward is that any operator asking for them works against your connector without either side knowing the other exists. Swapping Kustomer for Zendesk under a running operator is a binding change, not a rewrite, and that indirection is a platform tenet, not a convention.

Everything is MCP. A connector is an MCP server: its tools are MCP tools, its events feed the envelope stream, and the kernel speaks the same protocol to a helpdesk, a BACnet gateway, and an operator pack. The kernel hardcodes no integration. What Fibric adds on top of the protocol is the governance: a deterministic executor that evaluates every side-effecting call against a fail-closed trust policy, serializes per entity, dedupes on idempotency keys, and writes a receipt.

i
What the SDK deliberately does not include

No retry helpers, no credential storage, no policy hooks. Retries and rate limits belong to the runtime's HTTP client, secrets to the tenant's secret store, policy to the executor. A connector that reimplements any of these is harder to review and no safer. Keep the handler to one honest call.

Install

The SDK is a TypeScript package. It has no runtime dependencies of its own; the helpers return plain data the platform reads.

terminal
npm install @fibric/connector-sdk

# scaffold a connector project with fixtures and CI wiring
fibric init my-connector --template connector

The three exports

The whole public surface of @fibric/connector-sdk is three exports and their types. Everything on the following pages is detail on these.

ExportTakesReturnsJob
defineConnector()ConnectorDefConnectorDefDeclares the connector: id, version, category, auth, tools, events, probe. The def is the whole contract; the platform reads it to register capabilities and publish the listing.
tool()ToolDefToolDefDeclares one capability: an optional input validator, the sideEffecting flag that decides its execution path, and the handler.
oauth2(), apiKey(), none()optionsAuthSchemaDeclares how credentials are obtained. Shape only; the runtime resolves the material per tenant from the secret store at call time.

The division of labor between your code and the runtime is fixed, and it is worth internalizing before writing a handler:

ConcernOwner
The call to the target systemYour handler
Argument validationYour input function (Zod in practice)
Credentials, storage and injectionRuntime (tenant secret store, ctx.http)
Rate limiting and retriesRuntime (ctx.http)
Policy, single-flight, idempotency dedupDeterministic executor
Receipts and tracingKernel

The Build section

The section is ordered the way a connector gets built: declare it, wire its tools and auth, package it if it is an operator, test it, publish it.

End to end in one file

A compact but complete connector: one read, one governed write, one event stream, a probe. Everything the marketplace, the executor, and the dev harness need is in this def.

connectors/brightdesk/index.ts
import { defineConnector, tool, apiKey } from '@fibric/connector-sdk';

export default defineConnector({
  id: 'cn-brightdesk',
  version: '1.0.0',
  category: 'comms',
  auth: apiKey(),                       // shape only; the runtime holds the key

  tools: {
    'conversation.read': tool({
      input: (a) => ({ conversation_id: String((a as any).conversation_id) }),
      handler: (ctx, args) => {
        ctx.log('read', { id: args.conversation_id });
        return { id: args.conversation_id, status: 'open' };
      },
    }),
    'note.write': tool({
      sideEffecting: true,              // executor + trust policy + idempotency
      input: (a) => {
        const x = a as { conversation_id?: unknown; body?: unknown };
        if (typeof x.conversation_id !== 'string' || typeof x.body !== 'string')
          throw new Error('conversation_id and body: string required');
        return { conversation_id: x.conversation_id, body: x.body };
      },
      handler: async (ctx, args) => ({ ok: true }),
    }),
  },

  events: {
    'conversation.created': { kind: 'webhook', topic: 'conversations' },
  },

  probe: () => ({ status: 'ok' }),
});

From here: fibric dev runs it against the local kernel, fibric dev replay pushes recorded envelopes through it, and fibric publish submits it for review. The read is callable by any operator sensing state; the write is reachable only through a validated ExecutionPlan, which is the entire point.

The same shape for hardware

Nothing in the SDK is SaaS-specific, and the marketplace's building-systems catalog is built from the same def shape as the helpdesk example above. A BACnet gateway connector differs from Brightdesk in exactly three ways: category hardware, an auth kind suited to the endpoint (basic or mtls, declared as a literal AuthSchema since only the three common kinds have helpers), and events that are poll rather than webhook, because a building does not call you back.

the hardware diff, in full
export default defineConnector({
  id: 'cn-acme-bms',
  version: '0.4.0',
  category: 'hardware',
  auth: { kind: 'basic' },                       // no helper needed; the shape is the API
  tools: {
    'hvac.zone.read': tool({ handler: readZone }),
    'hvac.setpoint':  tool({ sideEffecting: true, input: SetpointArgs, handler: writeSetpoint }),
  },
  events: {
    'hvac.zone.fault': { kind: 'poll' },         // pull, not push
  },
  probe: (ctx) => ({ status: 'ok', metric: { label: 'zones online', value: 48 } }),
});

Everything downstream is identical: a proposed hvac.setpoint passes the same trust evaluation, holds the same per-zone single-flight lock, and leaves the same receipt as a proposed note.write. That a thermostat write and a helpdesk note flow through identical machinery is the point of the envelope, and of the SDK.

Keep going