ADR 0180 — External connections are a separate tools subexport
Decision
stitchkit/tools/connections consumes MCP and OpenAPI without adding peers to
the stable tools entrypoint. Definitions mount through the existing mountAgent
lifecycle, hooks and approval machinery exactly once. Schema budget remains
visible through the catalog.
Credentials are resolved for every invocation from the handler context; there
is no shared token cache. Discovery is a separate handshake with no runtime
principal context. MCP discovery and each call own separate clients, all torn
down on completion or refusal. A 401 raises
ConnectionAuthorizationRequiredError; through mountAgent, the typed cause
is retained on AgentToolError.cause while model-facing output stays generic.
The host handles reauthorization and the next call resolves credentials again.
A 403 remains a request refusal.
Streamable HTTP falls back to legacy SSE only on 400, 404 or 405. SSE endpoint
readiness and response waiting have deadlines and propagate cancellation.
Response bodies and frames are bounded; teardown cancels readers and waiters.
Redirects are refused on every request, including spec loading. A remote spec
cannot authorize its own foreign server: extra hosts require allowHosts or
an explicit application baseUrl. Configured hosts are trusted endpoints;
this is a host fence, not a DNS-rebinding or private-network classifier.
OpenAPI subset
JSON documents, inline or URL; GET/HEAD/POST/PUT/PATCH/DELETE; scalar path, query, header and cookie parameters; JSON request bodies. Local references resolve with bounded expansion. External/recursive references, server variables, structured parameter serialization, non-JSON bodies and combined security schemes are refused at mount. Security supports one bearer/basic/API-key scheme per alternative; OAuth/OIDC use a host-provided bearer, not an OAuth client. Unknown security requirements are refused. Response schemas are not compiled: the bounded response is decoded as JSON or text and has an unknown output type.
Boundaries
This is a bounded client implementation, not a claim of complete MCP/OpenAPI standard coverage. URL configuration and provider grants remain host-owned.