> ## Documentation Index
> Fetch the complete documentation index at: https://docs.skybridge.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure CSP

> Let your views reach the domains they need

Views render in a sandboxed iframe under a strict [Content Security Policy](https://developer.mozilla.org/fr/docs/Web/HTTP/Guides/CSP): it blocks every cross-origin request the view hasn't declared. Your server's own origin is allowed automatically, so a view that only talks to its server needs no configuration. For anything else, list the origin on the [tool's config `view.csp`](http://localhost:3000/api-reference/register-tool#csp).

```ts server.ts theme={null}
server.registerTool(
  {
    name: "search-products",
    inputSchema: { query: z.string() },
    view: {
      component: "carousel",
      csp: {
        connectDomains: ["https://api.myshop.com"],
        resourceDomains: ["https://cdn.myshop.com"],
        frameDomains: ["https://www.youtube.com"],
        redirectDomains: ["https://checkout.myshop.com"],
      },
    },
  },
  async ({ query }) => {
    /* … */
  },
);
```

Each field maps to one kind of request the iframe would otherwise block. The sections below cover the four you'll reach for, what's allowed without any config, and how the policy differs per host.

## Call an External API

`connectDomains` lists the origins the view may reach with [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or [`XHR`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest). Without it, a direct request from the view is blocked.

```ts server.ts highlight={8} theme={null}
server.registerTool(
  {
    name: "search-products",
    inputSchema: { query: z.string() },
    view: {
      component: "carousel",
      csp: {
        connectDomains: ["https://api.myshop.com"],
        resourceDomains: ["https://cdn.myshop.com"],
        frameDomains: ["https://www.youtube.com"],
        redirectDomains: ["https://checkout.myshop.com"],
      },
    },
  },
  async ({ query }) => {
    /* … */
  },
);
```

The carousel checks live stock from `api.myshop.com`, so that origin is listed.

<Info>
  Your server's origin is already in `connectDomains`, so same-origin requests need no entry. In development, the dev server's [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) origin is added too, for hot reload.
</Info>

## Load External Assets

`resourceDomains` covers the static assets the browser loads from another origin: images, fonts, scripts, and stylesheets, whether from a CDN, a fonts provider, or any other host.

```ts server.ts highlight={9} theme={null}
server.registerTool(
  {
    name: "search-products",
    inputSchema: { query: z.string() },
    view: {
      component: "carousel",
      csp: {
        connectDomains: ["https://api.myshop.com"],
        resourceDomains: ["https://cdn.myshop.com"],
        frameDomains: ["https://www.youtube.com"],
        redirectDomains: ["https://checkout.myshop.com"],
      },
    },
  },
  async ({ query }) => {
    /* … */
  },
);
```

The carousel's product images come from `cdn.myshop.com`, so that origin has to be listed or they won't render.

<Info>
  Your server's origin is already in `resourceDomains`, so assets served from it need no entry.
</Info>

## Embed an Iframe

`frameDomains` lists the origins the view may embed in a nested iframe, like a map or a video player. Third-party iframes opt your app into stricter review when you submit it to a directory.

```ts server.ts highlight={10} theme={null}
server.registerTool(
  {
    name: "search-products",
    inputSchema: { query: z.string() },
    view: {
      component: "carousel",
      csp: {
        connectDomains: ["https://api.myshop.com"],
        resourceDomains: ["https://cdn.myshop.com"],
        frameDomains: ["https://www.youtube.com"],
        redirectDomains: ["https://checkout.myshop.com"],
      },
    },
  },
  async ({ query }) => {
    /* … */
  },
);
```

The carousel embeds a product video from `www.youtube.com`, so that origin is listed.

## Redirect Off-App

`redirectDomains` lists the origins [`useOpenExternal`](/api-reference/use-open-external) can send the user to without the host's safe-link confirmation modal.

```ts server.ts highlight={11} theme={null}
server.registerTool(
  {
    name: "search-products",
    inputSchema: { query: z.string() },
    view: {
      component: "carousel",
      csp: {
        connectDomains: ["https://api.myshop.com"],
        resourceDomains: ["https://cdn.myshop.com"],
        frameDomains: ["https://www.youtube.com"],
        redirectDomains: ["https://checkout.myshop.com"],
      },
    },
  },
  async ({ query }) => {
    /* … */
  },
);
```

The carousel hands off to `checkout.myshop.com` to take payment; listing it skips the interstitial on the way out.

## Verify Your CSP

To check your CSP, run your app in a real host through the [tunnel](/test/tunnel), or let the [Audit](/test/audit) verify it before you submit.

<Info>
  [DevTools](/test/devtools) runs a loose policy, so CSP violations don't surface locally: a view that loads in the emulator can still have its requests blocked in production.
</Info>

For the full field reference, see [registerTool](/api-reference/register-tool#csp).

## Go Further

<Columns cols={3}>
  <Card title="Create Views" icon="palette" href="/build/view">
    Craft interactive UIs rendered in conversation
  </Card>

  <Card title="UX Design" icon="sparkles" href="/guides/ux">
    Account for what makes an MCP App different
  </Card>

  <Card title="Audit" icon="clipboard-check" href="/test/audit">
    Validate before submission
  </Card>
</Columns>
