Add CSP and CORS section to patterns docs

Adds a new section to the patterns guide explaining how to configure
Content Security Policy and CORS for MCP Apps that make network requests:

- CSP via `_meta.ui.csp` controls browser-side permissions (`connectDomains`,
  `resourceDomains`)
- CORS via `_meta.ui.domain` provides a stable origin for API servers to
  allowlist

Includes a type-checked example showing `computeAppDomainForClaude()` helper
that hashes an MCP server URL to create a stable `*.claudemcpcontent.com`
subdomain. Also adds `@see` links to `McpUiResourceMeta` and `McpUiResourceCsp`
in `registerAppResource` JSDoc.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jonathan Hefner 2026-01-30 13:50:53 -06:00
commit 13b6d06399
No known key found for this signature in database
GPG key ID: 0DA40AFD21043691
2 changed files with 162 additions and 0 deletions

View file

@ -7,6 +7,7 @@
* @module
*/
import * as crypto from "node:crypto";
import * as fs from "node:fs/promises";
import type {
McpServer,
@ -198,6 +199,59 @@ function registerAppResource_withCsp(
//#endregion registerAppResource_withCsp
}
/**
* Example: registerAppResource with stable origin for external API CORS allowlists.
*/
function registerAppResource_withDomain(
server: McpServer,
dashboardHtml: string,
) {
//#region registerAppResource_withDomain
// Computes a stable origin from an MCP server URL for hosting in Claude.
function computeAppDomainForClaude(mcpServerUrl: string): string {
const hash = crypto
.createHash("sha256")
.update(mcpServerUrl)
.digest("hex")
.slice(0, 32);
return `${hash}.claudemcpcontent.com`;
}
const APP_DOMAIN = computeAppDomainForClaude("https://example.com/mcp");
registerAppResource(
server,
"Company Dashboard",
"ui://dashboard/view.html",
{
description: "Internal dashboard with company data",
},
async () => ({
contents: [
{
uri: "ui://dashboard/view.html",
mimeType: RESOURCE_MIME_TYPE,
text: dashboardHtml,
_meta: {
ui: {
// CSP: tell browser the app is allowed to make requests
csp: {
connectDomains: ["https://api.example.com"],
},
// CORS: give app a stable origin for the API server to allowlist
//
// (Public APIs that use `Access-Control-Allow-Origin: *` or API
// key auth don't need this.)
domain: APP_DOMAIN,
},
},
},
],
}),
);
//#endregion registerAppResource_withDomain
}
/**
* Example: Check for MCP Apps support in server initialization.
*/

View file

@ -34,6 +34,7 @@
import {
RESOURCE_URI_META_KEY,
RESOURCE_MIME_TYPE,
McpUiResourceCsp,
McpUiResourceMeta,
McpUiToolMeta,
McpUiClientCapabilities,
@ -298,6 +299,54 @@ export function registerAppTool<
* );
* ```
*
* @example With stable origin for external API CORS allowlists
* ```ts source="./index.examples.ts#registerAppResource_withDomain"
* // Computes a stable origin from an MCP server URL for hosting in Claude.
* function computeAppDomainForClaude(mcpServerUrl: string): string {
* const hash = crypto
* .createHash("sha256")
* .update(mcpServerUrl)
* .digest("hex")
* .slice(0, 32);
* return `${hash}.claudemcpcontent.com`;
* }
*
* const APP_DOMAIN = computeAppDomainForClaude("https://example.com/mcp");
*
* registerAppResource(
* server,
* "Company Dashboard",
* "ui://dashboard/view.html",
* {
* description: "Internal dashboard with company data",
* },
* async () => ({
* contents: [
* {
* uri: "ui://dashboard/view.html",
* mimeType: RESOURCE_MIME_TYPE,
* text: dashboardHtml,
* _meta: {
* ui: {
* // CSP: tell browser the app is allowed to make requests
* csp: {
* connectDomains: ["https://api.example.com"],
* },
* // CORS: give app a stable origin for the API server to allowlist
* //
* // (Public APIs that use `Access-Control-Allow-Origin: *` or API
* // key auth don't need this.)
* domain: APP_DOMAIN,
* },
* },
* },
* ],
* }),
* );
* ```
*
* @see {@link McpUiResourceMeta `McpUiResourceMeta`} for `_meta.ui` configuration options
* @see {@link McpUiResourceCsp `McpUiResourceCsp`} for CSP domain allowlist configuration
* @see {@link registerAppTool `registerAppTool`} to register tools that reference this resource
*/
export function registerAppResource(