@agent-native/dispatch 0.8.11 → 0.8.13
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-gateway.d.ts","sourceRoot":"","sources":["../../../src/server/lib/mcp-gateway.ts"],"names":[],"mappings":"AAoBA,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp-gateway.d.ts","sourceRoot":"","sources":["../../../src/server/lib/mcp-gateway.ts"],"names":[],"mappings":"AAoBA,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,uBAAuB,CAAC;AAS/B,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AA6KD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IACnD,QAAQ,EAAE,4BAA4B,CAAC;IACvC,IAAI,EAAE,wBAAwB,EAAE,CAAC;CAClC,CAAC,CAcD;AAED,wBAAsB,0BAA0B,IAAI,OAAO,CACzD,wBAAwB,EAAE,CAC3B,CAGA;AAED,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,wBAAwB,CAAC,CAmBnC;AAED,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB9D;AAED,wBAAsB,yBAAyB,CAAC,KAAK,EAAE;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACnD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,GAAG,OAAO,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC,CAwCD;AAmKD,wBAAsB,oCAAoC,CAAC,KAAK,EAAE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,GAAG,OAAO,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CA6DD"}
|
|
@@ -9,6 +9,7 @@ const DISPATCH_APP_ID = "dispatch";
|
|
|
9
9
|
const DISPATCH_NAME = "Agent-Native Dispatch";
|
|
10
10
|
const DISPATCH_DESCRIPTION = "Workspace control plane for extensions, agents, vault, integrations, approvals, and app routing.";
|
|
11
11
|
const DISPATCH_COLOR = "#14B8A6";
|
|
12
|
+
const TARGET_EMBED_SESSION_ATTEMPTS = 3;
|
|
12
13
|
function normalizeAppId(value) {
|
|
13
14
|
return value.trim().toLowerCase();
|
|
14
15
|
}
|
|
@@ -283,6 +284,53 @@ function parseMcpToolTextResult(result) {
|
|
|
283
284
|
}
|
|
284
285
|
throw new Error("Target app did not return an embed session.");
|
|
285
286
|
}
|
|
287
|
+
function sleep(ms) {
|
|
288
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
289
|
+
}
|
|
290
|
+
function isRetryableTargetMcpError(error) {
|
|
291
|
+
const message = error instanceof Error
|
|
292
|
+
? error.message
|
|
293
|
+
: typeof error === "string"
|
|
294
|
+
? error
|
|
295
|
+
: String(error ?? "");
|
|
296
|
+
return /not connected|streamable http|handshake|failed to fetch|fetch failed|networkerror|econnrefused|enotfound|timed out|timeout|502|503|504/i.test(message);
|
|
297
|
+
}
|
|
298
|
+
async function callTargetCreateEmbedSession(input) {
|
|
299
|
+
const serverId = "target";
|
|
300
|
+
let lastError;
|
|
301
|
+
for (let attempt = 1; attempt <= TARGET_EMBED_SESSION_ATTEMPTS; attempt++) {
|
|
302
|
+
const manager = new McpClientManager({
|
|
303
|
+
servers: {
|
|
304
|
+
[serverId]: {
|
|
305
|
+
type: "http",
|
|
306
|
+
url: `${appBaseUrl(input.app)}/_agent-native/mcp`,
|
|
307
|
+
headers: {
|
|
308
|
+
Authorization: `Bearer ${input.token}`,
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
try {
|
|
314
|
+
await manager.start();
|
|
315
|
+
return await manager.callTool(buildMcpToolName(serverId, "create_embed_session"), {
|
|
316
|
+
url: input.url,
|
|
317
|
+
chrome: input.chrome ?? "full",
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
lastError = error;
|
|
322
|
+
if (attempt >= TARGET_EMBED_SESSION_ATTEMPTS ||
|
|
323
|
+
!isRetryableTargetMcpError(error)) {
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
await sleep(250 * attempt);
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
await manager.stop();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
throw lastError;
|
|
333
|
+
}
|
|
286
334
|
async function resolveDispatchEmbedTarget(input) {
|
|
287
335
|
const explicitApp = input.app?.trim()
|
|
288
336
|
? await resolveGrantedDispatchMcpApp(input.app)
|
|
@@ -369,45 +417,29 @@ export async function createGrantedDispatchMcpEmbedSession(input) {
|
|
|
369
417
|
: [null, null];
|
|
370
418
|
const token = await signA2AToken(userEmail, orgDomain ?? undefined, orgSecret ?? undefined, {
|
|
371
419
|
expiresIn: "5m",
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
preferGlobalSecret:
|
|
420
|
+
// Prefer the synced org A2A secret when present because first-party
|
|
421
|
+
// production apps do not have to share the same deployment env secret.
|
|
422
|
+
// Fall back to the global A2A_SECRET for orgs that have not synced yet.
|
|
423
|
+
preferGlobalSecret: !orgSecret,
|
|
376
424
|
});
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
url: `${appBaseUrl(target.app)}/_agent-native/mcp`,
|
|
383
|
-
headers: {
|
|
384
|
-
Authorization: `Bearer ${token}`,
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
},
|
|
425
|
+
const result = await callTargetCreateEmbedSession({
|
|
426
|
+
app: target.app,
|
|
427
|
+
token,
|
|
428
|
+
url: target.url,
|
|
429
|
+
chrome: input.chrome,
|
|
388
430
|
});
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
url: target.url,
|
|
393
|
-
chrome: input.chrome ?? "full",
|
|
394
|
-
});
|
|
395
|
-
const parsed = parseMcpToolTextResult(result);
|
|
396
|
-
if (!parsed.startUrl) {
|
|
397
|
-
throw new Error("Target app did not return an embed start URL.");
|
|
398
|
-
}
|
|
399
|
-
const output = {
|
|
400
|
-
startUrl: parsed.startUrl,
|
|
401
|
-
app: target.app.id,
|
|
402
|
-
};
|
|
403
|
-
if (parsed.targetPath)
|
|
404
|
-
output.targetPath = parsed.targetPath;
|
|
405
|
-
if (typeof parsed.expiresAt === "number")
|
|
406
|
-
output.expiresAt = parsed.expiresAt;
|
|
407
|
-
return output;
|
|
408
|
-
}
|
|
409
|
-
finally {
|
|
410
|
-
await manager.stop();
|
|
431
|
+
const parsed = parseMcpToolTextResult(result);
|
|
432
|
+
if (!parsed.startUrl) {
|
|
433
|
+
throw new Error("Target app did not return an embed start URL.");
|
|
411
434
|
}
|
|
435
|
+
const output = {
|
|
436
|
+
startUrl: parsed.startUrl,
|
|
437
|
+
app: target.app.id,
|
|
438
|
+
};
|
|
439
|
+
if (parsed.targetPath)
|
|
440
|
+
output.targetPath = parsed.targetPath;
|
|
441
|
+
if (typeof parsed.expiresAt === "number")
|
|
442
|
+
output.expiresAt = parsed.expiresAt;
|
|
443
|
+
return output;
|
|
412
444
|
}
|
|
413
445
|
//# sourceMappingURL=mcp-gateway.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-gateway.js","sourceRoot":"","sources":["../../../src/server/lib/mcp-gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import { callAgent, signA2AToken } from \"@agent-native/core/a2a\";\nimport {\n buildMcpToolName,\n McpClientManager,\n} from \"@agent-native/core/mcp-client\";\nimport {\n buildDeepLink,\n buildEmbedStartPath,\n createEmbedSessionTicket,\n getRequestContext,\n} from \"@agent-native/core/server\";\nimport {\n discoverAgents,\n type DiscoveredAgent,\n} from \"@agent-native/core/server/agent-discovery\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"@agent-native/core/server\";\nimport { getOrgA2ASecret, getOrgDomain } from \"@agent-native/core/org\";\nimport {\n getDispatchMcpAppAccessSettings,\n isAppAllowedByMcpAccess,\n type DispatchMcpAppAccessSettings,\n} from \"./mcp-access-store.js\";\n\nconst DISPATCH_APP_ID = \"dispatch\";\nconst DISPATCH_NAME = \"Agent-Native Dispatch\";\nconst DISPATCH_DESCRIPTION =\n \"Workspace control plane for extensions, agents, vault, integrations, approvals, and app routing.\";\nconst DISPATCH_COLOR = \"#14B8A6\";\n\nexport interface DispatchMcpAccessibleApp {\n id: string;\n name: string;\n description: string;\n url: string;\n color: string;\n granted: boolean;\n}\n\nfunction normalizeAppId(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction normalizeBaseUrl(raw: string | undefined | null): string | null {\n const value = raw?.trim();\n if (!value) return null;\n try {\n const url = new URL(value);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n return url.toString().replace(/\\/+$/, \"\");\n } catch {\n return null;\n }\n}\n\nfunction normalizeBasePath(value: string | undefined): string {\n const trimmed = value?.trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const normalized = trimmed.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n return normalized ? `/${normalized}` : \"\";\n}\n\nfunction withConfiguredBasePath(baseUrl: string): string {\n const basePath = normalizeBasePath(\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH,\n );\n if (!basePath) return baseUrl;\n try {\n const url = new URL(baseUrl);\n const path = normalizeBasePath(url.pathname);\n if (path === basePath || path.startsWith(`${basePath}/`)) {\n return baseUrl;\n }\n url.pathname = path && path !== \"/\" ? `${basePath}${path}` : `${basePath}/`;\n return url.toString().replace(/\\/+$/, \"\");\n } catch {\n return baseUrl;\n }\n}\n\nfunction dispatchSelfBaseUrl(): string {\n const requestOrigin = normalizeBaseUrl(getRequestContext()?.requestOrigin);\n if (requestOrigin) return withConfiguredBasePath(requestOrigin);\n\n const configured =\n normalizeBaseUrl(process.env.WORKSPACE_GATEWAY_URL) ??\n normalizeBaseUrl(process.env.APP_URL) ??\n normalizeBaseUrl(process.env.URL) ??\n normalizeBaseUrl(process.env.DEPLOY_URL) ??\n normalizeBaseUrl(process.env.BETTER_AUTH_URL);\n if (configured) return withConfiguredBasePath(configured);\n\n return process.env.NODE_ENV === \"production\"\n ? \"https://dispatch.agent-native.com\"\n : \"http://localhost:8092\";\n}\n\nfunction dispatchSelfApp(\n settings: DispatchMcpAppAccessSettings,\n): DispatchMcpAccessibleApp {\n return {\n id: DISPATCH_APP_ID,\n name: DISPATCH_NAME,\n description: DISPATCH_DESCRIPTION,\n url: dispatchSelfBaseUrl(),\n color: DISPATCH_COLOR,\n granted: isAppAllowedByMcpAccess(DISPATCH_APP_ID, settings),\n };\n}\n\nconst CONTROL_CHARS = new RegExp(\"[\\\\u0000-\\\\u001f\\\\u007f]\");\n\nfunction safeAppPath(raw: unknown): string | null {\n if (typeof raw !== \"string\" || !raw.trim()) return null;\n const value = raw.trim();\n if (CONTROL_CHARS.test(value)) return null;\n if (!value.startsWith(\"/\")) return null;\n if (value.startsWith(\"//\") || value.startsWith(\"/\\\\\")) return null;\n if (/^\\/[a-z][a-z0-9+.-]*:/i.test(value)) return null;\n if (/%(?:2f|5c)/i.test(value)) return null;\n const rawPath = value.split(/[?#]/, 1)[0] ?? value;\n let parsed: URL;\n try {\n parsed = new URL(value, \"http://agent-native.invalid\");\n } catch {\n return null;\n }\n if (parsed.pathname !== rawPath) return null;\n return value;\n}\n\nfunction appendParamsToPath(\n path: string,\n params: Record<string, string | number | boolean> | undefined,\n): string {\n if (!params || Object.keys(params).length === 0) return path;\n const url = new URL(path, \"http://agent-native.invalid\");\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, String(value));\n }\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nfunction appOrigin(app: DispatchMcpAccessibleApp): string {\n return new URL(app.url).origin;\n}\n\nfunction appBaseUrl(app: DispatchMcpAccessibleApp): string {\n return app.url.replace(/\\/+$/, \"\");\n}\n\nfunction appBasePath(app: DispatchMcpAccessibleApp): string {\n const pathname = new URL(appBaseUrl(app)).pathname.replace(/\\/+$/, \"\");\n return pathname === \"/\" ? \"\" : pathname;\n}\n\nfunction appMatchesUrlPath(app: DispatchMcpAccessibleApp, url: URL): boolean {\n if (url.origin !== appOrigin(app)) return false;\n const basePath = appBasePath(app);\n if (!basePath) return true;\n return url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);\n}\n\nfunction appPathSpecificity(app: DispatchMcpAccessibleApp): number {\n return appBasePath(app).length;\n}\n\nfunction appRelativePath(app: DispatchMcpAccessibleApp, url: URL): string {\n const basePath = appBasePath(app);\n const path = basePath\n ? url.pathname === basePath\n ? \"/\"\n : url.pathname.slice(basePath.length)\n : url.pathname;\n return `${path || \"/\"}${url.search}${url.hash}`;\n}\n\nfunction isDispatchControlPath(path: string | null): boolean {\n if (!path) return false;\n const route = path.split(/[?#]/, 1)[0] ?? path;\n return (\n route === \"/extensions\" ||\n route.startsWith(\"/extensions/\") ||\n route === \"/tools\" ||\n route.startsWith(\"/tools/\")\n );\n}\n\nfunction assertAppCanOpenPath(app: DispatchMcpAccessibleApp, path: string) {\n if (app.id !== DISPATCH_APP_ID && isDispatchControlPath(path)) {\n throw new Error(\n `Path \"${path}\" belongs to Dispatch. Use app: \"dispatch\" for Dispatch extension and tool routes.`,\n );\n }\n}\n\nfunction toAccessibleApp(\n agent: DiscoveredAgent,\n settings: DispatchMcpAppAccessSettings,\n): DispatchMcpAccessibleApp {\n return {\n id: agent.id,\n name: agent.name,\n description: agent.description,\n url: agent.url,\n color: agent.color,\n granted: isAppAllowedByMcpAccess(agent.id, settings),\n };\n}\n\nexport async function listDispatchMcpApps(): Promise<{\n settings: DispatchMcpAppAccessSettings;\n apps: DispatchMcpAccessibleApp[];\n}> {\n const [settings, agents] = await Promise.all([\n getDispatchMcpAppAccessSettings(),\n discoverAgents(\"dispatch\"),\n ]);\n return {\n settings,\n apps: [\n dispatchSelfApp(settings),\n ...agents\n .filter((agent) => normalizeAppId(agent.id) !== DISPATCH_APP_ID)\n .map((agent) => toAccessibleApp(agent, settings)),\n ],\n };\n}\n\nexport async function listGrantedDispatchMcpApps(): Promise<\n DispatchMcpAccessibleApp[]\n> {\n const { apps } = await listDispatchMcpApps();\n return apps.filter((app) => app.granted);\n}\n\nexport async function resolveGrantedDispatchMcpApp(\n app: string,\n): Promise<DispatchMcpAccessibleApp> {\n const target = normalizeAppId(app);\n if (!target) throw new Error(\"app is required\");\n const { apps } = await listDispatchMcpApps();\n const match = apps.find(\n (candidate) =>\n candidate.id === target || candidate.name.toLowerCase() === target,\n );\n if (!match) {\n throw new Error(\n `Unknown app \"${app}\". Call list_apps to see apps available through Dispatch MCP.`,\n );\n }\n if (!match.granted) {\n throw new Error(\n `Dispatch MCP access to \"${match.id}\" is not granted. Open Dispatch > Agents to change MCP app access.`,\n );\n }\n return match;\n}\n\nexport async function askGrantedDispatchMcpApp(\n app: string,\n message: string,\n): Promise<{ app: string; routedVia: \"a2a\"; response: string }> {\n const trimmedMessage = message.trim();\n if (!trimmedMessage) throw new Error(\"message is required\");\n const target = await resolveGrantedDispatchMcpApp(app);\n const userEmail = getRequestUserEmail();\n if (!userEmail) throw new Error(\"no authenticated user\");\n\n const orgId = getRequestOrgId();\n const [orgDomain, orgSecret] = orgId\n ? await Promise.all([\n getOrgDomain(orgId).catch(() => null),\n getOrgA2ASecret(orgId).catch(() => null),\n ])\n : [null, null];\n\n const response = await callAgent(target.url, trimmedMessage, {\n userEmail,\n orgDomain: orgDomain ?? undefined,\n orgSecret: orgSecret ?? undefined,\n timeoutMs: 5 * 60_000,\n });\n return { app: target.id, routedVia: \"a2a\", response };\n}\n\nexport async function openGrantedDispatchMcpApp(input: {\n app: string;\n view?: string;\n path?: string;\n params?: Record<string, string | number | boolean>;\n embed?: boolean;\n chrome?: \"full\" | \"minimal\";\n}): Promise<{\n app: string;\n view?: string;\n path?: string;\n url: string;\n embed?: boolean;\n chrome?: \"full\" | \"minimal\";\n embedStartUrl?: string;\n embedTargetPath?: string;\n embedExpiresAt?: number;\n}> {\n const view = input.view?.trim() ?? \"\";\n const hasPathInput = input.path != null;\n const path = safeAppPath(input.path);\n if (hasPathInput && !path) {\n throw new Error(\"path must be a safe app-relative route\");\n }\n if (!view && !path) throw new Error(\"open_app requires view or path\");\n const target = await resolveGrantedDispatchMcpApp(input.app);\n if (path) assertAppCanOpenPath(target, path);\n const relUrl = path\n ? appendParamsToPath(path, input.params)\n : buildDeepLink({\n app: target.id,\n view,\n params: input.params,\n });\n const url = `${appBaseUrl(target)}${relUrl}`;\n const embedSession = input.embed\n ? await createGrantedDispatchMcpEmbedSession({\n app: target.id,\n url,\n chrome: input.chrome,\n })\n : null;\n return {\n app: target.id,\n ...(view ? { view } : {}),\n ...(path ? { path } : {}),\n url,\n ...(input.embed === true ? { embed: true } : {}),\n ...(input.chrome ? { chrome: input.chrome } : {}),\n ...(embedSession?.startUrl ? { embedStartUrl: embedSession.startUrl } : {}),\n ...(embedSession?.targetPath\n ? { embedTargetPath: embedSession.targetPath }\n : {}),\n ...(typeof embedSession?.expiresAt === \"number\"\n ? { embedExpiresAt: embedSession.expiresAt }\n : {}),\n };\n}\n\nfunction parseMcpToolTextResult(result: unknown): Record<string, unknown> {\n if (result && typeof result === \"object\") {\n const structured = (result as any).structuredContent;\n if (structured && typeof structured === \"object\") return structured;\n const parts = Array.isArray((result as any).content)\n ? ((result as any).content as Array<Record<string, unknown>>)\n : [];\n const text = parts.find(\n (part) => part?.type === \"text\" && typeof part.text === \"string\",\n )?.text;\n if (typeof text === \"string\" && text.trim()) {\n if ((result as any).isError) throw new Error(text.trim());\n const parsed = JSON.parse(text);\n if (parsed && typeof parsed === \"object\") return parsed;\n }\n }\n throw new Error(\"Target app did not return an embed session.\");\n}\n\nasync function resolveDispatchEmbedTarget(input: {\n app?: string;\n url?: string;\n path?: string;\n}): Promise<{ app: DispatchMcpAccessibleApp; path: string; url: string }> {\n const explicitApp = input.app?.trim()\n ? await resolveGrantedDispatchMcpApp(input.app)\n : null;\n if (explicitApp && input.path) {\n const path = safeAppPath(input.path);\n if (!path) throw new Error(\"path must be a safe app-relative route\");\n assertAppCanOpenPath(explicitApp, path);\n return {\n app: explicitApp,\n path,\n url: `${appBaseUrl(explicitApp)}${path}`,\n };\n }\n\n if (!input.url) {\n throw new Error(\"create_embed_session requires a url or app + path.\");\n }\n\n let parsed: URL;\n try {\n parsed = new URL(input.url);\n } catch {\n if (!explicitApp) {\n throw new Error(\"Relative embed paths require an app id.\");\n }\n const path = safeAppPath(input.url);\n if (!path) throw new Error(\"url must be a safe app route.\");\n return {\n app: explicitApp,\n path,\n url: `${appBaseUrl(explicitApp)}${path}`,\n };\n }\n\n const apps = explicitApp ? [explicitApp] : await listGrantedDispatchMcpApps();\n const target = apps\n .filter((app) => appMatchesUrlPath(app, parsed))\n .sort((a, b) => appPathSpecificity(b) - appPathSpecificity(a))[0];\n if (!target) {\n throw new Error(\n \"Embed URL must belong to an app granted through Dispatch.\",\n );\n }\n const path = safeAppPath(appRelativePath(target, parsed));\n if (!path) throw new Error(\"Embed URL path is not safe.\");\n assertAppCanOpenPath(target, path);\n return { app: target, path, url: `${appBaseUrl(target)}${path}` };\n}\n\nasync function createDispatchSelfEmbedSession(input: {\n ownerEmail: string;\n orgId?: string;\n path: string;\n baseUrl: string;\n chrome?: \"full\" | \"minimal\";\n}): Promise<{\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n}> {\n const ticket = await createEmbedSessionTicket({\n ownerEmail: input.ownerEmail,\n orgId: input.orgId,\n targetPath: input.path,\n scope: input.chrome ?? null,\n });\n const startPath = buildEmbedStartPath(ticket.ticket);\n return {\n startUrl: new URL(startPath, input.baseUrl).toString(),\n targetPath: input.path,\n expiresAt: ticket.expiresAt,\n app: DISPATCH_APP_ID,\n };\n}\n\nexport async function createGrantedDispatchMcpEmbedSession(input: {\n app?: string;\n url?: string;\n path?: string;\n chrome?: \"full\" | \"minimal\";\n}): Promise<{\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n}> {\n const userEmail = getRequestUserEmail();\n if (!userEmail) throw new Error(\"no authenticated user\");\n const target = await resolveDispatchEmbedTarget(input);\n\n const orgId = getRequestOrgId();\n if (target.app.id === DISPATCH_APP_ID) {\n return createDispatchSelfEmbedSession({\n ownerEmail: userEmail,\n orgId,\n path: target.path,\n baseUrl: appBaseUrl(target.app),\n chrome: input.chrome,\n });\n }\n\n const [orgDomain, orgSecret] = orgId\n ? await Promise.all([\n getOrgDomain(orgId).catch(() => null),\n getOrgA2ASecret(orgId).catch(() => null),\n ])\n : [null, null];\n const token = await signA2AToken(\n userEmail,\n orgDomain ?? undefined,\n orgSecret ?? undefined,\n {\n expiresIn: \"5m\",\n // Target MCP endpoints verify A2A JWTs with the deployment-wide\n // A2A_SECRET. Prefer it for hosted cross-app embeds even when Dispatch\n // also has an org-level secret available.\n preferGlobalSecret: true,\n },\n );\n\n const serverId = \"target\";\n const manager = new McpClientManager({\n servers: {\n [serverId]: {\n type: \"http\",\n url: `${appBaseUrl(target.app)}/_agent-native/mcp`,\n headers: {\n Authorization: `Bearer ${token}`,\n },\n },\n },\n });\n await manager.start();\n try {\n const result = await manager.callTool(\n buildMcpToolName(serverId, \"create_embed_session\"),\n {\n url: target.url,\n chrome: input.chrome ?? \"full\",\n },\n );\n const parsed = parseMcpToolTextResult(result) as {\n startUrl?: string;\n targetPath?: string;\n expiresAt?: number;\n };\n if (!parsed.startUrl) {\n throw new Error(\"Target app did not return an embed start URL.\");\n }\n const output: {\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n } = {\n startUrl: parsed.startUrl,\n app: target.app.id,\n };\n if (parsed.targetPath) output.targetPath = parsed.targetPath;\n if (typeof parsed.expiresAt === \"number\")\n output.expiresAt = parsed.expiresAt;\n return output;\n } finally {\n await manager.stop();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-gateway.js","sourceRoot":"","sources":["../../../src/server/lib/mcp-gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import { callAgent, signA2AToken } from \"@agent-native/core/a2a\";\nimport {\n buildMcpToolName,\n McpClientManager,\n} from \"@agent-native/core/mcp-client\";\nimport {\n buildDeepLink,\n buildEmbedStartPath,\n createEmbedSessionTicket,\n getRequestContext,\n} from \"@agent-native/core/server\";\nimport {\n discoverAgents,\n type DiscoveredAgent,\n} from \"@agent-native/core/server/agent-discovery\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"@agent-native/core/server\";\nimport { getOrgA2ASecret, getOrgDomain } from \"@agent-native/core/org\";\nimport {\n getDispatchMcpAppAccessSettings,\n isAppAllowedByMcpAccess,\n type DispatchMcpAppAccessSettings,\n} from \"./mcp-access-store.js\";\n\nconst DISPATCH_APP_ID = \"dispatch\";\nconst DISPATCH_NAME = \"Agent-Native Dispatch\";\nconst DISPATCH_DESCRIPTION =\n \"Workspace control plane for extensions, agents, vault, integrations, approvals, and app routing.\";\nconst DISPATCH_COLOR = \"#14B8A6\";\nconst TARGET_EMBED_SESSION_ATTEMPTS = 3;\n\nexport interface DispatchMcpAccessibleApp {\n id: string;\n name: string;\n description: string;\n url: string;\n color: string;\n granted: boolean;\n}\n\nfunction normalizeAppId(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction normalizeBaseUrl(raw: string | undefined | null): string | null {\n const value = raw?.trim();\n if (!value) return null;\n try {\n const url = new URL(value);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return null;\n return url.toString().replace(/\\/+$/, \"\");\n } catch {\n return null;\n }\n}\n\nfunction normalizeBasePath(value: string | undefined): string {\n const trimmed = value?.trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const normalized = trimmed.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n return normalized ? `/${normalized}` : \"\";\n}\n\nfunction withConfiguredBasePath(baseUrl: string): string {\n const basePath = normalizeBasePath(\n process.env.VITE_APP_BASE_PATH || process.env.APP_BASE_PATH,\n );\n if (!basePath) return baseUrl;\n try {\n const url = new URL(baseUrl);\n const path = normalizeBasePath(url.pathname);\n if (path === basePath || path.startsWith(`${basePath}/`)) {\n return baseUrl;\n }\n url.pathname = path && path !== \"/\" ? `${basePath}${path}` : `${basePath}/`;\n return url.toString().replace(/\\/+$/, \"\");\n } catch {\n return baseUrl;\n }\n}\n\nfunction dispatchSelfBaseUrl(): string {\n const requestOrigin = normalizeBaseUrl(getRequestContext()?.requestOrigin);\n if (requestOrigin) return withConfiguredBasePath(requestOrigin);\n\n const configured =\n normalizeBaseUrl(process.env.WORKSPACE_GATEWAY_URL) ??\n normalizeBaseUrl(process.env.APP_URL) ??\n normalizeBaseUrl(process.env.URL) ??\n normalizeBaseUrl(process.env.DEPLOY_URL) ??\n normalizeBaseUrl(process.env.BETTER_AUTH_URL);\n if (configured) return withConfiguredBasePath(configured);\n\n return process.env.NODE_ENV === \"production\"\n ? \"https://dispatch.agent-native.com\"\n : \"http://localhost:8092\";\n}\n\nfunction dispatchSelfApp(\n settings: DispatchMcpAppAccessSettings,\n): DispatchMcpAccessibleApp {\n return {\n id: DISPATCH_APP_ID,\n name: DISPATCH_NAME,\n description: DISPATCH_DESCRIPTION,\n url: dispatchSelfBaseUrl(),\n color: DISPATCH_COLOR,\n granted: isAppAllowedByMcpAccess(DISPATCH_APP_ID, settings),\n };\n}\n\nconst CONTROL_CHARS = new RegExp(\"[\\\\u0000-\\\\u001f\\\\u007f]\");\n\nfunction safeAppPath(raw: unknown): string | null {\n if (typeof raw !== \"string\" || !raw.trim()) return null;\n const value = raw.trim();\n if (CONTROL_CHARS.test(value)) return null;\n if (!value.startsWith(\"/\")) return null;\n if (value.startsWith(\"//\") || value.startsWith(\"/\\\\\")) return null;\n if (/^\\/[a-z][a-z0-9+.-]*:/i.test(value)) return null;\n if (/%(?:2f|5c)/i.test(value)) return null;\n const rawPath = value.split(/[?#]/, 1)[0] ?? value;\n let parsed: URL;\n try {\n parsed = new URL(value, \"http://agent-native.invalid\");\n } catch {\n return null;\n }\n if (parsed.pathname !== rawPath) return null;\n return value;\n}\n\nfunction appendParamsToPath(\n path: string,\n params: Record<string, string | number | boolean> | undefined,\n): string {\n if (!params || Object.keys(params).length === 0) return path;\n const url = new URL(path, \"http://agent-native.invalid\");\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, String(value));\n }\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nfunction appOrigin(app: DispatchMcpAccessibleApp): string {\n return new URL(app.url).origin;\n}\n\nfunction appBaseUrl(app: DispatchMcpAccessibleApp): string {\n return app.url.replace(/\\/+$/, \"\");\n}\n\nfunction appBasePath(app: DispatchMcpAccessibleApp): string {\n const pathname = new URL(appBaseUrl(app)).pathname.replace(/\\/+$/, \"\");\n return pathname === \"/\" ? \"\" : pathname;\n}\n\nfunction appMatchesUrlPath(app: DispatchMcpAccessibleApp, url: URL): boolean {\n if (url.origin !== appOrigin(app)) return false;\n const basePath = appBasePath(app);\n if (!basePath) return true;\n return url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);\n}\n\nfunction appPathSpecificity(app: DispatchMcpAccessibleApp): number {\n return appBasePath(app).length;\n}\n\nfunction appRelativePath(app: DispatchMcpAccessibleApp, url: URL): string {\n const basePath = appBasePath(app);\n const path = basePath\n ? url.pathname === basePath\n ? \"/\"\n : url.pathname.slice(basePath.length)\n : url.pathname;\n return `${path || \"/\"}${url.search}${url.hash}`;\n}\n\nfunction isDispatchControlPath(path: string | null): boolean {\n if (!path) return false;\n const route = path.split(/[?#]/, 1)[0] ?? path;\n return (\n route === \"/extensions\" ||\n route.startsWith(\"/extensions/\") ||\n route === \"/tools\" ||\n route.startsWith(\"/tools/\")\n );\n}\n\nfunction assertAppCanOpenPath(app: DispatchMcpAccessibleApp, path: string) {\n if (app.id !== DISPATCH_APP_ID && isDispatchControlPath(path)) {\n throw new Error(\n `Path \"${path}\" belongs to Dispatch. Use app: \"dispatch\" for Dispatch extension and tool routes.`,\n );\n }\n}\n\nfunction toAccessibleApp(\n agent: DiscoveredAgent,\n settings: DispatchMcpAppAccessSettings,\n): DispatchMcpAccessibleApp {\n return {\n id: agent.id,\n name: agent.name,\n description: agent.description,\n url: agent.url,\n color: agent.color,\n granted: isAppAllowedByMcpAccess(agent.id, settings),\n };\n}\n\nexport async function listDispatchMcpApps(): Promise<{\n settings: DispatchMcpAppAccessSettings;\n apps: DispatchMcpAccessibleApp[];\n}> {\n const [settings, agents] = await Promise.all([\n getDispatchMcpAppAccessSettings(),\n discoverAgents(\"dispatch\"),\n ]);\n return {\n settings,\n apps: [\n dispatchSelfApp(settings),\n ...agents\n .filter((agent) => normalizeAppId(agent.id) !== DISPATCH_APP_ID)\n .map((agent) => toAccessibleApp(agent, settings)),\n ],\n };\n}\n\nexport async function listGrantedDispatchMcpApps(): Promise<\n DispatchMcpAccessibleApp[]\n> {\n const { apps } = await listDispatchMcpApps();\n return apps.filter((app) => app.granted);\n}\n\nexport async function resolveGrantedDispatchMcpApp(\n app: string,\n): Promise<DispatchMcpAccessibleApp> {\n const target = normalizeAppId(app);\n if (!target) throw new Error(\"app is required\");\n const { apps } = await listDispatchMcpApps();\n const match = apps.find(\n (candidate) =>\n candidate.id === target || candidate.name.toLowerCase() === target,\n );\n if (!match) {\n throw new Error(\n `Unknown app \"${app}\". Call list_apps to see apps available through Dispatch MCP.`,\n );\n }\n if (!match.granted) {\n throw new Error(\n `Dispatch MCP access to \"${match.id}\" is not granted. Open Dispatch > Agents to change MCP app access.`,\n );\n }\n return match;\n}\n\nexport async function askGrantedDispatchMcpApp(\n app: string,\n message: string,\n): Promise<{ app: string; routedVia: \"a2a\"; response: string }> {\n const trimmedMessage = message.trim();\n if (!trimmedMessage) throw new Error(\"message is required\");\n const target = await resolveGrantedDispatchMcpApp(app);\n const userEmail = getRequestUserEmail();\n if (!userEmail) throw new Error(\"no authenticated user\");\n\n const orgId = getRequestOrgId();\n const [orgDomain, orgSecret] = orgId\n ? await Promise.all([\n getOrgDomain(orgId).catch(() => null),\n getOrgA2ASecret(orgId).catch(() => null),\n ])\n : [null, null];\n\n const response = await callAgent(target.url, trimmedMessage, {\n userEmail,\n orgDomain: orgDomain ?? undefined,\n orgSecret: orgSecret ?? undefined,\n timeoutMs: 5 * 60_000,\n });\n return { app: target.id, routedVia: \"a2a\", response };\n}\n\nexport async function openGrantedDispatchMcpApp(input: {\n app: string;\n view?: string;\n path?: string;\n params?: Record<string, string | number | boolean>;\n embed?: boolean;\n chrome?: \"full\" | \"minimal\";\n}): Promise<{\n app: string;\n view?: string;\n path?: string;\n url: string;\n embed?: boolean;\n chrome?: \"full\" | \"minimal\";\n embedStartUrl?: string;\n embedTargetPath?: string;\n embedExpiresAt?: number;\n}> {\n const view = input.view?.trim() ?? \"\";\n const hasPathInput = input.path != null;\n const path = safeAppPath(input.path);\n if (hasPathInput && !path) {\n throw new Error(\"path must be a safe app-relative route\");\n }\n if (!view && !path) throw new Error(\"open_app requires view or path\");\n const target = await resolveGrantedDispatchMcpApp(input.app);\n if (path) assertAppCanOpenPath(target, path);\n const relUrl = path\n ? appendParamsToPath(path, input.params)\n : buildDeepLink({\n app: target.id,\n view,\n params: input.params,\n });\n const url = `${appBaseUrl(target)}${relUrl}`;\n const embedSession = input.embed\n ? await createGrantedDispatchMcpEmbedSession({\n app: target.id,\n url,\n chrome: input.chrome,\n })\n : null;\n return {\n app: target.id,\n ...(view ? { view } : {}),\n ...(path ? { path } : {}),\n url,\n ...(input.embed === true ? { embed: true } : {}),\n ...(input.chrome ? { chrome: input.chrome } : {}),\n ...(embedSession?.startUrl ? { embedStartUrl: embedSession.startUrl } : {}),\n ...(embedSession?.targetPath\n ? { embedTargetPath: embedSession.targetPath }\n : {}),\n ...(typeof embedSession?.expiresAt === \"number\"\n ? { embedExpiresAt: embedSession.expiresAt }\n : {}),\n };\n}\n\nfunction parseMcpToolTextResult(result: unknown): Record<string, unknown> {\n if (result && typeof result === \"object\") {\n const structured = (result as any).structuredContent;\n if (structured && typeof structured === \"object\") return structured;\n const parts = Array.isArray((result as any).content)\n ? ((result as any).content as Array<Record<string, unknown>>)\n : [];\n const text = parts.find(\n (part) => part?.type === \"text\" && typeof part.text === \"string\",\n )?.text;\n if (typeof text === \"string\" && text.trim()) {\n if ((result as any).isError) throw new Error(text.trim());\n const parsed = JSON.parse(text);\n if (parsed && typeof parsed === \"object\") return parsed;\n }\n }\n throw new Error(\"Target app did not return an embed session.\");\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isRetryableTargetMcpError(error: unknown): boolean {\n const message =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : String(error ?? \"\");\n return /not connected|streamable http|handshake|failed to fetch|fetch failed|networkerror|econnrefused|enotfound|timed out|timeout|502|503|504/i.test(\n message,\n );\n}\n\nasync function callTargetCreateEmbedSession(input: {\n app: DispatchMcpAccessibleApp;\n token: string;\n url: string;\n chrome?: \"full\" | \"minimal\";\n}): Promise<unknown> {\n const serverId = \"target\";\n let lastError: unknown;\n for (let attempt = 1; attempt <= TARGET_EMBED_SESSION_ATTEMPTS; attempt++) {\n const manager = new McpClientManager({\n servers: {\n [serverId]: {\n type: \"http\",\n url: `${appBaseUrl(input.app)}/_agent-native/mcp`,\n headers: {\n Authorization: `Bearer ${input.token}`,\n },\n },\n },\n });\n try {\n await manager.start();\n return await manager.callTool(\n buildMcpToolName(serverId, \"create_embed_session\"),\n {\n url: input.url,\n chrome: input.chrome ?? \"full\",\n },\n );\n } catch (error) {\n lastError = error;\n if (\n attempt >= TARGET_EMBED_SESSION_ATTEMPTS ||\n !isRetryableTargetMcpError(error)\n ) {\n throw error;\n }\n await sleep(250 * attempt);\n } finally {\n await manager.stop();\n }\n }\n throw lastError;\n}\n\nasync function resolveDispatchEmbedTarget(input: {\n app?: string;\n url?: string;\n path?: string;\n}): Promise<{ app: DispatchMcpAccessibleApp; path: string; url: string }> {\n const explicitApp = input.app?.trim()\n ? await resolveGrantedDispatchMcpApp(input.app)\n : null;\n if (explicitApp && input.path) {\n const path = safeAppPath(input.path);\n if (!path) throw new Error(\"path must be a safe app-relative route\");\n assertAppCanOpenPath(explicitApp, path);\n return {\n app: explicitApp,\n path,\n url: `${appBaseUrl(explicitApp)}${path}`,\n };\n }\n\n if (!input.url) {\n throw new Error(\"create_embed_session requires a url or app + path.\");\n }\n\n let parsed: URL;\n try {\n parsed = new URL(input.url);\n } catch {\n if (!explicitApp) {\n throw new Error(\"Relative embed paths require an app id.\");\n }\n const path = safeAppPath(input.url);\n if (!path) throw new Error(\"url must be a safe app route.\");\n return {\n app: explicitApp,\n path,\n url: `${appBaseUrl(explicitApp)}${path}`,\n };\n }\n\n const apps = explicitApp ? [explicitApp] : await listGrantedDispatchMcpApps();\n const target = apps\n .filter((app) => appMatchesUrlPath(app, parsed))\n .sort((a, b) => appPathSpecificity(b) - appPathSpecificity(a))[0];\n if (!target) {\n throw new Error(\n \"Embed URL must belong to an app granted through Dispatch.\",\n );\n }\n const path = safeAppPath(appRelativePath(target, parsed));\n if (!path) throw new Error(\"Embed URL path is not safe.\");\n assertAppCanOpenPath(target, path);\n return { app: target, path, url: `${appBaseUrl(target)}${path}` };\n}\n\nasync function createDispatchSelfEmbedSession(input: {\n ownerEmail: string;\n orgId?: string;\n path: string;\n baseUrl: string;\n chrome?: \"full\" | \"minimal\";\n}): Promise<{\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n}> {\n const ticket = await createEmbedSessionTicket({\n ownerEmail: input.ownerEmail,\n orgId: input.orgId,\n targetPath: input.path,\n scope: input.chrome ?? null,\n });\n const startPath = buildEmbedStartPath(ticket.ticket);\n return {\n startUrl: new URL(startPath, input.baseUrl).toString(),\n targetPath: input.path,\n expiresAt: ticket.expiresAt,\n app: DISPATCH_APP_ID,\n };\n}\n\nexport async function createGrantedDispatchMcpEmbedSession(input: {\n app?: string;\n url?: string;\n path?: string;\n chrome?: \"full\" | \"minimal\";\n}): Promise<{\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n}> {\n const userEmail = getRequestUserEmail();\n if (!userEmail) throw new Error(\"no authenticated user\");\n const target = await resolveDispatchEmbedTarget(input);\n\n const orgId = getRequestOrgId();\n if (target.app.id === DISPATCH_APP_ID) {\n return createDispatchSelfEmbedSession({\n ownerEmail: userEmail,\n orgId,\n path: target.path,\n baseUrl: appBaseUrl(target.app),\n chrome: input.chrome,\n });\n }\n\n const [orgDomain, orgSecret] = orgId\n ? await Promise.all([\n getOrgDomain(orgId).catch(() => null),\n getOrgA2ASecret(orgId).catch(() => null),\n ])\n : [null, null];\n const token = await signA2AToken(\n userEmail,\n orgDomain ?? undefined,\n orgSecret ?? undefined,\n {\n expiresIn: \"5m\",\n // Prefer the synced org A2A secret when present because first-party\n // production apps do not have to share the same deployment env secret.\n // Fall back to the global A2A_SECRET for orgs that have not synced yet.\n preferGlobalSecret: !orgSecret,\n },\n );\n\n const result = await callTargetCreateEmbedSession({\n app: target.app,\n token,\n url: target.url,\n chrome: input.chrome,\n });\n const parsed = parseMcpToolTextResult(result) as {\n startUrl?: string;\n targetPath?: string;\n expiresAt?: number;\n };\n if (!parsed.startUrl) {\n throw new Error(\"Target app did not return an embed start URL.\");\n }\n const output: {\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n } = {\n startUrl: parsed.startUrl,\n app: target.app.id,\n };\n if (parsed.targetPath) output.targetPath = parsed.targetPath;\n if (typeof parsed.expiresAt === \"number\") output.expiresAt = parsed.expiresAt;\n return output;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/dispatch",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -210,6 +210,44 @@ describe("openGrantedDispatchMcpApp", () => {
|
|
|
210
210
|
});
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
+
it("retries transient target MCP connection failures while pre-minting embeds", async () => {
|
|
214
|
+
mocks.managerCallTool
|
|
215
|
+
.mockRejectedValueOnce(
|
|
216
|
+
new Error(
|
|
217
|
+
'MCP server "target" is not connected: The server did not complete the Streamable HTTP MCP handshake.',
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
.mockResolvedValueOnce({
|
|
221
|
+
structuredContent: {
|
|
222
|
+
startUrl:
|
|
223
|
+
"http://localhost:8086/_agent-native/embed/start?ticket=remote",
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const result = await runWithRequestContext(
|
|
228
|
+
{
|
|
229
|
+
userEmail: "owner@example.test",
|
|
230
|
+
requestOrigin: "http://localhost:8092",
|
|
231
|
+
},
|
|
232
|
+
() =>
|
|
233
|
+
openGrantedDispatchMcpApp({
|
|
234
|
+
app: "analytics",
|
|
235
|
+
path: "/dashboards",
|
|
236
|
+
embed: true,
|
|
237
|
+
}),
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
expect(mocks.managerConstructor).toHaveBeenCalledTimes(2);
|
|
241
|
+
expect(mocks.managerStart).toHaveBeenCalledTimes(2);
|
|
242
|
+
expect(mocks.managerStop).toHaveBeenCalledTimes(2);
|
|
243
|
+
expect(mocks.managerCallTool).toHaveBeenCalledTimes(2);
|
|
244
|
+
expect(result).toMatchObject({
|
|
245
|
+
app: "analytics",
|
|
246
|
+
embedStartUrl:
|
|
247
|
+
"http://localhost:8086/_agent-native/embed/start?ticket=remote",
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
213
251
|
it("rejects Dispatch-owned extension routes on sibling apps", async () => {
|
|
214
252
|
await expect(
|
|
215
253
|
runWithRequestContext(
|
|
@@ -330,7 +368,7 @@ describe("createGrantedDispatchMcpEmbedSession", () => {
|
|
|
330
368
|
});
|
|
331
369
|
});
|
|
332
370
|
|
|
333
|
-
it("
|
|
371
|
+
it("uses the org A2A secret when minting cross-app MCP embed tokens", async () => {
|
|
334
372
|
mocks.getOrgDomain.mockResolvedValue("builder.io");
|
|
335
373
|
mocks.getOrgA2ASecret.mockResolvedValue("org-specific-secret");
|
|
336
374
|
|
|
@@ -351,6 +389,34 @@ describe("createGrantedDispatchMcpEmbedSession", () => {
|
|
|
351
389
|
"owner@example.test",
|
|
352
390
|
"builder.io",
|
|
353
391
|
"org-specific-secret",
|
|
392
|
+
{
|
|
393
|
+
expiresIn: "5m",
|
|
394
|
+
preferGlobalSecret: false,
|
|
395
|
+
},
|
|
396
|
+
);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("falls back to the shared A2A secret when no org secret is available", async () => {
|
|
400
|
+
mocks.getOrgDomain.mockResolvedValue("builder.io");
|
|
401
|
+
mocks.getOrgA2ASecret.mockResolvedValue(null);
|
|
402
|
+
|
|
403
|
+
await runWithRequestContext(
|
|
404
|
+
{
|
|
405
|
+
userEmail: "owner@example.test",
|
|
406
|
+
orgId: "org-1",
|
|
407
|
+
requestOrigin: "http://localhost:8092",
|
|
408
|
+
},
|
|
409
|
+
() =>
|
|
410
|
+
createGrantedDispatchMcpEmbedSession({
|
|
411
|
+
app: "analytics",
|
|
412
|
+
path: "/dashboards",
|
|
413
|
+
}),
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
expect(mocks.signA2AToken).toHaveBeenCalledWith(
|
|
417
|
+
"owner@example.test",
|
|
418
|
+
"builder.io",
|
|
419
|
+
undefined,
|
|
354
420
|
{
|
|
355
421
|
expiresIn: "5m",
|
|
356
422
|
preferGlobalSecret: true,
|
|
@@ -29,6 +29,7 @@ const DISPATCH_NAME = "Agent-Native Dispatch";
|
|
|
29
29
|
const DISPATCH_DESCRIPTION =
|
|
30
30
|
"Workspace control plane for extensions, agents, vault, integrations, approvals, and app routing.";
|
|
31
31
|
const DISPATCH_COLOR = "#14B8A6";
|
|
32
|
+
const TARGET_EMBED_SESSION_ATTEMPTS = 3;
|
|
32
33
|
|
|
33
34
|
export interface DispatchMcpAccessibleApp {
|
|
34
35
|
id: string;
|
|
@@ -364,6 +365,67 @@ function parseMcpToolTextResult(result: unknown): Record<string, unknown> {
|
|
|
364
365
|
throw new Error("Target app did not return an embed session.");
|
|
365
366
|
}
|
|
366
367
|
|
|
368
|
+
function sleep(ms: number): Promise<void> {
|
|
369
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function isRetryableTargetMcpError(error: unknown): boolean {
|
|
373
|
+
const message =
|
|
374
|
+
error instanceof Error
|
|
375
|
+
? error.message
|
|
376
|
+
: typeof error === "string"
|
|
377
|
+
? error
|
|
378
|
+
: String(error ?? "");
|
|
379
|
+
return /not connected|streamable http|handshake|failed to fetch|fetch failed|networkerror|econnrefused|enotfound|timed out|timeout|502|503|504/i.test(
|
|
380
|
+
message,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function callTargetCreateEmbedSession(input: {
|
|
385
|
+
app: DispatchMcpAccessibleApp;
|
|
386
|
+
token: string;
|
|
387
|
+
url: string;
|
|
388
|
+
chrome?: "full" | "minimal";
|
|
389
|
+
}): Promise<unknown> {
|
|
390
|
+
const serverId = "target";
|
|
391
|
+
let lastError: unknown;
|
|
392
|
+
for (let attempt = 1; attempt <= TARGET_EMBED_SESSION_ATTEMPTS; attempt++) {
|
|
393
|
+
const manager = new McpClientManager({
|
|
394
|
+
servers: {
|
|
395
|
+
[serverId]: {
|
|
396
|
+
type: "http",
|
|
397
|
+
url: `${appBaseUrl(input.app)}/_agent-native/mcp`,
|
|
398
|
+
headers: {
|
|
399
|
+
Authorization: `Bearer ${input.token}`,
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
try {
|
|
405
|
+
await manager.start();
|
|
406
|
+
return await manager.callTool(
|
|
407
|
+
buildMcpToolName(serverId, "create_embed_session"),
|
|
408
|
+
{
|
|
409
|
+
url: input.url,
|
|
410
|
+
chrome: input.chrome ?? "full",
|
|
411
|
+
},
|
|
412
|
+
);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
lastError = error;
|
|
415
|
+
if (
|
|
416
|
+
attempt >= TARGET_EMBED_SESSION_ATTEMPTS ||
|
|
417
|
+
!isRetryableTargetMcpError(error)
|
|
418
|
+
) {
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
await sleep(250 * attempt);
|
|
422
|
+
} finally {
|
|
423
|
+
await manager.stop();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
throw lastError;
|
|
427
|
+
}
|
|
428
|
+
|
|
367
429
|
async function resolveDispatchEmbedTarget(input: {
|
|
368
430
|
app?: string;
|
|
369
431
|
url?: string;
|
|
@@ -483,56 +545,37 @@ export async function createGrantedDispatchMcpEmbedSession(input: {
|
|
|
483
545
|
orgSecret ?? undefined,
|
|
484
546
|
{
|
|
485
547
|
expiresIn: "5m",
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
preferGlobalSecret:
|
|
548
|
+
// Prefer the synced org A2A secret when present because first-party
|
|
549
|
+
// production apps do not have to share the same deployment env secret.
|
|
550
|
+
// Fall back to the global A2A_SECRET for orgs that have not synced yet.
|
|
551
|
+
preferGlobalSecret: !orgSecret,
|
|
490
552
|
},
|
|
491
553
|
);
|
|
492
554
|
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
url: `${appBaseUrl(target.app)}/_agent-native/mcp`,
|
|
499
|
-
headers: {
|
|
500
|
-
Authorization: `Bearer ${token}`,
|
|
501
|
-
},
|
|
502
|
-
},
|
|
503
|
-
},
|
|
555
|
+
const result = await callTargetCreateEmbedSession({
|
|
556
|
+
app: target.app,
|
|
557
|
+
token,
|
|
558
|
+
url: target.url,
|
|
559
|
+
chrome: input.chrome,
|
|
504
560
|
});
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
},
|
|
513
|
-
);
|
|
514
|
-
const parsed = parseMcpToolTextResult(result) as {
|
|
515
|
-
startUrl?: string;
|
|
516
|
-
targetPath?: string;
|
|
517
|
-
expiresAt?: number;
|
|
518
|
-
};
|
|
519
|
-
if (!parsed.startUrl) {
|
|
520
|
-
throw new Error("Target app did not return an embed start URL.");
|
|
521
|
-
}
|
|
522
|
-
const output: {
|
|
523
|
-
startUrl: string;
|
|
524
|
-
targetPath?: string;
|
|
525
|
-
expiresAt?: number;
|
|
526
|
-
app: string;
|
|
527
|
-
} = {
|
|
528
|
-
startUrl: parsed.startUrl,
|
|
529
|
-
app: target.app.id,
|
|
530
|
-
};
|
|
531
|
-
if (parsed.targetPath) output.targetPath = parsed.targetPath;
|
|
532
|
-
if (typeof parsed.expiresAt === "number")
|
|
533
|
-
output.expiresAt = parsed.expiresAt;
|
|
534
|
-
return output;
|
|
535
|
-
} finally {
|
|
536
|
-
await manager.stop();
|
|
561
|
+
const parsed = parseMcpToolTextResult(result) as {
|
|
562
|
+
startUrl?: string;
|
|
563
|
+
targetPath?: string;
|
|
564
|
+
expiresAt?: number;
|
|
565
|
+
};
|
|
566
|
+
if (!parsed.startUrl) {
|
|
567
|
+
throw new Error("Target app did not return an embed start URL.");
|
|
537
568
|
}
|
|
569
|
+
const output: {
|
|
570
|
+
startUrl: string;
|
|
571
|
+
targetPath?: string;
|
|
572
|
+
expiresAt?: number;
|
|
573
|
+
app: string;
|
|
574
|
+
} = {
|
|
575
|
+
startUrl: parsed.startUrl,
|
|
576
|
+
app: target.app.id,
|
|
577
|
+
};
|
|
578
|
+
if (parsed.targetPath) output.targetPath = parsed.targetPath;
|
|
579
|
+
if (typeof parsed.expiresAt === "number") output.expiresAt = parsed.expiresAt;
|
|
580
|
+
return output;
|
|
538
581
|
}
|