vercel/ai · error · Error

Invalid MCP App resource URI: ${JSON.stringify(resourceUri)}

Error message

Invalid MCP App resource URI: ${JSON.stringify(resourceUri)}

What it means

Thrown by getMCPAppToolMeta when an MCP App tool declares a resourceUri (via ui metadata or the legacy _meta key) that is not a string or does not start with the required 'ui://' scheme. MCP Apps must point their UI resource at a ui:// URI, so the SDK validates the shape eagerly when reading tool metadata.

Source

Thrown at packages/mcp/src/tool/mcp-apps.ts:150

        (v): v is MCPAppToolVisibility => v === 'model' || v === 'app',
      )
    : undefined;
}

/**
 * Reads and validates MCP Apps metadata from a tool definition.
 */
export function getMCPAppToolMeta(
  tool: MCPAppToolLike,
): MCPAppToolMeta | undefined {
  const uiMeta = getToolUiMeta(tool._meta);
  const resourceUri =
    uiMeta?.resourceUri ?? tool._meta?.[MCP_APP_LEGACY_RESOURCE_URI_META_KEY];
  const visibility = parseVisibility(uiMeta?.visibility);

  if (resourceUri !== undefined) {
    if (typeof resourceUri !== 'string' || !resourceUri.startsWith('ui://')) {
      throw new Error(
        `Invalid MCP App resource URI: ${JSON.stringify(resourceUri)}`,
      );
    }
  } else if (uiMeta == null) {
    return undefined;
  }

  return {
    ...uiMeta,
    ...(resourceUri != null ? { resourceUri } : {}),
    ...(visibility != null ? { visibility } : {}),
  };
}

/**
 * Returns the `ui://` app resource URI attached to a tool, if present.
 */
export function getMCPAppResourceUri(tool: MCPAppToolLike): string | undefined {

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Change the tool's ui.resourceUri to a string beginning with 'ui://'.
  2. Serve the app HTML via the MCP server's resources/read (registered under the matching ui:// URI) rather than a plain HTTP URL.
  3. Remove resourceUri entirely if the tool has no MCP App UI, so metadata is treated as undefined.
  4. Update the MCP server to the current MCP Apps spec naming (ui metadata) and re-verify the emitted _meta.

Example fix

// before
{ _meta: { ui: { resourceUri: 'https://example.com/app.html' } } }
// after
{ _meta: { ui: { resourceUri: 'ui://my-server/app.html' } } }
Defensive patterns

Strategy: validation

Validate before calling

function isValidAppResourceUri(uri) {
  return typeof uri === 'string' && uri.startsWith('ui://');
}
const uiMeta = tool._meta?.ui;
const resourceUri = uiMeta?.resourceUri;
if (resourceUri !== undefined && !isValidAppResourceUri(resourceUri)) {
  throw new Error(`Fix tool manifest: resourceUri must be a 'ui://' string, got ${JSON.stringify(resourceUri)}`);
}

Type guard

function isValidAppResourceUri(uri: unknown): uri is string {
  return typeof uri === 'string' && uri.startsWith('ui://');
}

Try / catch

let meta;
try {
  meta = getMCPAppToolMeta(tool);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Invalid MCP App resource URI')) {
    console.error('Tool manifest violates MCP Apps spec:', e.message);
    meta = undefined; // treat as non-app tool or surface a config error
  } else { throw e; }
}

Prevention

When it happens

Trigger: Registering/reading an MCP tool whose ui.resourceUri (or legacy _meta uiResourceUri) is a non-string value, an http(s):// URL, or a malformed URI without the ui:// prefix; servers built against older or hand-rolled MCP App conventions.

Common situations: MCP servers migrated from pre-standard drafts that used different resource URI schemes; hand-written tool manifests with copy-pasted https:// links to HTML files; JSON configs where resourceUri is accidentally an object (hence JSON.stringify in the message).

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/1585d23570a8d032. Report an issue: GitHub.