diff --git a/documentation/docs/tutorials/building-mcp-apps.md b/documentation/docs/tutorials/building-mcp-apps.md index 8780b2d59ab8..7bcbd7ca6600 100644 --- a/documentation/docs/tutorials/building-mcp-apps.md +++ b/documentation/docs/tutorials/building-mcp-apps.md @@ -169,6 +169,8 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => { csp: { connectDomains: [], resourceDomains: [], + frameDomains: [], + baseUriDomains: [], }, prefersBorder: true, }, @@ -532,6 +534,60 @@ Try: Your server returns a `ui://` resource URI, goose fetches the HTML and renders it in an iframe. The app communicates back via `postMessage`—requesting theme info, sending messages to chat, or resizing itself. -MCP Apps run sandboxed with CSP restrictions. See the [MCP Apps Specification](https://github.com/modelcontextprotocol/ext-apps) for details on security and the full protocol. +MCP Apps run in a sandboxed iframe with strict Content Security Policy restrictions. + +### Content Security Policy Configuration + +By default, apps can only load resources from their own origin. If your app needs to interact with external domains—such as loading resources from a CDN, making API calls, or embedding maps—you can configure which domains are allowed through the `csp` object in the resource's `_meta.ui` section. + +```javascript +_meta: { + ui: { + csp: { + connectDomains: [], // Domains for fetch/XHR requests + resourceDomains: [], // Domains for scripts, styles, images, fonts, media + frameDomains: [], // Origins allowed for nested iframes + baseUriDomains: [], // Additional allowed base URIs + }, + }, +} +``` + +| Option | CSP Directive | Purpose | Default | +|--------|---------------|---------|---------| +| `connectDomains` | `connect-src` | Domains your app can make network requests to | Same-origin only | +| `resourceDomains` | `script-src`, `style-src`, `img-src`, `font-src`, `media-src` | Domains for loading external resources | Same-origin only | +| `frameDomains` | `frame-src` | Origins allowed for nested `