Protocol Adapters Pack
The Protocol Adapters Pack (@lumenflow/packs-protocol-adapters, WU-3521)
ingests third-party API descriptions and turns them into immutable,
content-addressed tool manifests that can be invoked with schema-validated
typed arguments under the same governance discipline as every other
LumenFlow tool.
What it provides
Section titled “What it provides”ingestOpenApiDocument(input)— parses an OpenAPI 2.x/3.x document (a parsed object, or a JSON/YAML string), validates required structure (openapi/swaggerversion,paths,info.title+info.version, at least oneservers[]entry, a uniqueoperationIdper operation), resolves local#/components/schemas/...$refs in request-body schemas, and returns a frozen, content-addressedOpenApiToolManifest. The manifest’sdigestis a stablesha256:hash, so re-ingesting the same document — as an object or an equivalent JSON/YAML string — always reproduces the same digest.invokeOpenApiOperation({ manifest, operationId, args, transport, allowedHosts })— looks up the operation, validatesargsagainst its per-bucket (path/query/headers/body) input schema, and fails closed with aninvalid_argumentsresult — never callingtransport— on the first validation failure. Only once arguments are valid does it resolve the target host and check it against the caller-suppliedallowedHosts; an unlisted host fails closed withegress_denied, again beforetransportis ever called.runOpenApiAdapterConformanceSuite({ transport })— a fixed conformance harness (stable digest, re-ingest idempotence, fail-closed invalid args, fail-closed disallowed host, successful invoke) exercised against a published, realistic “Petstore”-style golden fixture.createProtocolAdaptersCapabilityFactory— derives anetwork/allowlistscope entry fromprotocol_adapters.allowed_hostsin pack config, using the same host/CIDR grammar the kernel sandbox’s own network allowlist uses.
Supported OpenAPI subset
Section titled “Supported OpenAPI subset”ingest.ts intentionally accepts a bounded OpenAPI/JSON-Schema subset: it
rejects oneOf/anyOf/allOf/not and external $refs, and carries only
type/properties/required/items/enum/format/minLength/
maxLength/minimum/maximum/additionalProperties through to the
manifest. requestBody.content is read from the application/json key
only. Local #/components/schemas/... $refs in request-body schemas
resolve up to a depth of 20 with circular-$ref detection; external/remote
$refs and #/components/parameters/... refs are rejected. Cookie-location
parameters, multiple servers, security schemes, and non-JSON request
bodies are out of scope today.
This is a deliberate design choice, not a limitation to work around: a
general-purpose JSON Schema validator (ajv or similar) would accept a
strictly larger grammar than ingest.ts can ever produce, would add a new
runtime dependency to this pack’s SBOM, and would buy no additional
correctness for the subset actually in play. invoke.ts instead validates
against that exact bounded subset with a small hand-rolled walker.
Egress
Section titled “Egress”invoke.ts never bypasses the kernel sandbox’s network allowlist and does
not implement a second egress-control path: before calling the
caller-supplied transport, it resolves the target host from the
manifest’s recorded servers[0] and rejects the call with egress_denied
unless the host matches one of the caller-supplied allowedHosts entries —
using the exact same allowlist grammar (hostname, host:port, or IPv4
CIDR) the kernel sandbox itself uses, imported rather than reimplemented.
Actual OS-level egress enforcement (bwrap/iptables on Linux,
sandbox-exec/SBPL on macOS) remains the kernel sandbox’s job; this check
is fail-closed defense-in-depth.
Current status: not yet kernel-callable
Section titled “Current status: not yet kernel-callable”manifest.yaml declares:
This is by design, not an oversight: an OpenAPI document’s operations cannot be statically predeclared as kernel tools at pack-authoring time, because they depend on whatever document gets ingested at runtime.
A consequence follows directly from that: the kernel only invokes a pack’s
capability_factory once per tool declared in that pack’s own manifest
tools: list. Because that list is empty today,
createProtocolAdaptersCapabilityFactory is never invoked by the kernel —
it is exercised only when a caller imports and calls it directly, which is
exactly what this pack’s own tests do. It activates automatically, with no
code change required in this pack, the moment a future WU registers an
OpenAPI-derived tool in this manifest (tracked as a dynamic-tool-registration
concern, independently shippable from this pack).
In short: an agent cannot call openapi:<operation_id> today. What ships
now is the ingest/invoke/conformance library surface that a future
tool-registration layer will sit on top of, proven correct against a golden
fixture in isolation from that future wiring.
Related
Section titled “Related”- Kernel: Tool Execution — how the kernel invokes
a pack’s
capability_factoryfor a declared tool. - Pack Authoring: Quickstart — how a pack
manifest declares
tools:andcapability_factory.