@agent-native/core 0.26.6 → 0.26.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +3 -1
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/workspace-dev.d.ts.map +1 -1
- package/dist/cli/workspace-dev.js +9 -2
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +6 -5
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/frame.d.ts +1 -0
- package/dist/client/frame.d.ts.map +1 -1
- package/dist/client/frame.js +20 -6
- package/dist/client/frame.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/mcp/build-server.d.ts +1 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +5 -2
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts +3 -0
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +255 -13
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +1 -0
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/mcp/oauth-token.d.ts +3 -0
- package/dist/mcp/oauth-token.d.ts.map +1 -1
- package/dist/mcp/oauth-token.js +2 -0
- package/dist/mcp/oauth-token.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +5 -10
- package/dist/mcp/server.js.map +1 -1
- package/dist/server/deep-link.d.ts.map +1 -1
- package/dist/server/deep-link.js +16 -1
- package/dist/server/deep-link.js.map +1 -1
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +7 -1
- package/dist/server/security-headers.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -0
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +19 -2
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +124 -0
- package/dist/vite/client.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embed-app.js","sourceRoot":"","sources":["../../src/mcp/embed-app.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ActionMcpAppResourceConfig } from \"../action.js\";\nimport { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from \"../shared/embed-auth.js\";\n\nconst MCP_APP_IMPORT =\n \"https://esm.sh/@modelcontextprotocol/ext-apps@1.7.2/app-with-deps\";\n\nexport const MCP_APP_REQUEST_ORIGIN_CSP_SOURCE = \"$requestOrigin\";\nconst MCP_APP_WRAPPER_CHROME_HEIGHT = 44;\nexport const DEFAULT_MCP_APP_SHELL_HEIGHT = 560;\nexport const DEFAULT_MCP_APP_VIEWPORT_HEIGHT =\n DEFAULT_MCP_APP_SHELL_HEIGHT - MCP_APP_WRAPPER_CHROME_HEIGHT;\n\nexport interface EmbedAppOptions {\n title?: string;\n description?: string;\n iframeTitle?: string;\n openLabel?: string;\n embedByDefault?: boolean;\n startToolName?: string;\n frameDomains?: string[];\n height?: number;\n}\n\nfunction attr(value: string | undefined): string {\n return String(value ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nexport function embedApp(\n options: EmbedAppOptions = {},\n): ActionMcpAppResourceConfig {\n const title = options.title ?? \"Open app\";\n const iframeTitle = options.iframeTitle ?? \"Agent Native app\";\n const openLabel = options.openLabel ?? \"Open in app\";\n const startToolName = options.startToolName ?? \"create_embed_session\";\n const embedByDefault = options.embedByDefault !== false;\n const height = Math.max(\n 320,\n Math.min(900, options.height ?? DEFAULT_MCP_APP_SHELL_HEIGHT),\n );\n const viewportHeight = height - MCP_APP_WRAPPER_CHROME_HEIGHT;\n const frameDomains = [\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.frameDomains ?? []),\n ];\n\n return {\n title,\n ...(options.description ? { description: options.description } : {}),\n html: () => `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <style>\n :root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: Canvas; color: CanvasText; --agent-native-shell-height: ${height}px; --agent-native-viewport-height: ${viewportHeight}px; }\n * { box-sizing: border-box; }\n body { margin: 0; }\n .shell { display: grid; gap: 8px; min-height: var(--agent-native-shell-height); padding: 0; }\n .bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 36px; padding: 6px 8px; border-bottom: 1px solid color-mix(in srgb, CanvasText 12%, Canvas); }\n .title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; color: color-mix(in srgb, CanvasText 72%, Canvas); }\n .actions { display: flex; align-items: center; gap: 6px; }\n button { min-height: 28px; border: 1px solid color-mix(in srgb, CanvasText 14%, Canvas); border-radius: 7px; background: Canvas; color: CanvasText; cursor: pointer; font: inherit; font-size: 12px; font-weight: 700; padding: 0 9px; }\n button:disabled { opacity: .55; cursor: default; }\n .stage { position: relative; min-height: var(--agent-native-viewport-height); }\n iframe { display: block; width: 100%; height: var(--agent-native-viewport-height); border: 0; background: Canvas; }\n .message { display: grid; place-items: center; min-height: var(--agent-native-viewport-height); padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }\n .fallback { display: grid; align-content: center; justify-items: center; gap: 12px; min-height: var(--agent-native-viewport-height); padding: 24px; background: Canvas; color: CanvasText; text-align: center; }\n .fallback-title { max-width: 440px; font-size: 14px; font-weight: 700; }\n .fallback-copy { max-width: 520px; color: color-mix(in srgb, CanvasText 64%, Canvas); font-size: 13px; line-height: 1.45; }\n .fallback-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; }\n .fallback-url { max-width: min(560px, 100%); overflow-wrap: anywhere; color: color-mix(in srgb, CanvasText 76%, Canvas); font-size: 12px; }\n </style>\n</head>\n<body\n data-app-title=\"${attr(title)}\"\n data-iframe-title=\"${attr(iframeTitle)}\"\n data-open-label=\"${attr(openLabel)}\"\n data-start-tool=\"${attr(startToolName)}\"\n data-embed-default=\"${embedByDefault ? \"1\" : \"0\"}\"\n>\n <main class=\"shell\">\n <div class=\"bar\">\n <div class=\"title\" data-title-label>${attr(title)}</div>\n <div class=\"actions\">\n <button type=\"button\" data-display hidden disabled>Fullscreen</button>\n <button type=\"button\" data-open disabled>${attr(openLabel)}</button>\n </div>\n </div>\n <section class=\"stage\" data-stage>\n <div class=\"message\">Preparing app</div>\n </section>\n </main>\n <script type=\"module\">\n const body = document.body;\n const stage = document.querySelector(\"[data-stage]\");\n const titleEl = document.querySelector(\"[data-title-label]\");\n const openButton = document.querySelector(\"[data-open]\");\n const displayButton = document.querySelector(\"[data-display]\");\n const startTool = body.dataset.startTool || \"create_embed_session\";\n const embedByDefault = body.dataset.embedDefault !== \"0\";\n const chatBridgeParam = ${JSON.stringify(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)};\n const defaultIntrinsicHeight = ${height};\n const chromeHeight = ${MCP_APP_WRAPPER_CHROME_HEIGHT};\n const frameReadyMessageDelays = [0, 200, 500, 1500, 3000, 7000, 15000, 30000];\n const frameReadyTimeoutMs = 45000;\n const frameLoadTimeoutMs = 45000;\n let app = null;\n let openAiBridge = null;\n let toolInput = {};\n let toolResultData = {};\n let openUrl = \"\";\n let openStartUrl = \"\";\n let startedFor = \"\";\n let appFrame = null;\n let appFrameReady = false;\n let appFrameReadyTimer = null;\n let appFrameLoadTimer = null;\n let lastFrameSrc = \"\";\n\n function esc(value) {\n return String(value ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n }\n\n function parseJson(value, fallback) {\n if (value && typeof value === \"object\") return value;\n if (typeof value !== \"string\" || !value.trim()) return fallback;\n try { return JSON.parse(value); } catch { return fallback; }\n }\n\n function objectValue(value) {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value\n : {};\n }\n\n function finiteNumber(value) {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0\n ? value\n : null;\n }\n\n function contextMaxHeight(context) {\n if (!context || typeof context !== \"object\") return null;\n return finiteNumber(context.maxHeight) ||\n finiteNumber(context.containerDimensions && context.containerDimensions.maxHeight);\n }\n\n function visibleIntrinsicHeight() {\n const context = hostState().context || {};\n const hostMaxHeight = contextMaxHeight(context);\n if (hostMaxHeight) return Math.floor(hostMaxHeight);\n const viewportHeight = finiteNumber(window.visualViewport && window.visualViewport.height) ||\n finiteNumber(window.innerHeight);\n return Math.floor(viewportHeight || defaultIntrinsicHeight);\n }\n\n function applyIntrinsicHeight(nextHeight) {\n const boundedHeight = Math.min(\n defaultIntrinsicHeight,\n Math.floor(nextHeight || defaultIntrinsicHeight)\n );\n const height = Math.max(320, boundedHeight);\n const viewportHeight = Math.max(0, height - chromeHeight);\n document.documentElement.style.setProperty(\"--agent-native-shell-height\", height + \"px\");\n document.documentElement.style.setProperty(\"--agent-native-viewport-height\", viewportHeight + \"px\");\n if (appFrame) appFrame.style.height = viewportHeight + \"px\";\n return height;\n }\n\n function parseToolResult(params) {\n if (!params) return {};\n if (params.result && typeof params.result === \"object\") {\n return parseToolResult(params.result);\n }\n if (params.toolResult && typeof params.toolResult === \"object\") {\n return parseToolResult(params.toolResult);\n }\n if (params.structuredContent && typeof params.structuredContent === \"object\") {\n return params.structuredContent;\n }\n const parts = Array.isArray(params.content) ? params.content : [];\n const textPart = parts.find((part) => part && part.type === \"text\" && typeof part.text === \"string\");\n const text = textPart ? textPart.text : \"\";\n if (params.isError && typeof text === \"string\" && text.trim()) {\n return { error: text.trim() };\n }\n return parseJson(text, {});\n }\n\n function metadataRecord(value) {\n const meta = value && typeof value === \"object\" && !Array.isArray(value)\n ? value._meta\n : null;\n return meta && typeof meta === \"object\" && !Array.isArray(meta)\n ? meta\n : null;\n }\n\n function toolResultMeta(params) {\n if (!params || typeof params !== \"object\") return {};\n const direct = metadataRecord(params);\n if (direct) return direct;\n if (params.result && typeof params.result === \"object\") {\n return toolResultMeta(params.result);\n }\n if (params.toolResult && typeof params.toolResult === \"object\") {\n return toolResultMeta(params.toolResult);\n }\n return {};\n }\n\n function openLinkRecordFrom(value) {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value\n : {};\n }\n\n function openLinkWebUrlFrom(value) {\n const record = openLinkRecordFrom(value);\n return typeof record.webUrl === \"string\" ? record.webUrl : \"\";\n }\n\n function firstNonEmbedStartUrl(values) {\n for (const value of values) {\n if (typeof value === \"string\" && value && !isEmbedStartUrl(value)) return value;\n }\n return \"\";\n }\n\n function firstEmbedStartUrl(values) {\n for (const value of values) {\n if (typeof value === \"string\" && value && isEmbedStartUrl(value)) {\n return withChatBridgeParam(value);\n }\n }\n return \"\";\n }\n\n function openLinkFrom(params, data) {\n const meta = toolResultMeta(params);\n const openLink = meta[\"agent-native/openLink\"];\n const metaUrl = openLinkWebUrlFrom(openLink);\n const record = data && typeof data === \"object\" ? data : {};\n const structuredOpenLinkUrl = openLinkWebUrlFrom(record.openLink);\n return firstNonEmbedStartUrl([\n record.embedTargetPath,\n record.deepLinkUrl,\n record.deepLink,\n record.openUrl,\n record.url,\n structuredOpenLinkUrl,\n metaUrl\n ]);\n }\n\n function embedStartUrlFrom(params, data) {\n const meta = toolResultMeta(params);\n const embedStart = meta[\"agent-native/embedStart\"];\n const embedStartRecord =\n embedStart && typeof embedStart === \"object\" && !Array.isArray(embedStart)\n ? embedStart\n : {};\n const openLink = meta[\"agent-native/openLink\"];\n const metaUrl = openLinkWebUrlFrom(openLink);\n const record = data && typeof data === \"object\" ? data : {};\n return firstEmbedStartUrl([\n embedStartRecord.startUrl,\n record.embedStartUrl,\n record.startUrl,\n record.url,\n openLinkWebUrlFrom(record.openLink),\n metaUrl\n ]);\n }\n\n function hostState() {\n if (openAiBridge) {\n return {\n context: {\n displayMode: openAiBridge.displayMode,\n availableDisplayModes: typeof openAiBridge.requestDisplayMode === \"function\"\n ? [\"inline\", \"fullscreen\", \"pip\"]\n : [],\n maxHeight: openAiBridge.maxHeight,\n locale: openAiBridge.locale,\n theme: openAiBridge.theme,\n view: openAiBridge.view\n },\n capabilities: { openai: true },\n version: openAiBridge.userAgent\n };\n }\n return {\n context: app && app.getHostContext ? app.getHostContext() : undefined,\n capabilities: app && app.getHostCapabilities ? app.getHostCapabilities() : undefined,\n version: app && app.getHostVersion ? app.getHostVersion() : undefined\n };\n }\n\n function sendToAppFrame(message) {\n if (!appFrame || !appFrame.contentWindow) return;\n try { appFrame.contentWindow.postMessage(message, \"*\"); } catch {}\n }\n\n function sendHostContext() {\n sendToAppFrame({ type: \"agentNative.mcpHostContext\", data: hostState() });\n }\n\n function sendFrameReadyMessages(frame) {\n const originPayload = { type: \"agentNative.frameOrigin\", origin: window.location.origin };\n frameReadyMessageDelays.forEach((delay) => {\n setTimeout(() => {\n try { frame.contentWindow && frame.contentWindow.postMessage(originPayload, \"*\"); } catch {}\n sendHostContext();\n }, delay);\n });\n }\n\n function withChatBridgeParam(value) {\n if (typeof value !== \"string\" || !value) return value;\n try {\n const base = \"http://agent-native.invalid\";\n const url = value.startsWith(\"/\") ? new URL(value, base) : new URL(value);\n url.searchParams.set(chatBridgeParam, \"1\");\n return value.startsWith(\"/\")\n ? url.pathname + url.search + url.hash\n : url.toString();\n } catch {\n return value;\n }\n }\n\n function embedSessionArgsFor(value) {\n const chrome = typeof toolInput.chrome === \"string\" ? toolInput.chrome : \"full\";\n return typeof value === \"string\" && value.startsWith(\"/\")\n ? { path: value, chrome }\n : { url: value, chrome };\n }\n\n function isEmbedStartUrl(value) {\n if (typeof value !== \"string\" || !value) return false;\n try {\n const url = new URL(value, window.location.href);\n return url.pathname.endsWith(\"/_agent-native/embed/start\");\n } catch {\n return false;\n }\n }\n\n function localPathFromUrl(url, includeToken) {\n const next = new URL(url.href);\n if (!includeToken) next.searchParams.delete(\"__an_embed_token\");\n return next.pathname + next.search + next.hash;\n }\n\n function rewriteRootRelativeHtmlUrls(html, appOrigin) {\n return String(html).replace(\n /\\\\b(src|href|poster|action)\\\\s*=\\\\s*([\"'])\\\\/(?!\\\\/)/gi,\n (_match, name, quote) => String(name) + \"=\" + quote + appOrigin + \"/\"\n );\n }\n\n function removeHtmlCspMeta(html) {\n return String(html).replace(\n /<meta\\\\s+[^>]*http-equiv\\\\s*=\\\\s*([\"'])?content-security-policy\\\\1?[^>]*>/gi,\n \"\"\n );\n }\n\n function embedConfigForAppUrl(appUrl) {\n const sanitizedTarget = localPathFromUrl(appUrl, false);\n return {\n origin: appUrl.origin,\n href: appUrl.href,\n baseHref: appUrl.origin + appUrl.pathname,\n target: sanitizedTarget,\n token: appUrl.searchParams.get(\"__an_embed_token\") || \"\",\n chatBridgeActive: appUrl.searchParams.get(chatBridgeParam) === \"1\",\n chatBridgeParam,\n embedTokenParam: \"__an_embed_token\",\n embedTargetHeader: \"x-agent-native-embed-target\"\n };\n }\n\n function installExternalEmbedRuntime(config) {\n window.__AGENT_NATIVE_EXTERNAL_EMBED = config;\n try {\n if (config.target) {\n window.history.replaceState(window.history.state, \"\", config.target);\n }\n } catch (_err) {}\n try {\n if (config.token) {\n sessionStorage.setItem(\"agent-native:embed-auth-token\", config.token);\n }\n if (config.chatBridgeActive && config.token) {\n sessionStorage.setItem(\"agent-native:mcp-chat-bridge\", config.token);\n }\n } catch (_err) {}\n if (window.__agentNativeExternalEmbedRuntimeInstalled) return;\n window.__agentNativeExternalEmbedRuntimeInstalled = true;\n function appOrigin() {\n try {\n return new URL(config.origin).origin;\n } catch (_err) {\n return \"\";\n }\n }\n function targetPath() {\n return config.target || location.pathname + location.search;\n }\n function rewrittenUrl(value, appendToken) {\n const origin = appOrigin();\n if (!origin) return null;\n let url;\n try {\n url = new URL(value, location.href);\n } catch (_err) {\n return null;\n }\n if (url.origin !== location.origin && url.origin !== origin) return null;\n if (url.origin !== origin) {\n const app = new URL(origin);\n url.protocol = app.protocol;\n url.host = app.host;\n }\n if (appendToken && config.token && url.pathname === \"/_agent-native/events\") {\n url.searchParams.set(config.embedTokenParam, config.token);\n }\n return url.toString();\n }\n function authHeaders(input, init) {\n const headers = new Headers(\n init && init.headers ? init.headers : input instanceof Request ? input.headers : undefined\n );\n if (config.token && !headers.has(\"Authorization\")) {\n headers.set(\"Authorization\", \"Bearer \" + config.token);\n }\n if (!headers.has(config.embedTargetHeader)) {\n headers.set(config.embedTargetHeader, targetPath());\n }\n return headers;\n }\n if (typeof fetch === \"function\") {\n const originalFetch = fetch.bind(window);\n window.fetch = function(input, init) {\n const raw = input instanceof Request ? input.url : String(input);\n const url = rewrittenUrl(raw, false);\n if (!url) return originalFetch(input, init);\n const nextInit = Object.assign({}, init || {}, {\n headers: authHeaders(input, init),\n credentials: \"omit\"\n });\n if (input instanceof Request) {\n return originalFetch(new Request(url, input), nextInit);\n }\n return originalFetch(url, nextInit);\n };\n }\n if (typeof XMLHttpRequest !== \"undefined\") {\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n XMLHttpRequest.prototype.open = function(method, url) {\n const rewritten = rewrittenUrl(url, false);\n this.__agentNativeExternalEmbed = !!rewritten;\n return originalOpen.call(\n this,\n method,\n rewritten || url,\n arguments.length > 2 ? arguments[2] : true,\n arguments[3],\n arguments[4]\n );\n };\n XMLHttpRequest.prototype.send = function(body) {\n if (this.__agentNativeExternalEmbed) {\n try {\n if (config.token) this.setRequestHeader(\"Authorization\", \"Bearer \" + config.token);\n this.setRequestHeader(config.embedTargetHeader, targetPath());\n } catch (_err) {}\n }\n return originalSend.call(this, body);\n };\n }\n if (typeof EventSource !== \"undefined\") {\n const OriginalEventSource = EventSource;\n window.EventSource = function(url, options) {\n return new OriginalEventSource(rewrittenUrl(url, true) || url, options);\n };\n window.EventSource.prototype = OriginalEventSource.prototype;\n }\n }\n\n function copyDocumentElementAttributes(source) {\n const target = document.documentElement;\n for (const attr of Array.from(target.attributes)) {\n target.removeAttribute(attr.name);\n }\n for (const attr of Array.from(source.attributes)) {\n target.setAttribute(attr.name, attr.value);\n }\n }\n\n function importChildren(source, target) {\n target.replaceChildren(\n ...Array.from(source.childNodes).map((node) => document.importNode(node, true))\n );\n }\n\n function isModuleScript(script) {\n return (script.getAttribute(\"type\") || \"\").trim().toLowerCase() === \"module\";\n }\n\n function isRunnableClassicScript(script) {\n const type = (script.getAttribute(\"type\") || \"\").trim().toLowerCase();\n return !type || type === \"text/javascript\" || type === \"application/javascript\";\n }\n\n function runClassicScript(script) {\n const next = document.createElement(\"script\");\n for (const attr of Array.from(script.attributes)) {\n if (attr.name === \"type\") continue;\n next.setAttribute(attr.name, attr.value);\n }\n if (script.src) {\n next.src = script.src;\n } else {\n next.textContent = script.textContent || \"\";\n }\n document.body.appendChild(next);\n next.remove();\n }\n\n function rootRelativeSpecifierToAppUrl(specifier, config) {\n if (typeof specifier !== \"string\" || !specifier.startsWith(\"/\") || specifier.startsWith(\"//\")) {\n return specifier;\n }\n try {\n const url = new URL(specifier, config.origin);\n if (config.token) url.searchParams.set(config.embedTokenParam, config.token);\n if (config.chatBridgeActive) url.searchParams.set(config.chatBridgeParam, \"1\");\n return url.toString();\n } catch (_err) {\n return specifier;\n }\n }\n\n function rootRelativeSpecifiersToAbsolute(code, config) {\n return String(code).replace(/([\"'])\\\\/(?!\\\\/)([^\"']*)/g, (_match, quote, rest) => {\n return quote + rootRelativeSpecifierToAppUrl(\"/\" + rest, config);\n });\n }\n\n function relativeSpecifierToAppUrl(specifier, config, baseUrl) {\n if (typeof specifier !== \"string\" || !/^\\\\.\\\\.?\\//.test(specifier)) {\n return specifier;\n }\n try {\n const url = new URL(specifier, baseUrl || config.baseHref);\n if (url.origin === config.origin) {\n if (config.token) url.searchParams.set(config.embedTokenParam, config.token);\n if (config.chatBridgeActive) url.searchParams.set(config.chatBridgeParam, \"1\");\n }\n return url.toString();\n } catch (_err) {\n return specifier;\n }\n }\n\n function relativeModuleSpecifiersToAbsolute(code, config, baseUrl) {\n return String(code)\n .replace(/(\\\\bimport\\\\s+(?:[^\"']+?\\\\s+from\\\\s+)?)([\"'])(\\\\.\\\\.?\\\\/[^\"']*)\\\\2/g, (_match, prefix, quote, specifier) => {\n return prefix + quote + relativeSpecifierToAppUrl(specifier, config, baseUrl) + quote;\n })\n .replace(/(\\\\bimport\\\\s*\\\\(\\\\s*)([\"'])(\\\\.\\\\.?\\\\/[^\"']*)\\\\2/g, (_match, prefix, quote, specifier) => {\n return prefix + quote + relativeSpecifierToAppUrl(specifier, config, baseUrl) + quote;\n })\n .replace(/(\\\\bexport\\\\s+[^\"']*?\\\\s+from\\\\s+)([\"'])(\\\\.\\\\.?\\\\/[^\"']*)\\\\2/g, (_match, prefix, quote, specifier) => {\n return prefix + quote + relativeSpecifierToAppUrl(specifier, config, baseUrl) + quote;\n });\n }\n\n function stripDevOnlyModuleImports(code) {\n return String(code).replace(\n /\\\\bimport\\\\s+(?:[^\"']+\\\\s+from\\\\s+)?[\"'][^\"']*(?:virtual:react-router\\\\/inject-hmr-runtime|__x00__virtual:react-router\\\\/inject-hmr-runtime)[^\"']*[\"']\\\\s*;?/g,\n \"\"\n );\n }\n\n function namedImportBindings(specifierList) {\n return String(specifierList)\n .split(\",\")\n .map((part) => {\n const trimmed = part.trim();\n if (!trimmed) return \"\";\n return trimmed.replace(\n /^([A-Za-z_$][\\\\w$]*)\\\\s+as\\\\s+([A-Za-z_$][\\\\w$]*)$/,\n \"$1: $2\"\n );\n })\n .filter(Boolean)\n .join(\", \");\n }\n\n function moduleCodeToClassicAsync(code, config, baseUrl) {\n return relativeModuleSpecifiersToAbsolute(\n rootRelativeSpecifiersToAbsolute(stripDevOnlyModuleImports(code), config),\n config,\n baseUrl\n )\n .replace(\n /\\\\bimport\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s*,\\\\s*\\\\*\\\\s+as\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s+from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n \"const $2 = await import($3); const $1 = $2.default;\"\n )\n .replace(\n /\\\\bimport\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s*,\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\}\\\\s*from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n (_match, defaultName, specifiers, source) =>\n \"const { default: \" +\n defaultName +\n (namedImportBindings(specifiers) ? \", \" + namedImportBindings(specifiers) : \"\") +\n \" } = await import(\" +\n source +\n \");\"\n )\n .replace(\n /\\\\bimport\\\\s+\\\\{([\\\\s\\\\S]*?)\\\\}\\\\s*from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n (_match, specifiers, source) =>\n \"const { \" + namedImportBindings(specifiers) + \" } = await import(\" + source + \");\"\n )\n .replace(\n /\\\\bimport\\\\s+\\\\*\\\\s+as\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s+from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n \"const $1 = await import($2);\"\n )\n .replace(\n /\\\\bimport\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s+from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n \"const { default: $1 } = await import($2);\"\n )\n .replace(/\\\\bimport\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g, \"await import($1);\")\n .replace(/\\\\bimport\\\\(([\"'][^\"']+[\"'])\\\\)\\\\s*;?/g, \"await import($1);\");\n }\n\n function scriptSourceUrl(script, config) {\n const raw = script.getAttribute(\"src\") || \"\";\n if (!raw) return \"\";\n try {\n const url = new URL(raw, config.baseHref);\n if (url.origin === config.origin) {\n if (config.token) url.searchParams.set(config.embedTokenParam, config.token);\n if (config.chatBridgeActive) url.searchParams.set(config.chatBridgeParam, \"1\");\n }\n return url.toString();\n } catch (_err) {\n return raw;\n }\n }\n\n async function moduleScriptCode(script, config) {\n const src = scriptSourceUrl(script, config);\n if (!src) return script.textContent || \"\";\n const response = await fetch(src, {\n credentials: \"omit\",\n headers: { Accept: \"text/javascript, application/javascript, */*\" }\n });\n if (!response.ok) {\n throw new Error(\"Module script returned HTTP \" + response.status + \".\");\n }\n return await response.text();\n }\n\n async function runModuleScriptAsClassic(script, config) {\n const sourceUrl = scriptSourceUrl(script, config) || config.baseHref;\n const code = moduleCodeToClassicAsync(\n await moduleScriptCode(script, config),\n config,\n sourceUrl\n );\n const runner = document.createElement(\"script\");\n runner.textContent =\n \"(async()=>{\" +\n code +\n \"})().catch((err)=>{console.error('[agent-native] transplanted app module failed',err);document.body.setAttribute('data-agent-native-hydration-error',String(err&&err.message||err));});\";\n document.body.appendChild(runner);\n runner.remove();\n }\n\n async function mountTransplantedHtml(html, appUrl) {\n const config = embedConfigForAppUrl(appUrl);\n installExternalEmbedRuntime(config);\n const parsed = new DOMParser().parseFromString(\n rewriteRootRelativeHtmlUrls(removeHtmlCspMeta(html), appUrl.origin),\n \"text/html\"\n );\n const scripts = Array.from(parsed.querySelectorAll(\"script\"));\n copyDocumentElementAttributes(parsed.documentElement);\n importChildren(parsed.head, document.head);\n const base = document.createElement(\"base\");\n base.href = config.baseHref;\n document.head.prepend(base);\n importChildren(parsed.body, document.body);\n for (const script of scripts) {\n if (isRunnableClassicScript(script)) runClassicScript(script);\n }\n for (const script of scripts) {\n if (isModuleScript(script)) await runModuleScriptAsClassic(script, config);\n }\n }\n\n function absoluteUrl(value, base) {\n try {\n return new URL(value, base).toString();\n } catch {\n return \"\";\n }\n }\n\n async function resolveTransplantAppDocumentSource(src) {\n if (!isEmbedStartUrl(src)) {\n return { url: new URL(src), response: null };\n }\n const response = await fetch(src, {\n credentials: \"omit\",\n redirect: \"follow\",\n headers: {\n Accept: \"application/json\",\n \"X-Agent-Native-Embed-Transplant\": \"1\"\n }\n });\n const contentType = response.headers.get(\"content-type\") || \"\";\n if (response.ok && /\\\\bapplication\\\\/json\\\\b/i.test(contentType)) {\n const data = await response.json();\n const location = typeof data.location === \"string\" ? data.location : \"\";\n const url = absoluteUrl(location, src);\n if (url) return { url: new URL(withChatBridgeParam(url)), response: null };\n throw new Error(\"Embedded app did not return a launch URL.\");\n }\n return {\n url: new URL(response.url || src),\n response\n };\n }\n\n async function transplantAppDocument(src) {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrame = null;\n lastFrameSrc = src;\n setMessage(\"Loading app\");\n const source = await resolveTransplantAppDocumentSource(src);\n const response = source.response || await fetch(source.url.href, {\n credentials: \"omit\",\n redirect: \"follow\",\n headers: { Accept: \"text/html\" }\n });\n if (!response.ok) {\n if (response.status === 401 && isEmbedStartUrl(src)) {\n refreshExpiredEmbedSession();\n return;\n }\n throw new Error(\"Embedded app returned HTTP \" + response.status + \".\");\n }\n const html = await response.text();\n const appUrl = source.url || new URL(response.url || src);\n try {\n window.history.replaceState(window.history.state, \"\", localPathFromUrl(appUrl, false));\n } catch {}\n await mountTransplantedHtml(html, appUrl);\n notifyHostHeightRepeatedly();\n }\n\n function wantsEmbed() {\n if (toolInput.embed === false || toolInput.embed === \"false\") return false;\n if (embedByDefault) return true;\n return toolInput.embed === true || toolInput.embed === \"true\";\n }\n\n function renderModeSource() {\n const input = objectValue(toolInput);\n const result = objectValue(toolResultData);\n return {\n mode: typeof input.embedMode === \"string\"\n ? input.embedMode\n : typeof input.renderMode === \"string\"\n ? input.renderMode\n : typeof result.embedMode === \"string\"\n ? result.embedMode\n : typeof result.renderMode === \"string\"\n ? result.renderMode\n : \"\",\n frame: typeof input.frame === \"string\"\n ? input.frame\n : typeof result.frame === \"string\"\n ? result.frame\n : \"\",\n nested: input.nested === true || result.nested === true\n };\n }\n\n function supportedDisplayMode(mode) {\n if (openAiBridge && typeof openAiBridge.requestDisplayMode === \"function\") {\n return mode === \"inline\" || mode === \"fullscreen\" || mode === \"pip\";\n }\n const modes = hostState().context && hostState().context.availableDisplayModes;\n return Array.isArray(modes) && modes.includes(mode);\n }\n\n async function requestHostDisplayMode(mode) {\n let result;\n if (openAiBridge && typeof openAiBridge.requestDisplayMode === \"function\") {\n result = await openAiBridge.requestDisplayMode({ mode });\n } else {\n if (!app || typeof app.requestDisplayMode !== \"function\") {\n throw new Error(\"Display mode changes are not available in this host.\");\n }\n result = await app.requestDisplayMode({ mode });\n }\n updateDisplayButton();\n sendHostContext();\n return result;\n }\n\n function updateDisplayButton() {\n const context = hostState().context || {};\n const nextMode = context.displayMode === \"fullscreen\" ? \"inline\" : \"fullscreen\";\n const supported = supportedDisplayMode(nextMode);\n displayButton.hidden = !supported;\n displayButton.disabled = !supported;\n displayButton.textContent = nextMode === \"fullscreen\" ? \"Fullscreen\" : \"Inline\";\n displayButton.onclick = () => {\n if (!supportedDisplayMode(nextMode)) return;\n void requestHostDisplayMode(nextMode).catch((err) => {\n console.warn(\"[agent-native] MCP host rejected display mode request\", err);\n });\n };\n }\n\n function setMessage(message) {\n stage.innerHTML = '<div class=\"message\">' + esc(message) + '</div>';\n }\n\n function clearFrameReadyTimer() {\n if (!appFrameReadyTimer) return;\n clearTimeout(appFrameReadyTimer);\n appFrameReadyTimer = null;\n }\n\n function clearFrameLoadTimer() {\n if (!appFrameLoadTimer) return;\n clearTimeout(appFrameLoadTimer);\n appFrameLoadTimer = null;\n }\n\n function startFrameReadyTimer(frame) {\n clearFrameReadyTimer();\n appFrameReadyTimer = setTimeout(() => {\n if (!appFrameReady && appFrame === frame) renderFrameFallback();\n }, frameReadyTimeoutMs);\n }\n\n function renderFrameFallback() {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrame = null;\n const fallbackCopy = openUrl\n ? \"This chat host did not allow the embedded app frame to load inline. You can still open the same app route through the host or use the URL below.\"\n : \"This chat host did not allow the embedded app frame to load inline.\";\n stage.innerHTML =\n '<div class=\"fallback\">' +\n '<div class=\"fallback-title\">Open this app in its own tab</div>' +\n '<div class=\"fallback-copy\">' + esc(fallbackCopy) + '</div>' +\n '<div class=\"fallback-actions\">' +\n '<button type=\"button\" data-fallback-open>Open app</button>' +\n '<button type=\"button\" data-fallback-retry>Try inline again</button>' +\n '</div>' +\n (openUrl ? '<a class=\"fallback-url\" href=\"' + esc(openUrl) + '\" target=\"_blank\" rel=\"noreferrer\">' + esc(openUrl) + '</a>' : '') +\n '</div>';\n const fallbackOpen = stage.querySelector(\"[data-fallback-open]\");\n const fallbackRetry = stage.querySelector(\"[data-fallback-retry]\");\n if (fallbackOpen) {\n fallbackOpen.disabled = !openUrl;\n fallbackOpen.onclick = () => {\n if (openUrl) void openFallbackExternal();\n };\n }\n if (fallbackRetry) {\n fallbackRetry.disabled = !lastFrameSrc;\n fallbackRetry.onclick = () => {\n if (lastFrameSrc) renderFrame(lastFrameSrc);\n };\n }\n }\n\n async function openFallbackExternal() {\n if (!openUrl) return;\n let url = withChatBridgeParam(openUrl);\n try {\n if (url) {\n const result = await callEmbedSessionTool(embedSessionArgsFor(url));\n const data = parseToolResult(result);\n if (typeof data.startUrl === \"string\" && data.startUrl) {\n url = withChatBridgeParam(data.startUrl);\n }\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP fallback could not mint a fresh app session\", err);\n }\n await openHostLink({ url });\n }\n\n function renderFrame(src) {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n const frame = document.createElement(\"iframe\");\n frame.title = body.dataset.iframeTitle || \"Agent Native app\";\n frame.src = src;\n frame.allow = \"clipboard-read; clipboard-write\";\n appFrame = frame;\n appFrameReady = false;\n lastFrameSrc = src;\n frame.addEventListener(\"load\", () => {\n if (appFrame !== frame) return;\n clearFrameLoadTimer();\n sendFrameReadyMessages(frame);\n startFrameReadyTimer(frame);\n });\n stage.replaceChildren(frame);\n notifyHostHeight();\n appFrameLoadTimer = setTimeout(() => {\n if (!appFrameReady && appFrame === frame) renderFrameFallback();\n }, frameLoadTimeoutMs);\n }\n\n function refreshExpiredEmbedSession() {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrameReady = false;\n if (!openUrl) {\n renderFrameFallback();\n return;\n }\n openStartUrl = \"\";\n startedFor = \"\";\n lastFrameSrc = \"\";\n setMessage(\"Refreshing app session\");\n void launchEmbed();\n }\n\n function shouldSelfNavigateToApp() {\n const render = renderModeSource();\n const mode = render.mode;\n if (mode === \"iframe\" || mode === \"nested\") return false;\n if (render.nested || render.frame === \"iframe\") return false;\n return true;\n }\n\n function shouldTransplantAppDocument() {\n const render = renderModeSource();\n const mode = render.mode;\n return (\n mode === \"transplant\" ||\n render.frame === \"transplant\" ||\n isClaudeMcpContentHost()\n );\n }\n\n function isClaudeMcpContentHost() {\n try {\n return /(^|\\\\.)claudemcpcontent\\\\.com$/i.test(window.location.hostname || \"\");\n } catch {\n return false;\n }\n }\n\n function isChatGptSandboxHost() {\n try {\n const host = window.location.hostname || \"\";\n const appParam = new URL(window.location.href).searchParams.get(\"app\");\n return /(^|\\\\.)oaiusercontent\\\\.com$/i.test(host) || appParam === \"chatgpt\";\n } catch {\n return false;\n }\n }\n\n function shouldRenderControlledAppFrame() {\n return !!openAiBridge || isChatGptSandboxHost();\n }\n\n function navigateToAppFrame(src) {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrame = null;\n lastFrameSrc = src;\n setMessage(\"Opening app\");\n try {\n window.location.replace(src);\n } catch (err) {\n console.warn(\"[agent-native] MCP app self-navigation failed\", err);\n renderFrameFallback();\n }\n }\n\n async function updateHostModelContext(data) {\n const params = {};\n if (Array.isArray(data && data.content)) params.content = data.content;\n if (data && data.structuredContent && typeof data.structuredContent === \"object\") {\n params.structuredContent = data.structuredContent;\n }\n if (openAiBridge && typeof openAiBridge.setWidgetState === \"function\") {\n openAiBridge.setWidgetState({\n ...objectValue(openAiBridge.widgetState),\n agentNativeModelContext: params\n });\n return { ok: true };\n }\n if (!app || typeof app.updateModelContext !== \"function\") return { ok: false };\n await app.updateModelContext(params);\n return { ok: true };\n }\n\n async function openHostLink(data) {\n const url = typeof (data && data.url) === \"string\" ? data.url : \"\";\n if (!url) return { isError: true };\n if (openAiBridge && typeof openAiBridge.openExternal === \"function\") {\n return await openAiBridge.openExternal({ href: url, redirectUrl: false });\n }\n if (app && typeof app.openLink === \"function\") {\n return await app.openLink({ url });\n }\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n return { ok: true };\n }\n\n function notifyHostHeight() {\n const height = applyIntrinsicHeight(visibleIntrinsicHeight());\n if (!openAiBridge || typeof openAiBridge.notifyIntrinsicHeight !== \"function\") {\n if (app && typeof app.sendSizeChanged === \"function\") {\n try {\n app.sendSizeChanged({ height });\n } catch (err) {\n console.warn(\"[agent-native] MCP host rejected size update\", err);\n }\n }\n return;\n }\n try {\n openAiBridge.notifyIntrinsicHeight({ height });\n } catch (err) {\n console.warn(\"[agent-native] ChatGPT rejected intrinsic height update\", err);\n }\n }\n\n function respondToAppFrame(requestId, work) {\n if (!requestId) return;\n Promise.resolve(work)\n .then((result) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: { requestId, ok: true, result }\n });\n })\n .catch((err) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: {\n requestId,\n ok: false,\n error: err && err.message ? err.message : String(err)\n }\n });\n });\n }\n\n async function sendHostChat(chat) {\n if (!chat || chat.submit === false) return;\n const message = typeof chat.message === \"string\" ? chat.message : \"\";\n if (!message.trim()) return;\n const context = typeof chat.context === \"string\" ? chat.context.trim() : \"\";\n const content = Array.isArray(chat.content) && chat.content.length\n ? chat.content\n : [{ type: \"text\", text: message }];\n try {\n if (openAiBridge && typeof openAiBridge.setWidgetState === \"function\") {\n const contextContent = context\n ? [{ type: \"text\", text: context }, ...content.filter((part) => part && part.type !== \"text\")]\n : content.filter((part) => part && part.type !== \"text\");\n openAiBridge.setWidgetState({\n ...objectValue(openAiBridge.widgetState),\n agentNativeChatContext: context || null,\n agentNativeModelContext: { content: contextContent }\n });\n } else if (app && typeof app.updateModelContext === \"function\") {\n await app.updateModelContext({\n content: context\n ? [{ type: \"text\", text: context }, ...content.filter((part) => part && part.type !== \"text\")]\n : content.filter((part) => part && part.type !== \"text\")\n });\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP host rejected model context update\", err);\n }\n try {\n if (openAiBridge && typeof openAiBridge.sendFollowUpMessage === \"function\") {\n await openAiBridge.sendFollowUpMessage({\n prompt: message,\n scrollToBottom: true\n });\n return;\n }\n if (!app || typeof app.sendMessage !== \"function\") return;\n const result = await app.sendMessage({\n role: \"user\",\n content\n });\n if (result && result.isError) {\n console.warn(\"[agent-native] MCP host rejected chat message\", result);\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP host chat bridge failed\", err);\n }\n }\n\n window.addEventListener(\"message\", (event) => {\n if (!appFrame || event.source !== appFrame.contentWindow) return;\n if (!event.data) return;\n const data = event.data.data || {};\n if (event.data.type === \"agentNative.embeddedAppReady\") {\n appFrameReady = true;\n clearFrameLoadTimer();\n clearFrameReadyTimer();\n return;\n }\n if (event.data.type === \"agentNative.embedSessionExpired\") {\n refreshExpiredEmbedSession();\n return;\n }\n if (event.data.type === \"agentNative.submitChat\") {\n void sendHostChat(data);\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.updateModelContext\") {\n respondToAppFrame(data.requestId, updateHostModelContext(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.openLink\") {\n respondToAppFrame(data.requestId, openHostLink(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.requestDisplayMode\") {\n respondToAppFrame(data.requestId, requestHostDisplayMode(data.mode));\n }\n });\n\n function notifyHostHeightSoon() {\n requestAnimationFrame(() => notifyHostHeight());\n }\n\n function notifyHostHeightRepeatedly() {\n notifyHostHeight();\n [0, 250, 1000, 2500].forEach((delay) => {\n setTimeout(() => notifyHostHeight(), delay);\n });\n }\n\n window.addEventListener(\"resize\", notifyHostHeightSoon, { passive: true });\n if (window.visualViewport) {\n window.visualViewport.addEventListener(\"resize\", notifyHostHeightSoon, { passive: true });\n }\n\n async function launchEmbed() {\n const launchUrl = openStartUrl || openUrl;\n if (!launchUrl) {\n setMessage(\"Open link was not available.\");\n return;\n }\n if (!wantsEmbed()) {\n setMessage(\"Ready to open.\");\n return;\n }\n if (startedFor === launchUrl) return;\n startedFor = launchUrl;\n setMessage(\"Loading app\");\n try {\n const selfNavigate = shouldSelfNavigateToApp();\n const embedUrl = withChatBridgeParam(launchUrl);\n if (selfNavigate && isEmbedStartUrl(embedUrl)) {\n if (isClaudeMcpContentHost() && shouldTransplantAppDocument()) {\n await transplantAppDocument(embedUrl);\n } else if (shouldRenderControlledAppFrame()) {\n renderFrame(embedUrl);\n } else {\n navigateToAppFrame(embedUrl);\n }\n return;\n }\n if (!selfNavigate && isEmbedStartUrl(embedUrl)) {\n renderFrame(embedUrl);\n return;\n }\n const result = await callEmbedSessionTool(embedSessionArgsFor(embedUrl));\n const data = parseToolResult(result);\n if (typeof data.startUrl !== \"string\" || !data.startUrl) {\n startedFor = \"\";\n setMessage(data.error || \"This app can be opened, but not embedded from this MCP server.\");\n return;\n }\n const startUrl = withChatBridgeParam(data.startUrl);\n if (selfNavigate) {\n if (isClaudeMcpContentHost() && shouldTransplantAppDocument()) {\n await transplantAppDocument(startUrl);\n } else if (shouldRenderControlledAppFrame()) {\n renderFrame(startUrl);\n } else {\n navigateToAppFrame(startUrl);\n }\n } else {\n renderFrame(startUrl);\n }\n } catch (err) {\n startedFor = \"\";\n setMessage(err && err.message ? err.message : \"Could not launch embedded app.\");\n }\n }\n\n async function callEmbedSessionTool(args) {\n if (openAiBridge && typeof openAiBridge.callTool === \"function\") {\n return await openAiBridge.callTool(startTool, args);\n }\n if (!app || typeof app.callServerTool !== \"function\") {\n throw new Error(\"Host tool calls are not available.\");\n }\n return await app.callServerTool({ name: startTool, arguments: args });\n }\n\n function updateHostOpenInAppUrl() {\n if (!openAiBridge || !openUrl || typeof openAiBridge.setOpenInAppUrl !== \"function\") {\n return;\n }\n try {\n openAiBridge.setOpenInAppUrl({ href: openUrl });\n } catch (err) {\n console.warn(\"[agent-native] ChatGPT rejected open-in-app URL\", err);\n }\n }\n\n function updateOpenButton() {\n const buttonUrl = openUrl;\n openButton.disabled = !buttonUrl;\n openButton.onclick = () => {\n if (buttonUrl) void openHostLink({ url: buttonUrl });\n };\n updateHostOpenInAppUrl();\n }\n\n function updateTitle(data) {\n const label = data.label || data.app || data.view || body.dataset.appTitle || \"App\";\n titleEl.textContent = String(label);\n }\n\n function readOpenAiBridge() {\n return window.openai && typeof window.openai === \"object\"\n ? window.openai\n : null;\n }\n\n function openAiToolResultParams(bridge) {\n const params = {};\n if (bridge && bridge.toolOutput !== undefined) {\n if (bridge.toolOutput && typeof bridge.toolOutput === \"object\") {\n params.structuredContent = bridge.toolOutput;\n } else {\n params.content = [{ type: \"text\", text: String(bridge.toolOutput) }];\n }\n }\n if (bridge && bridge.toolResponseMetadata && typeof bridge.toolResponseMetadata === \"object\") {\n params._meta = bridge.toolResponseMetadata;\n }\n return params;\n }\n\n function syncOpenAiBridge(bridge) {\n if (!bridge) return false;\n openAiBridge = bridge;\n toolInput = objectValue(bridge.toolInput);\n const params = openAiToolResultParams(bridge);\n const data = parseToolResult(params);\n toolResultData = objectValue(data);\n openUrl = openLinkFrom(params, data);\n openStartUrl = embedStartUrlFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n if (openUrl || openStartUrl) {\n void launchEmbed();\n } else if (!appFrame) {\n setMessage(\"Waiting for app result\");\n }\n return true;\n }\n\n function waitForOpenAiBridge() {\n const existing = readOpenAiBridge();\n if (existing) return Promise.resolve(existing);\n return new Promise((resolve) => {\n let settled = false;\n const finish = (bridge) => {\n if (settled) return;\n settled = true;\n window.removeEventListener(\"openai:set_globals\", onGlobals);\n clearTimeout(timer);\n resolve(bridge || readOpenAiBridge());\n };\n const onGlobals = () => finish(readOpenAiBridge());\n const timer = setTimeout(() => finish(null), 200);\n window.addEventListener(\"openai:set_globals\", onGlobals, { passive: true });\n });\n }\n\n window.addEventListener(\"openai:set_globals\", () => {\n const bridge = readOpenAiBridge();\n if (bridge && (!appFrame || openAiBridge)) syncOpenAiBridge(bridge);\n }, { passive: true });\n\n async function startMcpAppsBridge() {\n const { App } = await import(\"${MCP_APP_IMPORT}\");\n app = new App(\n { name: \"Agent Native Embed\", version: \"1.0.0\" },\n {},\n { autoResize: false }\n );\n app.ontoolinput = (params) => {\n toolInput = params.arguments || {};\n };\n app.ontoolresult = (params) => {\n const data = parseToolResult(params);\n toolResultData = objectValue(data);\n openUrl = openLinkFrom(params, data);\n openStartUrl = embedStartUrlFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n void launchEmbed();\n };\n app.onhostcontextchanged = () => {\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n };\n await app.connect();\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n }\n\n const initialOpenAiBridge = await waitForOpenAiBridge();\n if (!syncOpenAiBridge(initialOpenAiBridge)) {\n await startMcpAppsBridge();\n }\n </script>\n</body>\n</html>`,\n csp: {\n connectDomains: [\n \"https://esm.sh\",\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.frameDomains ?? []),\n ],\n resourceDomains: [\n \"https://esm.sh\",\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.frameDomains ?? []),\n ],\n baseUriDomains: [MCP_APP_REQUEST_ORIGIN_CSP_SOURCE],\n frameDomains,\n },\n prefersBorder: false,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"embed-app.js","sourceRoot":"","sources":["../../src/mcp/embed-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,MAAM,cAAc,GAClB,mEAAmE,CAAC;AAEtE,MAAM,CAAC,MAAM,iCAAiC,GAAG,gBAAgB,CAAC;AAClE,MAAM,6BAA6B,GAAG,EAAE,CAAC;AACzC,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAChD,MAAM,CAAC,MAAM,+BAA+B,GAC1C,4BAA4B,GAAG,6BAA6B,CAAC;AAgB/D,SAAS,IAAI,CAAC,KAAyB;IACrC,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,UAA2B,EAAE;IAE7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC;IACrD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACtE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACrB,GAAG,EACH,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,IAAI,4BAA4B,CAAC,CAC9D,CAAC;IACF,MAAM,cAAc,GAAG,MAAM,GAAG,6BAA6B,CAAC;IAC9D,MAAM,YAAY,GAAG;QACnB,iCAAiC;QACjC,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;KAChC,CAAC;IAEF,OAAO;QACL,KAAK;QACL,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,IAAI,EAAE,GAAG,EAAE,CAAC;;;;;;8MAM8L,MAAM,uCAAuC,cAAc;;;;;;;;;;;;;;;;;;;;oBAoBrP,IAAI,CAAC,KAAK,CAAC;uBACR,IAAI,CAAC,WAAW,CAAC;qBACnB,IAAI,CAAC,SAAS,CAAC;qBACf,IAAI,CAAC,aAAa,CAAC;wBAChB,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;;;;4CAIN,IAAI,CAAC,KAAK,CAAC;;;mDAGJ,IAAI,CAAC,SAAS,CAAC;;;;;;;;;;;;;;;;8BAgBpC,IAAI,CAAC,SAAS,CAAC,+BAA+B,CAAC;qCACxC,MAAM;2BAChB,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCA26ClB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8C5C;QACJ,GAAG,EAAE;YACH,cAAc,EAAE;gBACd,gBAAgB;gBAChB,iCAAiC;gBACjC,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;gBACjC,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;aAChC;YACD,eAAe,EAAE;gBACf,gBAAgB;gBAChB,iCAAiC;gBACjC,GAAG,CAAC,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;gBAClC,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;aAChC;YACD,cAAc,EAAE;gBACd,iCAAiC;gBACjC,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;aAClC;YACD,YAAY;SACb;QACD,aAAa,EAAE,KAAK;KACrB,CAAC;AACJ,CAAC","sourcesContent":["import type { ActionMcpAppResourceConfig } from \"../action.js\";\nimport { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from \"../shared/embed-auth.js\";\n\nconst MCP_APP_IMPORT =\n \"https://esm.sh/@modelcontextprotocol/ext-apps@1.7.2/app-with-deps\";\n\nexport const MCP_APP_REQUEST_ORIGIN_CSP_SOURCE = \"$requestOrigin\";\nconst MCP_APP_WRAPPER_CHROME_HEIGHT = 44;\nexport const DEFAULT_MCP_APP_SHELL_HEIGHT = 560;\nexport const DEFAULT_MCP_APP_VIEWPORT_HEIGHT =\n DEFAULT_MCP_APP_SHELL_HEIGHT - MCP_APP_WRAPPER_CHROME_HEIGHT;\n\nexport interface EmbedAppOptions {\n title?: string;\n description?: string;\n iframeTitle?: string;\n openLabel?: string;\n embedByDefault?: boolean;\n startToolName?: string;\n connectDomains?: string[];\n resourceDomains?: string[];\n baseUriDomains?: string[];\n frameDomains?: string[];\n height?: number;\n}\n\nfunction attr(value: string | undefined): string {\n return String(value ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nexport function embedApp(\n options: EmbedAppOptions = {},\n): ActionMcpAppResourceConfig {\n const title = options.title ?? \"Open app\";\n const iframeTitle = options.iframeTitle ?? \"Agent Native app\";\n const openLabel = options.openLabel ?? \"Open in app\";\n const startToolName = options.startToolName ?? \"create_embed_session\";\n const embedByDefault = options.embedByDefault !== false;\n const height = Math.max(\n 320,\n Math.min(900, options.height ?? DEFAULT_MCP_APP_SHELL_HEIGHT),\n );\n const viewportHeight = height - MCP_APP_WRAPPER_CHROME_HEIGHT;\n const frameDomains = [\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.frameDomains ?? []),\n ];\n\n return {\n title,\n ...(options.description ? { description: options.description } : {}),\n html: () => `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <style>\n :root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: Canvas; color: CanvasText; --agent-native-shell-height: ${height}px; --agent-native-viewport-height: ${viewportHeight}px; }\n * { box-sizing: border-box; }\n body { margin: 0; }\n .shell { display: grid; gap: 8px; min-height: var(--agent-native-shell-height); padding: 0; }\n .bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 36px; padding: 6px 8px; border-bottom: 1px solid color-mix(in srgb, CanvasText 12%, Canvas); }\n .title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; color: color-mix(in srgb, CanvasText 72%, Canvas); }\n .actions { display: flex; align-items: center; gap: 6px; }\n button { min-height: 28px; border: 1px solid color-mix(in srgb, CanvasText 14%, Canvas); border-radius: 7px; background: Canvas; color: CanvasText; cursor: pointer; font: inherit; font-size: 12px; font-weight: 700; padding: 0 9px; }\n button:disabled { opacity: .55; cursor: default; }\n .stage { position: relative; min-height: var(--agent-native-viewport-height); }\n iframe { display: block; width: 100%; height: var(--agent-native-viewport-height); border: 0; background: Canvas; }\n .message { display: grid; place-items: center; min-height: var(--agent-native-viewport-height); padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }\n .fallback { display: grid; align-content: center; justify-items: center; gap: 12px; min-height: var(--agent-native-viewport-height); padding: 24px; background: Canvas; color: CanvasText; text-align: center; }\n .fallback-title { max-width: 440px; font-size: 14px; font-weight: 700; }\n .fallback-copy { max-width: 520px; color: color-mix(in srgb, CanvasText 64%, Canvas); font-size: 13px; line-height: 1.45; }\n .fallback-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; }\n .fallback-url { max-width: min(560px, 100%); overflow-wrap: anywhere; color: color-mix(in srgb, CanvasText 76%, Canvas); font-size: 12px; }\n </style>\n</head>\n<body\n data-app-title=\"${attr(title)}\"\n data-iframe-title=\"${attr(iframeTitle)}\"\n data-open-label=\"${attr(openLabel)}\"\n data-start-tool=\"${attr(startToolName)}\"\n data-embed-default=\"${embedByDefault ? \"1\" : \"0\"}\"\n>\n <main class=\"shell\">\n <div class=\"bar\">\n <div class=\"title\" data-title-label>${attr(title)}</div>\n <div class=\"actions\">\n <button type=\"button\" data-display hidden disabled>Fullscreen</button>\n <button type=\"button\" data-open disabled>${attr(openLabel)}</button>\n </div>\n </div>\n <section class=\"stage\" data-stage>\n <div class=\"message\">Preparing app</div>\n </section>\n </main>\n <script>\n (async () => {\n const body = document.body;\n const stage = document.querySelector(\"[data-stage]\");\n const titleEl = document.querySelector(\"[data-title-label]\");\n const openButton = document.querySelector(\"[data-open]\");\n const displayButton = document.querySelector(\"[data-display]\");\n const startTool = body.dataset.startTool || \"create_embed_session\";\n const embedByDefault = body.dataset.embedDefault !== \"0\";\n const chatBridgeParam = ${JSON.stringify(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)};\n const defaultIntrinsicHeight = ${height};\n const chromeHeight = ${MCP_APP_WRAPPER_CHROME_HEIGHT};\n const frameReadyMessageDelays = [0, 200, 500, 1500, 3000, 7000, 15000, 30000];\n const frameReadyTimeoutMs = 45000;\n const frameLoadTimeoutMs = 45000;\n const defaultOpenAiBridgeWaitMs = 200;\n const chatGptOpenAiBridgeWaitMs = 5000;\n const openAiBridgePollMs = 50;\n const nativeBridgeInitializeTimeoutMs = 5000;\n const nativeBridgeRequestTimeoutMs = 30000;\n let app = null;\n let openAiBridge = null;\n let toolInput = {};\n let toolResultData = {};\n let openUrl = \"\";\n let openStartUrl = \"\";\n let startedFor = \"\";\n let appFrame = null;\n let appFrameReady = false;\n let appFrameReadyTimer = null;\n let appFrameLoadTimer = null;\n let lastFrameSrc = \"\";\n\n function esc(value) {\n return String(value ?? \"\")\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n }\n\n function parseJson(value, fallback) {\n if (value && typeof value === \"object\") return value;\n if (typeof value !== \"string\" || !value.trim()) return fallback;\n try { return JSON.parse(value); } catch { return fallback; }\n }\n\n function objectValue(value) {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value\n : {};\n }\n\n function finiteNumber(value) {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0\n ? value\n : null;\n }\n\n function contextMaxHeight(context) {\n if (!context || typeof context !== \"object\") return null;\n return finiteNumber(context.maxHeight) ||\n finiteNumber(context.containerDimensions && context.containerDimensions.maxHeight);\n }\n\n function visibleIntrinsicHeight() {\n const context = hostState().context || {};\n const hostMaxHeight = contextMaxHeight(context);\n if (hostMaxHeight) return Math.floor(hostMaxHeight);\n const viewportHeight = finiteNumber(window.visualViewport && window.visualViewport.height) ||\n finiteNumber(window.innerHeight);\n return Math.floor(viewportHeight || defaultIntrinsicHeight);\n }\n\n function applyIntrinsicHeight(nextHeight) {\n const boundedHeight = Math.min(\n defaultIntrinsicHeight,\n Math.floor(nextHeight || defaultIntrinsicHeight)\n );\n const height = Math.max(320, boundedHeight);\n const viewportHeight = Math.max(0, height - chromeHeight);\n document.documentElement.style.setProperty(\"--agent-native-shell-height\", height + \"px\");\n document.documentElement.style.setProperty(\"--agent-native-viewport-height\", viewportHeight + \"px\");\n if (appFrame) appFrame.style.height = viewportHeight + \"px\";\n return height;\n }\n\n function parseToolResult(params) {\n if (!params) return {};\n if (params.result && typeof params.result === \"object\") {\n return parseToolResult(params.result);\n }\n if (params.toolResult && typeof params.toolResult === \"object\") {\n return parseToolResult(params.toolResult);\n }\n if (params.structuredContent && typeof params.structuredContent === \"object\") {\n return params.structuredContent;\n }\n const parts = Array.isArray(params.content) ? params.content : [];\n const textPart = parts.find((part) => part && part.type === \"text\" && typeof part.text === \"string\");\n const text = textPart ? textPart.text : \"\";\n if (params.isError && typeof text === \"string\" && text.trim()) {\n return { error: text.trim() };\n }\n return parseJson(text, {});\n }\n\n function metadataRecord(value) {\n const meta = value && typeof value === \"object\" && !Array.isArray(value)\n ? value._meta\n : null;\n return meta && typeof meta === \"object\" && !Array.isArray(meta)\n ? meta\n : null;\n }\n\n function toolResultMeta(params) {\n if (!params || typeof params !== \"object\") return {};\n const direct = metadataRecord(params);\n if (direct) return direct;\n if (params.result && typeof params.result === \"object\") {\n return toolResultMeta(params.result);\n }\n if (params.toolResult && typeof params.toolResult === \"object\") {\n return toolResultMeta(params.toolResult);\n }\n return {};\n }\n\n function openLinkRecordFrom(value) {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value\n : {};\n }\n\n function openLinkWebUrlFrom(value) {\n const record = openLinkRecordFrom(value);\n return typeof record.webUrl === \"string\" ? record.webUrl : \"\";\n }\n\n function firstNonEmbedStartUrl(values) {\n for (const value of values) {\n if (typeof value === \"string\" && value && !isEmbedStartUrl(value)) return value;\n }\n return \"\";\n }\n\n function firstEmbedStartUrl(values) {\n for (const value of values) {\n if (typeof value === \"string\" && value && isEmbedStartUrl(value)) {\n return withChatBridgeParam(value);\n }\n }\n return \"\";\n }\n\n function openLinkFrom(params, data) {\n const meta = toolResultMeta(params);\n const openLink = meta[\"agent-native/openLink\"];\n const metaUrl = openLinkWebUrlFrom(openLink);\n const record = data && typeof data === \"object\" ? data : {};\n const structuredOpenLinkUrl = openLinkWebUrlFrom(record.openLink);\n return firstNonEmbedStartUrl([\n record.embedTargetPath,\n record.deepLinkUrl,\n record.deepLink,\n record.openUrl,\n record.url,\n structuredOpenLinkUrl,\n metaUrl\n ]);\n }\n\n function embedStartUrlFrom(params, data) {\n const meta = toolResultMeta(params);\n const embedStart = meta[\"agent-native/embedStart\"];\n const embedStartRecord =\n embedStart && typeof embedStart === \"object\" && !Array.isArray(embedStart)\n ? embedStart\n : {};\n const openLink = meta[\"agent-native/openLink\"];\n const metaUrl = openLinkWebUrlFrom(openLink);\n const record = data && typeof data === \"object\" ? data : {};\n return firstEmbedStartUrl([\n embedStartRecord.startUrl,\n record.embedStartUrl,\n record.startUrl,\n record.url,\n openLinkWebUrlFrom(record.openLink),\n metaUrl\n ]);\n }\n\n function hostState() {\n if (openAiBridge) {\n return {\n context: {\n displayMode: openAiBridge.displayMode,\n availableDisplayModes: typeof openAiBridge.requestDisplayMode === \"function\"\n ? [\"inline\", \"fullscreen\", \"pip\"]\n : [],\n maxHeight: openAiBridge.maxHeight,\n locale: openAiBridge.locale,\n theme: openAiBridge.theme,\n view: openAiBridge.view\n },\n capabilities: { openai: true },\n version: openAiBridge.userAgent\n };\n }\n return {\n context: app && app.getHostContext ? app.getHostContext() : undefined,\n capabilities: app && app.getHostCapabilities ? app.getHostCapabilities() : undefined,\n version: app && app.getHostVersion ? app.getHostVersion() : undefined\n };\n }\n\n function sendToAppFrame(message) {\n if (!appFrame || !appFrame.contentWindow) return;\n try { appFrame.contentWindow.postMessage(message, \"*\"); } catch {}\n }\n\n function sendHostContext() {\n sendToAppFrame({ type: \"agentNative.mcpHostContext\", data: hostState() });\n }\n\n function sendFrameReadyMessages(frame) {\n const originPayload = { type: \"agentNative.frameOrigin\", origin: window.location.origin };\n frameReadyMessageDelays.forEach((delay) => {\n setTimeout(() => {\n try { frame.contentWindow && frame.contentWindow.postMessage(originPayload, \"*\"); } catch {}\n sendHostContext();\n }, delay);\n });\n }\n\n function withChatBridgeParam(value) {\n if (typeof value !== \"string\" || !value) return value;\n try {\n const base = \"http://agent-native.invalid\";\n const url = value.startsWith(\"/\") ? new URL(value, base) : new URL(value);\n url.searchParams.set(chatBridgeParam, \"1\");\n return value.startsWith(\"/\")\n ? url.pathname + url.search + url.hash\n : url.toString();\n } catch {\n return value;\n }\n }\n\n function embedSessionArgsFor(value) {\n const chrome = typeof toolInput.chrome === \"string\" ? toolInput.chrome : \"full\";\n return typeof value === \"string\" && value.startsWith(\"/\")\n ? { path: value, chrome }\n : { url: value, chrome };\n }\n\n function isEmbedStartUrl(value) {\n if (typeof value !== \"string\" || !value) return false;\n try {\n const url = new URL(value, window.location.href);\n return url.pathname.endsWith(\"/_agent-native/embed/start\");\n } catch {\n return false;\n }\n }\n\n function localPathFromUrl(url, includeToken) {\n const next = new URL(url.href);\n if (!includeToken) next.searchParams.delete(\"__an_embed_token\");\n return next.pathname + next.search + next.hash;\n }\n\n function rewriteRootRelativeHtmlUrls(html, appOrigin) {\n return String(html).replace(\n /\\\\b(src|href|poster|action)\\\\s*=\\\\s*([\"'])\\\\/(?!\\\\/)/gi,\n (_match, name, quote) => String(name) + \"=\" + quote + appOrigin + \"/\"\n );\n }\n\n function removeHtmlCspMeta(html) {\n return String(html).replace(\n /<meta\\\\s+[^>]*http-equiv\\\\s*=\\\\s*([\"'])?content-security-policy\\\\1?[^>]*>/gi,\n \"\"\n );\n }\n\n function embedConfigForAppUrl(appUrl) {\n const sanitizedTarget = localPathFromUrl(appUrl, false);\n return {\n origin: appUrl.origin,\n href: appUrl.href,\n baseHref: appUrl.origin + appUrl.pathname,\n target: sanitizedTarget,\n token: appUrl.searchParams.get(\"__an_embed_token\") || \"\",\n chatBridgeActive: appUrl.searchParams.get(chatBridgeParam) === \"1\",\n chatBridgeParam,\n embedTokenParam: \"__an_embed_token\",\n embedTargetHeader: \"x-agent-native-embed-target\"\n };\n }\n\n function installReactRefreshPreambleFallback() {\n window.__vite_plugin_react_preamble_installed__ = true;\n if (typeof window.$RefreshReg$ !== \"function\") {\n window.$RefreshReg$ = function() {};\n }\n if (typeof window.$RefreshSig$ !== \"function\") {\n window.$RefreshSig$ = function() {\n return function(type) {\n return type;\n };\n };\n }\n }\n\n function installExternalEmbedRuntime(config) {\n window.__AGENT_NATIVE_EXTERNAL_EMBED = config;\n installReactRefreshPreambleFallback();\n try {\n if (config.target) {\n window.history.replaceState(window.history.state, \"\", config.target);\n }\n } catch (_err) {}\n try {\n if (config.token) {\n sessionStorage.setItem(\"agent-native:embed-auth-token\", config.token);\n }\n if (config.chatBridgeActive && config.token) {\n sessionStorage.setItem(\"agent-native:mcp-chat-bridge\", config.token);\n }\n } catch (_err) {}\n if (window.__agentNativeExternalEmbedRuntimeInstalled) return;\n window.__agentNativeExternalEmbedRuntimeInstalled = true;\n function appOrigin() {\n try {\n return new URL(config.origin).origin;\n } catch (_err) {\n return \"\";\n }\n }\n function targetPath() {\n return config.target || location.pathname + location.search;\n }\n function rewrittenUrl(value, appendToken) {\n const origin = appOrigin();\n if (!origin) return null;\n let url;\n try {\n url = new URL(value, location.href);\n } catch (_err) {\n return null;\n }\n if (url.origin !== location.origin && url.origin !== origin) return null;\n if (url.origin !== origin) {\n const app = new URL(origin);\n url.protocol = app.protocol;\n url.host = app.host;\n }\n if (appendToken && config.token && url.pathname === \"/_agent-native/events\") {\n url.searchParams.set(config.embedTokenParam, config.token);\n }\n return url.toString();\n }\n function authHeaders(input, init) {\n const headers = new Headers(\n init && init.headers ? init.headers : input instanceof Request ? input.headers : undefined\n );\n if (config.token && !headers.has(\"Authorization\")) {\n headers.set(\"Authorization\", \"Bearer \" + config.token);\n }\n if (!headers.has(config.embedTargetHeader)) {\n headers.set(config.embedTargetHeader, targetPath());\n }\n return headers;\n }\n if (typeof fetch === \"function\") {\n const originalFetch = fetch.bind(window);\n window.fetch = function(input, init) {\n const raw = input instanceof Request ? input.url : String(input);\n const url = rewrittenUrl(raw, false);\n if (!url) return originalFetch(input, init);\n const nextInit = Object.assign({}, init || {}, {\n headers: authHeaders(input, init),\n credentials: \"omit\"\n });\n if (input instanceof Request) {\n return originalFetch(new Request(url, input), nextInit);\n }\n return originalFetch(url, nextInit);\n };\n }\n if (typeof XMLHttpRequest !== \"undefined\") {\n const originalOpen = XMLHttpRequest.prototype.open;\n const originalSend = XMLHttpRequest.prototype.send;\n XMLHttpRequest.prototype.open = function(method, url) {\n const rewritten = rewrittenUrl(url, false);\n this.__agentNativeExternalEmbed = !!rewritten;\n return originalOpen.call(\n this,\n method,\n rewritten || url,\n arguments.length > 2 ? arguments[2] : true,\n arguments[3],\n arguments[4]\n );\n };\n XMLHttpRequest.prototype.send = function(body) {\n if (this.__agentNativeExternalEmbed) {\n try {\n if (config.token) this.setRequestHeader(\"Authorization\", \"Bearer \" + config.token);\n this.setRequestHeader(config.embedTargetHeader, targetPath());\n } catch (_err) {}\n }\n return originalSend.call(this, body);\n };\n }\n if (typeof EventSource !== \"undefined\") {\n const OriginalEventSource = EventSource;\n window.EventSource = function(url, options) {\n return new OriginalEventSource(rewrittenUrl(url, true) || url, options);\n };\n window.EventSource.prototype = OriginalEventSource.prototype;\n }\n }\n\n function copyDocumentElementAttributes(source) {\n const target = document.documentElement;\n for (const attr of Array.from(target.attributes)) {\n target.removeAttribute(attr.name);\n }\n for (const attr of Array.from(source.attributes)) {\n target.setAttribute(attr.name, attr.value);\n }\n }\n\n function importChildren(source, target) {\n target.replaceChildren(\n ...Array.from(source.childNodes).map((node) => document.importNode(node, true))\n );\n }\n\n function isModuleScript(script) {\n return (script.getAttribute(\"type\") || \"\").trim().toLowerCase() === \"module\";\n }\n\n function isRunnableClassicScript(script) {\n const type = (script.getAttribute(\"type\") || \"\").trim().toLowerCase();\n return !type || type === \"text/javascript\" || type === \"application/javascript\";\n }\n\n function runClassicScript(script) {\n const next = document.createElement(\"script\");\n for (const attr of Array.from(script.attributes)) {\n if (attr.name === \"type\") continue;\n next.setAttribute(attr.name, attr.value);\n }\n if (script.src) {\n next.src = script.src;\n } else {\n next.textContent = script.textContent || \"\";\n }\n document.body.appendChild(next);\n next.remove();\n }\n\n function rootRelativeSpecifierToAppUrl(specifier, config) {\n if (typeof specifier !== \"string\" || !specifier.startsWith(\"/\") || specifier.startsWith(\"//\")) {\n return specifier;\n }\n try {\n const url = new URL(specifier, config.origin);\n if (config.token) url.searchParams.set(config.embedTokenParam, config.token);\n if (config.chatBridgeActive) url.searchParams.set(config.chatBridgeParam, \"1\");\n return url.toString();\n } catch (_err) {\n return specifier;\n }\n }\n\n function rootRelativeSpecifiersToAbsolute(code, config) {\n return String(code).replace(/([\"'])\\\\/(?!\\\\/)([^\"']*)/g, (_match, quote, rest) => {\n return quote + rootRelativeSpecifierToAppUrl(\"/\" + rest, config);\n });\n }\n\n function relativeSpecifierToAppUrl(specifier, config, baseUrl) {\n if (typeof specifier !== \"string\" || !/^\\\\.\\\\.?\\\\//.test(specifier)) {\n return specifier;\n }\n try {\n const url = new URL(specifier, baseUrl || config.baseHref);\n if (url.origin === config.origin) {\n if (config.token) url.searchParams.set(config.embedTokenParam, config.token);\n if (config.chatBridgeActive) url.searchParams.set(config.chatBridgeParam, \"1\");\n }\n return url.toString();\n } catch (_err) {\n return specifier;\n }\n }\n\n function relativeModuleSpecifiersToAbsolute(code, config, baseUrl) {\n return String(code)\n .replace(/(\\\\bimport\\\\s+(?:[^\"']+?\\\\s+from\\\\s+)?)([\"'])(\\\\.\\\\.?\\\\/[^\"']*)\\\\2/g, (_match, prefix, quote, specifier) => {\n return prefix + quote + relativeSpecifierToAppUrl(specifier, config, baseUrl) + quote;\n })\n .replace(/(\\\\bimport\\\\s*\\\\(\\\\s*)([\"'])(\\\\.\\\\.?\\\\/[^\"']*)\\\\2/g, (_match, prefix, quote, specifier) => {\n return prefix + quote + relativeSpecifierToAppUrl(specifier, config, baseUrl) + quote;\n })\n .replace(/(\\\\bexport\\\\s+[^\"']*?\\\\s+from\\\\s+)([\"'])(\\\\.\\\\.?\\\\/[^\"']*)\\\\2/g, (_match, prefix, quote, specifier) => {\n return prefix + quote + relativeSpecifierToAppUrl(specifier, config, baseUrl) + quote;\n });\n }\n\n function stripDevOnlyModuleImports(code) {\n return String(code).replace(\n /\\\\bimport\\\\s+(?:[^\"']+\\\\s+from\\\\s+)?[\"'][^\"']*(?:virtual:react-router\\\\/inject-hmr-runtime|__x00__virtual:react-router\\\\/inject-hmr-runtime)[^\"']*[\"']\\\\s*;?/g,\n \"\"\n );\n }\n\n function namedImportBindings(specifierList) {\n return String(specifierList)\n .split(\",\")\n .map((part) => {\n const trimmed = part.trim();\n if (!trimmed) return \"\";\n return trimmed.replace(\n /^([A-Za-z_$][\\\\w$]*)\\\\s+as\\\\s+([A-Za-z_$][\\\\w$]*)$/,\n \"$1: $2\"\n );\n })\n .filter(Boolean)\n .join(\", \");\n }\n\n function moduleCodeToClassicAsync(code, config, baseUrl) {\n return relativeModuleSpecifiersToAbsolute(\n rootRelativeSpecifiersToAbsolute(stripDevOnlyModuleImports(code), config),\n config,\n baseUrl\n )\n .replace(\n /\\\\bimport\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s*,\\\\s*\\\\*\\\\s+as\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s+from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n \"const $2 = await import($3); const $1 = $2.default;\"\n )\n .replace(\n /\\\\bimport\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s*,\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\}\\\\s*from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n (_match, defaultName, specifiers, source) =>\n \"const { default: \" +\n defaultName +\n (namedImportBindings(specifiers) ? \", \" + namedImportBindings(specifiers) : \"\") +\n \" } = await import(\" +\n source +\n \");\"\n )\n .replace(\n /\\\\bimport\\\\s+\\\\{([\\\\s\\\\S]*?)\\\\}\\\\s*from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n (_match, specifiers, source) =>\n \"const { \" + namedImportBindings(specifiers) + \" } = await import(\" + source + \");\"\n )\n .replace(\n /\\\\bimport\\\\s+\\\\*\\\\s+as\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s+from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n \"const $1 = await import($2);\"\n )\n .replace(\n /\\\\bimport\\\\s+([A-Za-z_$][\\\\w$]*)\\\\s+from\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g,\n \"const { default: $1 } = await import($2);\"\n )\n .replace(/\\\\bimport\\\\s+([\"'][^\"']+[\"'])\\\\s*;?/g, \"await import($1);\")\n .replace(/\\\\bimport\\\\(([\"'][^\"']+[\"'])\\\\)\\\\s*;?/g, \"await import($1);\");\n }\n\n function scriptSourceUrl(script, config) {\n const raw = script.getAttribute(\"src\") || \"\";\n if (!raw) return \"\";\n try {\n const url = new URL(raw, config.baseHref);\n if (url.origin === config.origin) {\n if (config.token) url.searchParams.set(config.embedTokenParam, config.token);\n if (config.chatBridgeActive) url.searchParams.set(config.chatBridgeParam, \"1\");\n }\n return url.toString();\n } catch (_err) {\n return raw;\n }\n }\n\n async function moduleScriptCode(script, config) {\n const src = scriptSourceUrl(script, config);\n if (!src) return script.textContent || \"\";\n const response = await fetch(src, {\n credentials: \"omit\",\n headers: { Accept: \"text/javascript, application/javascript, */*\" }\n });\n if (!response.ok) {\n throw new Error(\"Module script returned HTTP \" + response.status + \".\");\n }\n return await response.text();\n }\n\n async function runModuleScriptAsClassic(script, config) {\n const sourceUrl = scriptSourceUrl(script, config) || config.baseHref;\n const code = moduleCodeToClassicAsync(\n await moduleScriptCode(script, config),\n config,\n sourceUrl\n );\n const runner = document.createElement(\"script\");\n runner.textContent =\n \"(async()=>{\" +\n code +\n \"})().catch((err)=>{console.error('[agent-native] transplanted app module failed',err);document.body.setAttribute('data-agent-native-hydration-error',String(err&&err.message||err));});\";\n document.body.appendChild(runner);\n runner.remove();\n }\n\n async function mountTransplantedHtml(html, appUrl) {\n const config = embedConfigForAppUrl(appUrl);\n installExternalEmbedRuntime(config);\n const parsed = new DOMParser().parseFromString(\n rewriteRootRelativeHtmlUrls(removeHtmlCspMeta(html), appUrl.origin),\n \"text/html\"\n );\n const scripts = Array.from(parsed.querySelectorAll(\"script\"));\n copyDocumentElementAttributes(parsed.documentElement);\n importChildren(parsed.head, document.head);\n const base = document.createElement(\"base\");\n base.href = config.baseHref;\n document.head.prepend(base);\n importChildren(parsed.body, document.body);\n for (const script of scripts) {\n if (isRunnableClassicScript(script)) runClassicScript(script);\n }\n for (const script of scripts) {\n if (isModuleScript(script)) await runModuleScriptAsClassic(script, config);\n }\n }\n\n function absoluteUrl(value, base) {\n try {\n return new URL(value, base).toString();\n } catch {\n return \"\";\n }\n }\n\n async function resolveTransplantAppDocumentSource(src) {\n if (!isEmbedStartUrl(src)) {\n return { url: new URL(src), response: null };\n }\n const response = await fetch(src, {\n credentials: \"omit\",\n redirect: \"follow\",\n headers: {\n Accept: \"application/json\",\n \"X-Agent-Native-Embed-Transplant\": \"1\"\n }\n });\n const contentType = response.headers.get(\"content-type\") || \"\";\n if (response.ok && /\\\\bapplication\\\\/json\\\\b/i.test(contentType)) {\n const data = await response.json();\n const location = typeof data.location === \"string\" ? data.location : \"\";\n const url = absoluteUrl(location, src);\n if (url) return { url: new URL(withChatBridgeParam(url)), response: null };\n throw new Error(\"Embedded app did not return a launch URL.\");\n }\n return {\n url: new URL(response.url || src),\n response\n };\n }\n\n async function transplantAppDocument(src) {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrame = null;\n lastFrameSrc = src;\n setMessage(\"Loading app\");\n const source = await resolveTransplantAppDocumentSource(src);\n const response = source.response || await fetch(source.url.href, {\n credentials: \"omit\",\n redirect: \"follow\",\n headers: { Accept: \"text/html\" }\n });\n if (!response.ok) {\n if (response.status === 401 && isEmbedStartUrl(src)) {\n refreshExpiredEmbedSession();\n return;\n }\n throw new Error(\"Embedded app returned HTTP \" + response.status + \".\");\n }\n const html = await response.text();\n const appUrl = source.url || new URL(response.url || src);\n try {\n window.history.replaceState(window.history.state, \"\", localPathFromUrl(appUrl, false));\n } catch {}\n await mountTransplantedHtml(html, appUrl);\n notifyHostHeightRepeatedly();\n }\n\n function wantsEmbed() {\n if (toolInput.embed === false || toolInput.embed === \"false\") return false;\n if (embedByDefault) return true;\n return toolInput.embed === true || toolInput.embed === \"true\";\n }\n\n function renderModeSource() {\n const input = objectValue(toolInput);\n const result = objectValue(toolResultData);\n return {\n mode: typeof input.embedMode === \"string\"\n ? input.embedMode\n : typeof input.renderMode === \"string\"\n ? input.renderMode\n : typeof result.embedMode === \"string\"\n ? result.embedMode\n : typeof result.renderMode === \"string\"\n ? result.renderMode\n : \"\",\n frame: typeof input.frame === \"string\"\n ? input.frame\n : typeof result.frame === \"string\"\n ? result.frame\n : \"\",\n nested: input.nested === true || result.nested === true\n };\n }\n\n function supportedDisplayMode(mode) {\n if (openAiBridge && typeof openAiBridge.requestDisplayMode === \"function\") {\n return mode === \"inline\" || mode === \"fullscreen\" || mode === \"pip\";\n }\n const modes = hostState().context && hostState().context.availableDisplayModes;\n return Array.isArray(modes) && modes.includes(mode);\n }\n\n async function requestHostDisplayMode(mode) {\n let result;\n if (openAiBridge && typeof openAiBridge.requestDisplayMode === \"function\") {\n result = await openAiBridge.requestDisplayMode({ mode });\n } else {\n if (!app || typeof app.requestDisplayMode !== \"function\") {\n throw new Error(\"Display mode changes are not available in this host.\");\n }\n result = await app.requestDisplayMode({ mode });\n }\n updateDisplayButton();\n sendHostContext();\n return result;\n }\n\n function updateDisplayButton() {\n const context = hostState().context || {};\n const nextMode = context.displayMode === \"fullscreen\" ? \"inline\" : \"fullscreen\";\n const supported = supportedDisplayMode(nextMode);\n displayButton.hidden = !supported;\n displayButton.disabled = !supported;\n displayButton.textContent = nextMode === \"fullscreen\" ? \"Fullscreen\" : \"Inline\";\n displayButton.onclick = () => {\n if (!supportedDisplayMode(nextMode)) return;\n void requestHostDisplayMode(nextMode).catch((err) => {\n console.warn(\"[agent-native] MCP host rejected display mode request\", err);\n });\n };\n }\n\n function setMessage(message) {\n stage.innerHTML = '<div class=\"message\">' + esc(message) + '</div>';\n }\n\n function clearFrameReadyTimer() {\n if (!appFrameReadyTimer) return;\n clearTimeout(appFrameReadyTimer);\n appFrameReadyTimer = null;\n }\n\n function clearFrameLoadTimer() {\n if (!appFrameLoadTimer) return;\n clearTimeout(appFrameLoadTimer);\n appFrameLoadTimer = null;\n }\n\n function startFrameReadyTimer(frame) {\n clearFrameReadyTimer();\n appFrameReadyTimer = setTimeout(() => {\n if (!appFrameReady && appFrame === frame) renderFrameFallback();\n }, frameReadyTimeoutMs);\n }\n\n function renderFrameFallback() {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrame = null;\n const fallbackCopy = openUrl\n ? \"This chat host did not allow the embedded app frame to load inline. You can still open the same app route through the host or use the URL below.\"\n : \"This chat host did not allow the embedded app frame to load inline.\";\n stage.innerHTML =\n '<div class=\"fallback\">' +\n '<div class=\"fallback-title\">Open this app in its own tab</div>' +\n '<div class=\"fallback-copy\">' + esc(fallbackCopy) + '</div>' +\n '<div class=\"fallback-actions\">' +\n '<button type=\"button\" data-fallback-open>Open app</button>' +\n '<button type=\"button\" data-fallback-retry>Try inline again</button>' +\n '</div>' +\n (openUrl ? '<a class=\"fallback-url\" href=\"' + esc(openUrl) + '\" target=\"_blank\" rel=\"noreferrer\">' + esc(openUrl) + '</a>' : '') +\n '</div>';\n const fallbackOpen = stage.querySelector(\"[data-fallback-open]\");\n const fallbackRetry = stage.querySelector(\"[data-fallback-retry]\");\n if (fallbackOpen) {\n fallbackOpen.disabled = !openUrl;\n fallbackOpen.onclick = () => {\n if (openUrl) void openFallbackExternal();\n };\n }\n if (fallbackRetry) {\n fallbackRetry.disabled = !lastFrameSrc;\n fallbackRetry.onclick = () => {\n if (lastFrameSrc) renderFrame(lastFrameSrc);\n };\n }\n }\n\n async function openFallbackExternal() {\n if (!openUrl) return;\n let url = withChatBridgeParam(openUrl);\n try {\n if (url) {\n const result = await callEmbedSessionTool(embedSessionArgsFor(url));\n const data = parseToolResult(result);\n if (typeof data.startUrl === \"string\" && data.startUrl) {\n url = withChatBridgeParam(data.startUrl);\n }\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP fallback could not mint a fresh app session\", err);\n }\n await openHostLink({ url });\n }\n\n function renderFrame(src) {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n const frame = document.createElement(\"iframe\");\n frame.title = body.dataset.iframeTitle || \"Agent Native app\";\n frame.src = src;\n frame.allow = \"clipboard-read; clipboard-write\";\n appFrame = frame;\n appFrameReady = false;\n lastFrameSrc = src;\n frame.addEventListener(\"load\", () => {\n if (appFrame !== frame) return;\n clearFrameLoadTimer();\n sendFrameReadyMessages(frame);\n startFrameReadyTimer(frame);\n });\n stage.replaceChildren(frame);\n notifyHostHeight();\n appFrameLoadTimer = setTimeout(() => {\n if (!appFrameReady && appFrame === frame) renderFrameFallback();\n }, frameLoadTimeoutMs);\n }\n\n function refreshExpiredEmbedSession() {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrameReady = false;\n if (!openUrl) {\n renderFrameFallback();\n return;\n }\n openStartUrl = \"\";\n startedFor = \"\";\n lastFrameSrc = \"\";\n setMessage(\"Refreshing app session\");\n void launchEmbed();\n }\n\n function shouldSelfNavigateToApp() {\n const render = renderModeSource();\n const mode = render.mode;\n if (isClaudeMcpContentHost()) return true;\n if (mode === \"iframe\" || mode === \"nested\") return false;\n if (render.nested || render.frame === \"iframe\") return false;\n return true;\n }\n\n function shouldTransplantAppDocument() {\n const render = renderModeSource();\n const mode = render.mode;\n return (\n isClaudeMcpContentHost() ||\n isChatGptSandboxHost() ||\n mode === \"transplant\" ||\n render.frame === \"transplant\"\n );\n }\n\n function isClaudeMcpContentHost() {\n try {\n return /(^|\\\\.)claudemcpcontent\\\\.com$/i.test(window.location.hostname || \"\");\n } catch {\n return false;\n }\n }\n\n function isChatGptSandboxHost() {\n try {\n const host = window.location.hostname || \"\";\n const appParam = new URL(window.location.href).searchParams.get(\"app\");\n return /^[^.]+\\\\.web-sandbox\\\\.oaiusercontent\\\\.com$/i.test(host) || appParam === \"chatgpt\";\n } catch {\n return false;\n }\n }\n\n function shouldRenderControlledAppFrame() {\n return !!openAiBridge || isChatGptSandboxHost();\n }\n\n function navigateToAppFrame(src) {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrame = null;\n lastFrameSrc = src;\n setMessage(\"Opening app\");\n try {\n window.location.replace(src);\n } catch (err) {\n console.warn(\"[agent-native] MCP app self-navigation failed\", err);\n renderFrameFallback();\n }\n }\n\n async function updateHostModelContext(data) {\n const params = {};\n if (Array.isArray(data && data.content)) params.content = data.content;\n if (data && data.structuredContent && typeof data.structuredContent === \"object\") {\n params.structuredContent = data.structuredContent;\n }\n if (openAiBridge && typeof openAiBridge.setWidgetState === \"function\") {\n openAiBridge.setWidgetState({\n ...objectValue(openAiBridge.widgetState),\n agentNativeModelContext: params\n });\n return { ok: true };\n }\n if (!app || typeof app.updateModelContext !== \"function\") return { ok: false };\n await app.updateModelContext(params);\n return { ok: true };\n }\n\n async function openHostLink(data) {\n const url = typeof (data && data.url) === \"string\" ? data.url : \"\";\n if (!url) return { isError: true };\n if (openAiBridge && typeof openAiBridge.openExternal === \"function\") {\n return await openAiBridge.openExternal({ href: url, redirectUrl: false });\n }\n if (app && typeof app.openLink === \"function\") {\n return await app.openLink({ url });\n }\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n return { ok: true };\n }\n\n function notifyHostHeight() {\n const height = applyIntrinsicHeight(visibleIntrinsicHeight());\n if (!openAiBridge || typeof openAiBridge.notifyIntrinsicHeight !== \"function\") {\n if (app && typeof app.sendSizeChanged === \"function\") {\n try {\n app.sendSizeChanged({ height });\n } catch (err) {\n console.warn(\"[agent-native] MCP host rejected size update\", err);\n }\n }\n return;\n }\n try {\n openAiBridge.notifyIntrinsicHeight({ height });\n } catch (err) {\n console.warn(\"[agent-native] ChatGPT rejected intrinsic height update\", err);\n }\n }\n\n function respondToAppFrame(requestId, work) {\n if (!requestId) return;\n Promise.resolve(work)\n .then((result) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: { requestId, ok: true, result }\n });\n })\n .catch((err) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: {\n requestId,\n ok: false,\n error: err && err.message ? err.message : String(err)\n }\n });\n });\n }\n\n async function sendHostChat(chat) {\n if (!chat || chat.submit === false) return;\n const message = typeof chat.message === \"string\" ? chat.message : \"\";\n if (!message.trim()) return;\n const context = typeof chat.context === \"string\" ? chat.context.trim() : \"\";\n const content = Array.isArray(chat.content) && chat.content.length\n ? chat.content\n : [{ type: \"text\", text: message }];\n try {\n if (openAiBridge && typeof openAiBridge.setWidgetState === \"function\") {\n const contextContent = context\n ? [{ type: \"text\", text: context }, ...content.filter((part) => part && part.type !== \"text\")]\n : content.filter((part) => part && part.type !== \"text\");\n openAiBridge.setWidgetState({\n ...objectValue(openAiBridge.widgetState),\n agentNativeChatContext: context || null,\n agentNativeModelContext: { content: contextContent }\n });\n } else if (app && typeof app.updateModelContext === \"function\") {\n await app.updateModelContext({\n content: context\n ? [{ type: \"text\", text: context }, ...content.filter((part) => part && part.type !== \"text\")]\n : content.filter((part) => part && part.type !== \"text\")\n });\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP host rejected model context update\", err);\n }\n try {\n if (openAiBridge && typeof openAiBridge.sendFollowUpMessage === \"function\") {\n await openAiBridge.sendFollowUpMessage({\n prompt: message,\n scrollToBottom: true\n });\n return;\n }\n if (!app || typeof app.sendMessage !== \"function\") return;\n const result = await app.sendMessage({\n role: \"user\",\n content\n });\n if (result && result.isError) {\n console.warn(\"[agent-native] MCP host rejected chat message\", result);\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP host chat bridge failed\", err);\n }\n }\n\n window.addEventListener(\"message\", (event) => {\n if (!appFrame || event.source !== appFrame.contentWindow) return;\n if (!event.data) return;\n const data = event.data.data || {};\n if (event.data.type === \"agentNative.embeddedAppReady\") {\n appFrameReady = true;\n clearFrameLoadTimer();\n clearFrameReadyTimer();\n return;\n }\n if (event.data.type === \"agentNative.embedSessionExpired\") {\n refreshExpiredEmbedSession();\n return;\n }\n if (event.data.type === \"agentNative.submitChat\") {\n void sendHostChat(data);\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.updateModelContext\") {\n respondToAppFrame(data.requestId, updateHostModelContext(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.openLink\") {\n respondToAppFrame(data.requestId, openHostLink(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.requestDisplayMode\") {\n respondToAppFrame(data.requestId, requestHostDisplayMode(data.mode));\n }\n });\n\n function notifyHostHeightSoon() {\n requestAnimationFrame(() => notifyHostHeight());\n }\n\n function notifyHostHeightRepeatedly() {\n notifyHostHeight();\n [0, 250, 1000, 2500].forEach((delay) => {\n setTimeout(() => notifyHostHeight(), delay);\n });\n }\n\n window.addEventListener(\"resize\", notifyHostHeightSoon, { passive: true });\n if (window.visualViewport) {\n window.visualViewport.addEventListener(\"resize\", notifyHostHeightSoon, { passive: true });\n }\n\n async function launchEmbed() {\n const launchUrl = openStartUrl || openUrl;\n if (!launchUrl) {\n setMessage(\"Open link was not available.\");\n return;\n }\n if (!wantsEmbed()) {\n setMessage(\"Ready to open.\");\n return;\n }\n if (startedFor === launchUrl) return;\n startedFor = launchUrl;\n setMessage(\"Loading app\");\n try {\n const selfNavigate = shouldSelfNavigateToApp();\n const embedUrl = withChatBridgeParam(launchUrl);\n if (selfNavigate && isEmbedStartUrl(embedUrl)) {\n if (shouldTransplantAppDocument()) {\n await transplantAppDocument(embedUrl);\n } else if (shouldRenderControlledAppFrame()) {\n renderFrame(embedUrl);\n } else {\n navigateToAppFrame(embedUrl);\n }\n return;\n }\n if (!selfNavigate && isEmbedStartUrl(embedUrl)) {\n renderFrame(embedUrl);\n return;\n }\n const result = await callEmbedSessionTool(embedSessionArgsFor(embedUrl));\n const data = parseToolResult(result);\n if (typeof data.startUrl !== \"string\" || !data.startUrl) {\n startedFor = \"\";\n setMessage(data.error || \"This app can be opened, but not embedded from this MCP server.\");\n return;\n }\n const startUrl = withChatBridgeParam(data.startUrl);\n if (selfNavigate) {\n if (shouldTransplantAppDocument()) {\n await transplantAppDocument(startUrl);\n } else if (shouldRenderControlledAppFrame()) {\n renderFrame(startUrl);\n } else {\n navigateToAppFrame(startUrl);\n }\n } else {\n renderFrame(startUrl);\n }\n } catch (err) {\n startedFor = \"\";\n setMessage(err && err.message ? err.message : \"Could not launch embedded app.\");\n }\n }\n\n async function callEmbedSessionTool(args) {\n if (openAiBridge && typeof openAiBridge.callTool === \"function\") {\n return await openAiBridge.callTool(startTool, args);\n }\n if (!app || typeof app.callServerTool !== \"function\") {\n throw new Error(\"Host tool calls are not available.\");\n }\n return await app.callServerTool({ name: startTool, arguments: args });\n }\n\n function updateHostOpenInAppUrl() {\n if (!openAiBridge || !openUrl || typeof openAiBridge.setOpenInAppUrl !== \"function\") {\n return;\n }\n try {\n openAiBridge.setOpenInAppUrl({ href: openUrl });\n } catch (err) {\n console.warn(\"[agent-native] ChatGPT rejected open-in-app URL\", err);\n }\n }\n\n function updateOpenButton() {\n const buttonUrl = openUrl;\n openButton.disabled = !buttonUrl;\n openButton.onclick = () => {\n if (buttonUrl) void openHostLink({ url: buttonUrl });\n };\n updateHostOpenInAppUrl();\n }\n\n function updateTitle(data) {\n const record = objectValue(data);\n const label = record.label || record.app || record.view || body.dataset.appTitle || \"App\";\n titleEl.textContent = String(label);\n }\n\n function readOpenAiBridge() {\n return window.openai && typeof window.openai === \"object\"\n ? window.openai\n : null;\n }\n\n function isChatGptHostHint() {\n try {\n if (new URLSearchParams(window.location.search).get(\"app\") === \"chatgpt\") return true;\n } catch (_err) {}\n try {\n return /chatgpt/i.test(String(navigator.userAgent || \"\"));\n } catch (_err) {\n return false;\n }\n }\n\n function openAiToolResultParams(bridge) {\n const params = {};\n if (bridge && bridge.toolOutput !== undefined) {\n if (bridge.toolOutput && typeof bridge.toolOutput === \"object\") {\n params.structuredContent = bridge.toolOutput;\n } else {\n params.content = [{ type: \"text\", text: String(bridge.toolOutput) }];\n }\n }\n if (bridge && bridge.toolResponseMetadata && typeof bridge.toolResponseMetadata === \"object\") {\n params._meta = bridge.toolResponseMetadata;\n }\n return params;\n }\n\n function syncOpenAiBridge(bridge) {\n if (!bridge) return false;\n openAiBridge = bridge;\n toolInput = objectValue(bridge.toolInput);\n const params = openAiToolResultParams(bridge);\n const data = parseToolResult(params);\n toolResultData = objectValue(data);\n openUrl = openLinkFrom(params, data);\n openStartUrl = embedStartUrlFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n if (openUrl || openStartUrl) {\n void launchEmbed();\n } else if (!appFrame) {\n setMessage(\"Waiting for app result\");\n }\n return true;\n }\n\n function waitForOpenAiBridge() {\n const existing = readOpenAiBridge();\n if (existing) return Promise.resolve(existing);\n return new Promise((resolve) => {\n let settled = false;\n let pollTimer = 0;\n const finish = (bridge) => {\n if (settled) return;\n settled = true;\n window.removeEventListener(\"openai:set_globals\", onGlobals);\n clearTimeout(timer);\n clearTimeout(pollTimer);\n resolve(bridge || readOpenAiBridge());\n };\n const poll = () => {\n const bridge = readOpenAiBridge();\n if (bridge) {\n finish(bridge);\n return;\n }\n pollTimer = window.setTimeout(poll, openAiBridgePollMs);\n };\n const onGlobals = () => finish(readOpenAiBridge());\n const timer = window.setTimeout(\n () => finish(null),\n isChatGptHostHint() ? chatGptOpenAiBridgeWaitMs : defaultOpenAiBridgeWaitMs\n );\n window.addEventListener(\"openai:set_globals\", onGlobals, { passive: true });\n pollTimer = window.setTimeout(poll, openAiBridgePollMs);\n });\n }\n\n window.addEventListener(\"openai:set_globals\", () => {\n const bridge = readOpenAiBridge();\n if (bridge && (!appFrame || openAiBridge)) syncOpenAiBridge(bridge);\n }, { passive: true });\n\n function createNativeMcpAppsBridge() {\n let rpcId = 0;\n let connected = false;\n let hostContext = {};\n const pendingRequests = new Map();\n\n function rpcNotify(method, params) {\n window.parent.postMessage({ jsonrpc: \"2.0\", method, params: params || {} }, \"*\");\n }\n\n function rpcRequest(method, params, timeoutMs) {\n return new Promise((resolve, reject) => {\n const id = ++rpcId;\n const timer = window.setTimeout(() => {\n pendingRequests.delete(id);\n reject(new Error(\"MCP Apps bridge request timed out: \" + method));\n }, timeoutMs || nativeBridgeRequestTimeoutMs);\n pendingRequests.set(id, { resolve, reject, timer });\n window.parent.postMessage(\n { jsonrpc: \"2.0\", id, method, params: params || {} },\n \"*\"\n );\n });\n }\n\n function settleRpcResponse(message) {\n const pending = pendingRequests.get(message.id);\n if (!pending) return true;\n pendingRequests.delete(message.id);\n clearTimeout(pending.timer);\n if (message.error) {\n const error = new Error(message.error.message || \"MCP Apps bridge request failed.\");\n error.data = message.error.data;\n pending.reject(error);\n return true;\n }\n pending.resolve(message.result);\n return true;\n }\n\n function notificationParams(message) {\n return message && typeof message.params === \"object\" && message.params\n ? message.params\n : {};\n }\n\n function toolInputNotificationParams(params) {\n if (params && typeof params.arguments === \"object\" && params.arguments) {\n return params;\n }\n if (params && typeof params.input === \"object\" && params.input) {\n return { arguments: params.input };\n }\n return { arguments: objectValue(params) };\n }\n\n const nativeApp = {\n ontoolinput: null,\n ontoolresult: null,\n onhostcontextchanged: null,\n getHostContext() {\n return hostContext.context || hostContext;\n },\n getHostCapabilities() {\n return hostContext.capabilities || { tools: true, messaging: true };\n },\n getHostVersion() {\n return hostContext.protocolVersion || \"mcp-apps-postmessage\";\n },\n async connect() {\n if (connected) return hostContext;\n connected = true;\n window.addEventListener(\"message\", onMessage, { passive: true });\n const result = await rpcRequest(\n \"ui/initialize\",\n {\n appInfo: { name: \"Agent Native Embed\", version: \"1.0.0\" },\n appCapabilities: {},\n protocolVersion: \"2026-01-26\"\n },\n nativeBridgeInitializeTimeoutMs\n );\n hostContext = objectValue(result);\n rpcNotify(\"ui/notifications/initialized\", {});\n if (typeof nativeApp.onhostcontextchanged === \"function\") {\n nativeApp.onhostcontextchanged(hostContext);\n }\n return hostContext;\n },\n async callServerTool(request) {\n const record = objectValue(request);\n return await rpcRequest(\"tools/call\", {\n name: record.name,\n arguments: objectValue(record.arguments)\n });\n },\n async updateModelContext(params) {\n return await rpcRequest(\"ui/update-model-context\", objectValue(params));\n },\n async openLink(params) {\n const url = typeof (params && params.url) === \"string\" ? params.url : \"\";\n if (!url) return { isError: true };\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n return { ok: true };\n },\n async requestDisplayMode(params) {\n return await rpcRequest(\"ui/request-display-mode\", objectValue(params));\n },\n sendSizeChanged(params) {\n rpcNotify(\"ui/notifications/size-changed\", objectValue(params));\n },\n async sendMessage(params) {\n return await rpcRequest(\"ui/message\", objectValue(params));\n }\n };\n\n function onMessage(event) {\n if (event.source !== window.parent) return;\n const message = event.data;\n if (!message || message.jsonrpc !== \"2.0\") return;\n if (typeof message.id === \"number\" || typeof message.id === \"string\") {\n settleRpcResponse(message);\n return;\n }\n if (typeof message.method !== \"string\") return;\n const params = notificationParams(message);\n if (message.method === \"ui/notifications/tool-input\") {\n if (typeof nativeApp.ontoolinput === \"function\") {\n nativeApp.ontoolinput(toolInputNotificationParams(params));\n }\n return;\n }\n if (message.method === \"ui/notifications/tool-result\") {\n if (typeof nativeApp.ontoolresult === \"function\") {\n nativeApp.ontoolresult(params);\n }\n return;\n }\n if (\n message.method === \"ui/notifications/host-context\" ||\n message.method === \"ui/notifications/context\"\n ) {\n hostContext = objectValue(params);\n if (typeof nativeApp.onhostcontextchanged === \"function\") {\n nativeApp.onhostcontextchanged(hostContext);\n }\n }\n }\n\n return nativeApp;\n }\n\n async function startNativeMcpAppsBridge() {\n app = createNativeMcpAppsBridge();\n app.ontoolinput = (params) => {\n toolInput = params.arguments || {};\n };\n app.ontoolresult = (params) => {\n const data = parseToolResult(params);\n toolResultData = objectValue(data);\n openUrl = openLinkFrom(params, data);\n openStartUrl = embedStartUrlFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n void launchEmbed();\n };\n app.onhostcontextchanged = () => {\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n };\n await app.connect();\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n }\n\n async function startMcpAppsBridge() {\n const { App } = await import(\"${MCP_APP_IMPORT}\");\n app = new App(\n { name: \"Agent Native Embed\", version: \"1.0.0\" },\n {},\n { autoResize: false }\n );\n app.ontoolinput = (params) => {\n toolInput = params.arguments || {};\n };\n app.ontoolresult = (params) => {\n const data = parseToolResult(params);\n toolResultData = objectValue(data);\n openUrl = openLinkFrom(params, data);\n openStartUrl = embedStartUrlFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n void launchEmbed();\n };\n app.onhostcontextchanged = () => {\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n };\n await app.connect();\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n }\n\n try {\n const initialOpenAiBridge = await waitForOpenAiBridge();\n if (!syncOpenAiBridge(initialOpenAiBridge)) {\n try {\n await startNativeMcpAppsBridge();\n } catch (nativeErr) {\n console.warn(\"[agent-native] native MCP Apps bridge failed\", nativeErr);\n await startMcpAppsBridge();\n }\n }\n } catch (err) {\n console.error(\"[agent-native] MCP app shell failed\", err);\n setMessage(err && err.message ? err.message : \"Could not initialize app.\");\n }\n })();\n </script>\n</body>\n</html>`,\n csp: {\n connectDomains: [\n \"https://esm.sh\",\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.connectDomains ?? []),\n ...(options.frameDomains ?? []),\n ],\n resourceDomains: [\n \"https://esm.sh\",\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.resourceDomains ?? []),\n ...(options.frameDomains ?? []),\n ],\n baseUriDomains: [\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.baseUriDomains ?? []),\n ],\n frameDomains,\n },\n prefersBorder: false,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-route.d.ts","sourceRoot":"","sources":["../../src/mcp/oauth-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAyBlC,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAkFD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIpE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAItE;AAED,wBAAgB,uCAAuC,CACrD,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM7D;AAiBD,wBAAgB,uCAAuC,CACrD,KAAK,EAAE,OAAO,GACb,QAAQ,CAeV;AAED,wBAAgB,yCAAyC,CACvD,KAAK,EAAE,OAAO,GACb,QAAQ,CAsBV;
|
|
1
|
+
{"version":3,"file":"oauth-route.d.ts","sourceRoot":"","sources":["../../src/mcp/oauth-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAyBlC,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAkFD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIpE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAItE;AAED,wBAAgB,uCAAuC,CACrD,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM7D;AAiBD,wBAAgB,uCAAuC,CACrD,KAAK,EAAE,OAAO,GACb,QAAQ,CAeV;AAED,wBAAgB,yCAAyC,CACvD,KAAK,EAAE,OAAO,GACb,QAAQ,CAsBV;AA4jBD,wBAAsB,cAAc,CAClC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,QAAQ,CAAC,CAenB"}
|
package/dist/mcp/oauth-route.js
CHANGED
|
@@ -571,6 +571,7 @@ async function handleRefreshTokenGrant(event, body) {
|
|
|
571
571
|
return oauthError("server_error", "Unable to derive issuer", 500);
|
|
572
572
|
const accessToken = await signMcpOAuthAccessToken({
|
|
573
573
|
ownerEmail: row.ownerEmail,
|
|
574
|
+
orgId: row.orgId,
|
|
574
575
|
orgDomain: row.orgDomain,
|
|
575
576
|
clientId: row.clientId,
|
|
576
577
|
scope: row.scope,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-route.js","sourceRoot":"","sources":["../../src/mcp/oauth-route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Standard remote MCP OAuth 2.1 endpoints.\n *\n * These routes let MCP hosts such as Claude Code and ChatGPT authenticate\n * through their native remote-MCP OAuth flow instead of pasting bearer tokens.\n * The issued access tokens are audience-bound to `/_agent-native/mcp`, carry\n * the same user/org identity as the existing connect flow, and are mediated by\n * `verifyAuth` before any MCP tool/resource request runs.\n */\n\nimport type { H3Event } from \"h3\";\nimport { getHeader, getMethod, getQuery, setResponseStatus } from \"h3\";\nimport { createHash, createHmac, timingSafeEqual } from \"node:crypto\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { getConfiguredLoginHtml, getSession } from \"../server/auth.js\";\nimport { getAuthSecret } from \"../server/better-auth-instance.js\";\nimport { getOrgDomain } from \"../org/context.js\";\nimport {\n createOAuthCode,\n createOAuthRefreshToken,\n consumeOAuthCode,\n generateOpaqueToken,\n getOAuthClient,\n getOAuthCode,\n getOAuthRefreshToken,\n registerOAuthClient,\n rotateOAuthRefreshToken,\n} from \"./oauth-store.js\";\nimport {\n MCP_OAUTH_DEFAULT_SCOPE,\n MCP_OAUTH_SCOPES,\n normalizeOAuthScope,\n signMcpOAuthAccessToken,\n} from \"./oauth-token.js\";\n\nexport interface McpOAuthRouteOptions {\n appId?: string;\n appName?: string;\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Cache-Control\": \"no-store\",\n Pragma: \"no-cache\",\n },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, {\n status,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"Cache-Control\": \"no-store\",\n },\n });\n}\n\nfunction redirect(location: string): Response {\n return new Response(null, {\n status: 302,\n headers: { Location: location, \"Cache-Control\": \"no-store\" },\n });\n}\n\nfunction isSameOriginPost(event: H3Event): boolean {\n const origin = getHeader(event, \"origin\");\n if (!origin) return true;\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer) return false;\n try {\n return new URL(origin).origin === new URL(issuer).origin;\n } catch {\n return false;\n }\n}\n\nfunction oauthError(\n error: string,\n description: string,\n status = 400,\n): Response {\n return json({ error, error_description: description }, status);\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\nfunction normalizeBasePath(raw: string | undefined): string {\n const trimmed = (raw ?? \"\").trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nfunction configuredBasePath(): string {\n return normalizeBasePath(\n process.env.APP_BASE_PATH || process.env.VITE_APP_BASE_PATH,\n );\n}\n\nfunction deriveOrigin(event: H3Event): string {\n const forwardedProto = getHeader(event, \"x-forwarded-proto\");\n const host = getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1|\\[::1\\])(:|$)/.test(host)\n ? \"http\"\n : \"https\");\n return host ? `${proto}://${host}` : \"\";\n}\n\nexport function getMcpOAuthIssuer(event: H3Event): string | undefined {\n const origin = deriveOrigin(event);\n if (!origin) return undefined;\n return `${origin}${configuredBasePath()}`;\n}\n\nexport function getMcpOAuthResource(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer) return undefined;\n return `${issuer}/_agent-native/mcp`;\n}\n\nexport function getMcpOAuthProtectedResourceMetadataUrl(\n event: H3Event,\n): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer) return undefined;\n return `${issuer}/.well-known/oauth-protected-resource`;\n}\n\nexport function buildMcpOAuthChallenge(event: H3Event): string {\n const metadata = getMcpOAuthProtectedResourceMetadataUrl(event);\n const scope = MCP_OAUTH_DEFAULT_SCOPE;\n return metadata\n ? `Bearer resource_metadata=\"${metadata}\", scope=\"${scope}\"`\n : `Bearer scope=\"${scope}\"`;\n}\n\nfunction authorizationEndpoint(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n return issuer ? `${issuer}/_agent-native/mcp/oauth/authorize` : undefined;\n}\n\nfunction tokenEndpoint(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n return issuer ? `${issuer}/_agent-native/mcp/oauth/token` : undefined;\n}\n\nfunction registrationEndpoint(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n return issuer ? `${issuer}/_agent-native/mcp/oauth/register` : undefined;\n}\n\nexport function handleMcpOAuthProtectedResourceMetadata(\n event: H3Event,\n): Response {\n if (getMethod(event) !== \"GET\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const resource = getMcpOAuthResource(event);\n const issuer = getMcpOAuthIssuer(event);\n if (!resource || !issuer) {\n return oauthError(\"server_error\", \"Unable to derive MCP resource\", 500);\n }\n return json({\n resource,\n authorization_servers: [issuer],\n scopes_supported: MCP_OAUTH_SCOPES,\n resource_documentation: issuer,\n });\n}\n\nexport function handleMcpOAuthAuthorizationServerMetadata(\n event: H3Event,\n): Response {\n if (getMethod(event) !== \"GET\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const issuer = getMcpOAuthIssuer(event);\n const authorize = authorizationEndpoint(event);\n const token = tokenEndpoint(event);\n const register = registrationEndpoint(event);\n if (!issuer || !authorize || !token || !register) {\n return oauthError(\"server_error\", \"Unable to derive OAuth endpoints\", 500);\n }\n return json({\n issuer,\n authorization_endpoint: authorize,\n token_endpoint: token,\n registration_endpoint: register,\n response_types_supported: [\"code\"],\n grant_types_supported: [\"authorization_code\", \"refresh_token\"],\n code_challenge_methods_supported: [\"S256\"],\n token_endpoint_auth_methods_supported: [\"none\"],\n scopes_supported: MCP_OAUTH_SCOPES,\n });\n}\n\nfunction isAllowedRedirectUri(value: unknown): value is string {\n if (typeof value !== \"string\" || value.length > 2048) return false;\n try {\n const url = new URL(value);\n if (url.hash) return false;\n if (url.username || url.password) return false;\n if (url.protocol === \"https:\") return true;\n if (url.protocol !== \"http:\") return false;\n return (\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\" ||\n url.hostname === \"[::1]\"\n );\n } catch {\n return false;\n }\n}\n\nfunction parseStringArray(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : [];\n}\n\nasync function handleRegister(event: H3Event): Promise<Response> {\n if (getMethod(event) !== \"POST\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as Record<\n string,\n unknown\n >;\n const redirectUris = parseStringArray(body.redirect_uris);\n if (\n redirectUris.length === 0 ||\n redirectUris.length > 20 ||\n !redirectUris.every(isAllowedRedirectUri)\n ) {\n return oauthError(\n \"invalid_client_metadata\",\n \"redirect_uris must contain valid HTTPS or localhost callback URLs\",\n );\n }\n\n const grantTypes = parseStringArray(body.grant_types);\n if (\n grantTypes.length &&\n !grantTypes.every(\n (g) => g === \"authorization_code\" || g === \"refresh_token\",\n )\n ) {\n return oauthError(\"invalid_client_metadata\", \"Unsupported grant_type\");\n }\n const responseTypes = parseStringArray(body.response_types);\n if (responseTypes.length && !responseTypes.every((r) => r === \"code\")) {\n return oauthError(\"invalid_client_metadata\", \"Unsupported response_type\");\n }\n const method =\n typeof body.token_endpoint_auth_method === \"string\"\n ? body.token_endpoint_auth_method\n : \"none\";\n if (method !== \"none\") {\n return oauthError(\n \"invalid_client_metadata\",\n \"Only public OAuth clients are supported\",\n );\n }\n\n const clientName =\n typeof body.client_name === \"string\"\n ? body.client_name.trim().slice(0, 120)\n : null;\n let client;\n try {\n client = await registerOAuthClient({\n clientName,\n redirectUris: [...new Set(redirectUris)],\n grantTypes: grantTypes.length ? grantTypes : undefined,\n responseTypes: responseTypes.length ? responseTypes : undefined,\n tokenEndpointAuthMethod: method,\n });\n } catch (err: any) {\n if (err?.message === \"RATE_LIMITED\") {\n return oauthError(\"slow_down\", \"Too many client registrations\", 429);\n }\n throw err;\n }\n return json(\n {\n client_id: client.clientId,\n client_id_issued_at: Math.floor((client.createdAt ?? Date.now()) / 1000),\n client_name: client.clientName ?? undefined,\n redirect_uris: client.redirectUris,\n grant_types: client.grantTypes,\n response_types: client.responseTypes,\n token_endpoint_auth_method: client.tokenEndpointAuthMethod,\n },\n 201,\n );\n}\n\nfunction redirectWithOAuthError(params: {\n redirectUri: string;\n state?: string;\n error: string;\n description?: string;\n}): Response {\n const url = new URL(params.redirectUri);\n url.searchParams.set(\"error\", params.error);\n if (params.description) {\n url.searchParams.set(\"error_description\", params.description);\n }\n if (params.state) url.searchParams.set(\"state\", params.state);\n return redirect(url.toString());\n}\n\nfunction redirectWithCode(params: {\n redirectUri: string;\n code: string;\n state?: string;\n}): Response {\n const url = new URL(params.redirectUri);\n url.searchParams.set(\"code\", params.code);\n if (params.state) url.searchParams.set(\"state\", params.state);\n return redirect(url.toString());\n}\n\nfunction codeChallengeForVerifier(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction safeEqual(a: string, b: string): boolean {\n const aa = Buffer.from(a);\n const bb = Buffer.from(b);\n return aa.length === bb.length && timingSafeEqual(aa, bb);\n}\n\nfunction base64UrlEncode(value: Buffer | string): string {\n const buf = typeof value === \"string\" ? Buffer.from(value, \"utf8\") : value;\n return buf.toString(\"base64url\");\n}\n\nfunction base64UrlDecode(value: string): Buffer {\n return Buffer.from(value, \"base64url\");\n}\n\nfunction consentSigningKey(): string {\n return process.env.A2A_SECRET || getAuthSecret();\n}\n\nfunction consentPayload(params: {\n email: string;\n clientId: string;\n redirectUri: string;\n resource: string;\n scope: string;\n codeChallenge: string;\n}): string {\n return JSON.stringify({\n ...params,\n exp: Math.floor(Date.now() / 1000) + 10 * 60,\n });\n}\n\nfunction signConsentToken(params: {\n email: string;\n clientId: string;\n redirectUri: string;\n resource: string;\n scope: string;\n codeChallenge: string;\n}): string {\n const payload = base64UrlEncode(consentPayload(params));\n const sig = base64UrlEncode(\n createHmac(\"sha256\", consentSigningKey()).update(payload).digest(),\n );\n return `${payload}.${sig}`;\n}\n\nfunction verifyConsentToken(\n token: string | undefined,\n expected: {\n email: string;\n clientId: string;\n redirectUri: string;\n resource: string;\n scope: string;\n codeChallenge: string;\n },\n): boolean {\n if (!token || !token.includes(\".\")) return false;\n const [payload, sig] = token.split(\".\", 2);\n if (!payload || !sig) return false;\n const expectedSig = base64UrlEncode(\n createHmac(\"sha256\", consentSigningKey()).update(payload).digest(),\n );\n if (!safeEqual(sig, expectedSig)) return false;\n try {\n const parsed = JSON.parse(base64UrlDecode(payload).toString(\"utf8\"));\n return (\n parsed.email === expected.email &&\n parsed.clientId === expected.clientId &&\n parsed.redirectUri === expected.redirectUri &&\n parsed.resource === expected.resource &&\n parsed.scope === expected.scope &&\n parsed.codeChallenge === expected.codeChallenge &&\n typeof parsed.exp === \"number\" &&\n parsed.exp * 1000 >= Date.now()\n );\n } catch {\n return false;\n }\n}\n\nfunction isValidCodeVerifier(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length >= 43 &&\n value.length <= 128 &&\n /^[A-Za-z0-9._~-]+$/.test(value)\n );\n}\n\nfunction renderConsentPage(params: {\n appName: string;\n email: string;\n clientName: string;\n scopes: string[];\n fields: Record<string, string>;\n}): string {\n const hidden = Object.entries(params.fields)\n .map(\n ([key, value]) =>\n `<input type=\"hidden\" name=\"${escapeHtml(key)}\" value=\"${escapeHtml(value)}\">`,\n )\n .join(\"\\n\");\n const scopes = params.scopes\n .map((scope) => `<li><code>${escapeHtml(scope)}</code></li>`)\n .join(\"\");\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Authorize ${escapeHtml(params.appName)}</title>\n<style>\n :root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #09090b; color: #f4f4f5; }\n body { min-height: 100vh; display: grid; place-items: center; margin: 0; padding: 24px; }\n main { width: min(520px, 100%); border: 1px solid #27272a; border-radius: 8px; background: #111113; padding: 24px; box-shadow: 0 24px 80px rgba(0,0,0,.35); }\n h1 { font-size: 22px; line-height: 1.2; margin: 0 0 10px; }\n p { color: #a1a1aa; line-height: 1.5; margin: 0 0 18px; }\n ul { margin: 0 0 22px; padding-left: 22px; color: #d4d4d8; }\n code { color: #67e8f9; }\n .actions { display: flex; gap: 10px; justify-content: flex-end; }\n button { border: 0; border-radius: 6px; padding: 10px 14px; font-weight: 650; cursor: pointer; }\n .primary { background: #f4f4f5; color: #09090b; }\n .secondary { background: #27272a; color: #f4f4f5; }\n</style>\n</head>\n<body>\n<main>\n <h1>Authorize ${escapeHtml(params.clientName)}</h1>\n <p>${escapeHtml(params.appName)} will let this MCP client act as ${escapeHtml(params.email)} for these scopes:</p>\n <ul>${scopes}</ul>\n <form method=\"post\">\n ${hidden}\n <div class=\"actions\">\n <button class=\"secondary\" type=\"submit\" name=\"decision\" value=\"deny\">Deny</button>\n <button class=\"primary\" type=\"submit\" name=\"decision\" value=\"approve\">Authorize</button>\n </div>\n </form>\n</main>\n</body>\n</html>`;\n}\n\nasync function resolveOrgDomain(\n orgId: string | undefined,\n): Promise<string | undefined> {\n if (!orgId) return undefined;\n try {\n return (await getOrgDomain(orgId)) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function readOAuthParams(\n event: H3Event,\n): Promise<Record<string, string>> {\n if (getMethod(event) === \"GET\") {\n const query = getQuery(event);\n return Object.fromEntries(\n Object.entries(query).flatMap(([key, value]) =>\n typeof value === \"string\" ? [[key, value]] : [],\n ),\n );\n }\n const body = await readBody(event).catch(() => ({}));\n if (typeof body === \"string\") {\n return Object.fromEntries(new URLSearchParams(body));\n }\n if (body && typeof body === \"object\") {\n return Object.fromEntries(\n Object.entries(body as Record<string, unknown>).flatMap(([key, value]) =>\n typeof value === \"string\" ? [[key, value]] : [],\n ),\n );\n }\n return {};\n}\n\nasync function handleAuthorize(\n event: H3Event,\n options: McpOAuthRouteOptions,\n): Promise<Response> {\n const method = getMethod(event);\n if (method !== \"GET\" && method !== \"POST\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n if (method === \"POST\" && !isSameOriginPost(event)) {\n return oauthError(\n \"invalid_request\",\n \"Cross-origin authorize POST rejected\",\n 403,\n );\n }\n const params = await readOAuthParams(event);\n const state = params.state;\n const clientId = params.client_id;\n const redirectUri = params.redirect_uri;\n const resource = params.resource || getMcpOAuthResource(event);\n const expectedResource = getMcpOAuthResource(event);\n\n if (params.response_type !== \"code\") {\n return oauthError(\n \"unsupported_response_type\",\n \"response_type must be code\",\n );\n }\n if (!clientId || !redirectUri || !resource || resource !== expectedResource) {\n return oauthError(\"invalid_request\", \"Invalid OAuth authorization request\");\n }\n if (params.code_challenge_method !== \"S256\" || !params.code_challenge) {\n return oauthError(\"invalid_request\", \"PKCE S256 is required\");\n }\n\n const client = await getOAuthClient(clientId);\n if (!client || !client.redirectUris.includes(redirectUri)) {\n return oauthError(\"invalid_client\", \"Unknown client or redirect_uri\");\n }\n\n const session = await getSession(event);\n if (!session?.email) {\n if (params.prompt === \"none\") {\n return redirectWithOAuthError({\n redirectUri,\n state,\n error: \"login_required\",\n });\n }\n const loginHtml = getConfiguredLoginHtml(event);\n return loginHtml\n ? html(loginHtml, 200)\n : oauthError(\"login_required\", \"Sign in required\", 401);\n }\n\n const scope = normalizeOAuthScope(params.scope);\n if (!scope) {\n return redirectWithOAuthError({\n redirectUri,\n state,\n error: \"invalid_scope\",\n });\n }\n if (method === \"GET\") {\n return html(\n renderConsentPage({\n appName: options.appName || options.appId || \"Agent Native\",\n email: session.email,\n clientName: client.clientName || client.clientId,\n scopes: scope.split(/\\s+/),\n fields: {\n response_type: \"code\",\n client_id: clientId,\n redirect_uri: redirectUri,\n resource,\n scope,\n state: state ?? \"\",\n code_challenge: params.code_challenge,\n code_challenge_method: \"S256\",\n consent_token: signConsentToken({\n email: session.email,\n clientId,\n redirectUri,\n resource,\n scope,\n codeChallenge: params.code_challenge,\n }),\n },\n }),\n );\n }\n\n if (\n !verifyConsentToken(params.consent_token, {\n email: session.email,\n clientId,\n redirectUri,\n resource,\n scope,\n codeChallenge: params.code_challenge,\n })\n ) {\n return oauthError(\"invalid_request\", \"Invalid authorization consent token\");\n }\n\n if (params.decision !== \"approve\") {\n return redirectWithOAuthError({\n redirectUri,\n state,\n error: \"access_denied\",\n });\n }\n\n const orgDomain = await resolveOrgDomain(session.orgId);\n const code = await createOAuthCode({\n clientId,\n redirectUri,\n codeChallenge: params.code_challenge,\n codeChallengeMethod: \"S256\",\n ownerEmail: session.email,\n orgId: session.orgId ?? null,\n orgDomain: orgDomain ?? null,\n scope,\n resource,\n });\n return redirectWithCode({ redirectUri, state, code: code.code });\n}\n\nasync function issueTokenSet(params: {\n ownerEmail: string;\n orgId?: string | null;\n orgDomain?: string | null;\n clientId: string;\n scope: string;\n resource: string;\n issuer: string;\n}): Promise<Record<string, unknown>> {\n const refreshToken = generateOpaqueToken();\n await createOAuthRefreshToken({\n refreshToken,\n clientId: params.clientId,\n ownerEmail: params.ownerEmail,\n orgId: params.orgId ?? null,\n orgDomain: params.orgDomain ?? null,\n scope: params.scope,\n resource: params.resource,\n });\n const accessToken = await signMcpOAuthAccessToken(params);\n return {\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: 3600,\n refresh_token: refreshToken,\n scope: params.scope,\n };\n}\n\nasync function handleAuthorizationCodeGrant(\n event: H3Event,\n body: Record<string, string>,\n): Promise<Response> {\n const code = body.code;\n const clientId = body.client_id;\n const redirectUri = body.redirect_uri;\n const verifier = body.code_verifier;\n if (!code || !clientId || !redirectUri || !isValidCodeVerifier(verifier)) {\n return oauthError(\"invalid_request\", \"Missing authorization-code fields\");\n }\n const row = await getOAuthCode(code);\n if (!row) return oauthError(\"invalid_grant\", \"Invalid or expired code\");\n if (row.clientId !== clientId || row.redirectUri !== redirectUri) {\n return oauthError(\"invalid_grant\", \"Code was issued to another client\");\n }\n const expectedChallenge = codeChallengeForVerifier(verifier);\n if (!safeEqual(expectedChallenge, row.codeChallenge)) {\n return oauthError(\"invalid_grant\", \"PKCE verification failed\");\n }\n const consumed = await consumeOAuthCode(code);\n if (!consumed) return oauthError(\"invalid_grant\", \"Invalid or expired code\");\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer)\n return oauthError(\"server_error\", \"Unable to derive issuer\", 500);\n return json(\n await issueTokenSet({\n ownerEmail: row.ownerEmail,\n orgId: row.orgId,\n orgDomain: row.orgDomain,\n clientId,\n scope: row.scope,\n resource: row.resource,\n issuer,\n }),\n );\n}\n\nasync function handleRefreshTokenGrant(\n event: H3Event,\n body: Record<string, string>,\n): Promise<Response> {\n const refreshToken = body.refresh_token;\n const clientId = body.client_id;\n if (!refreshToken) {\n return oauthError(\"invalid_request\", \"refresh_token is required\");\n }\n if (!clientId) {\n return oauthError(\"invalid_request\", \"client_id is required\");\n }\n const existing = await getOAuthRefreshToken(refreshToken);\n if (!existing) return oauthError(\"invalid_grant\", \"Invalid refresh token\");\n if (existing.clientId !== clientId) {\n return oauthError(\n \"invalid_grant\",\n \"Refresh token belongs to another client\",\n );\n }\n const nextRefreshToken = generateOpaqueToken();\n const row = await rotateOAuthRefreshToken({\n oldRefreshToken: refreshToken,\n newRefreshToken: nextRefreshToken,\n });\n if (!row) return oauthError(\"invalid_grant\", \"Invalid refresh token\");\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer)\n return oauthError(\"server_error\", \"Unable to derive issuer\", 500);\n const accessToken = await signMcpOAuthAccessToken({\n ownerEmail: row.ownerEmail,\n orgDomain: row.orgDomain,\n clientId: row.clientId,\n scope: row.scope,\n resource: row.resource,\n issuer,\n });\n return json({\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: 3600,\n refresh_token: nextRefreshToken,\n scope: row.scope,\n });\n}\n\nasync function handleToken(event: H3Event): Promise<Response> {\n if (getMethod(event) !== \"POST\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const body = await readOAuthParams(event);\n switch (body.grant_type) {\n case \"authorization_code\":\n return handleAuthorizationCodeGrant(event, body);\n case \"refresh_token\":\n return handleRefreshTokenGrant(event, body);\n default:\n return oauthError(\"unsupported_grant_type\", \"Unsupported grant_type\");\n }\n}\n\nexport async function handleMcpOAuth(\n event: H3Event,\n subpath: string,\n options: McpOAuthRouteOptions = {},\n): Promise<Response> {\n const path = subpath.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n try {\n if (path === \"authorize\") return await handleAuthorize(event, options);\n if (path === \"token\") return await handleToken(event);\n if (path === \"register\") return await handleRegister(event);\n setResponseStatus(event, 404);\n return json({ error: \"Not found\" }, 404);\n } catch (err: any) {\n return oauthError(\n \"server_error\",\n err?.message || \"OAuth request failed\",\n 500,\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"oauth-route.js","sourceRoot":"","sources":["../../src/mcp/oauth-route.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Standard remote MCP OAuth 2.1 endpoints.\n *\n * These routes let MCP hosts such as Claude Code and ChatGPT authenticate\n * through their native remote-MCP OAuth flow instead of pasting bearer tokens.\n * The issued access tokens are audience-bound to `/_agent-native/mcp`, carry\n * the same user/org identity as the existing connect flow, and are mediated by\n * `verifyAuth` before any MCP tool/resource request runs.\n */\n\nimport type { H3Event } from \"h3\";\nimport { getHeader, getMethod, getQuery, setResponseStatus } from \"h3\";\nimport { createHash, createHmac, timingSafeEqual } from \"node:crypto\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport { getConfiguredLoginHtml, getSession } from \"../server/auth.js\";\nimport { getAuthSecret } from \"../server/better-auth-instance.js\";\nimport { getOrgDomain } from \"../org/context.js\";\nimport {\n createOAuthCode,\n createOAuthRefreshToken,\n consumeOAuthCode,\n generateOpaqueToken,\n getOAuthClient,\n getOAuthCode,\n getOAuthRefreshToken,\n registerOAuthClient,\n rotateOAuthRefreshToken,\n} from \"./oauth-store.js\";\nimport {\n MCP_OAUTH_DEFAULT_SCOPE,\n MCP_OAUTH_SCOPES,\n normalizeOAuthScope,\n signMcpOAuthAccessToken,\n} from \"./oauth-token.js\";\n\nexport interface McpOAuthRouteOptions {\n appId?: string;\n appName?: string;\n}\n\nfunction json(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n \"Content-Type\": \"application/json\",\n \"Cache-Control\": \"no-store\",\n Pragma: \"no-cache\",\n },\n });\n}\n\nfunction html(body: string, status = 200): Response {\n return new Response(body, {\n status,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"Cache-Control\": \"no-store\",\n },\n });\n}\n\nfunction redirect(location: string): Response {\n return new Response(null, {\n status: 302,\n headers: { Location: location, \"Cache-Control\": \"no-store\" },\n });\n}\n\nfunction isSameOriginPost(event: H3Event): boolean {\n const origin = getHeader(event, \"origin\");\n if (!origin) return true;\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer) return false;\n try {\n return new URL(origin).origin === new URL(issuer).origin;\n } catch {\n return false;\n }\n}\n\nfunction oauthError(\n error: string,\n description: string,\n status = 400,\n): Response {\n return json({ error, error_description: description }, status);\n}\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\nfunction normalizeBasePath(raw: string | undefined): string {\n const trimmed = (raw ?? \"\").trim();\n if (!trimmed || trimmed === \"/\") return \"\";\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n return withSlash.replace(/\\/+$/, \"\");\n}\n\nfunction configuredBasePath(): string {\n return normalizeBasePath(\n process.env.APP_BASE_PATH || process.env.VITE_APP_BASE_PATH,\n );\n}\n\nfunction deriveOrigin(event: H3Event): string {\n const forwardedProto = getHeader(event, \"x-forwarded-proto\");\n const host = getHeader(event, \"x-forwarded-host\") || getHeader(event, \"host\");\n const proto =\n forwardedProto?.split(\",\")[0]?.trim() ||\n (host && /^(localhost|127\\.0\\.0\\.1|\\[::1\\])(:|$)/.test(host)\n ? \"http\"\n : \"https\");\n return host ? `${proto}://${host}` : \"\";\n}\n\nexport function getMcpOAuthIssuer(event: H3Event): string | undefined {\n const origin = deriveOrigin(event);\n if (!origin) return undefined;\n return `${origin}${configuredBasePath()}`;\n}\n\nexport function getMcpOAuthResource(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer) return undefined;\n return `${issuer}/_agent-native/mcp`;\n}\n\nexport function getMcpOAuthProtectedResourceMetadataUrl(\n event: H3Event,\n): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer) return undefined;\n return `${issuer}/.well-known/oauth-protected-resource`;\n}\n\nexport function buildMcpOAuthChallenge(event: H3Event): string {\n const metadata = getMcpOAuthProtectedResourceMetadataUrl(event);\n const scope = MCP_OAUTH_DEFAULT_SCOPE;\n return metadata\n ? `Bearer resource_metadata=\"${metadata}\", scope=\"${scope}\"`\n : `Bearer scope=\"${scope}\"`;\n}\n\nfunction authorizationEndpoint(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n return issuer ? `${issuer}/_agent-native/mcp/oauth/authorize` : undefined;\n}\n\nfunction tokenEndpoint(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n return issuer ? `${issuer}/_agent-native/mcp/oauth/token` : undefined;\n}\n\nfunction registrationEndpoint(event: H3Event): string | undefined {\n const issuer = getMcpOAuthIssuer(event);\n return issuer ? `${issuer}/_agent-native/mcp/oauth/register` : undefined;\n}\n\nexport function handleMcpOAuthProtectedResourceMetadata(\n event: H3Event,\n): Response {\n if (getMethod(event) !== \"GET\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const resource = getMcpOAuthResource(event);\n const issuer = getMcpOAuthIssuer(event);\n if (!resource || !issuer) {\n return oauthError(\"server_error\", \"Unable to derive MCP resource\", 500);\n }\n return json({\n resource,\n authorization_servers: [issuer],\n scopes_supported: MCP_OAUTH_SCOPES,\n resource_documentation: issuer,\n });\n}\n\nexport function handleMcpOAuthAuthorizationServerMetadata(\n event: H3Event,\n): Response {\n if (getMethod(event) !== \"GET\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const issuer = getMcpOAuthIssuer(event);\n const authorize = authorizationEndpoint(event);\n const token = tokenEndpoint(event);\n const register = registrationEndpoint(event);\n if (!issuer || !authorize || !token || !register) {\n return oauthError(\"server_error\", \"Unable to derive OAuth endpoints\", 500);\n }\n return json({\n issuer,\n authorization_endpoint: authorize,\n token_endpoint: token,\n registration_endpoint: register,\n response_types_supported: [\"code\"],\n grant_types_supported: [\"authorization_code\", \"refresh_token\"],\n code_challenge_methods_supported: [\"S256\"],\n token_endpoint_auth_methods_supported: [\"none\"],\n scopes_supported: MCP_OAUTH_SCOPES,\n });\n}\n\nfunction isAllowedRedirectUri(value: unknown): value is string {\n if (typeof value !== \"string\" || value.length > 2048) return false;\n try {\n const url = new URL(value);\n if (url.hash) return false;\n if (url.username || url.password) return false;\n if (url.protocol === \"https:\") return true;\n if (url.protocol !== \"http:\") return false;\n return (\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\" ||\n url.hostname === \"[::1]\"\n );\n } catch {\n return false;\n }\n}\n\nfunction parseStringArray(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === \"string\")\n : [];\n}\n\nasync function handleRegister(event: H3Event): Promise<Response> {\n if (getMethod(event) !== \"POST\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const body = ((await readBody(event).catch(() => ({}))) ?? {}) as Record<\n string,\n unknown\n >;\n const redirectUris = parseStringArray(body.redirect_uris);\n if (\n redirectUris.length === 0 ||\n redirectUris.length > 20 ||\n !redirectUris.every(isAllowedRedirectUri)\n ) {\n return oauthError(\n \"invalid_client_metadata\",\n \"redirect_uris must contain valid HTTPS or localhost callback URLs\",\n );\n }\n\n const grantTypes = parseStringArray(body.grant_types);\n if (\n grantTypes.length &&\n !grantTypes.every(\n (g) => g === \"authorization_code\" || g === \"refresh_token\",\n )\n ) {\n return oauthError(\"invalid_client_metadata\", \"Unsupported grant_type\");\n }\n const responseTypes = parseStringArray(body.response_types);\n if (responseTypes.length && !responseTypes.every((r) => r === \"code\")) {\n return oauthError(\"invalid_client_metadata\", \"Unsupported response_type\");\n }\n const method =\n typeof body.token_endpoint_auth_method === \"string\"\n ? body.token_endpoint_auth_method\n : \"none\";\n if (method !== \"none\") {\n return oauthError(\n \"invalid_client_metadata\",\n \"Only public OAuth clients are supported\",\n );\n }\n\n const clientName =\n typeof body.client_name === \"string\"\n ? body.client_name.trim().slice(0, 120)\n : null;\n let client;\n try {\n client = await registerOAuthClient({\n clientName,\n redirectUris: [...new Set(redirectUris)],\n grantTypes: grantTypes.length ? grantTypes : undefined,\n responseTypes: responseTypes.length ? responseTypes : undefined,\n tokenEndpointAuthMethod: method,\n });\n } catch (err: any) {\n if (err?.message === \"RATE_LIMITED\") {\n return oauthError(\"slow_down\", \"Too many client registrations\", 429);\n }\n throw err;\n }\n return json(\n {\n client_id: client.clientId,\n client_id_issued_at: Math.floor((client.createdAt ?? Date.now()) / 1000),\n client_name: client.clientName ?? undefined,\n redirect_uris: client.redirectUris,\n grant_types: client.grantTypes,\n response_types: client.responseTypes,\n token_endpoint_auth_method: client.tokenEndpointAuthMethod,\n },\n 201,\n );\n}\n\nfunction redirectWithOAuthError(params: {\n redirectUri: string;\n state?: string;\n error: string;\n description?: string;\n}): Response {\n const url = new URL(params.redirectUri);\n url.searchParams.set(\"error\", params.error);\n if (params.description) {\n url.searchParams.set(\"error_description\", params.description);\n }\n if (params.state) url.searchParams.set(\"state\", params.state);\n return redirect(url.toString());\n}\n\nfunction redirectWithCode(params: {\n redirectUri: string;\n code: string;\n state?: string;\n}): Response {\n const url = new URL(params.redirectUri);\n url.searchParams.set(\"code\", params.code);\n if (params.state) url.searchParams.set(\"state\", params.state);\n return redirect(url.toString());\n}\n\nfunction codeChallengeForVerifier(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction safeEqual(a: string, b: string): boolean {\n const aa = Buffer.from(a);\n const bb = Buffer.from(b);\n return aa.length === bb.length && timingSafeEqual(aa, bb);\n}\n\nfunction base64UrlEncode(value: Buffer | string): string {\n const buf = typeof value === \"string\" ? Buffer.from(value, \"utf8\") : value;\n return buf.toString(\"base64url\");\n}\n\nfunction base64UrlDecode(value: string): Buffer {\n return Buffer.from(value, \"base64url\");\n}\n\nfunction consentSigningKey(): string {\n return process.env.A2A_SECRET || getAuthSecret();\n}\n\nfunction consentPayload(params: {\n email: string;\n clientId: string;\n redirectUri: string;\n resource: string;\n scope: string;\n codeChallenge: string;\n}): string {\n return JSON.stringify({\n ...params,\n exp: Math.floor(Date.now() / 1000) + 10 * 60,\n });\n}\n\nfunction signConsentToken(params: {\n email: string;\n clientId: string;\n redirectUri: string;\n resource: string;\n scope: string;\n codeChallenge: string;\n}): string {\n const payload = base64UrlEncode(consentPayload(params));\n const sig = base64UrlEncode(\n createHmac(\"sha256\", consentSigningKey()).update(payload).digest(),\n );\n return `${payload}.${sig}`;\n}\n\nfunction verifyConsentToken(\n token: string | undefined,\n expected: {\n email: string;\n clientId: string;\n redirectUri: string;\n resource: string;\n scope: string;\n codeChallenge: string;\n },\n): boolean {\n if (!token || !token.includes(\".\")) return false;\n const [payload, sig] = token.split(\".\", 2);\n if (!payload || !sig) return false;\n const expectedSig = base64UrlEncode(\n createHmac(\"sha256\", consentSigningKey()).update(payload).digest(),\n );\n if (!safeEqual(sig, expectedSig)) return false;\n try {\n const parsed = JSON.parse(base64UrlDecode(payload).toString(\"utf8\"));\n return (\n parsed.email === expected.email &&\n parsed.clientId === expected.clientId &&\n parsed.redirectUri === expected.redirectUri &&\n parsed.resource === expected.resource &&\n parsed.scope === expected.scope &&\n parsed.codeChallenge === expected.codeChallenge &&\n typeof parsed.exp === \"number\" &&\n parsed.exp * 1000 >= Date.now()\n );\n } catch {\n return false;\n }\n}\n\nfunction isValidCodeVerifier(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length >= 43 &&\n value.length <= 128 &&\n /^[A-Za-z0-9._~-]+$/.test(value)\n );\n}\n\nfunction renderConsentPage(params: {\n appName: string;\n email: string;\n clientName: string;\n scopes: string[];\n fields: Record<string, string>;\n}): string {\n const hidden = Object.entries(params.fields)\n .map(\n ([key, value]) =>\n `<input type=\"hidden\" name=\"${escapeHtml(key)}\" value=\"${escapeHtml(value)}\">`,\n )\n .join(\"\\n\");\n const scopes = params.scopes\n .map((scope) => `<li><code>${escapeHtml(scope)}</code></li>`)\n .join(\"\");\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Authorize ${escapeHtml(params.appName)}</title>\n<style>\n :root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #09090b; color: #f4f4f5; }\n body { min-height: 100vh; display: grid; place-items: center; margin: 0; padding: 24px; }\n main { width: min(520px, 100%); border: 1px solid #27272a; border-radius: 8px; background: #111113; padding: 24px; box-shadow: 0 24px 80px rgba(0,0,0,.35); }\n h1 { font-size: 22px; line-height: 1.2; margin: 0 0 10px; }\n p { color: #a1a1aa; line-height: 1.5; margin: 0 0 18px; }\n ul { margin: 0 0 22px; padding-left: 22px; color: #d4d4d8; }\n code { color: #67e8f9; }\n .actions { display: flex; gap: 10px; justify-content: flex-end; }\n button { border: 0; border-radius: 6px; padding: 10px 14px; font-weight: 650; cursor: pointer; }\n .primary { background: #f4f4f5; color: #09090b; }\n .secondary { background: #27272a; color: #f4f4f5; }\n</style>\n</head>\n<body>\n<main>\n <h1>Authorize ${escapeHtml(params.clientName)}</h1>\n <p>${escapeHtml(params.appName)} will let this MCP client act as ${escapeHtml(params.email)} for these scopes:</p>\n <ul>${scopes}</ul>\n <form method=\"post\">\n ${hidden}\n <div class=\"actions\">\n <button class=\"secondary\" type=\"submit\" name=\"decision\" value=\"deny\">Deny</button>\n <button class=\"primary\" type=\"submit\" name=\"decision\" value=\"approve\">Authorize</button>\n </div>\n </form>\n</main>\n</body>\n</html>`;\n}\n\nasync function resolveOrgDomain(\n orgId: string | undefined,\n): Promise<string | undefined> {\n if (!orgId) return undefined;\n try {\n return (await getOrgDomain(orgId)) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function readOAuthParams(\n event: H3Event,\n): Promise<Record<string, string>> {\n if (getMethod(event) === \"GET\") {\n const query = getQuery(event);\n return Object.fromEntries(\n Object.entries(query).flatMap(([key, value]) =>\n typeof value === \"string\" ? [[key, value]] : [],\n ),\n );\n }\n const body = await readBody(event).catch(() => ({}));\n if (typeof body === \"string\") {\n return Object.fromEntries(new URLSearchParams(body));\n }\n if (body && typeof body === \"object\") {\n return Object.fromEntries(\n Object.entries(body as Record<string, unknown>).flatMap(([key, value]) =>\n typeof value === \"string\" ? [[key, value]] : [],\n ),\n );\n }\n return {};\n}\n\nasync function handleAuthorize(\n event: H3Event,\n options: McpOAuthRouteOptions,\n): Promise<Response> {\n const method = getMethod(event);\n if (method !== \"GET\" && method !== \"POST\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n if (method === \"POST\" && !isSameOriginPost(event)) {\n return oauthError(\n \"invalid_request\",\n \"Cross-origin authorize POST rejected\",\n 403,\n );\n }\n const params = await readOAuthParams(event);\n const state = params.state;\n const clientId = params.client_id;\n const redirectUri = params.redirect_uri;\n const resource = params.resource || getMcpOAuthResource(event);\n const expectedResource = getMcpOAuthResource(event);\n\n if (params.response_type !== \"code\") {\n return oauthError(\n \"unsupported_response_type\",\n \"response_type must be code\",\n );\n }\n if (!clientId || !redirectUri || !resource || resource !== expectedResource) {\n return oauthError(\"invalid_request\", \"Invalid OAuth authorization request\");\n }\n if (params.code_challenge_method !== \"S256\" || !params.code_challenge) {\n return oauthError(\"invalid_request\", \"PKCE S256 is required\");\n }\n\n const client = await getOAuthClient(clientId);\n if (!client || !client.redirectUris.includes(redirectUri)) {\n return oauthError(\"invalid_client\", \"Unknown client or redirect_uri\");\n }\n\n const session = await getSession(event);\n if (!session?.email) {\n if (params.prompt === \"none\") {\n return redirectWithOAuthError({\n redirectUri,\n state,\n error: \"login_required\",\n });\n }\n const loginHtml = getConfiguredLoginHtml(event);\n return loginHtml\n ? html(loginHtml, 200)\n : oauthError(\"login_required\", \"Sign in required\", 401);\n }\n\n const scope = normalizeOAuthScope(params.scope);\n if (!scope) {\n return redirectWithOAuthError({\n redirectUri,\n state,\n error: \"invalid_scope\",\n });\n }\n if (method === \"GET\") {\n return html(\n renderConsentPage({\n appName: options.appName || options.appId || \"Agent Native\",\n email: session.email,\n clientName: client.clientName || client.clientId,\n scopes: scope.split(/\\s+/),\n fields: {\n response_type: \"code\",\n client_id: clientId,\n redirect_uri: redirectUri,\n resource,\n scope,\n state: state ?? \"\",\n code_challenge: params.code_challenge,\n code_challenge_method: \"S256\",\n consent_token: signConsentToken({\n email: session.email,\n clientId,\n redirectUri,\n resource,\n scope,\n codeChallenge: params.code_challenge,\n }),\n },\n }),\n );\n }\n\n if (\n !verifyConsentToken(params.consent_token, {\n email: session.email,\n clientId,\n redirectUri,\n resource,\n scope,\n codeChallenge: params.code_challenge,\n })\n ) {\n return oauthError(\"invalid_request\", \"Invalid authorization consent token\");\n }\n\n if (params.decision !== \"approve\") {\n return redirectWithOAuthError({\n redirectUri,\n state,\n error: \"access_denied\",\n });\n }\n\n const orgDomain = await resolveOrgDomain(session.orgId);\n const code = await createOAuthCode({\n clientId,\n redirectUri,\n codeChallenge: params.code_challenge,\n codeChallengeMethod: \"S256\",\n ownerEmail: session.email,\n orgId: session.orgId ?? null,\n orgDomain: orgDomain ?? null,\n scope,\n resource,\n });\n return redirectWithCode({ redirectUri, state, code: code.code });\n}\n\nasync function issueTokenSet(params: {\n ownerEmail: string;\n orgId?: string | null;\n orgDomain?: string | null;\n clientId: string;\n scope: string;\n resource: string;\n issuer: string;\n}): Promise<Record<string, unknown>> {\n const refreshToken = generateOpaqueToken();\n await createOAuthRefreshToken({\n refreshToken,\n clientId: params.clientId,\n ownerEmail: params.ownerEmail,\n orgId: params.orgId ?? null,\n orgDomain: params.orgDomain ?? null,\n scope: params.scope,\n resource: params.resource,\n });\n const accessToken = await signMcpOAuthAccessToken(params);\n return {\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: 3600,\n refresh_token: refreshToken,\n scope: params.scope,\n };\n}\n\nasync function handleAuthorizationCodeGrant(\n event: H3Event,\n body: Record<string, string>,\n): Promise<Response> {\n const code = body.code;\n const clientId = body.client_id;\n const redirectUri = body.redirect_uri;\n const verifier = body.code_verifier;\n if (!code || !clientId || !redirectUri || !isValidCodeVerifier(verifier)) {\n return oauthError(\"invalid_request\", \"Missing authorization-code fields\");\n }\n const row = await getOAuthCode(code);\n if (!row) return oauthError(\"invalid_grant\", \"Invalid or expired code\");\n if (row.clientId !== clientId || row.redirectUri !== redirectUri) {\n return oauthError(\"invalid_grant\", \"Code was issued to another client\");\n }\n const expectedChallenge = codeChallengeForVerifier(verifier);\n if (!safeEqual(expectedChallenge, row.codeChallenge)) {\n return oauthError(\"invalid_grant\", \"PKCE verification failed\");\n }\n const consumed = await consumeOAuthCode(code);\n if (!consumed) return oauthError(\"invalid_grant\", \"Invalid or expired code\");\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer)\n return oauthError(\"server_error\", \"Unable to derive issuer\", 500);\n return json(\n await issueTokenSet({\n ownerEmail: row.ownerEmail,\n orgId: row.orgId,\n orgDomain: row.orgDomain,\n clientId,\n scope: row.scope,\n resource: row.resource,\n issuer,\n }),\n );\n}\n\nasync function handleRefreshTokenGrant(\n event: H3Event,\n body: Record<string, string>,\n): Promise<Response> {\n const refreshToken = body.refresh_token;\n const clientId = body.client_id;\n if (!refreshToken) {\n return oauthError(\"invalid_request\", \"refresh_token is required\");\n }\n if (!clientId) {\n return oauthError(\"invalid_request\", \"client_id is required\");\n }\n const existing = await getOAuthRefreshToken(refreshToken);\n if (!existing) return oauthError(\"invalid_grant\", \"Invalid refresh token\");\n if (existing.clientId !== clientId) {\n return oauthError(\n \"invalid_grant\",\n \"Refresh token belongs to another client\",\n );\n }\n const nextRefreshToken = generateOpaqueToken();\n const row = await rotateOAuthRefreshToken({\n oldRefreshToken: refreshToken,\n newRefreshToken: nextRefreshToken,\n });\n if (!row) return oauthError(\"invalid_grant\", \"Invalid refresh token\");\n const issuer = getMcpOAuthIssuer(event);\n if (!issuer)\n return oauthError(\"server_error\", \"Unable to derive issuer\", 500);\n const accessToken = await signMcpOAuthAccessToken({\n ownerEmail: row.ownerEmail,\n orgId: row.orgId,\n orgDomain: row.orgDomain,\n clientId: row.clientId,\n scope: row.scope,\n resource: row.resource,\n issuer,\n });\n return json({\n access_token: accessToken,\n token_type: \"Bearer\",\n expires_in: 3600,\n refresh_token: nextRefreshToken,\n scope: row.scope,\n });\n}\n\nasync function handleToken(event: H3Event): Promise<Response> {\n if (getMethod(event) !== \"POST\") {\n return oauthError(\"invalid_request\", \"Method not allowed\", 405);\n }\n const body = await readOAuthParams(event);\n switch (body.grant_type) {\n case \"authorization_code\":\n return handleAuthorizationCodeGrant(event, body);\n case \"refresh_token\":\n return handleRefreshTokenGrant(event, body);\n default:\n return oauthError(\"unsupported_grant_type\", \"Unsupported grant_type\");\n }\n}\n\nexport async function handleMcpOAuth(\n event: H3Event,\n subpath: string,\n options: McpOAuthRouteOptions = {},\n): Promise<Response> {\n const path = subpath.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n try {\n if (path === \"authorize\") return await handleAuthorize(event, options);\n if (path === \"token\") return await handleToken(event);\n if (path === \"register\") return await handleRegister(event);\n setResponseStatus(event, 404);\n return json({ error: \"Not found\" }, 404);\n } catch (err: any) {\n return oauthError(\n \"server_error\",\n err?.message || \"OAuth request failed\",\n 500,\n );\n }\n}\n"]}
|
|
@@ -2,6 +2,7 @@ export declare const MCP_OAUTH_SCOPES: readonly ["mcp:read", "mcp:write", "mcp:a
|
|
|
2
2
|
export declare const MCP_OAUTH_DEFAULT_SCOPE: string;
|
|
3
3
|
export interface McpOAuthAccessTokenClaims {
|
|
4
4
|
sub: string;
|
|
5
|
+
org_id?: string;
|
|
5
6
|
org_domain?: string;
|
|
6
7
|
scope: string;
|
|
7
8
|
client_id: string;
|
|
@@ -13,6 +14,7 @@ export declare function scopeList(scope: string | undefined): string[];
|
|
|
13
14
|
export declare function hasMcpOAuthScope(scopes: string[] | undefined, scope: (typeof MCP_OAUTH_SCOPES)[number]): boolean;
|
|
14
15
|
export declare function signMcpOAuthAccessToken(params: {
|
|
15
16
|
ownerEmail: string;
|
|
17
|
+
orgId?: string | null;
|
|
16
18
|
orgDomain?: string | null;
|
|
17
19
|
clientId: string;
|
|
18
20
|
scope: string;
|
|
@@ -21,6 +23,7 @@ export declare function signMcpOAuthAccessToken(params: {
|
|
|
21
23
|
}): Promise<string>;
|
|
22
24
|
export declare function verifyMcpOAuthAccessToken(token: string, resource: string | undefined): Promise<{
|
|
23
25
|
userEmail: string;
|
|
26
|
+
orgId?: string;
|
|
24
27
|
orgDomain?: string;
|
|
25
28
|
scopes: string[];
|
|
26
29
|
clientId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-token.d.ts","sourceRoot":"","sources":["../../src/mcp/oauth-token.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,gDAAiD,CAAC;AAE/E,eAAO,MAAM,uBAAuB,QAA6B,CAAC;AAElE,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,wBAAwB,CAAC;CAC/B;AAMD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAYjE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAK7D;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAC5B,KAAK,EAAE,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,GACvC,OAAO,CAGT;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"oauth-token.d.ts","sourceRoot":"","sources":["../../src/mcp/oauth-token.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,gDAAiD,CAAC;AAE/E,eAAO,MAAM,uBAAuB,QAA6B,CAAC;AAElE,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,wBAAwB,CAAC;CAC/B;AAMD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAYjE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAK7D;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAC5B,KAAK,EAAE,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,GACvC,OAAO,CAGT;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBlB;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAAC,CA4BR"}
|