| 1 | import type { |
| 2 | ToolActivityIcon, |
| 3 | ToolActivityNativeAppReference, |
| 4 | ToolActivitySource, |
| 5 | } from "@t3tools/contracts"; |
| 6 | import * as Crypto from "effect/Crypto"; |
| 7 | import * as Effect from "effect/Effect"; |
| 8 | import * as Hex from "effect/encoding/Hex"; |
| 9 | import type * as EffectCodexSchema from "effect-codex-app-server/schema"; |
| 10 | |
| 11 | import { |
| 12 | mcpToolPresentation as integrationToolPresentation, |
| 13 | normalizeMcpHttpUrl as normalizedHttpUrl, |
| 14 | normalizeMcpText as normalizedDisplayName, |
| 15 | } from "@t3tools/provider-core/server/mcpToolPresentation"; |
| 16 | |
| 17 | type CodexLifecycleItem = EffectCodexSchema.V2ItemCompletedNotification["item"]; |
| 18 | |
| 19 | function asUnknownRecord(value: unknown): Record<string, unknown> | undefined { |
| 20 | return value && typeof value === "object" ? (value as Record<string, unknown>) : undefined; |
| 21 | } |
| 22 | |
| 23 | function normalizedImageUrl(value: unknown): string | undefined { |
| 24 | if (typeof value !== "string" || value.length > 4096) return undefined; |
| 25 | try { |
| 26 | const url = new URL(value); |
| 27 | return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "data:" |
| 28 | ? url.href |
| 29 | : undefined; |
| 30 | } catch { |
| 31 | return undefined; |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | function normalizedAppId(value: unknown): string | undefined { |
| 36 | if (typeof value !== "string") return undefined; |
| 37 | const appId = value.trim(); |
| 38 | return appId.length > 0 && appId.length <= 512 && /^[A-Za-z0-9._-]+$/u.test(appId) |
| 39 | ? appId |
| 40 | : undefined; |
| 41 | } |
| 42 | |
| 43 | function normalizedSourceKeyPart(value: string): string { |
| 44 | return value.trim().toLowerCase(); |
| 45 | } |
| 46 | |
| 47 | const nativeAppSourceKey = Effect.fn("CodexToolPresentation.nativeAppSourceKey")(function* ( |
| 48 | appId: string, |
| 49 | ) { |
| 50 | const key = `native-app:${appId.toLowerCase()}`; |
| 51 | if (key.length <= 512) return key; |
| 52 | const crypto = yield* Crypto.Crypto; |
| 53 | const digest = Hex.encode( |
| 54 | yield* crypto.digest("SHA-256", new TextEncoder().encode(key)).pipe(Effect.orDie), |
| 55 | ); |
| 56 | return `${key.slice(0, 512 - digest.length - 1)}:${digest}`; |
| 57 | }); |
| 58 | |
| 59 | function browserDisplayName(value: unknown): string | undefined { |
| 60 | const normalized = normalizedDisplayName(value)?.toLowerCase(); |
| 61 | if (!normalized) return undefined; |
| 62 | if (normalized.includes("chrome") || normalized === "chromium") return "Chrome"; |
| 63 | if (normalized.includes("edge")) return "Microsoft Edge"; |
| 64 | if (normalized.includes("firefox")) return "Firefox"; |
| 65 | if (normalized.includes("safari")) return "Safari"; |
| 66 | if (normalized.includes("arc")) return "Arc"; |
| 67 | if (normalized === "iab" || normalized.includes("in-app")) return "Browser"; |
| 68 | return normalizedDisplayName(value); |
| 69 | } |
| 70 | |
| 71 | function browserNativeAppReference(name: string): ToolActivityNativeAppReference | undefined { |
| 72 | switch (name) { |
| 73 | case "Chrome": |
| 74 | return { _tag: "display-name", displayName: "Google Chrome" }; |
| 75 | case "Microsoft Edge": |
| 76 | case "Firefox": |
| 77 | case "Safari": |
| 78 | case "Arc": |
| 79 | return { _tag: "display-name", displayName: name }; |
| 80 | default: |
| 81 | return undefined; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | function appDisplayNameFromId(appId: string): string | undefined { |
| 86 | const knownNames: Readonly<Record<string, string>> = { |
| 87 | "com.apple.finder": "Finder", |
| 88 | "com.apple.safari": "Safari", |
| 89 | "com.google.chrome": "Chrome", |
| 90 | "com.microsoft.edgemac": "Microsoft Edge", |
| 91 | "org.mozilla.firefox": "Firefox", |
| 92 | "company.thebrowser.browser": "Arc", |
| 93 | }; |
| 94 | return knownNames[appId.toLowerCase()]; |
| 95 | } |
| 96 | |
| 97 | function nativeAppReference(value: unknown): ToolActivityNativeAppReference | undefined { |
| 98 | const app = asUnknownRecord(value); |
| 99 | if (app?.kind === "appId") { |
| 100 | const appId = normalizedAppId(app.appId); |
| 101 | return appId ? { _tag: "app-id", appId } : undefined; |
| 102 | } |
| 103 | if (app?.kind === "displayName") { |
| 104 | const displayName = normalizedDisplayName(app.displayName); |
| 105 | return displayName ? { _tag: "display-name", displayName } : undefined; |
| 106 | } |
| 107 | return undefined; |
| 108 | } |
| 109 | |
| 110 | function themedLogoIcon( |
| 111 | ...records: ReadonlyArray<Record<string, unknown> | undefined> |
| 112 | ): ToolActivityIcon | undefined { |
| 113 | for (const record of records) { |
| 114 | const logoUrl = normalizedImageUrl(record?.logoUrl); |
| 115 | if (!logoUrl) continue; |
| 116 | const logoUrlDark = normalizedImageUrl(record?.logoUrlDark ?? record?.logoDarkUrl); |
| 117 | return { |
| 118 | _tag: "themed-logo", |
| 119 | logoUrl, |
| 120 | ...(logoUrlDark ? { logoUrlDark } : {}), |
| 121 | }; |
| 122 | } |
| 123 | return undefined; |
| 124 | } |
| 125 | |
| 126 | export interface McpToolPresentation { |
| 127 | readonly title?: string; |
| 128 | readonly toolSurface?: "browser" | "computer"; |
| 129 | readonly toolIcon?: ToolActivityIcon; |
| 130 | readonly toolSource?: ToolActivitySource; |
| 131 | } |
| 132 | |
| 133 | export const mcpToolPresentation = Effect.fn("CodexToolPresentation.mcpToolPresentation")( |
| 134 | function* ( |
| 135 | item: Extract<CodexLifecycleItem, { readonly type: "mcpToolCall" }>, |
| 136 | ): Effect.fn.Return<McpToolPresentation, never, Crypto.Crypto> { |
| 137 | const result = asUnknownRecord(item.result); |
| 138 | const metadata = asUnknownRecord(result?._meta); |
| 139 | const surface = asUnknownRecord(metadata?.["codex/toolSurface"]); |
| 140 | const sourceMetadata = asUnknownRecord(metadata?.source); |
| 141 | const appContext = asUnknownRecord(item.appContext); |
| 142 | const sourceLogo = themedLogoIcon(surface, sourceMetadata, appContext); |
| 143 | if (surface?.kind === "browserUse") { |
| 144 | const screenshot = asUnknownRecord(surface.screenshot); |
| 145 | const browserUse = asUnknownRecord(metadata?.browser_use); |
| 146 | const openTabs = Array.isArray(surface.openTabs) ? surface.openTabs : []; |
| 147 | const latestOpenTab = openTabs |
| 148 | .toReversed() |
| 149 | .map(asUnknownRecord) |
| 150 | .find((tab) => normalizedHttpUrl(tab?.url) !== undefined); |
| 151 | const selectedPage = [ |
| 152 | { record: screenshot, url: screenshot?.pageUrl }, |
| 153 | { record: browserUse, url: browserUse?.url }, |
| 154 | { record: latestOpenTab, url: latestOpenTab?.url }, |
| 155 | ] |
| 156 | .map((candidate) => ({ ...candidate, pageUrl: normalizedHttpUrl(candidate.url) })) |
| 157 | .find((candidate) => candidate.pageUrl !== undefined); |
| 158 | const pageUrl = selectedPage?.pageUrl; |
| 159 | const faviconUrl = normalizedImageUrl( |
| 160 | selectedPage?.record?.faviconUrl ?? selectedPage?.record?.favIconUrl, |
| 161 | ); |
| 162 | const faviconUrlDark = normalizedImageUrl( |
| 163 | selectedPage?.record?.faviconUrlDark ?? selectedPage?.record?.favIconUrlDark, |
| 164 | ); |
| 165 | const name = |
| 166 | browserDisplayName(appContext?.appName) ?? |
| 167 | browserDisplayName(surface.browserFamily) ?? |
| 168 | browserDisplayName(surface.backend) ?? |
| 169 | "Browser"; |
| 170 | const nativeBrowserIcon = browserNativeAppReference(name); |
| 171 | const sourceIcon = |
| 172 | sourceLogo ?? |
| 173 | (nativeBrowserIcon ? ({ _tag: "native-app", app: nativeBrowserIcon } as const) : undefined); |
| 174 | const sourceKeyPart = normalizedSourceKeyPart(name) || "browser"; |
| 175 | return { |
| 176 | toolSurface: "browser", |
| 177 | ...(pageUrl |
| 178 | ? { |
| 179 | toolIcon: { |
| 180 | _tag: "website", |
| 181 | pageUrl, |
| 182 | ...(faviconUrl ? { faviconUrl } : {}), |
| 183 | ...(faviconUrlDark ? { faviconUrlDark } : {}), |
| 184 | } as const, |
| 185 | } |
| 186 | : {}), |
| 187 | toolSource: { |
| 188 | key: `browser-use:${sourceKeyPart}`, |
| 189 | name, |
| 190 | kind: name === "Browser" ? "browser" : "integration", |
| 191 | ...(sourceIcon ? { icon: sourceIcon } : {}), |
| 192 | }, |
| 193 | }; |
| 194 | } |
| 195 | if (surface?.kind === "computerUse") { |
| 196 | const app = nativeAppReference(surface.app); |
| 197 | const args = asUnknownRecord(item.arguments); |
| 198 | const argumentAppName = |
| 199 | normalizedDisplayName(args?.appName) ?? |
| 200 | normalizedDisplayName(args?.application) ?? |
| 201 | normalizedDisplayName(typeof args?.app === "string" ? args.app : undefined); |
| 202 | const name = |
| 203 | normalizedDisplayName(appContext?.appName) ?? |
| 204 | argumentAppName ?? |
| 205 | (app?._tag === "display-name" ? app.displayName : undefined) ?? |
| 206 | (app?._tag === "app-id" ? appDisplayNameFromId(app.appId) : undefined) ?? |
| 207 | "Computer Use"; |
| 208 | const sourceIcon = sourceLogo ?? (app ? ({ _tag: "native-app", app } as const) : undefined); |
| 209 | const sourceKey = app |
| 210 | ? app._tag === "app-id" |
| 211 | ? yield* nativeAppSourceKey(app.appId) |
| 212 | : `native-app-name:${normalizedSourceKeyPart(app.displayName)}` |
| 213 | : "computer-use"; |
| 214 | return { |
| 215 | toolSurface: "computer", |
| 216 | ...(app ? { toolIcon: { _tag: "native-app", app } as const } : {}), |
| 217 | toolSource: { |
| 218 | key: sourceKey, |
| 219 | name, |
| 220 | kind: "computer", |
| 221 | ...(sourceIcon ? { icon: sourceIcon } : {}), |
| 222 | }, |
| 223 | }; |
| 224 | } |
| 225 | |
| 226 | return integrationToolPresentation({ |
| 227 | serverName: appContext?.connectorId ?? item.server, |
| 228 | toolName: item.tool, |
| 229 | title: appContext?.actionName, |
| 230 | serverDisplayName: appContext?.appName, |
| 231 | source: sourceMetadata, |
| 232 | iconUrl: sourceLogo?._tag === "themed-logo" ? sourceLogo.logoUrl : undefined, |
| 233 | iconUrlDark: sourceLogo?._tag === "themed-logo" ? sourceLogo.logoUrlDark : undefined, |
| 234 | }); |
| 235 | }, |
| 236 | ); |