ADR-0029: The connector manifest schema and how a spec uses a component¶
- Status: Accepted
- Date: 2026-10-06
- Deciders: Jian (product owner)
Context¶
ADR-0025 decided that components are hybrid: declarative manifests interpreted by the engine, plus reviewed TypeScript modules, under one operation contract. This ADR fixes the shape of that contract before the connector framework (KAN-1832 and its parts) is built, because every later component builds on it.
Today a connector operation touches five places: the toolSchema union in packages/spec, the
buildToolFromForm builder, the canvas ToolForm, the engine's buildToolRequest switch
(http-tool.ts, which hard-codes the Slack and Gmail request shapes), and the exporter's vendored
runtime copy (ADR-0010). A manifest expresses that translation as data so none of those need to
change per connector.
Decision¶
- A spec references a component with a new tool variant
action: component:{ name, action: component, use: <id>@<version>, op, with: {...}, secrets?: { <slot>: ${ENV} } }. Workflowtype: toolsteps are unchanged. The legacy kindshttp_request,slack_post_messageandgmail_sendstay valid indefinitely and are desugared to component calls when a spec loads; the published JSON Schema keeps validating them. - Two manifest kinds share one operation contract:
kind: rest(declarative) andkind: module(TypeScript entry exportinginvoke(op, input, ctx), run behind theRunnerinterface of ADR-0026). A module sees only aComponentContext(secrets.get(slot), an egress-checkedfetch, anAbortSignal), neverprocess.env. - An operation declares:
title,effect: read | write | destructive, aninputJSON Schema subset (drives validation and canvas forms), arequestdescription (method,url,headers,query,body), aresponsemode (json | text | bytes),failure_whenrules, an optionaloutputschema,pace,retryandfixture_key.effect: destructiverequires approval unless the spec overrides it. - One shared
RequestSpectype describes the request/response/failure/pacing/retry fields. It is built first as thehttp_requestv2 work (KAN-1845, KAN-1846); a manifest op embeds it, andhttp_requestbecomes an anonymous inline op. It is not implemented twice. - Auth is separate from operations:
auth.slots.<name>has a defaultenv,hostsit may be sent to, and aninjectrule (headerorqueryplus atemplateover{{ secret }}). The engine injects after templating, so a secret never appears in input, fixtures or logs. A spec may remap a slot's env var withsecrets. - Templating is plain
{{ path }}interpolation only in manifests. It is independent of the expression-language spike (ADR-0027) and can be extended later without breaking manifests. - Paths (
extract,failure_when) use a minimal syntax: dotted keys,["quoted keys"]and numeric indexes. No full JSONPath. - Hosts that depend on configuration: an op may take non-secret
configparams, andegressandurlmay reference them ({{ config.project }}.supabase.co). The permission check runs on the resolved host. Non-HTTP transports usehost:portentries, enforced through the modulectx. A spec may set a config value to a whole-value${ENV}reference (KAN-1844) so one spec works across deployments; the resolved value is checked against the param's pattern like a literal. Config is not secret, so the spec lint refuses a variable named like a credential; a multi-part credential (IMAP user and password) is several auth slots, each bound to its hosts. - Versions: a spec names an exact
id@version; the digest is recorded inkampong.lock(ADR-0025). No ranges. - One error shape:
{ code, op, message, retryable, status? }with codesinput,permission,auth,rate_limit,failure_when,http,timeout. Retries apply only toretryableerrors; failures always surface visibly. - Record and replay moves to the operation boundary (KAN-1833): key is component digest, op and a canonical hash of the redacted input.
Consequences¶
- KAN-1832 is split into four pull requests: A schema and interpreter, B registry and loader (KAN-1884),
C canvas forms (KAN-1885), D migration and exporter vendoring (KAN-1886).
http_requestv2 (KAN-1845, KAN-1846) lands first. - Moving fixtures above
fetchmeans fixtures recorded at the fetch level need re-recording; none are committed in this repository. - Permission and slot fields are in the schema from the first release so enforcement (KAN-1835) lands without a breaking change.
- Not covered here: trigger manifests, workflow steps, an MCP
source, registry signing.