# SDK reference

> `@executor-js/sdk` exports: `createExecutor`, plugin wiring, tool listing and invocation, connection APIs, policy helpers, typed IDs, and promise-mode entry points.

- Repository: RhysSullivan/executor
- GitHub: https://github.com/RhysSullivan/executor
- Human docs: https://grok-wiki.com/public/docs/rhyssullivan-executor-564383868052
- Complete Markdown: https://grok-wiki.com/public/docs/rhyssullivan-executor-564383868052/llms-full.txt

## Source Files

- `packages/core/sdk/package.json`
- `packages/core/sdk/src/executor.ts`
- `packages/core/sdk/src/promise.ts`
- `packages/core/sdk/src/ids.ts`
- `packages/core/sdk/src/policies.ts`
- `packages/core/sdk/src/errors.ts`
- `packages/core/sdk/src/elicitation.ts`

---

---
title: "SDK reference"
description: "`@executor-js/sdk` exports: `createExecutor`, plugin wiring, tool listing and invocation, connection APIs, policy helpers, typed IDs, and promise-mode entry points."
---

`@executor-js/sdk` is the programmatic surface for composing an Executor runtime in application code: register integrations through protocol plugins, create owner-scoped connections, list and invoke tools by address, manage tool policies, and shut down cleanly. The published package defaults to a Promise façade; the Effect-native core entry remains available for plugin authors and hosts that already run on Effect.

## Package entry points

| Import path | Audience | Returns |
| --- | --- | --- |
| `@executor-js/sdk` / `@executor-js/sdk/promise` | Application embedders | Promise-based `createExecutor`, domain types, tagged errors |
| `@executor-js/sdk/core` | Hosts and Effect-native callers | Effect-based `createExecutor`, full plugin SDK, FumaDB helpers |
| `@executor-js/sdk/shared` | Browser UI, plugin client code | Branded IDs, domain types, policy helpers (no Node/FumaDB) |
| `@executor-js/sdk/client` | Plugin React UI | `defineClientPlugin`, Atom/HttpApi reactivity primitives |
| `@executor-js/sdk/testing` | Tests | `makeTestExecutor`, OAuth test server, SQLite test DB |
| `@executor-js/sdk/http-auth` | Protocol plugins | Placement-based auth template vocabulary (`variable`, `ApiKeyAuthTemplate`, …) |

<Note>
Published npm types for the root export resolve to the Promise surface (`dist/promise.d.ts`). Plugin factories remain Effect-native; the Promise proxy unwraps plugin extension methods (for example `executor.openapi.addSpec`) into `async` calls.
</Note>

## `createExecutor`

### Promise mode (recommended for embedders)

```typescript
import { createExecutor } from "@executor-js/sdk/promise";
import { openApiPlugin } from "@executor-js/plugin-openapi/promise";

const executor = await createExecutor({
  plugins: [openApiPlugin()],
  providers: [memoryProvider],
  onElicitation: "accept-all",
});

await executor.close();
```

<ParamField body="tenant" type="string">
Org/workspace partition. Defaults to `"default-tenant"`. `owner: "org"` rows file here.
</ParamField>

<ParamField body="subject" type="string">
Acting member identity. Omit for a pure-org executor that cannot write `owner: "user"` connections.
</ParamField>

<ParamField body="plugins" type="readonly AnyPlugin[]">
Protocol and provider plugins to load. Each plugin contributes an extension namespace keyed by `plugin.id` (for example `openapi`, `mcp`).
</ParamField>

<ParamField body="providers" type="readonly CredentialProvider[]" required={false}>
Config-level credential backends, merged with every `plugin.credentialProviders`. Config providers register first, so the default writable store is chosen from them when present. A writable provider is required before `connections.create({ value })` can persist an inline credential.
</ParamField>

<ParamField body="db" type="FumaDb | factory" required={false}>
FumaDB handle or factory receiving `collectTables()`. When omitted, `createExecutor` provisions an in-memory database.
</ParamField>

<ParamField body="onElicitation" type="PromiseOnElicitation" required>
How to answer mid-invocation user prompts. Pass `"accept-all"` for tests and non-interactive hosts, or `(ctx) => ElicitationResponse | Promise<ElicitationResponse>` for interactive flows. Required at construction so callers do not thread per-invoke options.
</ParamField>

### Effect mode (hosts and plugin authors)

```typescript
import { Effect } from "effect";
import { createExecutor } from "@executor-js/sdk/core";

const executor = await Effect.runPromise(
  createExecutor({
    tenant: Tenant.make("acme"),
    subject: Subject.make("user-1"),
    plugins: [openApiPlugin()],
    onElicitation: "accept-all",
  }),
);

await Effect.runPromise(executor.close());
```

The Effect `ExecutorConfig` adds host-only options not exposed on the Promise config:

| Option | Purpose |
| --- | --- |
| `blobs` | Override the plugin blob store (default: FumaDB `blob` table) |
| `httpClientLayer` | Custom `HttpClient` layer for plugin HTTP |
| `fetch` | Fetch implementation when a layer is impractical |
| `redirectUri` | OAuth callback URL (`${webBaseUrl}/oauth/callback`). Omit only when the executor never runs interactive OAuth |
| `oauthEndpointUrlPolicy` | Policy for OAuth endpoint URL validation |
| `coreTools` | Enable built-in agent-facing static tools over integrations, connections, and policies |

## Executor surface

Every namespace method on the returned executor is available in both modes; Promise mode unwraps `Effect` return values into `Promise`s and rejects with tagged errors.

```text
Executor
├── integrations   list, get, update, remove, detect
├── connections    create, list, get, update, remove, refresh
├── oauth          createClient, registerDynamicClient, listClients, removeClient,
│                  start, complete, cancel, probe
├── tools          list, schema
├── providers      list, items
├── policies       list, create, update, remove, resolve
├── execute        invoke one tool by address
├── close          release DB and runtime resources
└── <pluginId>     per-plugin extensions (e.g. openapi.addSpec)
```

### Integrations

| Method | Returns | Errors |
| --- | --- | --- |
| `list()` | `readonly Integration[]` | `StorageFailure` |
| `get(slug)` | `Integration \| null` | `StorageFailure` |
| `update(slug, patch)` | `void` | `IntegrationNotFoundError`, `StorageFailure` |
| `remove(slug)` | `void` | `IntegrationRemovalNotAllowedError`, `StorageFailure` |
| `detect(url)` | `readonly IntegrationDetectionResult[]` | `StorageFailure` |

Integrations are tenant-level catalog identities. Type-specific configuration (OpenAPI spec, MCP URL, auth templates) is owned by the registering plugin and stored as opaque `config`. Use plugin extensions to add integrations (for example `executor.openapi.addSpec`); the core `integrations` namespace manages the catalog after registration.

### Connections

| Method | Purpose |
| --- | --- |
| `create(input)` | Save a credential for one integration. Identity is `(owner, integration, name)`. |
| `list(filter?)` | List connections, optionally filtered by `integration` or `owner`. |
| `get(ref)` | Fetch one connection by `ConnectionRef`. |
| `update(ref, input)` | Edit `description` or `identityLabel` only. |
| `remove(ref)` | Delete the connection and its tool rows. |
| `refresh(ref)` | Re-resolve tools from the upstream spec or server. |

<ParamField body="owner" type='"org" | "user"' required>
`"org"` for tenant-shared credentials; `"user"` for the acting subject's private credentials. Requires `subject` on the executor when `"user"`.
</ParamField>

<ParamField body="name" type="ConnectionName" required>
Connection name segment in tool addresses.
</ParamField>

<ParamField body="integration" type="IntegrationSlug" required>
Target integration slug.
</ParamField>

<ParamField body="template" type="AuthTemplateSlug" required>
Which declared auth method the credential applies through. Use `"none"` (`NO_AUTH_TEMPLATE`) for integrations that require no credential.
</ParamField>

<ParamField body="value | from | values | inputs" type="ConnectionValueInput" required={false}>
Credential origin. `value` pastes a single `token` input to the default writable provider; `from` references an external provider item; `values` and `inputs` carry multi-input maps. For OAuth, use `oauth.start` instead of inline values.
</ParamField>

Creating a connection triggers the owning plugin's `resolveTools` hook and persists the produced tools per connection.

### Tools

| Method | Purpose |
| --- | --- |
| `list(filter?)` | Return persisted, addressable tools. |
| `schema(address)` | Return `ToolSchemaView` with JSON Schema roots, shared definitions, and optional TypeScript preview strings. |

`ToolListFilter` fields:

| Field | Default | Behavior |
| --- | --- | --- |
| `integration` | — | Restrict to one integration slug |
| `owner` | — | Restrict to `org` or `user` |
| `connection` | — | Restrict to one connection name |
| `query` | — | Case-insensitive substring match on `name` or `description` |
| `includeAnnotations` | `true` | Attach plugin-derived annotations |
| `includeBlocked` | `false` | Include tools whose effective policy is `block` |

### `execute`

```typescript
const result = await executor.execute(
  "tools.inventory.org.default.listItems",
  { limit: 10 },
);
```

<ParamField body="address" type="ToolAddress" required>
Full callable address. See [Tool addresses](#tool-addresses).
</ParamField>

<ParamField body="args" type="unknown" required>
Arguments matching the tool's `inputSchema`. Omit or pass `{}` when no input is required.
</ParamField>

<ParamField body="options.onElicitation" type="OnElicitation" required={false}>
Override the executor-level elicitation handler for this call only.
</ParamField>

Invoke path (simplified):

```mermaid
sequenceDiagram
  participant App
  participant Executor
  participant Policy
  participant Plugin

  App->>Executor: execute(address, args)
  Executor->>Executor: parse address / lookup static tool
  Executor->>Policy: resolveEffectivePolicy
  alt action is block
    Executor-->>App: ToolBlockedError
  else action is require_approval or annotation
    Executor->>App: onElicitation (FormElicitation)
    App-->>Executor: accept / decline / cancel
  end
  Executor->>Executor: resolve connection credentials
  Executor->>Plugin: invokeTool / static handler
  Plugin-->>Executor: result
  Executor-->>App: unknown
```

`ExecuteError` union: `ToolNotFoundError`, `ToolInvocationError`, `ToolBlockedError`, `PluginNotLoadedError`, `NoHandlerError`, `ConnectionNotFoundError`, `CredentialProviderNotRegisteredError`, `CredentialResolutionError`, `ElicitationDeclinedError`, `StorageFailure`.

### Policies

| Method | Purpose |
| --- | --- |
| `list()` | All owner-scoped `tool_policy` rows |
| `create(input)` | Add a rule (`owner`, `pattern`, `action`, optional `position`) |
| `update(input)` | Patch an existing rule by `id` and `owner` |
| `remove(input)` | Delete a rule |
| `resolve(address)` | Compute `EffectivePolicy` for one tool address |

Policy actions: `approve`, `require_approval`, `block`. Each owner contributes its first matching rule by local position; the most restrictive matched action across owners wins, so a user preference cannot weaken an org guardrail.

Pure policy helpers (also exported for plugins and UI):

| Export | Purpose |
| --- | --- |
| `matchPattern(pattern, toolId)` | Test whether a pattern matches a tool id |
| `isValidPattern(pattern)` | Validate pattern grammar |
| `resolveToolPolicy` / `resolveEffectivePolicy` | Layered resolution with plugin default `requiresApproval` |
| `effectivePolicyFromSorted` | Resolve against a pre-sorted policy list |
| `rowToToolPolicy` | Map a DB row to `ToolPolicy` |
| `ToolPolicyActionSchema` | Effect Schema literal union for actions |

Pattern grammar (matched against `<integration>.<owner>.<connection>.<tool>` or the full static address):

| Form | Example | Matches |
| --- | --- | --- |
| Universal | `*` | Everything |
| Exact | `inventory.org.default.listItems` | One tool |
| Subtree | `inventory.org.*` | Prefix and anything deeper |
| Mid-segment wildcard | `inventory.*.*.listItems` | One segment per `*` |

### OAuth (`executor.oauth`)

Shared OAuth service used by hosts, plugins (`ctx.oauth`), and SDK consumers.

| Method | Purpose |
| --- | --- |
| `createClient(input)` | Register a static OAuth app |
| `registerDynamicClient(input)` | RFC 7591 dynamic client registration |
| `listClients()` | Metadata-only client summaries (no secrets) |
| `removeClient(owner, slug)` | Delete a registered app |
| `start(input)` | Begin a flow; returns `connected` or `redirect` with `authorizationUrl` |
| `complete(input)` | Exchange authorization code for a `Connection` |
| `cancel(state)` | Abort an in-flight session |
| `probe(input)` | Discover authorization-server metadata from a URL |

OAuth errors: `OAuthStartError`, `OAuthCompleteError`, `OAuthProbeError`, `OAuthRegisterDynamicError`, `OAuthSessionNotFoundError`.

### Providers (`executor.providers`)

| Method | Purpose |
| --- | --- |
| `list()` | Registered `ProviderKey` values |
| `items(key)` | Browse discoverable entries from a provider (for example 1Password items) |

`CredentialProvider` contract:

<ResponseField name="key" type="ProviderKey">
Provider identifier (for example `"default"`, `"1password"`, `"keychain"`).
</ResponseField>

<ResponseField name="writable" type="boolean">
When `false`, `set`/`delete` are skipped and `remove` only drops routing.
</ResponseField>

<ResponseField name="get" type="(id) => Effect<string | null>">
Resolve a value by opaque `ProviderItemId`.
</ResponseField>

<ResponseField name="set" type="(id, value) => Effect<void>" optional>
Write a value (writable providers only).
</ResponseField>

<ResponseField name="list" type="() => Effect<ProviderEntry[]>" optional>
Enumerate items for discovery UIs.
</ResponseField>

## Tool addresses

Callable tools use the five-segment form:

```text
tools.<integration>.<owner>.<connection>.<tool>
```

The `<tool>` segment is everything after the fourth dot, so dotted tool names (for example OpenAPI `aliases.deleteAlias`) address naturally as `tools.inventory.org.default.aliases.deleteAlias`.

Helpers:

| Function | Produces |
| --- | --- |
| `parseToolAddress(address)` | `ParsedToolAddress` or `null` |
| `connectionAddress(owner, integration, connection)` | `tools.<integration>.<owner>.<connection>` |
| `toolAddress(owner, integration, connection, tool)` | Full `ToolAddress` |

Static plugin tools (for example core-tools) use their fully qualified id as the address and bypass the five-segment parser.

## Typed IDs

Branded Effect Schema types. Construct with `X.make("…")`; at runtime they are plain strings.

| Export | Role |
| --- | --- |
| `Tenant` | Org/workspace partition |
| `Subject` | Acting member (required for `owner: "user"`) |
| `Owner` | `"org"` or `"user"` |
| `IntegrationSlug` | Integration catalog slug |
| `ConnectionName` | Connection name within `(integration, owner)` |
| `AuthTemplateSlug` | Auth method template slug |
| `NO_AUTH_TEMPLATE` | Sentinel `"none"` for credential-free integrations |
| `ProviderKey` / `ProviderItemId` | Credential backend routing |
| `ConnectionAddress` | `tools.<integration>.<owner>.<connection>` |
| `ToolAddress` / `ToolName` | Callable tool identity |
| `PolicyId` | Tool policy row id |
| `OAuthClientSlug` / `OAuthState` | OAuth app and flow correlation |
| `ElicitationId` | URL elicitation callback correlation |

## Plugin wiring

Plugins are registered at construction and expose two surfaces:

1. **Extension namespace** on the executor: `executor.<pluginId>.*` (typed via `PluginExtensions<TPlugins>`).
2. **Runtime hooks** the executor calls internally: `resolveTools`, `invokeTool`, `credentialProviders`, `staticTools`, `detect`, and optional HTTP API groups.

Author plugins with `definePlugin` from `@executor-js/sdk/core`. The plugin model is Effect-only (storage factories, `PluginCtx`, schema). Application embedders import prebuilt plugins (for example `@executor-js/plugin-openapi/promise`) rather than authoring plugins in Promise style.

`defineExecutorConfig` declares the plugin list for CLI and host runtimes:

```typescript
import { defineExecutorConfig } from "@executor-js/sdk/promise";
import { openApiPlugin } from "@executor-js/plugin-openapi/promise";

export default defineExecutorConfig({
  plugins: () => [openApiPlugin()],
});
```

`plugins` is always a factory so hosts can inject runtime deps (for example a config file sink keyed to the active scope cwd).

## Elicitation

When a tool needs user input mid-call, the invoke fiber suspends and the configured `onElicitation` handler decides the response.

| Request type | Fields | Use case |
| --- | --- | --- |
| `FormElicitation` | `message`, `requestedSchema` | Structured input or approval prompts |
| `UrlElicitation` | `message`, `url`, `elicitationId` | OAuth or browser approval pages |

<ResponseField name="action" type='"accept" | "decline" | "cancel"'>
Response action. Non-`accept` actions raise `ElicitationDeclinedError`.
</ResponseField>

<ResponseField name="content" type="Record<string, unknown>" optional>
Payload when `action` is `"accept"`.
</ResponseField>

`execute` enforces approval by sending a `FormElicitation` when the effective policy is `require_approval` or the tool annotation sets `requiresApproval`.

## Errors

Tagged `Schema.TaggedErrorClass` instances. Promise callers receive them as rejected values; Effect callers see them in the typed error channel.

| Error | When raised |
| --- | --- |
| `ToolNotFoundError` | Address not found; may include `suggestions` |
| `ToolInvocationError` | Handler or upstream failure (wraps `cause`) |
| `ToolBlockedError` | Matched `block` policy (`pattern` included) |
| `PluginNotLoadedError` | Tool's plugin not in this executor config |
| `NoHandlerError` | Plugin has no `invokeTool` handler |
| `IntegrationNotFoundError` | Unknown integration slug |
| `IntegrationAlreadyExistsError` | Add operation targets an existing slug (HTTP 409) |
| `IntegrationRemovalNotAllowedError` | Static plugin-declared integration |
| `ConnectionNotFoundError` | Missing `(owner, integration, name)` |
| `InvalidConnectionInputError` | Structurally invalid create input |
| `CredentialProviderNotRegisteredError` | Unknown `ProviderKey` |
| `CredentialResolutionError` | Provider returned nothing or OAuth refresh failed; may set `reauthRequired` |
| `ElicitationDeclinedError` | User declined or cancelled during elicitation |

Convenience unions: `ExecuteError` (invoke path), `ExecutorError` (broader SDK surface).

## Address and schema utilities

| Export | Purpose |
| --- | --- |
| `collectTables()` | Return executor-owned Fuma table definitions (host DB bring-up) |
| `buildToolTypeScriptPreview` | Generate TS preview strings for tool schemas |
| `ToolSchemaView` | Schema-side tool view from `tools.schema` |
| `IntegrationDetectionResult` | URL autodetect result from `integrations.detect` |
| `ToolResult` / `ToolFileSchema` | Typed discriminated union for tool outcomes |
| `defineExecutorConfig` | Typed CLI config declaration |

## Related pages

<CardGroup>
<Card title="Embed with the SDK" href="/embed-sdk">
End-to-end workflow: compose plugins, register integrations, create connections, list and invoke tools, and shut down.
</Card>
<Card title="SDK quickstart example" href="/sdk-quickstart-example">
Walkthrough of `examples/docs-sdk-quickstart` with in-memory credentials and OpenAPI `addSpec`.
</Card>
<Card title="Plugin catalog" href="/plugin-catalog">
Published protocol and provider plugins and how `examples/all-plugins` wires them.
</Card>
<Card title="Tools" href="/tools">
Tool addresses, discovery, schema inspection, and invocation across surfaces.
</Card>
<Card title="Connections" href="/connections">
Owner-scoped credential model and OAuth minting.
</Card>
<Card title="Policies" href="/policies">
Per-tool allow, require-approval, and block actions with pattern matching.
</Card>
<Card title="HTTP API reference" href="/http-api-reference">
HTTP routes that mirror the SDK namespaces for hosted and daemon runtimes.
</Card>
</CardGroup>
