@agent-native/dispatch 0.8.14 → 0.8.16

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":"open_app.d.ts","sourceRoot":"","sources":["../../src/actions/open_app.ts"],"names":[],"mappings":";AA4EA,wBAwBG"}
1
+ {"version":3,"file":"open_app.d.ts","sourceRoot":"","sources":["../../src/actions/open_app.ts"],"names":[],"mappings":";AA6HA,wBAwBG"}
@@ -29,9 +29,52 @@ const openAppSchema = z
29
29
  message: "open_app requires either view or path",
30
30
  path: ["view"],
31
31
  });
32
+ function normalizeOpenAppArgs(args) {
33
+ if (!args.params)
34
+ return args;
35
+ const params = { ...args.params };
36
+ const embedded = params.embed;
37
+ const chrome = params.chrome;
38
+ let changed = false;
39
+ let normalizedEmbed = args.embed;
40
+ if (normalizedEmbed == null) {
41
+ if (embedded === true || embedded === "true") {
42
+ normalizedEmbed = true;
43
+ changed = true;
44
+ }
45
+ else if (embedded === false || embedded === "false") {
46
+ normalizedEmbed = false;
47
+ changed = true;
48
+ }
49
+ }
50
+ if (embedded === true ||
51
+ embedded === false ||
52
+ embedded === "true" ||
53
+ embedded === "false") {
54
+ delete params.embed;
55
+ changed = true;
56
+ }
57
+ let normalizedChrome = args.chrome;
58
+ if (normalizedChrome == null && (chrome === "full" || chrome === "minimal")) {
59
+ normalizedChrome = chrome;
60
+ changed = true;
61
+ }
62
+ if (chrome === "full" || chrome === "minimal") {
63
+ delete params.chrome;
64
+ changed = true;
65
+ }
66
+ if (!changed)
67
+ return args;
68
+ return {
69
+ ...args,
70
+ params: Object.keys(params).length ? params : undefined,
71
+ ...(normalizedEmbed == null ? {} : { embed: normalizedEmbed }),
72
+ ...(normalizedChrome == null ? {} : { chrome: normalizedChrome }),
73
+ };
74
+ }
32
75
  const localDevFrameSources = ["http://localhost:*", "http://127.0.0.1:*"];
33
76
  const dispatchOpenAppCsp = async (ctx) => {
34
- const appOrigins = (await listGrantedDispatchMcpAppOrigins()).filter((origin) => origin !== ctx.requestOrigin);
77
+ const appOrigins = (await listGrantedDispatchMcpAppOrigins()).filter((origin) => !ctx.requestOrigin || origin !== ctx.requestOrigin);
35
78
  const routeSources = [
36
79
  MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,
37
80
  ...appOrigins,
@@ -56,7 +99,7 @@ export default defineAction({
56
99
  http: { method: "GET" },
57
100
  readOnly: true,
58
101
  parallelSafe: true,
59
- run: async (args) => openGrantedDispatchMcpApp(args),
102
+ run: async (args) => openGrantedDispatchMcpApp(normalizeOpenAppArgs(args)),
60
103
  link: ({ result }) => {
61
104
  if (!result || typeof result !== "object")
62
105
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"open_app.js","sourceRoot":"","sources":["../../src/actions/open_app.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineAction,\n embedApp,\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n type ActionMcpAppCsp,\n type ActionMcpAppCspBuilder,\n} from \"@agent-native/core\";\nimport { z } from \"zod\";\nimport {\n listGrantedDispatchMcpAppOrigins,\n openGrantedDispatchMcpApp,\n} from \"../server/lib/mcp-gateway.js\";\n\nconst deepLinkParam = z.union([z.string(), z.number(), z.boolean()]);\nconst openAppSchema = z\n .object({\n app: z\n .string()\n .describe(\n 'Granted app id, e.g. mail or calendar. Use \"dispatch\" for Dispatch-owned pages such as /extensions.',\n ),\n view: z.string().optional().describe(\"Target view in the app, e.g. inbox.\"),\n path: z\n .string()\n .optional()\n .describe(\n 'Optional route within the target app, e.g. /adhoc/q2 or /chart?panel=... . Dispatch extension routes such as /extensions/<id>/<slug> belong to app \"dispatch\".',\n ),\n params: z\n .record(z.string(), deepLinkParam)\n .optional()\n .describe(\"Optional record-focus or filter params.\"),\n embed: z\n .boolean()\n .optional()\n .describe(\n \"Render the app or focused route/component inline in MCP Apps when supported.\",\n ),\n chrome: z\n .enum([\"full\", \"minimal\"])\n .optional()\n .describe(\"Embed chrome preference for compatible app routes.\"),\n })\n .refine((input) => input.view?.trim() || input.path?.trim(), {\n message: \"open_app requires either view or path\",\n path: [\"view\"],\n });\n\nconst localDevFrameSources = [\"http://localhost:*\", \"http://127.0.0.1:*\"];\n\nconst dispatchOpenAppCsp: ActionMcpAppCspBuilder = async (\n ctx,\n): Promise<ActionMcpAppCsp> => {\n const appOrigins = (await listGrantedDispatchMcpAppOrigins()).filter(\n (origin) => origin !== ctx.requestOrigin,\n );\n const routeSources = [\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...appOrigins,\n ...localDevFrameSources,\n ];\n return {\n connectDomains: [\"https://esm.sh\", ...routeSources],\n resourceDomains: [\"https://esm.sh\", ...routeSources],\n frameDomains: routeSources,\n baseUriDomains: routeSources,\n };\n};\n\nconst openAppResource = embedApp({\n title: \"Open app\",\n description: \"Render the requested granted app route inline.\",\n iframeTitle: \"Dispatch MCP app\",\n openLabel: \"Open app\",\n});\n\nexport default defineAction({\n description:\n 'Build a deep link or embeddable app route/component route for an app available through Dispatch MCP. Use app \"dispatch\" for Dispatch extension/tool pages. No side effects; surface the returned Open link to the user.',\n schema: openAppSchema,\n http: { method: \"GET\" },\n readOnly: true,\n parallelSafe: true,\n run: async (args) => openGrantedDispatchMcpApp(args),\n link: ({ result }) => {\n if (!result || typeof result !== \"object\") return null;\n const r = result as { url?: string; app?: string; view?: string };\n if (!r.url) return null;\n return {\n url: r.url,\n label: `Open ${r.app ?? \"app\"}`,\n view: r.view,\n };\n },\n mcpApp: {\n resource: {\n ...openAppResource,\n csp: dispatchOpenAppCsp,\n },\n },\n});\n"]}
1
+ {"version":3,"file":"open_app.js","sourceRoot":"","sources":["../../src/actions/open_app.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n defineAction,\n embedApp,\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n type ActionMcpAppCsp,\n type ActionMcpAppCspBuilder,\n} from \"@agent-native/core\";\nimport { z } from \"zod\";\nimport {\n listGrantedDispatchMcpAppOrigins,\n openGrantedDispatchMcpApp,\n} from \"../server/lib/mcp-gateway.js\";\n\nconst deepLinkParam = z.union([z.string(), z.number(), z.boolean()]);\nconst openAppSchema = z\n .object({\n app: z\n .string()\n .describe(\n 'Granted app id, e.g. mail or calendar. Use \"dispatch\" for Dispatch-owned pages such as /extensions.',\n ),\n view: z.string().optional().describe(\"Target view in the app, e.g. inbox.\"),\n path: z\n .string()\n .optional()\n .describe(\n 'Optional route within the target app, e.g. /adhoc/q2 or /chart?panel=... . Dispatch extension routes such as /extensions/<id>/<slug> belong to app \"dispatch\".',\n ),\n params: z\n .record(z.string(), deepLinkParam)\n .optional()\n .describe(\"Optional record-focus or filter params.\"),\n embed: z\n .boolean()\n .optional()\n .describe(\n \"Render the app or focused route/component inline in MCP Apps when supported.\",\n ),\n chrome: z\n .enum([\"full\", \"minimal\"])\n .optional()\n .describe(\"Embed chrome preference for compatible app routes.\"),\n })\n .refine((input) => input.view?.trim() || input.path?.trim(), {\n message: \"open_app requires either view or path\",\n path: [\"view\"],\n });\n\ntype OpenAppArgs = z.infer<typeof openAppSchema>;\n\nfunction normalizeOpenAppArgs(args: OpenAppArgs): OpenAppArgs {\n if (!args.params) return args;\n\n const params = { ...args.params };\n const embedded = params.embed;\n const chrome = params.chrome;\n let changed = false;\n\n let normalizedEmbed = args.embed;\n if (normalizedEmbed == null) {\n if (embedded === true || embedded === \"true\") {\n normalizedEmbed = true;\n changed = true;\n } else if (embedded === false || embedded === \"false\") {\n normalizedEmbed = false;\n changed = true;\n }\n }\n if (\n embedded === true ||\n embedded === false ||\n embedded === \"true\" ||\n embedded === \"false\"\n ) {\n delete params.embed;\n changed = true;\n }\n\n let normalizedChrome = args.chrome;\n if (normalizedChrome == null && (chrome === \"full\" || chrome === \"minimal\")) {\n normalizedChrome = chrome;\n changed = true;\n }\n if (chrome === \"full\" || chrome === \"minimal\") {\n delete params.chrome;\n changed = true;\n }\n\n if (!changed) return args;\n return {\n ...args,\n params: Object.keys(params).length ? params : undefined,\n ...(normalizedEmbed == null ? {} : { embed: normalizedEmbed }),\n ...(normalizedChrome == null ? {} : { chrome: normalizedChrome }),\n };\n}\n\nconst localDevFrameSources = [\"http://localhost:*\", \"http://127.0.0.1:*\"];\n\nconst dispatchOpenAppCsp: ActionMcpAppCspBuilder = async (\n ctx,\n): Promise<ActionMcpAppCsp> => {\n const appOrigins = (await listGrantedDispatchMcpAppOrigins()).filter(\n (origin) => !ctx.requestOrigin || origin !== ctx.requestOrigin,\n );\n const routeSources = [\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...appOrigins,\n ...localDevFrameSources,\n ];\n return {\n connectDomains: [\"https://esm.sh\", ...routeSources],\n resourceDomains: [\"https://esm.sh\", ...routeSources],\n frameDomains: routeSources,\n baseUriDomains: routeSources,\n };\n};\n\nconst openAppResource = embedApp({\n title: \"Open app\",\n description: \"Render the requested granted app route inline.\",\n iframeTitle: \"Dispatch MCP app\",\n openLabel: \"Open app\",\n});\n\nexport default defineAction({\n description:\n 'Build a deep link or embeddable app route/component route for an app available through Dispatch MCP. Use app \"dispatch\" for Dispatch extension/tool pages. No side effects; surface the returned Open link to the user.',\n schema: openAppSchema,\n http: { method: \"GET\" },\n readOnly: true,\n parallelSafe: true,\n run: async (args) => openGrantedDispatchMcpApp(normalizeOpenAppArgs(args)),\n link: ({ result }) => {\n if (!result || typeof result !== \"object\") return null;\n const r = result as { url?: string; app?: string; view?: string };\n if (!r.url) return null;\n return {\n url: r.url,\n label: `Open ${r.app ?? \"app\"}`,\n view: r.view,\n };\n },\n mcpApp: {\n resource: {\n ...openAppResource,\n csp: dispatchOpenAppCsp,\n },\n },\n});\n"]}
@@ -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;AAU/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,gCAAgC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAG1E;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,CAkDD;AA+KD,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,CAmED"}
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;AAU/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;AA2LD,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,gCAAgC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAG1E;AAED,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,wBAAwB,CAAC,CAwBnC;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,CAkDD;AA+KD,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,CAmED"}
@@ -113,8 +113,22 @@ function appendParamsToPath(path, params) {
113
113
  }
114
114
  return `${url.pathname}${url.search}${url.hash}`;
115
115
  }
116
+ function safeAppOrigin(app) {
117
+ try {
118
+ const url = new URL(app.url);
119
+ return url.protocol === "http:" || url.protocol === "https:"
120
+ ? url.origin
121
+ : null;
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
116
127
  function appOrigin(app) {
117
- return new URL(app.url).origin;
128
+ const origin = safeAppOrigin(app);
129
+ if (!origin)
130
+ throw new Error(`Invalid app URL for "${app.id}": ${app.url}`);
131
+ return origin;
118
132
  }
119
133
  function appBaseUrl(app) {
120
134
  return app.url.replace(/\/+$/, "");
@@ -124,7 +138,8 @@ function appBasePath(app) {
124
138
  return pathname === "/" ? "" : pathname;
125
139
  }
126
140
  function appMatchesUrlPath(app, url) {
127
- if (url.origin !== appOrigin(app))
141
+ const origin = safeAppOrigin(app);
142
+ if (!origin || url.origin !== origin)
128
143
  return false;
129
144
  const basePath = appBasePath(app);
130
145
  if (!basePath)
@@ -184,11 +199,11 @@ export async function listDispatchMcpApps() {
184
199
  }
185
200
  export async function listGrantedDispatchMcpApps() {
186
201
  const { apps } = await listDispatchMcpApps();
187
- return apps.filter((app) => app.granted);
202
+ return apps.filter((app) => app.granted && safeAppOrigin(app));
188
203
  }
189
204
  export async function listGrantedDispatchMcpAppOrigins() {
190
205
  const apps = await listGrantedDispatchMcpApps();
191
- return Array.from(new Set(apps.map((app) => appOrigin(app))));
206
+ return Array.from(new Set(apps.flatMap((app) => safeAppOrigin(app) ?? [])));
192
207
  }
193
208
  export async function resolveGrantedDispatchMcpApp(app) {
194
209
  const target = normalizeAppId(app);
@@ -202,6 +217,9 @@ export async function resolveGrantedDispatchMcpApp(app) {
202
217
  if (!match.granted) {
203
218
  throw new Error(`Dispatch MCP access to "${match.id}" is not granted. Open Dispatch > Agents to change MCP app access.`);
204
219
  }
220
+ if (!safeAppOrigin(match)) {
221
+ throw new Error(`Dispatch MCP app "${match.id}" has an invalid URL and cannot be opened through MCP.`);
222
+ }
205
223
  return match;
206
224
  }
207
225
  export async function askGrantedDispatchMcpApp(app, message) {
@@ -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\";\nconst TARGET_EMBED_SESSION_ATTEMPTS = 3;\nconst TARGET_EMBED_SESSION_RETRY_BASE_MS = 250;\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 listGrantedDispatchMcpAppOrigins(): Promise<string[]> {\n const apps = await listGrantedDispatchMcpApps();\n return Array.from(new Set(apps.map((app) => appOrigin(app))));\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 let embedSession: Awaited<\n ReturnType<typeof createGrantedDispatchMcpEmbedSession>\n > | null = null;\n if (input.embed) {\n try {\n embedSession = await createGrantedDispatchMcpEmbedSession({\n app: target.id,\n url,\n chrome: input.chrome,\n });\n } catch (error) {\n console.warn(\n `[dispatch] Could not pre-mint MCP embed session for ${target.id}:`,\n error,\n );\n }\n }\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 if (\n /rejected the request|unauthorized|forbidden|401|403|404|405|html/i.test(\n message,\n )\n ) {\n return false;\n }\n return /streamable http|handshake|failed to fetch|fetch failed|networkerror|econnrefused|enotfound|timed out|timeout|502|503|504/i.test(\n message,\n );\n}\n\nfunction targetMcpRetryDelay(attempt: number): number {\n const base =\n TARGET_EMBED_SESSION_RETRY_BASE_MS * Math.pow(2, Math.max(0, attempt - 1));\n return base + Math.floor(Math.random() * 100);\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 for (let attempt = 1; ; attempt += 1) {\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 if (\n attempt >= TARGET_EMBED_SESSION_ATTEMPTS ||\n !isRetryableTargetMcpError(error)\n ) {\n throw error;\n }\n await sleep(targetMcpRetryDelay(attempt));\n } finally {\n await manager.stop().catch((stopError) => {\n console.warn(\"[dispatch] Failed to stop target MCP client:\", stopError);\n });\n }\n }\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 usableOrgSecret =\n typeof orgSecret === \"string\" && orgSecret.trim().length > 0;\n const usableOrgDomain =\n typeof orgDomain === \"string\" && orgDomain.trim().length > 0;\n const useOrgSigning = usableOrgDomain && usableOrgSecret;\n const signedOrgDomain = usableOrgDomain ? orgDomain.trim() : undefined;\n const token = await signA2AToken(\n userEmail,\n signedOrgDomain,\n useOrgSigning ? orgSecret.trim() : 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: !useOrgSigning,\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"]}
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;\nconst TARGET_EMBED_SESSION_RETRY_BASE_MS = 250;\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 safeAppOrigin(app: DispatchMcpAccessibleApp): string | null {\n try {\n const url = new URL(app.url);\n return url.protocol === \"http:\" || url.protocol === \"https:\"\n ? url.origin\n : null;\n } catch {\n return null;\n }\n}\n\nfunction appOrigin(app: DispatchMcpAccessibleApp): string {\n const origin = safeAppOrigin(app);\n if (!origin) throw new Error(`Invalid app URL for \"${app.id}\": ${app.url}`);\n return 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 const origin = safeAppOrigin(app);\n if (!origin || url.origin !== origin) 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 && safeAppOrigin(app));\n}\n\nexport async function listGrantedDispatchMcpAppOrigins(): Promise<string[]> {\n const apps = await listGrantedDispatchMcpApps();\n return Array.from(new Set(apps.flatMap((app) => safeAppOrigin(app) ?? [])));\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 if (!safeAppOrigin(match)) {\n throw new Error(\n `Dispatch MCP app \"${match.id}\" has an invalid URL and cannot be opened through MCP.`,\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 let embedSession: Awaited<\n ReturnType<typeof createGrantedDispatchMcpEmbedSession>\n > | null = null;\n if (input.embed) {\n try {\n embedSession = await createGrantedDispatchMcpEmbedSession({\n app: target.id,\n url,\n chrome: input.chrome,\n });\n } catch (error) {\n console.warn(\n `[dispatch] Could not pre-mint MCP embed session for ${target.id}:`,\n error,\n );\n }\n }\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 if (\n /rejected the request|unauthorized|forbidden|401|403|404|405|html/i.test(\n message,\n )\n ) {\n return false;\n }\n return /streamable http|handshake|failed to fetch|fetch failed|networkerror|econnrefused|enotfound|timed out|timeout|502|503|504/i.test(\n message,\n );\n}\n\nfunction targetMcpRetryDelay(attempt: number): number {\n const base =\n TARGET_EMBED_SESSION_RETRY_BASE_MS * Math.pow(2, Math.max(0, attempt - 1));\n return base + Math.floor(Math.random() * 100);\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 for (let attempt = 1; ; attempt += 1) {\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 if (\n attempt >= TARGET_EMBED_SESSION_ATTEMPTS ||\n !isRetryableTargetMcpError(error)\n ) {\n throw error;\n }\n await sleep(targetMcpRetryDelay(attempt));\n } finally {\n await manager.stop().catch((stopError) => {\n console.warn(\"[dispatch] Failed to stop target MCP client:\", stopError);\n });\n }\n }\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 usableOrgSecret =\n typeof orgSecret === \"string\" && orgSecret.trim().length > 0;\n const usableOrgDomain =\n typeof orgDomain === \"string\" && orgDomain.trim().length > 0;\n const useOrgSigning = usableOrgDomain && usableOrgSecret;\n const signedOrgDomain = usableOrgDomain ? orgDomain.trim() : undefined;\n const token = await signA2AToken(\n userEmail,\n signedOrgDomain,\n useOrgSigning ? orgSecret.trim() : 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: !useOrgSigning,\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.14",
3
+ "version": "0.8.16",
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",
@@ -1,4 +1,4 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
 
3
3
  const mocks = vi.hoisted(() => ({
4
4
  listGrantedDispatchMcpAppOrigins: vi.fn(),
@@ -13,6 +13,10 @@ vi.mock("../server/lib/mcp-gateway.js", () => ({
13
13
  import openAppAction from "./open_app.js";
14
14
 
15
15
  describe("open_app MCP App metadata", () => {
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ });
19
+
16
20
  it("uses exact granted app origins instead of broad HTTPS CSP", async () => {
17
21
  mocks.listGrantedDispatchMcpAppOrigins.mockResolvedValue([
18
22
  "https://dispatch.agent-native.com",
@@ -48,4 +52,55 @@ describe("open_app MCP App metadata", () => {
48
52
  expect(csp.baseUriDomains).toEqual(csp.frameDomains);
49
53
  expect(JSON.stringify(csp)).not.toContain('"https:"');
50
54
  });
55
+
56
+ it("keeps exact granted origins when request origin is unavailable", async () => {
57
+ mocks.listGrantedDispatchMcpAppOrigins.mockResolvedValue([
58
+ "https://mail.agent-native.com",
59
+ "https://calendar.agent-native.com",
60
+ ]);
61
+
62
+ const cspBuilder = openAppAction.mcpApp?.resource.csp;
63
+ const csp = await (cspBuilder as any)({
64
+ actionName: "open_app",
65
+ appId: "dispatch",
66
+ });
67
+
68
+ expect(csp.frameDomains).toEqual([
69
+ "$requestOrigin",
70
+ "https://mail.agent-native.com",
71
+ "https://calendar.agent-native.com",
72
+ "http://localhost:*",
73
+ "http://127.0.0.1:*",
74
+ ]);
75
+ });
76
+
77
+ it("promotes embed and chrome from params for hosts that nest open options", async () => {
78
+ mocks.openGrantedDispatchMcpApp.mockResolvedValue({
79
+ app: "mail",
80
+ view: "inbox",
81
+ url: "https://mail.agent-native.com/inbox",
82
+ embed: true,
83
+ chrome: "minimal",
84
+ embedStartUrl:
85
+ "https://mail.agent-native.com/_agent-native/embed/start?ticket=test",
86
+ });
87
+
88
+ await openAppAction.run({
89
+ app: "mail",
90
+ view: "inbox",
91
+ params: {
92
+ embed: true,
93
+ chrome: "minimal",
94
+ threadId: "abc",
95
+ },
96
+ });
97
+
98
+ expect(mocks.openGrantedDispatchMcpApp).toHaveBeenCalledWith({
99
+ app: "mail",
100
+ view: "inbox",
101
+ params: { threadId: "abc" },
102
+ embed: true,
103
+ chrome: "minimal",
104
+ });
105
+ });
51
106
  });
@@ -46,13 +46,62 @@ const openAppSchema = z
46
46
  path: ["view"],
47
47
  });
48
48
 
49
+ type OpenAppArgs = z.infer<typeof openAppSchema>;
50
+
51
+ function normalizeOpenAppArgs(args: OpenAppArgs): OpenAppArgs {
52
+ if (!args.params) return args;
53
+
54
+ const params = { ...args.params };
55
+ const embedded = params.embed;
56
+ const chrome = params.chrome;
57
+ let changed = false;
58
+
59
+ let normalizedEmbed = args.embed;
60
+ if (normalizedEmbed == null) {
61
+ if (embedded === true || embedded === "true") {
62
+ normalizedEmbed = true;
63
+ changed = true;
64
+ } else if (embedded === false || embedded === "false") {
65
+ normalizedEmbed = false;
66
+ changed = true;
67
+ }
68
+ }
69
+ if (
70
+ embedded === true ||
71
+ embedded === false ||
72
+ embedded === "true" ||
73
+ embedded === "false"
74
+ ) {
75
+ delete params.embed;
76
+ changed = true;
77
+ }
78
+
79
+ let normalizedChrome = args.chrome;
80
+ if (normalizedChrome == null && (chrome === "full" || chrome === "minimal")) {
81
+ normalizedChrome = chrome;
82
+ changed = true;
83
+ }
84
+ if (chrome === "full" || chrome === "minimal") {
85
+ delete params.chrome;
86
+ changed = true;
87
+ }
88
+
89
+ if (!changed) return args;
90
+ return {
91
+ ...args,
92
+ params: Object.keys(params).length ? params : undefined,
93
+ ...(normalizedEmbed == null ? {} : { embed: normalizedEmbed }),
94
+ ...(normalizedChrome == null ? {} : { chrome: normalizedChrome }),
95
+ };
96
+ }
97
+
49
98
  const localDevFrameSources = ["http://localhost:*", "http://127.0.0.1:*"];
50
99
 
51
100
  const dispatchOpenAppCsp: ActionMcpAppCspBuilder = async (
52
101
  ctx,
53
102
  ): Promise<ActionMcpAppCsp> => {
54
103
  const appOrigins = (await listGrantedDispatchMcpAppOrigins()).filter(
55
- (origin) => origin !== ctx.requestOrigin,
104
+ (origin) => !ctx.requestOrigin || origin !== ctx.requestOrigin,
56
105
  );
57
106
  const routeSources = [
58
107
  MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,
@@ -81,7 +130,7 @@ export default defineAction({
81
130
  http: { method: "GET" },
82
131
  readOnly: true,
83
132
  parallelSafe: true,
84
- run: async (args) => openGrantedDispatchMcpApp(args),
133
+ run: async (args) => openGrantedDispatchMcpApp(normalizeOpenAppArgs(args)),
85
134
  link: ({ result }) => {
86
135
  if (!result || typeof result !== "object") return null;
87
136
  const r = result as { url?: string; app?: string; view?: string };
@@ -75,6 +75,7 @@ import {
75
75
  listGrantedDispatchMcpApps,
76
76
  listGrantedDispatchMcpAppOrigins,
77
77
  openGrantedDispatchMcpApp,
78
+ resolveGrantedDispatchMcpApp,
78
79
  } from "./mcp-gateway.js";
79
80
  import { runWithRequestContext } from "@agent-native/core/server";
80
81
 
@@ -163,10 +164,24 @@ describe("Dispatch MCP gateway app discovery", () => {
163
164
  url: "https://mail.agent-native.com/inbox",
164
165
  color: "#2563EB",
165
166
  },
167
+ {
168
+ id: "bad-url",
169
+ name: "Bad URL",
170
+ description: "Invalid manifest URL",
171
+ url: "mail.agent-native.com",
172
+ color: "#111827",
173
+ },
174
+ {
175
+ id: "file-url",
176
+ name: "File URL",
177
+ description: "Unsupported manifest URL scheme",
178
+ url: "file:///tmp/app",
179
+ color: "#111827",
180
+ },
166
181
  ]);
167
182
  mocks.getUserSetting.mockResolvedValue({
168
183
  mode: "selected-apps",
169
- selectedAppIds: ["dispatch", "analytics", "mail"],
184
+ selectedAppIds: ["dispatch", "analytics", "mail", "bad-url", "file-url"],
170
185
  });
171
186
 
172
187
  const origins = await runWithRequestContext(
@@ -176,12 +191,46 @@ describe("Dispatch MCP gateway app discovery", () => {
176
191
  },
177
192
  () => listGrantedDispatchMcpAppOrigins(),
178
193
  );
194
+ const apps = await runWithRequestContext(
195
+ {
196
+ userEmail: "owner@example.test",
197
+ requestOrigin: "http://localhost:8092",
198
+ },
199
+ () => listGrantedDispatchMcpApps(),
200
+ );
179
201
 
180
202
  expect(origins).toEqual([
181
203
  "http://localhost:8092",
182
204
  "http://localhost:8086",
183
205
  "https://mail.agent-native.com",
184
206
  ]);
207
+ expect(apps.map((app) => app.id)).toEqual([
208
+ "dispatch",
209
+ "analytics",
210
+ "mail",
211
+ ]);
212
+ });
213
+
214
+ it("rejects malformed granted app URLs before routing MCP actions", async () => {
215
+ mocks.discoverAgents.mockResolvedValue([
216
+ {
217
+ id: "bad-url",
218
+ name: "Bad URL",
219
+ description: "Invalid manifest URL",
220
+ url: "mail.agent-native.com",
221
+ color: "#111827",
222
+ },
223
+ ]);
224
+
225
+ await expect(
226
+ runWithRequestContext(
227
+ {
228
+ userEmail: "owner@example.test",
229
+ requestOrigin: "http://localhost:8092",
230
+ },
231
+ () => resolveGrantedDispatchMcpApp("bad-url"),
232
+ ),
233
+ ).rejects.toThrow(/invalid URL/);
185
234
  });
186
235
  });
187
236
 
@@ -438,6 +487,48 @@ describe("createGrantedDispatchMcpEmbedSession", () => {
438
487
  });
439
488
  });
440
489
 
490
+ it("skips malformed granted app URLs when matching embed URLs", async () => {
491
+ mocks.discoverAgents.mockResolvedValue([
492
+ {
493
+ id: "bad-url",
494
+ name: "Bad URL",
495
+ description: "Invalid manifest URL",
496
+ url: "mail.agent-native.com",
497
+ color: "#111827",
498
+ },
499
+ {
500
+ id: "mail",
501
+ name: "Mail",
502
+ description: "Mail",
503
+ url: "https://mail.agent-native.com",
504
+ color: "#2563EB",
505
+ },
506
+ ]);
507
+
508
+ const result = await runWithRequestContext(
509
+ {
510
+ userEmail: "owner@example.test",
511
+ requestOrigin: "http://localhost:8092",
512
+ },
513
+ () =>
514
+ createGrantedDispatchMcpEmbedSession({
515
+ url: "https://mail.agent-native.com/inbox",
516
+ }),
517
+ );
518
+
519
+ expect(mocks.managerConstructor).toHaveBeenCalledWith({
520
+ servers: {
521
+ target: expect.objectContaining({
522
+ url: "https://mail.agent-native.com/_agent-native/mcp",
523
+ }),
524
+ },
525
+ });
526
+ expect(result).toEqual({
527
+ app: "mail",
528
+ startUrl: "http://localhost:8086/_agent-native/embed/start?ticket=remote",
529
+ });
530
+ });
531
+
441
532
  it("uses the org A2A secret when minting cross-app MCP embed tokens", async () => {
442
533
  mocks.getOrgDomain.mockResolvedValue("builder.io");
443
534
  mocks.getOrgA2ASecret.mockResolvedValue("org-specific-secret");
@@ -145,8 +145,21 @@ function appendParamsToPath(
145
145
  return `${url.pathname}${url.search}${url.hash}`;
146
146
  }
147
147
 
148
+ function safeAppOrigin(app: DispatchMcpAccessibleApp): string | null {
149
+ try {
150
+ const url = new URL(app.url);
151
+ return url.protocol === "http:" || url.protocol === "https:"
152
+ ? url.origin
153
+ : null;
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+
148
159
  function appOrigin(app: DispatchMcpAccessibleApp): string {
149
- return new URL(app.url).origin;
160
+ const origin = safeAppOrigin(app);
161
+ if (!origin) throw new Error(`Invalid app URL for "${app.id}": ${app.url}`);
162
+ return origin;
150
163
  }
151
164
 
152
165
  function appBaseUrl(app: DispatchMcpAccessibleApp): string {
@@ -159,7 +172,8 @@ function appBasePath(app: DispatchMcpAccessibleApp): string {
159
172
  }
160
173
 
161
174
  function appMatchesUrlPath(app: DispatchMcpAccessibleApp, url: URL): boolean {
162
- if (url.origin !== appOrigin(app)) return false;
175
+ const origin = safeAppOrigin(app);
176
+ if (!origin || url.origin !== origin) return false;
163
177
  const basePath = appBasePath(app);
164
178
  if (!basePath) return true;
165
179
  return url.pathname === basePath || url.pathname.startsWith(`${basePath}/`);
@@ -235,12 +249,12 @@ export async function listGrantedDispatchMcpApps(): Promise<
235
249
  DispatchMcpAccessibleApp[]
236
250
  > {
237
251
  const { apps } = await listDispatchMcpApps();
238
- return apps.filter((app) => app.granted);
252
+ return apps.filter((app) => app.granted && safeAppOrigin(app));
239
253
  }
240
254
 
241
255
  export async function listGrantedDispatchMcpAppOrigins(): Promise<string[]> {
242
256
  const apps = await listGrantedDispatchMcpApps();
243
- return Array.from(new Set(apps.map((app) => appOrigin(app))));
257
+ return Array.from(new Set(apps.flatMap((app) => safeAppOrigin(app) ?? [])));
244
258
  }
245
259
 
246
260
  export async function resolveGrantedDispatchMcpApp(
@@ -263,6 +277,11 @@ export async function resolveGrantedDispatchMcpApp(
263
277
  `Dispatch MCP access to "${match.id}" is not granted. Open Dispatch > Agents to change MCP app access.`,
264
278
  );
265
279
  }
280
+ if (!safeAppOrigin(match)) {
281
+ throw new Error(
282
+ `Dispatch MCP app "${match.id}" has an invalid URL and cannot be opened through MCP.`,
283
+ );
284
+ }
266
285
  return match;
267
286
  }
268
287