@intentface/latch-openapi 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Intentface
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @intentface/latch-openapi
2
+
3
+ Turn an **OpenAPI 3.x** document into agent tools — the "API without an MCP server" path. Each operation becomes a callable tool, no hand-written glue.
4
+
5
+ ## What it does
6
+
7
+ - **`createOpenApiConnections` / `defineOpenApiConnection`** — fetch + dereference an OpenAPI spec and expose its operations as tools, scoped per connection. `operations.allow` restricts which operations are exposed.
8
+ - **SSRF allowlist** (`allowedHosts`) — bounds which hosts a connection may call (derived from the spec/base URLs).
9
+ - **Brokered auth** (`headers` / `apiKey` helpers) — inject the credential server-side (from the Vault); the token never reaches the model.
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { createOpenApiConnections, defineOpenApiConnection, apiKey } from "@intentface/latch-openapi";
15
+
16
+ const openapi = createOpenApiConnections<Principal>({
17
+ allowedHosts: ["api.example.com"],
18
+ connections: {
19
+ example: defineOpenApiConnection<Principal>({
20
+ spec: "https://api.example.com/openapi.json",
21
+ auth: apiKey({ header: "X-Api-Key", getKey: (p) => vault.get(p, "example_key", "org") }),
22
+ }),
23
+ },
24
+ });
25
+ ```
26
+
27
+ ## Where it fits
28
+
29
+ A connection source for `@intentface/latch-core`'s registry, alongside `@intentface/latch-mcp`. Merge them with `mergeConnections(...)`.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Auth for OpenAPI connections — an opaque header resolver.
3
+ *
4
+ * The connection's auth is `(principal) => headers`: it runs server-side at call
5
+ * time and returns the headers to attach to the outgoing request. Where the
6
+ * credential comes from — and at what scope (per-user vs org-shared) — is the
7
+ * caller's choice, expressed inside the resolver (e.g. reading the Vault at a
8
+ * user- or org-scoped principal). The framework never sees the secret in agent
9
+ * config, and it never reaches the model.
10
+ */
11
+ export type OpenApiAuth<P> = (principal: P) => Record<string, string> | Promise<Record<string, string>>;
12
+ /** Bearer token → `Authorization: Bearer <token>`. */
13
+ export declare function bearer<P>(opts: {
14
+ getToken: (principal: P) => string | undefined | Promise<string | undefined>;
15
+ }): OpenApiAuth<P>;
16
+ /** API key in a named header (e.g. `X-Api-Key`). Missing key → no header (call likely 401s). */
17
+ export declare function apiKey<P>(opts: {
18
+ header: string;
19
+ getKey: (principal: P) => string | undefined | Promise<string | undefined>;
20
+ }): OpenApiAuth<P>;
21
+ /** Static headers (no per-principal resolution). */
22
+ export declare function headers<P = unknown>(h: Record<string, string>): OpenApiAuth<P>;
23
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAC3B,SAAS,EAAE,CAAC,KACT,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9D,sDAAsD;AACtD,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE;IAC9B,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC9E,GAAG,WAAW,CAAC,CAAC,CAAC,CAOjB;AAED,gGAAgG;AAChG,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC5E,GAAG,WAAW,CAAC,CAAC,CAAC,CAOjB;AAED,oDAAoD;AACpD,wBAAgB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAE9E"}
package/dist/auth.js ADDED
@@ -0,0 +1,25 @@
1
+ /** Bearer token → `Authorization: Bearer <token>`. */
2
+ export function bearer(opts) {
3
+ return async (principal) => {
4
+ const token = await opts.getToken(principal);
5
+ const h = {};
6
+ if (token)
7
+ h.Authorization = `Bearer ${token}`;
8
+ return h;
9
+ };
10
+ }
11
+ /** API key in a named header (e.g. `X-Api-Key`). Missing key → no header (call likely 401s). */
12
+ export function apiKey(opts) {
13
+ return async (principal) => {
14
+ const key = await opts.getKey(principal);
15
+ const h = {};
16
+ if (key)
17
+ h[opts.header] = key;
18
+ return h;
19
+ };
20
+ }
21
+ /** Static headers (no per-principal resolution). */
22
+ export function headers(h) {
23
+ return () => ({ ...h });
24
+ }
25
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAcA,sDAAsD;AACtD,MAAM,UAAU,MAAM,CAAI,IAEzB;IACC,OAAO,KAAK,EAAE,SAAS,EAAE,EAAE;QACzB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,CAAC,GAA2B,EAAE,CAAC;QACrC,IAAI,KAAK;YAAE,CAAC,CAAC,aAAa,GAAG,UAAU,KAAK,EAAE,CAAC;QAC/C,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;AACJ,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,MAAM,CAAI,IAGzB;IACC,OAAO,KAAK,EAAE,SAAS,EAAE,EAAE;QACzB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,GAA2B,EAAE,CAAC;QACrC,IAAI,GAAG;YAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;QAC9B,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;AACJ,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,OAAO,CAAc,CAAyB;IAC5D,OAAO,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { isHostAllowed } from "@intentface/latch-net";
2
+ import type { OpenApiAuth } from "./auth.js";
3
+ /**
4
+ * A custom fetch. Input is `string | URL` only (all call sites pass strings)
5
+ * so that non-global fetch implementations — undici's own, the DNS-pinned
6
+ * `@intentface/latch-net/pinned` — are assignable: they reject foreign
7
+ * `Request` objects at runtime. `globalThis.fetch` still satisfies this.
8
+ */
9
+ export type FetchFunction = (input: string | URL, init?: RequestInit) => Promise<Response>;
10
+ /** Which operations require human approval. Default `"mutating"` (gate non-GET). */
11
+ export type OpenApiApprovalPolicy = "mutating" | "all" | "none" | ((op: {
12
+ operationId: string;
13
+ method: string;
14
+ }) => boolean);
15
+ export { isHostAllowed };
16
+ /** Minimal OpenAPI shapes we read (the doc is already dereferenced). */
17
+ interface Oap {
18
+ servers?: {
19
+ url: string;
20
+ }[];
21
+ paths?: Record<string, Record<string, OapOperation>>;
22
+ }
23
+ interface OapParam {
24
+ name: string;
25
+ in: "path" | "query" | "header" | "cookie";
26
+ required?: boolean;
27
+ description?: string;
28
+ schema?: Record<string, unknown>;
29
+ }
30
+ interface OapOperation {
31
+ operationId?: string;
32
+ summary?: string;
33
+ description?: string;
34
+ parameters?: OapParam[];
35
+ requestBody?: {
36
+ required?: boolean;
37
+ content?: Record<string, {
38
+ schema?: Record<string, unknown>;
39
+ }>;
40
+ };
41
+ }
42
+ export interface BuiltTools {
43
+ tools: Record<string, unknown>;
44
+ toolApproval: Record<string, unknown>;
45
+ }
46
+ /**
47
+ * Turn a dereferenced OpenAPI 3.x document into one tool per operation. Tools
48
+ * are principal-independent (auth is resolved at call time from the tool
49
+ * context), so they can be built once and reused across turns/tenants.
50
+ */
51
+ export declare function buildOpenApiTools<P>(args: {
52
+ name: string;
53
+ doc: Oap;
54
+ baseUrl: string;
55
+ auth?: OpenApiAuth<P>;
56
+ approval?: OpenApiApprovalPolicy;
57
+ operations?: {
58
+ allow?: string[];
59
+ block?: string[];
60
+ };
61
+ allowedHosts: string[];
62
+ fetchFn: FetchFunction;
63
+ /**
64
+ * Skip the private/internal-address SSRF backstop for this connection.
65
+ * Mirrors the MCP host's `allowPrivateHost`: set it only for a trusted
66
+ * local/internal API — the allowlist check still applies. This skips the
67
+ * PRE-CHECK only: if `fetchFn` is a DNS-pinned fetch (recommended —
68
+ * `@intentface/latch-net/pinned`), a private backend also needs
69
+ * `createPinnedFetch({ allowPrivate: true })` injected.
70
+ */
71
+ allowPrivateHost?: boolean;
72
+ }): BuiltTools;
73
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,aAAa,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,KAAK,EAAE,MAAM,GAAG,GAAG,EACnB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB,oFAAoF;AACpF,MAAM,MAAM,qBAAqB,GAC7B,UAAU,GACV,KAAK,GACL,MAAM,GACN,CAAC,CAAC,EAAE,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,CAAC;AAO/D,OAAO,EAAE,aAAa,EAAE,CAAC;AAiBzB,wEAAwE;AACxE,UAAU,GAAG;IACX,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;CACtD;AACD,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AACD,UAAU,YAAY;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE;QACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAC,CAAC;KAChE,CAAC;CACH;AAoCD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,IAAI,EAAE;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACtB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,UAAU,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,UAAU,CAsGb"}
package/dist/build.js ADDED
@@ -0,0 +1,145 @@
1
+ import { tool, jsonSchema } from "ai";
2
+ import { isBlockedHost, isBlockedResolved, isHostAllowed, } from "@intentface/latch-net";
3
+ const HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
4
+ // Canonical implementation lives in @intentface/latch-net; re-exported here
5
+ // for callers that historically imported it from this package.
6
+ export { isHostAllowed };
7
+ function needsApproval(policy, op) {
8
+ const p = policy ?? "mutating";
9
+ if (typeof p === "function")
10
+ return p(op);
11
+ if (p === "all")
12
+ return true;
13
+ if (p === "none")
14
+ return false;
15
+ return op.method.toLowerCase() !== "get"; // "mutating"
16
+ }
17
+ function sanitizeName(s) {
18
+ return s.replace(/[^a-zA-Z0-9_-]/g, "_");
19
+ }
20
+ /** Build the `{ path?, query?, body? }` input schema for one operation. */
21
+ function inputSchemaFor(op) {
22
+ const properties = {};
23
+ const required = [];
24
+ const group = (loc) => {
25
+ const params = (op.parameters ?? []).filter((p) => p.in === loc);
26
+ if (params.length === 0)
27
+ return undefined;
28
+ const props = {};
29
+ const req = [];
30
+ for (const p of params) {
31
+ props[p.name] = { ...(p.schema ?? { type: "string" }), description: p.description };
32
+ if (p.required)
33
+ req.push(p.name);
34
+ }
35
+ return { type: "object", properties: props, required: req, additionalProperties: false };
36
+ };
37
+ const path = group("path");
38
+ if (path) {
39
+ properties.path = path;
40
+ required.push("path");
41
+ }
42
+ const query = group("query");
43
+ if (query)
44
+ properties.query = query;
45
+ const bodySchema = op.requestBody?.content?.["application/json"]?.schema;
46
+ if (bodySchema) {
47
+ properties.body = bodySchema;
48
+ if (op.requestBody?.required)
49
+ required.push("body");
50
+ }
51
+ return { type: "object", properties, required, additionalProperties: false };
52
+ }
53
+ /**
54
+ * Turn a dereferenced OpenAPI 3.x document into one tool per operation. Tools
55
+ * are principal-independent (auth is resolved at call time from the tool
56
+ * context), so they can be built once and reused across turns/tenants.
57
+ */
58
+ export function buildOpenApiTools(args) {
59
+ const { name, doc, baseUrl, auth, approval, operations, allowedHosts, fetchFn, allowPrivateHost, } = args;
60
+ const tools = {};
61
+ const toolApproval = {};
62
+ const base = baseUrl.replace(/\/$/, "");
63
+ const allowed = (opId) => {
64
+ if (operations?.block?.includes(opId))
65
+ return false;
66
+ if (operations?.allow && !operations.allow.includes(opId))
67
+ return false;
68
+ return true;
69
+ };
70
+ for (const [rawPath, methods] of Object.entries(doc.paths ?? {})) {
71
+ for (const method of HTTP_METHODS) {
72
+ const op = methods[method];
73
+ if (!op)
74
+ continue;
75
+ const operationId = op.operationId ?? `${method}_${rawPath.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_|_$/g, "")}`;
76
+ if (!allowed(operationId))
77
+ continue;
78
+ const key = sanitizeName(`${name}_${operationId}`);
79
+ const opPath = rawPath;
80
+ tools[key] = tool({
81
+ description: op.summary ?? op.description ?? `${method.toUpperCase()} ${rawPath}`,
82
+ inputSchema: jsonSchema(inputSchemaFor(op)),
83
+ execute: async (input, options) => {
84
+ const { path = {}, query = {}, body } = (input ?? {});
85
+ // Template path params, then assemble the URL.
86
+ let urlPath = opPath;
87
+ for (const [k, v] of Object.entries(path)) {
88
+ urlPath = urlPath.replace(`{${k}}`, encodeURIComponent(String(v)));
89
+ }
90
+ const url = new URL(base + urlPath);
91
+ for (const [k, v] of Object.entries(query)) {
92
+ if (v !== undefined && v !== null)
93
+ url.searchParams.set(k, String(v));
94
+ }
95
+ // SSRF guard — the resolved host must be on the allowlist.
96
+ if (!isHostAllowed(url.hostname, allowedHosts)) {
97
+ return { error: `host not allowed: ${url.hostname}` };
98
+ }
99
+ // Defense-in-depth beyond the allowlist: reject private/internal
100
+ // addresses and DNS rebinding (an allowlisted name that resolves to
101
+ // a private/link-local IP — e.g. cloud metadata at 169.254.169.254).
102
+ // Opt out per connection for a trusted local backend.
103
+ if (!allowPrivateHost &&
104
+ (isBlockedHost(url.hostname) || (await isBlockedResolved(url.hostname)))) {
105
+ return {
106
+ error: `host resolves to a private/internal address: ${url.hostname}`,
107
+ };
108
+ }
109
+ const principal = options?.context?.principal;
110
+ const authHeaders = auth ? await auth(principal) : {};
111
+ const hasBody = body !== undefined && method !== "get";
112
+ try {
113
+ const res = await fetchFn(url.toString(), {
114
+ method: method.toUpperCase(),
115
+ headers: {
116
+ accept: "application/json",
117
+ ...(hasBody ? { "content-type": "application/json" } : {}),
118
+ ...authHeaders,
119
+ },
120
+ body: hasBody ? JSON.stringify(body) : undefined,
121
+ redirect: "error",
122
+ });
123
+ const text = await res.text();
124
+ let data = text;
125
+ try {
126
+ data = text ? JSON.parse(text) : null;
127
+ }
128
+ catch {
129
+ /* non-JSON — return as text */
130
+ }
131
+ return res.ok ? data : { error: `HTTP ${res.status}`, status: res.status, body: data };
132
+ }
133
+ catch (e) {
134
+ return { error: e instanceof Error ? e.message : "request failed" };
135
+ }
136
+ },
137
+ });
138
+ if (needsApproval(approval, { operationId, method })) {
139
+ toolApproval[key] = "user-approval";
140
+ }
141
+ }
142
+ }
143
+ return { tools, toolApproval };
144
+ }
145
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { ConnectionRegistry, ToolSource } from "@intentface/latch-core";
2
+ import type { OpenApiAuth } from "./auth.js";
3
+ import { type FetchFunction, type OpenApiApprovalPolicy } from "./build.js";
4
+ /**
5
+ * Declarative OpenAPI connection — turns an OpenAPI 3.x document into agent
6
+ * tools, one per operation. A second connection *kind* next to MCP: it produces
7
+ * a `ToolSource<P>` that slots into the same per-agent `connections: [...]` and
8
+ * registry, reusing the SSRF allowlist, brokered auth, and approval gating.
9
+ */
10
+ export interface OpenApiConnectionDef<P> {
11
+ /** HTTPS URL of the spec (fetched once) or an inline parsed 3.x object. */
12
+ spec: string | Record<string, unknown>;
13
+ /** Human-readable purpose — helps the model choose the connection. */
14
+ description?: string;
15
+ /** Base URL operation paths resolve against. Defaults to the spec's first `servers[]`. */
16
+ baseUrl?: string;
17
+ /** Filter operations by `operationId` (allow/block). Essential for large specs. */
18
+ operations?: {
19
+ allow?: string[];
20
+ block?: string[];
21
+ };
22
+ /** Which operations require approval. Default `"mutating"` (gate non-GET). */
23
+ approval?: OpenApiApprovalPolicy;
24
+ /** Header resolver (where credential scope lives). See `apiKey`/`bearer`/`headers`. */
25
+ auth?: OpenApiAuth<P>;
26
+ /**
27
+ * Skip the private/internal-address SSRF backstop for this connection.
28
+ * Mirrors the MCP host's `allowPrivateHost`: set it only for a trusted
29
+ * local/internal API — the allowlist check still applies. This skips the
30
+ * PRE-CHECK only: if the registry's `fetch` is a DNS-pinned fetch
31
+ * (recommended — `@intentface/latch-net/pinned`), a private backend also
32
+ * needs `createPinnedFetch({ allowPrivate: true })` injected.
33
+ */
34
+ allowPrivateHost?: boolean;
35
+ }
36
+ export declare function defineOpenApiConnection<P = unknown>(def: OpenApiConnectionDef<P>): OpenApiConnectionDef<P>;
37
+ export interface OpenApiConnectionsConfig<P> {
38
+ connections: Record<string, OpenApiConnectionDef<P>>;
39
+ /** SSRF allowlist of permitted hostnames (covers BOTH the spec host and the API host). */
40
+ allowedHosts: string[];
41
+ /** Custom fetch for spec download + tool calls. Default `globalThis.fetch`. */
42
+ fetch?: FetchFunction;
43
+ /** Observe a connection that failed to build (it's skipped, not fatal). */
44
+ onConnectionError?: (name: string, error: unknown) => void;
45
+ }
46
+ export declare function createOpenApiConnections<P>(config: OpenApiConnectionsConfig<P>): ConnectionRegistry<P> & {
47
+ names: string[];
48
+ host: ToolSource<P>;
49
+ };
50
+ //# sourceMappingURL=connections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../src/connections.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAElB,UAAU,EACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACrC,2EAA2E;IAC3E,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,UAAU,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACpD,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,uFAAuF;IACvF,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACtB;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,uBAAuB,CAAC,CAAC,GAAG,OAAO,EACjD,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC3B,oBAAoB,CAAC,CAAC,CAAC,CAEzB;AAED,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,0FAA0F;IAC1F,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5D;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAClC,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;CAAE,CA2FlE"}
@@ -0,0 +1,89 @@
1
+ import SwaggerParser from "@apidevtools/swagger-parser";
2
+ import { isBlockedHost, isBlockedResolved } from "@intentface/latch-net";
3
+ import { buildOpenApiTools, isHostAllowed, } from "./build.js";
4
+ export function defineOpenApiConnection(def) {
5
+ return def;
6
+ }
7
+ export function createOpenApiConnections(config) {
8
+ const fetchFn = config.fetch ?? globalThis.fetch;
9
+ const entries = Object.entries(config.connections);
10
+ // Build each connection's tools once (the spec is static; auth is resolved
11
+ // per-call from the tool context). Memoized as a promise per connection.
12
+ const built = new Map();
13
+ async function buildOne(name, def) {
14
+ let raw;
15
+ if (typeof def.spec === "string") {
16
+ const host = new URL(def.spec).hostname;
17
+ if (!isHostAllowed(host, config.allowedHosts)) {
18
+ throw new Error(`OpenAPI spec host not allowed: ${host} (connection "${name}")`);
19
+ }
20
+ // Same private/internal-address backstop as operation requests: an
21
+ // allowlisted spec hostname must not resolve to a private/link-local IP.
22
+ if (!def.allowPrivateHost &&
23
+ (isBlockedHost(host) || (await isBlockedResolved(host)))) {
24
+ throw new Error(`OpenAPI spec host resolves to a private/internal address: ${host} (connection "${name}")`);
25
+ }
26
+ const res = await fetchFn(def.spec, { redirect: "error" });
27
+ if (!res.ok)
28
+ throw new Error(`spec fetch failed: HTTP ${res.status}`);
29
+ raw = (await res.json());
30
+ }
31
+ else {
32
+ raw = def.spec;
33
+ }
34
+ // Resolve $ref/allOf so each operation has a concrete schema.
35
+ const doc = (await SwaggerParser.dereference(raw));
36
+ const baseUrl = def.baseUrl ?? doc.servers?.[0]?.url;
37
+ if (!baseUrl) {
38
+ throw new Error(`OpenAPI connection "${name}" has no baseUrl and the spec lists no servers`);
39
+ }
40
+ return buildOpenApiTools({
41
+ name,
42
+ doc: doc,
43
+ baseUrl,
44
+ auth: def.auth,
45
+ approval: def.approval,
46
+ operations: def.operations,
47
+ allowedHosts: config.allowedHosts,
48
+ fetchFn,
49
+ allowPrivateHost: def.allowPrivateHost,
50
+ });
51
+ }
52
+ function ensure(name, def) {
53
+ let p = built.get(name);
54
+ if (!p) {
55
+ p = buildOne(name, def).catch((err) => {
56
+ built.delete(name); // allow a retry on the next turn
57
+ throw err;
58
+ });
59
+ built.set(name, p);
60
+ }
61
+ return p;
62
+ }
63
+ function makeHost(only) {
64
+ const inScope = entries.filter(([name]) => !only || only.has(name));
65
+ return {
66
+ async open() {
67
+ const tools = {};
68
+ const toolApproval = {};
69
+ for (const [name, def] of inScope) {
70
+ try {
71
+ const b = await ensure(name, def);
72
+ Object.assign(tools, b.tools);
73
+ Object.assign(toolApproval, b.toolApproval);
74
+ }
75
+ catch (error) {
76
+ config.onConnectionError?.(name, error); // skip a broken connection
77
+ }
78
+ }
79
+ return { tools, toolApproval, close: async () => { } };
80
+ },
81
+ };
82
+ }
83
+ return {
84
+ host: makeHost(),
85
+ hostFor: (names) => makeHost(new Set(names)),
86
+ names: entries.map(([name]) => name),
87
+ };
88
+ }
89
+ //# sourceMappingURL=connections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../src/connections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @intentface/latch-openapi
3
+ *
4
+ * OpenAPI connections for Latch: turn an OpenAPI 3.x document into agent tools
5
+ * (one per operation), behind a per-tenant SSRF allowlist and brokered auth.
6
+ * A second connection kind alongside MCP — the resulting `ConnectionRegistry`
7
+ * merges with the MCP one (`mergeConnections` in core), so an agent's
8
+ * `connections: [...]` resolves across both.
9
+ */
10
+ export { defineOpenApiConnection, createOpenApiConnections, } from "./connections.js";
11
+ export type { OpenApiConnectionDef, OpenApiConnectionsConfig, } from "./connections.js";
12
+ export { bearer, apiKey, headers } from "./auth.js";
13
+ export type { OpenApiAuth } from "./auth.js";
14
+ export { isHostAllowed } from "./build.js";
15
+ export type { OpenApiApprovalPolicy, FetchFunction } from "./build.js";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,YAAY,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @intentface/latch-openapi
3
+ *
4
+ * OpenAPI connections for Latch: turn an OpenAPI 3.x document into agent tools
5
+ * (one per operation), behind a per-tenant SSRF allowlist and brokered auth.
6
+ * A second connection kind alongside MCP — the resulting `ConnectionRegistry`
7
+ * merges with the MCP one (`mergeConnections` in core), so an agent's
8
+ * `connections: [...]` resolves across both.
9
+ */
10
+ export { defineOpenApiConnection, createOpenApiConnections, } from "./connections.js";
11
+ export { bearer, apiKey, headers } from "./auth.js";
12
+ export { isHostAllowed } from "./build.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@intentface/latch-openapi",
3
+ "version": "0.9.1",
4
+ "description": "Latch OpenAPI connections — turn an OpenAPI 3.x doc into agent tools, with a per-tenant SSRF allowlist and brokered auth.",
5
+ "keywords": [
6
+ "ai",
7
+ "ai-sdk",
8
+ "agents",
9
+ "llm",
10
+ "openapi",
11
+ "tools"
12
+ ],
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "main": "./dist/index.js",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "sideEffects": false,
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "registry": "https://registry.npmjs.org"
31
+ },
32
+ "dependencies": {
33
+ "@apidevtools/swagger-parser": "^10.1.1",
34
+ "@intentface/latch-core": "^0.9.1",
35
+ "@intentface/latch-net": "^0.9.1"
36
+ },
37
+ "peerDependencies": {
38
+ "ai": ">=7.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "ai": "^7.0.2",
42
+ "typescript": "^5.7.0"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/Intentface/intentface-latch.git",
47
+ "directory": "packages/openapi"
48
+ },
49
+ "scripts": {
50
+ "build": "tsc -p tsconfig.json",
51
+ "typecheck": "tsc -p tsconfig.json --noEmit"
52
+ }
53
+ }