{"record":{"id":"fbbc8aa0159e94a1","repo":"different-ai/openwork","slug":"invalid-resource-uri","errorCode":"invalid_resource_uri","errorMessage":"MCP App resource URI must use ui://.","messagePattern":"MCP App resource URI must use ui://\\.","errorType":"error_code","errorClass":"McpAppHostError","httpStatus":null,"severity":"error","filePath":"apps/server/src/mcp-app-host.ts","lineNumber":90,"sourceCode":"  if (!isRecord(value)) return {};\n  return Object.fromEntries(\n    Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"),\n  );\n}\n\nexport function projectedMcpToolName(serverName: string, toolName: string): string {\n  const sanitize = (value: string) => value.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n  return `${sanitize(serverName)}_${sanitize(toolName)}`;\n}\n\nexport function toolUiResourceUri(tool: Partial<Tool>): string | null {\n  const meta = isRecord(tool._meta) ? tool._meta : {};\n  const ui = isRecord(meta.ui) ? meta.ui : {};\n  const nested = typeof ui.resourceUri === \"string\" ? ui.resourceUri : null;\n  const legacy = typeof meta[\"ui/resourceUri\"] === \"string\" ? meta[\"ui/resourceUri\"] : null;\n  const uri = nested ?? legacy;\n  if (!uri) return null;\n  if (!uri.startsWith(\"ui://\")) throw new McpAppHostError(\"invalid_resource_uri\", \"MCP App resource URI must use ui://.\");\n  return uri;\n}\n\nfunction safeDomain(value: unknown): string | null {\n  if (typeof value !== \"string\" || value.length > 2048) return null;\n  try {\n    const url = new URL(value);\n    if (url.username || url.password || url.pathname !== \"/\" || url.search || url.hash) return null;\n    if (url.protocol === \"https:\") return url.origin;\n    if (url.protocol === \"http:\" && isLoopbackHostname(url.hostname)) return url.origin;\n  } catch {\n    return null;\n  }\n  return null;\n}\n\nfunction isLoopbackHostname(hostname: string): boolean {\n  return hostname === \"127.0.0.1\" || hostname === \"localhost\" || hostname === \"[::1]\";","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/mcp-app-host.ts#L72-L108","documentation":"toolUiResourceUri reads an MCP App's declared UI resource URI from tool._meta (either meta.ui.resourceUri or legacy meta['ui/resourceUri']). If a URI is present but does not use the required ui:// scheme, the host rejects it because MCP Apps are only rendered from ui:// resources.","triggerScenarios":"Connecting to an MCP server whose tool metadata declares a resourceUri with http://, https://, file://, or any non-ui:// scheme; hand-written _meta with a typo like 'ui://' missing or a plain web URL.","commonSituations":"Server author pointed the app UI at a normal web page instead of packaging it as a ui:// MCP App resource; older or non-conforming MCP App tooling emitting legacy absolute URLs; malformed custom tool _meta.","solutions":["Fix the server's tool _meta so ui.resourceUri starts with ui:// (e.g. ui://my-app/index.html).","Serve the UI through the MCP server's resources (ui:// resource) rather than an external URL.","If the tool is not meant to be an MCP App, remove the resourceUri from _meta entirely so toolUiResourceUri returns null.","Update the MCP App SDK/server framework to a version that emits ui:// URIs."],"exampleFix":"// before\n'\"_meta\": { \"ui\": { \"resourceUri\": \"https://cdn.example.com/app.html\" } }'\n// after\n'\"_meta\": { \"ui\": { \"resourceUri\": \"ui://example-app/index.html\" } }'","handlingStrategy":"validation","validationCode":"function assertUiResourceUri(tool: { _meta?: unknown }): string | null {\n  const meta = (tool._meta ?? {}) as Record<string, unknown>\n  const ui = (meta.ui ?? {}) as Record<string, unknown>\n  const uri = typeof ui.resourceUri === 'string' ? ui.resourceUri : typeof meta['ui/resourceUri'] === 'string' ? (meta['ui/resourceUri'] as string) : null\n  if (uri && !uri.startsWith('ui://')) throw new Error(`resourceUri must start with ui://, got ${uri}`)\n  return uri\n}","typeGuard":"function hasValidUiUri(meta: unknown): meta is { ui: { resourceUri: `ui://${string}` } } {\n  const m = meta as Record<string, unknown>\n  const ui = m?.ui as Record<string, unknown> | undefined\n  const legacy = m?.['ui/resourceUri']\n  const uri = (ui?.resourceUri ?? legacy)\n  return typeof uri === 'string' && uri.startsWith('ui://')\n}","tryCatchPattern":"try {\n  const uri = host.toolUiResourceUri(tool)\n} catch (e) {\n  if (e instanceof McpAppHostError && e.code === 'invalid_resource_uri') {\n    skipAppRendering(tool.name); reportBadToolMeta(tool.name)\n  } else throw e\n}","preventionTips":["Emit ui.resourceUri with the ui:// scheme when authoring MCP App tools","Serve app UIs as MCP resources, never plain http(s) URLs in _meta","Test your server's tool metadata against an MCP App host before shipping","Prefer the nested meta.ui.resourceUri form over the legacy key"],"tags":["mcp","mcp-apps","uri-scheme","validation"],"backgroundTag":"invalid-resource-uri","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}