different-ai/openwork · error · McpAppHostError
invalid_resource_csp
invalid_resource_csp
Error message
MCP App CSP domain lists must contain at most 16 origins.
What it means
domainList parses a CSP domain array from an MCP App's resource _meta and enforces a hard cap of 16 origins. If the value is not an array or exceeds 16 entries, the host throws invalid_resource_csp to bound the size of content-security-policy allow-lists it will render.
Source
Thrown at apps/server/src/mcp-app-host.ts:114
try {
const url = new URL(value);
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
if (url.protocol === "https:") return url.origin;
if (url.protocol === "http:" && isLoopbackHostname(url.hostname)) return url.origin;
} catch {
return null;
}
return null;
}
function isLoopbackHostname(hostname: string): boolean {
return hostname === "127.0.0.1" || hostname === "localhost" || hostname === "[::1]";
}
function domainList(value: unknown): string[] {
if (!Array.isArray(value) || value.length > 16) {
if (value === undefined) return [];
throw new McpAppHostError("invalid_resource_csp", "MCP App CSP domain lists must contain at most 16 origins.");
}
const domains = value.map(safeDomain);
if (domains.some((domain) => domain === null)) {
throw new McpAppHostError("invalid_resource_csp", "MCP App CSP domains must be HTTPS origins (or loopback HTTP origins).");
}
return Array.from(new Set(domains as string[]));
}
function resourcePresentationMeta(value: unknown): { csp: McpAppCsp; prefersBorder: boolean } {
const meta = isRecord(value) ? value : {};
const ui = isRecord(meta.ui) ? meta.ui : {};
const csp = isRecord(ui.csp) ? ui.csp : {};
const permissions = isRecord(ui.permissions) ? ui.permissions : {};
if (Object.keys(permissions).length > 0 || ui.domain !== undefined) {
throw new McpAppHostError(
"unsupported_resource_permissions",
"This OpenWork host slice does not grant device permissions or dedicated sandbox origins.",
);View on GitHub (pinned to 2b7df46e8a)
Solutions
- Trim the domains array in the app's _meta.ui.csp to 16 or fewer HTTPS origins.
- Consolidate origins (use a shared CDN domain or wildcard-free minimal set).
- Ensure ui.csp.domains is a JSON array of strings, not a string.
- Split UI across multiple resources if more than 16 origins are truly required.
Example fix
// before
'"csp": { "connectDomains": ["https://a.com","https://b.com", /* 20 more */] }'
// after
'"csp": { "connectDomains": ["https://api.example.com", "https://cdn.example.com"] }' Defensive patterns
Strategy: validation
Validate before calling
const domains = meta?.ui?.csp?.connectDomains
if (!Array.isArray(domains)) throw new Error('csp domains must be an array')
if (domains.length > 16) throw new Error(`max 16 origins, got ${domains.length}`) Type guard
function isCspDomainList(v: unknown): v is `https://${string}`[] {
return Array.isArray(v) && v.length <= 16 && v.every((d): d is `https://${string}` => typeof d === 'string' && d.startsWith('https://'))
} Try / catch
try {
const { csp } = resourcePresentationMeta(resource._meta)
} catch (e) {
if (e instanceof McpAppHostError && e.code === 'invalid_resource_csp') {
renderWithDefaultCsp(resource); warn('app CSP rejected, using defaults')
} else throw e
} Prevention
- Keep CSP domain lists small — under 16 origins per list
- Consolidate third-party origins onto one CDN
- Always emit arrays, never comma-joined strings, for csp domain fields
- Lint tool _meta CSP shape in CI
When it happens
Trigger: An MCP App resource declares ui.csp with a domains array (or non-array value) longer than 16 origins, or the field is present but not an array (e.g. a string).
Common situations: Server author allow-listing many CDN/API origins in one CSP entry; auto-generated CSP including every vendor domain; passing a comma-joined string instead of an array.
Related errors
- invalid_resource_uri
- OpenWork Connect MCP metadata is invalid.
- OpenWork-managed OAuth requires a remote MCP URL.
- extensions.add_mcp_url_required
- protocol "${url.protocol}" is not allowed
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/0771fd893b0205fd.
Report an issue: GitHub.