@axiom-lattice/gateway 8.4.0 → 10.0.0
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/dist/{a2a-TDZ3TT6H.mjs → a2a-YLKZE5JG.mjs} +6 -4
- package/dist/{a2a-standard-INVCUSAW.mjs → a2a-standard-NOAVPYXJ.mjs} +42 -127
- package/dist/a2a-standard-NOAVPYXJ.mjs.map +1 -0
- package/dist/chunk-NYYSBNDG.mjs +575 -0
- package/dist/chunk-NYYSBNDG.mjs.map +1 -0
- package/dist/{chunk-2SR4F3ZD.mjs → chunk-PHWGCRW2.mjs} +2 -1
- package/dist/{chunk-2SR4F3ZD.mjs.map → chunk-PHWGCRW2.mjs.map} +1 -1
- package/dist/{chunk-W3YH4S6G.mjs → chunk-SIMEG2JT.mjs} +19 -2
- package/dist/chunk-SIMEG2JT.mjs.map +1 -0
- package/dist/embed/{embed-page-AgentDetailSideApp-TQEJK6ZF.js → embed-page-AgentDetailSideApp-X5HKDN3N.js} +1 -1
- package/dist/embed/embed-page-AgentGovernanceWorkbench-MFXL7BOW.js +1 -0
- package/dist/embed/{embed-page-LangfuseDashboard-7OANGQXY.js → embed-page-LangfuseDashboard-NPYFMKK3.js} +1 -1
- package/dist/embed/{embed-page-MessageGraph-KJPQWTHE.js → embed-page-MessageGraph-JUCEXYLU.js} +1 -1
- package/dist/embed/{embed-page-ReactChart-2CAIM4JD.js → embed-page-ReactChart-2QJ3QRBH.js} +1 -1
- package/dist/embed/{embed-page-SkillFlow-243MXRXC.js → embed-page-SkillFlow-PFEXQ75Z.js} +1 -1
- package/dist/embed/{embed-page-TasksSideApp-OA7CULHS.js → embed-page-TasksSideApp-SBHVHNVT.js} +1 -1
- package/dist/embed/{embed-page-ThreadMaintenancePanel-Z4424X3J.js → embed-page-ThreadMaintenancePanel-LCCRUA75.js} +1 -1
- package/dist/embed/{embed-page-TopologyRuntimeView-THTCZUS7.js → embed-page-TopologyRuntimeView-ATA23XXF.js} +1 -1
- package/dist/embed/embed-page-WorkflowAutomationView-OVGOUVXH.js +3 -0
- package/dist/embed/embed-page-chunk-7F5BSMP3.js +211 -0
- package/dist/embed/embed-page-chunk-HLBQ5JKL.js +6968 -0
- package/dist/embed/{embed-page-chunk-RKQJBKN2.js → embed-page-chunk-HUH33Z2E.js} +1 -1
- package/dist/embed/embed-page-chunk-LZ4ITV3M.js +242 -0
- package/dist/embed/{embed-page-chunk-LDQGH44H.js → embed-page-chunk-ODTY6SE7.js} +1 -1
- package/dist/embed/embed-page-chunk-Q7Z7MHOM.js +76 -0
- package/dist/embed/embed-page-chunk-QOPHOXA7.js +3 -0
- package/dist/embed/{embed-page-chunk-FZX4A32D.js → embed-page-chunk-RFF7S4H5.js} +5 -5
- package/dist/embed/{embed-page-chunk-L2EGVVJS.js → embed-page-chunk-VNRFN74K.js} +1 -1
- package/dist/embed/{embed-page-chunk-JMP7EQEF.js → embed-page-chunk-WE6PZS2N.js} +1 -1
- package/dist/embed/{embed-page-en-DNDGCPMA.js → embed-page-en-4HTY4XK4.js} +1 -1
- package/dist/embed/embed-page-en-KZQZS4JN.js +1 -0
- package/dist/embed/embed-page-en-VHD2WUUG.js +1 -0
- package/dist/embed/embed-page-zh-A7DSGF6P.js +1 -0
- package/dist/embed/embed-page-zh-KSZOJPOV.js +1 -0
- package/dist/embed/{embed-page-zh-2H4EB3F3.js → embed-page-zh-UHTZEW5G.js} +1 -1
- package/dist/embed/embed-page.js +1 -1
- package/dist/embed/mcp-app-page.js +76 -15
- package/dist/index.js +1699 -1498
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +241 -382
- package/dist/index.mjs.map +1 -1
- package/dist/{open-27ZPLBIW.mjs → open-PCOORPQ5.mjs} +63 -16
- package/dist/open-PCOORPQ5.mjs.map +1 -0
- package/dist/{open-exposure-BPLYUIX6.mjs → open-exposure-HGYT5VFC.mjs} +5 -6
- package/dist/open-exposure-HGYT5VFC.mjs.map +1 -0
- package/package.json +18 -13
- package/dist/a2a-standard-INVCUSAW.mjs.map +0 -1
- package/dist/chunk-OBODBHIS.mjs +0 -230
- package/dist/chunk-OBODBHIS.mjs.map +0 -1
- package/dist/chunk-W3YH4S6G.mjs.map +0 -1
- package/dist/embed/embed-page-AgentGovernanceWorkbench-II2MRGCG.js +0 -1
- package/dist/embed/embed-page-WorkflowAutomationView-YDIDG35E.js +0 -3
- package/dist/embed/embed-page-chunk-7ULTMRM2.js +0 -211
- package/dist/embed/embed-page-chunk-OMN6G52V.js +0 -3
- package/dist/embed/embed-page-chunk-VYS3TAK4.js +0 -242
- package/dist/embed/embed-page-chunk-W5OP2CZG.js +0 -15
- package/dist/embed/embed-page-chunk-Z6KWW64I.js +0 -6837
- package/dist/embed/embed-page-en-D4M3DPFT.js +0 -1
- package/dist/embed/embed-page-en-JOTELYAL.js +0 -1
- package/dist/embed/embed-page-zh-BPO6ZSJM.js +0 -1
- package/dist/embed/embed-page-zh-NEXLMDHF.js +0 -1
- package/dist/open-27ZPLBIW.mjs.map +0 -1
- package/dist/open-exposure-BPLYUIX6.mjs.map +0 -1
- /package/dist/{a2a-TDZ3TT6H.mjs.map → a2a-YLKZE5JG.mjs.map} +0 -0
|
@@ -68,14 +68,31 @@ var OpenRateLimiter = class {
|
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
+
// src/routes/open.ts
|
|
72
|
+
import { createMcpHandler, isLegacyRequest } from "@modelcontextprotocol/server";
|
|
73
|
+
import { toNodeHandler, toWebRequest } from "@modelcontextprotocol/node";
|
|
74
|
+
|
|
71
75
|
// src/services/open/openMcpServer.ts
|
|
72
|
-
import {
|
|
73
|
-
import {
|
|
76
|
+
import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
|
|
77
|
+
import {
|
|
78
|
+
McpServer
|
|
79
|
+
} from "@modelcontextprotocol/server";
|
|
80
|
+
import { z } from "zod";
|
|
74
81
|
var OPEN_MCP_SERVER_INFO = {
|
|
75
82
|
name: "axiom-open",
|
|
76
83
|
version: "0.1.0"
|
|
77
84
|
};
|
|
78
|
-
|
|
85
|
+
function isStandardSchemaWithJson(value) {
|
|
86
|
+
if (typeof value !== "object" || value === null) return false;
|
|
87
|
+
const standard = value["~standard"];
|
|
88
|
+
return typeof standard === "object" && standard !== null && "jsonSchema" in standard;
|
|
89
|
+
}
|
|
90
|
+
function toStandardSchema(schema) {
|
|
91
|
+
if (isStandardSchemaWithJson(schema)) return schema;
|
|
92
|
+
const zodSchema = schema instanceof z.ZodType ? schema : z.object(schema);
|
|
93
|
+
return zodSchema;
|
|
94
|
+
}
|
|
95
|
+
async function createSessionServer(options) {
|
|
79
96
|
const { catalog, invoke, uiResources } = options;
|
|
80
97
|
const server = new McpServer(OPEN_MCP_SERVER_INFO, {
|
|
81
98
|
capabilities: {
|
|
@@ -89,7 +106,7 @@ async function buildSessionServer(options) {
|
|
|
89
106
|
tool.toolName,
|
|
90
107
|
{
|
|
91
108
|
description: tool.description,
|
|
92
|
-
inputSchema: tool.schema,
|
|
109
|
+
inputSchema: toStandardSchema(tool.schema),
|
|
93
110
|
annotations: {
|
|
94
111
|
readOnlyHint: tool.annotations.readOnlyHint,
|
|
95
112
|
destructiveHint: tool.annotations.destructiveHint
|
|
@@ -126,11 +143,15 @@ async function buildSessionServer(options) {
|
|
|
126
143
|
}
|
|
127
144
|
);
|
|
128
145
|
}
|
|
129
|
-
|
|
146
|
+
return { server, tools: catalog };
|
|
147
|
+
}
|
|
148
|
+
async function buildSessionServer(options) {
|
|
149
|
+
const { server, tools } = await createSessionServer(options);
|
|
150
|
+
const transport = new NodeStreamableHTTPServerTransport({
|
|
130
151
|
sessionIdGenerator: void 0,
|
|
131
152
|
enableJsonResponse: true
|
|
132
153
|
});
|
|
133
|
-
return { server, transport, tools
|
|
154
|
+
return { server, transport, tools };
|
|
134
155
|
}
|
|
135
156
|
|
|
136
157
|
// src/routes/open.ts
|
|
@@ -201,7 +222,7 @@ function registerOpenRoutes(app, deps = {}) {
|
|
|
201
222
|
reply.header(REPLICA_HEADER, replica);
|
|
202
223
|
reply.raw.setHeader(REPLICA_HEADER, replica);
|
|
203
224
|
};
|
|
204
|
-
const
|
|
225
|
+
const buildSessionOptions = async (context, log) => {
|
|
205
226
|
const grants = grantsSource(context);
|
|
206
227
|
const catalog = await openLattice.catalogFor(grants, { tenantId: context.tenantId });
|
|
207
228
|
const globalToolNames = new Set(catalog.map((t) => t.toolName));
|
|
@@ -227,7 +248,7 @@ function registerOpenRoutes(app, deps = {}) {
|
|
|
227
248
|
...collectExposedPluginUiResources(grants),
|
|
228
249
|
...tenant.uiResources
|
|
229
250
|
]);
|
|
230
|
-
|
|
251
|
+
return {
|
|
231
252
|
credential: { credentialId: context.credentialId, kind: context.kind },
|
|
232
253
|
grants,
|
|
233
254
|
catalog: mergedCatalog,
|
|
@@ -334,8 +355,7 @@ function registerOpenRoutes(app, deps = {}) {
|
|
|
334
355
|
);
|
|
335
356
|
}
|
|
336
357
|
}
|
|
337
|
-
}
|
|
338
|
-
return built;
|
|
358
|
+
};
|
|
339
359
|
};
|
|
340
360
|
const handleOpenMcpPost = async (request, reply) => {
|
|
341
361
|
markReplica(reply);
|
|
@@ -343,15 +363,42 @@ function registerOpenRoutes(app, deps = {}) {
|
|
|
343
363
|
if (!context) {
|
|
344
364
|
return reply.status(401).send({ error: "AUTH_FAILED" });
|
|
345
365
|
}
|
|
346
|
-
const
|
|
347
|
-
|
|
366
|
+
const modern = createMcpHandler(
|
|
367
|
+
async () => (await createSessionServer(
|
|
368
|
+
await buildSessionOptions(context, request.log)
|
|
369
|
+
)).server,
|
|
370
|
+
{
|
|
371
|
+
legacy: "reject",
|
|
372
|
+
onerror: (error) => {
|
|
373
|
+
request.log.warn({ err: error }, "open mcp: handler error");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
const nodeHandler = toNodeHandler(modern, {
|
|
378
|
+
onerror: (error) => {
|
|
379
|
+
request.log.warn({ err: error }, "open mcp: transport error");
|
|
380
|
+
}
|
|
381
|
+
});
|
|
348
382
|
reply.hijack();
|
|
383
|
+
const webRequest = await toWebRequest(request.raw, request.body);
|
|
384
|
+
if (await isLegacyRequest(webRequest, request.body)) {
|
|
385
|
+
const built = await buildSessionServer(
|
|
386
|
+
await buildSessionOptions(context, request.log)
|
|
387
|
+
);
|
|
388
|
+
await built.server.connect(built.transport);
|
|
389
|
+
const close2 = () => {
|
|
390
|
+
void built.transport.close().catch(() => void 0);
|
|
391
|
+
void built.server.close().catch(() => void 0);
|
|
392
|
+
};
|
|
393
|
+
reply.raw.once("close", close2);
|
|
394
|
+
await built.transport.handleRequest(request.raw, reply.raw, request.body);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
349
397
|
const close = () => {
|
|
350
|
-
void
|
|
351
|
-
void built.server.close().catch(() => void 0);
|
|
398
|
+
void modern.close().catch(() => void 0);
|
|
352
399
|
};
|
|
353
400
|
reply.raw.once("close", close);
|
|
354
|
-
await
|
|
401
|
+
await nodeHandler(request.raw, reply.raw, request.body);
|
|
355
402
|
};
|
|
356
403
|
const methodNotAllowed = async (_request, reply) => {
|
|
357
404
|
reply.header("Allow", "POST");
|
|
@@ -371,4 +418,4 @@ function registerOpenRoutes(app, deps = {}) {
|
|
|
371
418
|
export {
|
|
372
419
|
registerOpenRoutes
|
|
373
420
|
};
|
|
374
|
-
//# sourceMappingURL=open-
|
|
421
|
+
//# sourceMappingURL=open-PCOORPQ5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routes/open.ts","../src/services/open/openRateLimiter.ts","../src/services/open/openMcpServer.ts"],"sourcesContent":["/**\n * Open API surface routes: GET/POST/DELETE /open/mcp.\n *\n * The route is a thin shell — credential resolution (OpenAuthService),\n * catalog filtering and invocation (OpenLattice), and stateless MCP server\n * construction live in services/open/*. The transport writes the response\n * itself, so the reply is hijacked (same precedent as the Project Room SSE\n * route).\n */\n\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport {\n getReplicaId,\n getStoreLattice,\n resolvePluginConnections,\n} from \"@axiom-lattice/core\";\nimport { REPLICA_HEADER, type A2AApiKeyStore, type OpenAuditStore, type OpenGrant, type ProjectStore } from \"@axiom-lattice/protocols\";\nimport { OpenCredentialService } from \"../services/open/openCredentialService\";\nimport { OpenAuditService } from \"../services/open/openAuditService\";\nimport { OpenRateLimiter } from \"../services/open/openRateLimiter\";\nimport type { RateLimitService } from \"../services/rate-limit/RateLimitService\";\nimport type { OpenCredential } from \"../services/open/openCredentialService\";\nimport {\n openLattice,\n ActionNotFoundError,\n ConnectionNotAllowedError,\n GrantDeniedError,\n ValidationError,\n resolveCoveringConnectionKeys,\n} from \"../services/open/openLattice\";\nimport { createMcpHandler, isLegacyRequest } from \"@modelcontextprotocol/server\";\nimport { toNodeHandler, toWebRequest } from \"@modelcontextprotocol/node\";\nimport {\n buildSessionServer,\n createSessionServer,\n type SessionServerOptions,\n} from \"../services/open/openMcpServer\";\nimport { collectExposedPluginUiResources } from \"../services/open/registerExposedPlugins\";\nimport {\n buildTenantOpenPlugins,\n mergeOpenCatalog,\n type TenantOpenPlugins,\n} from \"../services/open/tenantOpenPlugins\";\n\nexport interface OpenRouteDeps {\n openAuth?: OpenCredentialService;\n grantsSource?: (ctx: OpenCredential) => OpenGrant[];\n audit?: OpenAuditService;\n /** Legacy in-memory limiter; used only when `rateLimits` is absent. */\n rateLimiter?: OpenRateLimiter;\n /** Gateway rate limiter; preferred when provided. */\n rateLimits?: RateLimitService;\n /**\n * Test seam: build the credential's tenant sandbox plugin bundle. Defaults to\n * {@link buildTenantOpenPlugins} with the global catalog's tool names reserved\n * so a tenant tool can never shadow a global/built-in one.\n */\n tenantPlugins?: (\n tenantId: string,\n grants: OpenGrant[],\n reservedToolNames: ReadonlySet<string>,\n ) => Promise<TenantOpenPlugins>;\n /** Test seam for grant-pinned connection validation. */\n resolveConnections?: typeof resolvePluginConnections;\n}\n\nfunction extractBearer(request: FastifyRequest): string | undefined {\n const header = request.headers.authorization;\n if (!header?.startsWith(\"Bearer \")) return undefined;\n return header.slice(\"Bearer \".length).trim() || undefined;\n}\n\n/**\n * Degraded tenant plugin bundle used when the sandbox catalog cannot be read.\n *\n * A catalog read failure must not 500 the session: the MCP server still builds\n * and serves the global tools. `invoke` is unreachable through normal routing\n * (`hasTool` is always false, so calls route to OpenLattice) but returns an\n * error result if it is ever reached.\n */\nfunction emptyTenantPlugins(): TenantOpenPlugins {\n return {\n tools: [],\n uiResources: new Map(),\n hasTool: () => false,\n degraded: true,\n invoke: async (toolName) => ({\n content: [\n { type: \"text\", text: `Tenant plugin catalog unavailable for ${toolName}` },\n ],\n isError: true,\n }),\n };\n}\n\n/** JSON-RPC error codes used on the Open surface. */\nconst RPC_ERROR = {\n GRANT_DENIED: -32001,\n ACTION_NOT_FOUND: -32002,\n VALIDATION_FAILED: -32003,\n EXECUTION_ERROR: -32004,\n RATE_LIMITED: -32005,\n CONNECTION_NOT_ALLOWED: -32006,\n} as const;\n\nexport function registerOpenRoutes(\n app: FastifyInstance,\n deps: OpenRouteDeps = {},\n): void {\n const openAuth =\n deps.openAuth ??\n new OpenCredentialService(\n getStoreLattice(\"default\", \"a2aApiKey\").store as A2AApiKeyStore,\n getStoreLattice(\"default\", \"project\").store as ProjectStore,\n );\n const grantsSource =\n deps.grantsSource ?? ((ctx: OpenCredential) => ctx.grants);\n const audit =\n deps.audit ??\n (() => {\n try {\n const store = getStoreLattice(\"default\", \"openAudit\").store as OpenAuditStore;\n return new OpenAuditService(store);\n } catch {\n return undefined;\n }\n })();\n\n const rateLimiter = deps.rateLimiter ?? new OpenRateLimiter();\n const rateLimits = deps.rateLimits;\n\n const resolveConnections = deps.resolveConnections ?? resolvePluginConnections;\n\n /**\n * Resolve and validate the connection keys pinned by the covering grant.\n *\n * The credential tenant is authoritative; a selected key that is missing or\n * belongs to another tenant/type fails closed.\n */\n const selectedConnectionKeys = async (\n grants: OpenGrant[],\n domain: string,\n action: string,\n tenantId: string,\n ): Promise<string[] | undefined> => {\n const keys = resolveCoveringConnectionKeys(grants, domain, action);\n if (!keys) return undefined;\n let resolved: Awaited<ReturnType<typeof resolvePluginConnections>>;\n try {\n resolved = await resolveConnections(domain, { connections: keys }, { tenantId });\n } catch {\n throw new ConnectionNotAllowedError(`connection store unavailable for ${domain}`);\n }\n const found = new Set(resolved.map((entry) => entry.key));\n const missing = keys.filter((key) => !found.has(key));\n if (missing.length > 0) {\n throw new ConnectionNotAllowedError(\n `unknown connection(s) for ${domain}: ${missing.join(\", \")}`,\n );\n }\n return keys;\n };\n\n const markReplica = (reply: FastifyReply): void => {\n const replica = getReplicaId();\n reply.header(REPLICA_HEADER, replica);\n reply.raw.setHeader(REPLICA_HEADER, replica);\n };\n\n const buildSessionOptions = async (\n context: OpenCredential,\n log: FastifyRequest[\"log\"],\n ): Promise<SessionServerOptions> => {\n const grants = grantsSource(context);\n const catalog = await openLattice.catalogFor(grants, { tenantId: context.tenantId });\n // Tenant sandbox plugins are volume-resident and outside the OpenLattice\n // registry; merge their grant-filtered tools/UI alongside the globals.\n // Global/built-in tool names are reserved so a tenant plugin of the same\n // type (kb/sandbox/agent) can neither crash `registerTool` with a duplicate\n // nor capture the invoke routing for a global tool.\n const globalToolNames = new Set(catalog.map((t) => t.toolName));\n const buildTenant =\n deps.tenantPlugins ??\n ((tenantId, tenantGrants, reservedToolNames) =>\n buildTenantOpenPlugins(tenantId, tenantGrants, { reservedToolNames }));\n let tenant: TenantOpenPlugins;\n try {\n tenant = await buildTenant(context.tenantId, grants, globalToolNames);\n if (tenant.degraded) {\n // The catalog's common failure mode is a diagnostic, not a throw\n // (`ensureLoaded` returns error-level diagnostics). Surface it and\n // serve global tools only.\n log.warn(\n { tenantId: context.tenantId },\n \"open mcp: tenant plugin catalog degraded; serving global tools only\",\n );\n }\n } catch (error) {\n // Sandbox catalogs are volume-resident and can be transiently\n // unavailable; degrade to globals-only rather than failing the whole\n // session so external hosts can still connect. `err` keeps the stack.\n log.warn(\n { tenantId: context.tenantId, err: error },\n \"open mcp: tenant plugin catalog unavailable; serving global tools only\",\n );\n tenant = emptyTenantPlugins();\n }\n const mergedCatalog = mergeOpenCatalog(catalog, tenant.tools);\n const uiResources = new Map([\n ...collectExposedPluginUiResources(grants),\n ...tenant.uiResources,\n ]);\n return {\n credential: { credentialId: context.credentialId, kind: context.kind },\n grants,\n catalog: mergedCatalog,\n uiResources,\n invoke: async (toolName, args) => {\n const startedAt = Date.now();\n const sep = toolName.indexOf(\"_\");\n const auditDomain = sep > 0 ? toolName.slice(0, sep) : toolName;\n const auditAction = sep > 0 ? toolName.slice(sep + 1) : \"\";\n\n let allowed: boolean;\n let retryAfterMs: number | undefined;\n if (rateLimits) {\n const decision = await rateLimits.consume(\n auditDomain === \"agent\" ? \"open.agent\" : \"open.api\",\n { credential: context.credentialId },\n );\n allowed = decision.allowed;\n retryAfterMs = decision.allowed\n ? undefined\n : decision.retryAfterSeconds * 1000;\n } else {\n const decision = rateLimiter.check(context.credentialId, auditDomain);\n allowed = decision.allowed;\n retryAfterMs = decision.retryAfterMs;\n }\n if (!allowed) {\n await audit?.record({\n tenantId: context.tenantId,\n credentialId: context.credentialId,\n credentialKind: context.kind,\n projectId: context.projectId,\n domain: auditDomain,\n action: auditAction,\n args,\n status: \"error\",\n errorCode: \"RATE_LIMITED\",\n durationMs: Date.now() - startedAt,\n });\n throw Object.assign(\n new Error(\n `RATE_LIMITED: retry after ${retryAfterMs ?? 1000}ms`,\n ),\n { rpcCode: RPC_ERROR.RATE_LIMITED },\n );\n }\n\n try {\n const connectionKeys = await selectedConnectionKeys(\n grants,\n auditDomain,\n auditAction,\n context.tenantId,\n );\n const isTenantRoute = tenant.hasTool(toolName) && !globalToolNames.has(toolName);\n const runConfig: Record<string, unknown> = {\n sandboxName: context.sandboxName,\n credentialId: context.credentialId,\n depth: context.depth,\n };\n const result = isTenantRoute\n ? await tenant.invoke(toolName, args, {\n tenantId: context.tenantId,\n projectId: context.projectId,\n workspaceId: context.workspaceId,\n runConfig,\n ...(connectionKeys ? { connectionKeys } : {}),\n })\n : await openLattice.invoke(\n grants,\n {\n tenantId: context.tenantId,\n projectId: context.projectId,\n workspaceId: context.workspaceId,\n runConfig,\n },\n toolName,\n args,\n );\n await audit?.record({\n tenantId: context.tenantId,\n credentialId: context.credentialId,\n credentialKind: context.kind,\n projectId: context.projectId,\n domain: auditDomain,\n action: auditAction,\n args,\n status: result.isError ? \"error\" : \"ok\",\n ...(result.isError ? { errorCode: \"EXECUTION_ERROR\" } : {}),\n durationMs: Date.now() - startedAt,\n });\n return result;\n } catch (err) {\n const code =\n err instanceof ConnectionNotAllowedError\n ? RPC_ERROR.CONNECTION_NOT_ALLOWED\n : err instanceof GrantDeniedError\n ? RPC_ERROR.GRANT_DENIED\n : err instanceof ActionNotFoundError\n ? RPC_ERROR.ACTION_NOT_FOUND\n : err instanceof ValidationError\n ? RPC_ERROR.VALIDATION_FAILED\n : RPC_ERROR.EXECUTION_ERROR;\n await audit?.record({\n tenantId: context.tenantId,\n credentialId: context.credentialId,\n credentialKind: context.kind,\n projectId: context.projectId,\n domain: auditDomain,\n action: auditAction,\n args,\n status: \"error\",\n errorCode:\n err instanceof ConnectionNotAllowedError\n ? \"CONNECTION_NOT_ALLOWED\"\n : err instanceof GrantDeniedError\n ? \"GRANT_DENIED\"\n : err instanceof ActionNotFoundError\n ? \"ACTION_NOT_FOUND\"\n : err instanceof ValidationError\n ? \"VALIDATION_FAILED\"\n : \"EXECUTION_ERROR\",\n durationMs: Date.now() - startedAt,\n });\n throw Object.assign(\n new Error(err instanceof Error ? err.message : String(err)),\n { rpcCode: code },\n );\n }\n },\n };\n };\n\n const handleOpenMcpPost = async (\n request: FastifyRequest,\n reply: FastifyReply,\n ) => {\n markReplica(reply);\n const context = await openAuth.resolve(extractBearer(request));\n if (!context) {\n return reply.status(401).send({ error: \"AUTH_FAILED\" });\n }\n\n // One handler per request: the credential-scoped catalog (and the\n // credential itself) is resolved inside the factory, so no session or\n // server state is shared between requests or replicas.\n //\n // Modern 2026-07-28 traffic is served by `createMcpHandler`. Legacy\n // 2025-era traffic is routed in user land to the existing stateless\n // `NodeStreamableHTTPServerTransport`, which keeps the pre-upgrade\n // `enableJsonResponse: true` contract (the SDK's built-in legacy fallback\n // answers with SSE). `isLegacyRequest` is the entry's own classifier, so\n // this branch can never disagree with the handler.\n const modern = createMcpHandler(\n async () =>\n (\n await createSessionServer(\n await buildSessionOptions(context, request.log),\n )\n ).server,\n {\n legacy: \"reject\",\n onerror: (error) => {\n request.log.warn({ err: error }, \"open mcp: handler error\");\n },\n },\n );\n const nodeHandler = toNodeHandler(modern, {\n onerror: (error) => {\n request.log.warn({ err: error }, \"open mcp: transport error\");\n },\n });\n\n reply.hijack();\n\n const webRequest = await toWebRequest(request.raw, request.body);\n if (await isLegacyRequest(webRequest, request.body)) {\n const built = await buildSessionServer(\n await buildSessionOptions(context, request.log),\n );\n await built.server.connect(built.transport);\n const close = (): void => {\n void built.transport.close().catch(() => undefined);\n void built.server.close().catch(() => undefined);\n };\n reply.raw.once(\"close\", close);\n await built.transport.handleRequest(request.raw, reply.raw, request.body);\n return;\n }\n\n const close = (): void => {\n void modern.close().catch(() => undefined);\n };\n reply.raw.once(\"close\", close);\n await nodeHandler(request.raw, reply.raw, request.body);\n };\n\n const methodNotAllowed = async (\n _request: FastifyRequest,\n reply: FastifyReply,\n ) => {\n reply.header(\"Allow\", \"POST\");\n return reply.status(405).send({ error: \"METHOD_NOT_ALLOWED\" });\n };\n\n app.post(\"/open/mcp\", handleOpenMcpPost);\n // Canonical MCP surface aliases the unified Open MCP server.\n app.post(\"/mcp\", handleOpenMcpPost);\n app.post(\"/mcp/\", handleOpenMcpPost);\n app.get(\"/mcp\", methodNotAllowed);\n app.get(\"/mcp/\", methodNotAllowed);\n\n // Stateless Streamable HTTP has no server→client GET stream or session\n // DELETE. A 404 is not a recognized \"not supported\" signal: the official\n // client transport tolerates 405 (`Allow` set) and continues.\n app.get(\"/open/mcp\", methodNotAllowed);\n app.get(\"/open/mcp/\", methodNotAllowed);\n app.delete(\"/open/mcp\", methodNotAllowed);\n app.delete(\"/mcp\", methodNotAllowed);\n app.delete(\"/mcp/\", methodNotAllowed);\n\n}\n","/**\n * In-memory per-credential rate limiter (token bucket). The agent domain gets\n * a stricter bucket because each call consumes LLM budget.\n *\n * Process-local by design (V1 single-gateway); a distributed limiter is V2.\n */\n\nexport interface RateLimitDecision {\n allowed: boolean;\n retryAfterMs?: number;\n}\n\ninterface Bucket {\n tokens: number;\n updatedAt: number;\n}\n\nexport interface OpenRateLimiterOptions {\n /** Default requests per minute per credential. */\n defaultPerMinute?: number;\n /** Stricter limit for the agent domain. */\n agentPerMinute?: number;\n}\n\nexport class OpenRateLimiter {\n private readonly buckets = new Map<string, Bucket>();\n private readonly defaultPerMinute: number;\n private readonly agentPerMinute: number;\n\n constructor(options: OpenRateLimiterOptions = {}) {\n this.defaultPerMinute = options.defaultPerMinute ?? 60000;\n this.agentPerMinute = options.agentPerMinute ?? 2000;\n }\n\n private bucketClass(domain: string): string {\n return domain === \"agent\" ? \"agent\" : \"default\";\n }\n\n private limitFor(domain: string): number {\n return domain === \"agent\" ? this.agentPerMinute : this.defaultPerMinute;\n }\n\n /** Consume one token; denied calls report how long to wait. */\n check(credentialId: string, domain: string): RateLimitDecision {\n const limit = this.limitFor(domain);\n const key = `${this.bucketClass(domain)}:${credentialId}`;\n const now = Date.now();\n const bucket = this.buckets.get(key) ?? { tokens: limit, updatedAt: now };\n\n const refillPerMs = limit / 60_000;\n const elapsed = now - bucket.updatedAt;\n bucket.tokens = Math.min(limit, bucket.tokens + elapsed * refillPerMs);\n bucket.updatedAt = now;\n\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n this.buckets.set(key, bucket);\n return { allowed: true };\n }\n\n this.buckets.set(key, bucket);\n const deficit = 1 - bucket.tokens;\n return { allowed: false, retryAfterMs: Math.ceil(deficit / refillPerMs) };\n }\n\n /** Test helper. */\n reset(): void {\n this.buckets.clear();\n }\n}\n","/**\n * Builds a stateless MCP server instance whose tool list is the\n * credential-filtered OpenLattice catalog. A fresh instance is built per HTTP\n * request so no server/session state is shared between replicas.\n *\n * The transport uses `sessionIdGenerator: undefined`: it never issues\n * `Mcp-Session-Id`, and every request is self-contained. Credential-scoped\n * tool lists therefore stay per-request without a session registry.\n *\n * Every tool executor routes through the OpenLattice invoke kernel, so\n * authorization is re-checked on each invocation (default-deny, live grants).\n */\n\nimport { NodeStreamableHTTPServerTransport } from \"@modelcontextprotocol/node\";\nimport {\n McpServer,\n type StandardSchemaWithJSON,\n} from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\nimport type { McpUiCsp, OpenGrant } from \"@axiom-lattice/protocols\";\nimport type { OpenToolDescriptor } from \"./openLattice\";\n\nexport const OPEN_MCP_SERVER_INFO = {\n name: \"axiom-open\",\n version: \"0.1.0\",\n} as const;\n\n/**\n * A `ui://` HTML resource served by the session. `read` is invoked per\n * `resources/read`, so the caller can fetch plugin UI lazily.\n */\nexport interface OpenUiResource {\n /** MIME for the resource; defaults to \"text/html;profile=mcp-app\". */\n mimeType?: string;\n csp?: McpUiCsp;\n read: () => Promise<{ html: string; csp?: McpUiCsp }>;\n}\n\n/** v2 accepts Standard Schema with JSON; our catalog stores Zod 3 schemas. */\nfunction isStandardSchemaWithJson(value: unknown): value is StandardSchemaWithJSON {\n if (typeof value !== \"object\" || value === null) return false;\n const standard = (value as { \"~standard\"?: { jsonSchema?: unknown } })[\"~standard\"];\n return typeof standard === \"object\" && standard !== null && \"jsonSchema\" in standard;\n}\n\nfunction toStandardSchema(schema: z.ZodRawShape | z.ZodType): StandardSchemaWithJSON {\n if (isStandardSchemaWithJson(schema)) return schema;\n const zodSchema = schema instanceof z.ZodType ? schema : z.object(schema);\n return zodSchema;\n}\n\nexport interface BuiltSession {\n server: McpServer;\n transport: NodeStreamableHTTPServerTransport;\n tools: OpenToolDescriptor[];\n}\n\n/** Options shared by the session server builder and the HTTP handler factory. */\nexport interface SessionServerOptions {\n credential: { credentialId: string; kind: string };\n grants: OpenGrant[];\n catalog: OpenToolDescriptor[];\n invoke: (\n toolName: string,\n args: unknown,\n ) => Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n }>;\n uiResources?: Map<string, OpenUiResource>;\n}\n\n/**\n * Builds (but does not connect) one MCP server instance for a single serving\n * unit. `createMcpHandler` calls this factory once per request (and for the\n * initial `server/discover` probe), so every call must return a fresh server.\n *\n * The same server definition serves both protocol eras: legacy 2025-era\n * requests are answered by the stateless fallback, modern 2026-07-28 requests\n * carry a per-request envelope.\n */\nexport async function createSessionServer(\n options: SessionServerOptions,\n): Promise<{ server: McpServer; tools: OpenToolDescriptor[] }> {\n const { catalog, invoke, uiResources } = options;\n const server = new McpServer(OPEN_MCP_SERVER_INFO, {\n capabilities: {\n // The SDK has no `extensions` field; MCP App hosts discover the UI\n // extension through `experimental`.\n experimental: { \"io.modelcontextprotocol/ui\": {} },\n },\n });\n for (const tool of catalog) {\n server.registerTool(\n tool.toolName,\n {\n description: tool.description,\n inputSchema: toStandardSchema(tool.schema),\n annotations: {\n readOnlyHint: tool.annotations.readOnlyHint,\n destructiveHint: tool.annotations.destructiveHint,\n },\n ...(tool.ui ? { _meta: { ui: { resourceUri: tool.ui.resourceUri } } } : {}),\n },\n async (args: unknown) => {\n const result = await invoke(tool.toolName, args);\n return { content: result.content, isError: result.isError };\n },\n );\n }\n for (const [uri, res] of uiResources ?? []) {\n const mimeType = res.mimeType ?? \"text/html;profile=mcp-app\";\n server.registerResource(\n `mcp-app:${uri}`,\n uri,\n {\n mimeType,\n ...(res.csp ? { _meta: { ui: { csp: res.csp } } } : {}),\n },\n async (u) => {\n const { html, csp } = await res.read();\n return {\n contents: [\n {\n uri: u.href,\n mimeType,\n text: html,\n ...(csp ? { _meta: { ui: { csp } } } : {}),\n },\n ],\n };\n },\n );\n }\n return { server, tools: catalog };\n}\n\n/**\n * Builds (but does not connect) a stateless MCP server plus the legacy\n * streamable HTTP transport. The caller MUST connect it before handling a\n * request — otherwise the transport has no onmessage handler and the request\n * is swallowed. Deferring connect is what lets tests attach the server to an\n * `InMemoryTransport`.\n *\n * @remarks The Open routes use {@link createSessionServer} with\n * `createMcpHandler` for dual-era serving; this helper remains for callers\n * that drive a single transport directly.\n */\nexport async function buildSessionServer(\n options: SessionServerOptions,\n): Promise<BuiltSession> {\n const { server, tools } = await createSessionServer(options);\n const transport = new NodeStreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n enableJsonResponse: true,\n });\n return { server, transport, tools };\n}\n"],"mappings":"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","names":["close"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildAgentCardForAssistant
|
|
3
|
-
|
|
2
|
+
buildAgentCardForAssistant,
|
|
3
|
+
resolveRequestBaseUrl
|
|
4
|
+
} from "./chunk-SIMEG2JT.mjs";
|
|
4
5
|
import {
|
|
5
6
|
buildTenantOpenPlugins,
|
|
6
7
|
mergeOpenCatalog
|
|
@@ -28,9 +29,7 @@ function registerOpenExposureRoutes(app, deps = {}) {
|
|
|
28
29
|
return reply.status(404).send({ success: false, error: "Key not found" });
|
|
29
30
|
}
|
|
30
31
|
const grants = effectiveGrants(record);
|
|
31
|
-
const
|
|
32
|
-
const host = (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost) ?? (Array.isArray(request.headers.host) ? request.headers.host[0] : request.headers.host) ?? request.hostname;
|
|
33
|
-
const baseUrl = `${request.protocol}://${host}`;
|
|
32
|
+
const baseUrl = resolveRequestBaseUrl(request);
|
|
34
33
|
const globalCatalog = await openLattice.catalogFor(grants, { tenantId });
|
|
35
34
|
let tenantTools = [];
|
|
36
35
|
let tenantCatalogDegraded = false;
|
|
@@ -106,4 +105,4 @@ function registerOpenExposureRoutes(app, deps = {}) {
|
|
|
106
105
|
export {
|
|
107
106
|
registerOpenExposureRoutes
|
|
108
107
|
};
|
|
109
|
-
//# sourceMappingURL=open-exposure-
|
|
108
|
+
//# sourceMappingURL=open-exposure-HGYT5VFC.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routes/open-exposure.ts"],"sourcesContent":["/**\n * Key exposure endpoint: what a given API key exposes to external callers —\n * the MCP tools (per grants) and the A2A agent cards (for granted agents).\n * Console-session guarded and tenant-scoped.\n */\n\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport { getStoreLattice } from \"@axiom-lattice/core\";\nimport type {\n A2AApiKeyStore,\n AssistantStore,\n OpenGrant,\n} from \"@axiom-lattice/protocols\";\nimport { effectiveGrants } from \"@axiom-lattice/protocols\";\nimport {\n openLattice,\n type OpenToolDescriptor,\n} from \"../services/open/openLattice\";\nimport {\n buildTenantOpenPlugins,\n mergeOpenCatalog,\n type TenantOpenPluginDeps,\n} from \"../services/open/tenantOpenPlugins\";\nimport { buildAgentCardForAssistant } from \"../services/a2a/A2AAgentCardBuilder\";\nimport { resolveRequestBaseUrl } from \"../utils/request-base-url\";\n\ninterface RequestWithUser extends FastifyRequest {\n user?: { id: string; tenantId?: string };\n}\n\n/** Overridable seams for the exposure route (tests / embedding). */\nexport interface OpenExposureDeps {\n /** Tenant sandbox plugin catalog seams; defaults to the live sandbox catalog. */\n tenantPluginDeps?: TenantOpenPluginDeps;\n}\n\nexport function registerOpenExposureRoutes(\n app: FastifyInstance,\n deps: OpenExposureDeps = {},\n): void {\n app.get<{ Params: { id: string } }>(\n \"/api/keys/:id/exposure\",\n async (\n request: FastifyRequest<{ Params: { id: string } }>,\n reply: FastifyReply,\n ) => {\n const tenantId = (request as RequestWithUser).user?.tenantId;\n if (!tenantId) {\n return reply.status(401).send({ success: false, error: \"Unauthorized\" });\n }\n\n const keyStore = getStoreLattice(\"default\", \"a2aApiKey\")\n .store as A2AApiKeyStore;\n const record = await keyStore.findById(request.params.id);\n if (!record || record.tenantId !== tenantId) {\n return reply.status(404).send({ success: false, error: \"Key not found\" });\n }\n\n const grants: OpenGrant[] = effectiveGrants(record);\n const baseUrl = resolveRequestBaseUrl(request);\n\n // MCP surface: the credential-filtered tool catalog.\n const globalCatalog = await openLattice.catalogFor(grants, { tenantId });\n\n // Tenant sandbox plugins live in per-tenant volumes, outside the\n // OpenLattice registry, so the globals are merged with the credential's\n // grant-filtered tenant tools (global wins) to mirror the actual Open MCP\n // session. A missing/unavailable sandbox catalog degrades to globals.\n let tenantTools: OpenToolDescriptor[] = [];\n let tenantCatalogDegraded = false;\n try {\n const tenant = await buildTenantOpenPlugins(tenantId, grants, {\n ...deps.tenantPluginDeps,\n reservedToolNames: new Set(globalCatalog.map((t) => t.toolName)),\n });\n tenantTools = tenant.tools;\n tenantCatalogDegraded = tenant.degraded;\n } catch (error) {\n tenantCatalogDegraded = true;\n request.log.warn(\n { tenantId, err: error },\n \"key exposure: tenant plugin catalog unavailable\",\n );\n }\n\n const tools = mergeOpenCatalog(globalCatalog, tenantTools).map((t) => ({\n domain: t.domain,\n toolName: t.toolName,\n description: t.description,\n annotations: t.annotations,\n hasUi: Boolean(t.ui),\n }));\n\n // A2A surface: one card per granted agent.\n const agentGrant = grants.find((g) => g.domain === \"agent\");\n const assistantStore = getStoreLattice(\"default\", \"assistant\")\n .store as AssistantStore;\n let pushNotifications = false;\n try {\n pushNotifications = Boolean(getStoreLattice(\"default\", \"store\").store);\n } catch {\n pushNotifications = false;\n }\n const agents: Array<{\n assistantId: string;\n cardUrl: string;\n jsonrpcUrl: string;\n v1CardUrl: string;\n v1JsonrpcUrl: string;\n card: unknown;\n }> = [];\n for (const assistantId of agentGrant?.items ?? []) {\n const assistant = await assistantStore.getAssistantById(\n tenantId,\n assistantId,\n );\n if (!assistant) continue;\n agents.push({\n assistantId,\n cardUrl: `${baseUrl}/api/a2a/agents/${assistantId}/.well-known/agent-card.json`,\n jsonrpcUrl: `${baseUrl}/api/a2a/agents/${assistantId}/jsonrpc`,\n v1CardUrl: `${baseUrl}/a2a/v1/${assistantId}/.well-known/agent-card.json`,\n v1JsonrpcUrl: `${baseUrl}/a2a/v1/${assistantId}`,\n card: buildAgentCardForAssistant({\n assistant,\n baseUrl,\n pushNotifications,\n }),\n });\n }\n\n return reply.status(200).send({\n success: true,\n data: {\n tenantId,\n projectId: record.projectId,\n grants,\n mcp: {\n endpoint: `${baseUrl}/open/mcp`,\n tools,\n // Surfaced so the console can distinguish \"no tenant tools granted\"\n // from \"tenant catalog unreadable\"; the live Open session degrades\n // to globals-only either way.\n tenantCatalogDegraded,\n },\n a2a: { agents },\n },\n });\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAOA,SAAS,uBAAuB;AAMhC,SAAS,uBAAuB;AAuBzB,SAAS,2BACd,KACA,OAAyB,CAAC,GACpB;AACN,MAAI;AAAA,IACF;AAAA,IACA,OACE,SACA,UACG;AACH,YAAM,WAAY,QAA4B,MAAM;AACpD,UAAI,CAAC,UAAU;AACb,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,eAAe,CAAC;AAAA,MACzE;AAEA,YAAM,WAAW,gBAAgB,WAAW,WAAW,EACpD;AACH,YAAM,SAAS,MAAM,SAAS,SAAS,QAAQ,OAAO,EAAE;AACxD,UAAI,CAAC,UAAU,OAAO,aAAa,UAAU;AAC3C,eAAO,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,OAAO,OAAO,gBAAgB,CAAC;AAAA,MAC1E;AAEA,YAAM,SAAsB,gBAAgB,MAAM;AAClD,YAAM,UAAU,sBAAsB,OAAO;AAG7C,YAAM,gBAAgB,MAAM,YAAY,WAAW,QAAQ,EAAE,SAAS,CAAC;AAMvE,UAAI,cAAoC,CAAC;AACzC,UAAI,wBAAwB;AAC5B,UAAI;AACF,cAAM,SAAS,MAAM,uBAAuB,UAAU,QAAQ;AAAA,UAC5D,GAAG,KAAK;AAAA,UACR,mBAAmB,IAAI,IAAI,cAAc,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC;AAAA,QACjE,CAAC;AACD,sBAAc,OAAO;AACrB,gCAAwB,OAAO;AAAA,MACjC,SAAS,OAAO;AACd,gCAAwB;AACxB,gBAAQ,IAAI;AAAA,UACV,EAAE,UAAU,KAAK,MAAM;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AAEA,YAAM,QAAQ,iBAAiB,eAAe,WAAW,EAAE,IAAI,CAAC,OAAO;AAAA,QACrE,QAAQ,EAAE;AAAA,QACV,UAAU,EAAE;AAAA,QACZ,aAAa,EAAE;AAAA,QACf,aAAa,EAAE;AAAA,QACf,OAAO,QAAQ,EAAE,EAAE;AAAA,MACrB,EAAE;AAGF,YAAM,aAAa,OAAO,KAAK,CAAC,MAAM,EAAE,WAAW,OAAO;AAC1D,YAAM,iBAAiB,gBAAgB,WAAW,WAAW,EAC1D;AACH,UAAI,oBAAoB;AACxB,UAAI;AACF,4BAAoB,QAAQ,gBAAgB,WAAW,OAAO,EAAE,KAAK;AAAA,MACvE,QAAQ;AACN,4BAAoB;AAAA,MACtB;AACA,YAAM,SAOD,CAAC;AACN,iBAAW,eAAe,YAAY,SAAS,CAAC,GAAG;AACjD,cAAM,YAAY,MAAM,eAAe;AAAA,UACrC;AAAA,UACA;AAAA,QACF;AACA,YAAI,CAAC,UAAW;AAChB,eAAO,KAAK;AAAA,UACV;AAAA,UACA,SAAS,GAAG,OAAO,mBAAmB,WAAW;AAAA,UACjD,YAAY,GAAG,OAAO,mBAAmB,WAAW;AAAA,UACpD,WAAW,GAAG,OAAO,WAAW,WAAW;AAAA,UAC3C,cAAc,GAAG,OAAO,WAAW,WAAW;AAAA,UAC9C,MAAM,2BAA2B;AAAA,YAC/B;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAEA,aAAO,MAAM,OAAO,GAAG,EAAE,KAAK;AAAA,QAC5B,SAAS;AAAA,QACT,MAAM;AAAA,UACJ;AAAA,UACA,WAAW,OAAO;AAAA,UAClB;AAAA,UACA,KAAK;AAAA,YACH,UAAU,GAAG,OAAO;AAAA,YACpB;AAAA;AAAA;AAAA;AAAA,YAIA;AAAA,UACF;AAAA,UACA,KAAK,EAAE,OAAO;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/gateway",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -30,12 +30,14 @@
|
|
|
30
30
|
"@fastify/swagger": "^9.5.1",
|
|
31
31
|
"@fastify/swagger-ui": "^5.2.3",
|
|
32
32
|
"@fastify/websocket": "^11.0.1",
|
|
33
|
-
"@langchain/core": "1.
|
|
34
|
-
"@langchain/langgraph": "1.
|
|
33
|
+
"@langchain/core": "1.3.2",
|
|
34
|
+
"@langchain/langgraph": "1.5.3",
|
|
35
35
|
"@langfuse/langchain": "^6.0.2",
|
|
36
36
|
"@langfuse/otel": "^6.0.2",
|
|
37
37
|
"@larksuiteoapi/node-sdk": "^1.47.0",
|
|
38
|
-
"@modelcontextprotocol/
|
|
38
|
+
"@modelcontextprotocol/client": "2.4.0",
|
|
39
|
+
"@modelcontextprotocol/node": "2.1.2",
|
|
40
|
+
"@modelcontextprotocol/server": "2.4.0",
|
|
39
41
|
"@opentelemetry/api": "^1.9.1",
|
|
40
42
|
"@opentelemetry/sdk-trace-base": "2.6.1",
|
|
41
43
|
"@opentelemetry/sdk-trace-node": "2.6.1",
|
|
@@ -54,12 +56,12 @@
|
|
|
54
56
|
"tar": "^7.5.22",
|
|
55
57
|
"uuid": "^9.0.1",
|
|
56
58
|
"web-push": "^3.6.7",
|
|
57
|
-
"zod": "
|
|
58
|
-
"@axiom-lattice/cli-a2a": "0.2.
|
|
59
|
-
"@axiom-lattice/core": "
|
|
60
|
-
"@axiom-lattice/pg-stores": "
|
|
61
|
-
"@axiom-lattice/protocols": "
|
|
62
|
-
"@axiom-lattice/queue-redis": "1.1.
|
|
59
|
+
"zod": "4.6.7",
|
|
60
|
+
"@axiom-lattice/cli-a2a": "0.2.30",
|
|
61
|
+
"@axiom-lattice/core": "12.0.0",
|
|
62
|
+
"@axiom-lattice/pg-stores": "5.0.0",
|
|
63
|
+
"@axiom-lattice/protocols": "7.0.0",
|
|
64
|
+
"@axiom-lattice/queue-redis": "1.1.30"
|
|
63
65
|
},
|
|
64
66
|
"devDependencies": {
|
|
65
67
|
"@types/bcrypt": "^6.0.0",
|
|
@@ -75,7 +77,7 @@
|
|
|
75
77
|
"@typescript-eslint/parser": "^7.2.0",
|
|
76
78
|
"eslint": "^8.57.0",
|
|
77
79
|
"jest": "^29.7.0",
|
|
78
|
-
"langchain": "1.0
|
|
80
|
+
"langchain": "1.6.0",
|
|
79
81
|
"nodemon": "^3.1.9",
|
|
80
82
|
"ts-jest": "^29.4.0",
|
|
81
83
|
"ts-node": "^10.9.2",
|
|
@@ -83,8 +85,11 @@
|
|
|
83
85
|
"tsup": "^8.5.0",
|
|
84
86
|
"typescript": "^5.8.2",
|
|
85
87
|
"ws": "^8.19.0",
|
|
86
|
-
"@axiom-lattice/open-mcp-client": "0.2.
|
|
87
|
-
"@axiom-lattice/react-sdk": "
|
|
88
|
+
"@axiom-lattice/open-mcp-client": "0.2.1",
|
|
89
|
+
"@axiom-lattice/react-sdk": "10.0.0"
|
|
90
|
+
},
|
|
91
|
+
"engines": {
|
|
92
|
+
"node": ">=20.10.0"
|
|
88
93
|
},
|
|
89
94
|
"scripts": {
|
|
90
95
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean --sourcemap && pnpm copy:embed-assets",
|