Skip to content

Commit 59e01f4

Browse files
committed
refactor: pass permissions through loadSandboxProxy like csp
- Add permissions parameter to loadSandboxProxy in implementation.ts - Build iframe allow attribute from permissions instead of hardcoding - Update sandbox.ts to not hardcode allow attribute (set via notification) - Add microphone + clipboardWrite permissions to transcript-server resource - Update README to reflect permission configuration via resource _meta.ui
1 parent ffa43f5 commit 59e01f4

5 files changed

Lines changed: 33 additions & 9 deletions

File tree

examples/basic-host/src/implementation.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,34 @@ async function getUiResource(serverInfo: ServerInfo, uri: string): Promise<UiRes
119119
}
120120

121121

122+
// Build iframe allow attribute from permissions
123+
function buildAllowAttribute(permissions?: McpUiResourcePermissions): string {
124+
if (!permissions) return "";
125+
126+
const allowList: string[] = [];
127+
if (permissions.camera) allowList.push("camera");
128+
if (permissions.microphone) allowList.push("microphone");
129+
if (permissions.geolocation) allowList.push("geolocation");
130+
if (permissions.clipboardWrite) allowList.push("clipboard-write");
131+
132+
return allowList.join("; ");
133+
}
134+
122135
export function loadSandboxProxy(
123136
iframe: HTMLIFrameElement,
124137
csp?: McpUiResourceCsp,
138+
permissions?: McpUiResourcePermissions,
125139
): Promise<boolean> {
126140
// Prevent reload
127141
if (iframe.src) return Promise.resolve(false);
128142

129143
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
130-
// Allow microphone and clipboard for apps that need audio input or copy functionality
131-
iframe.setAttribute("allow", "microphone; clipboard-write");
144+
145+
// Set Permission Policy allow attribute based on requested permissions
146+
const allowAttribute = buildAllowAttribute(permissions);
147+
if (allowAttribute) {
148+
iframe.setAttribute("allow", allowAttribute);
149+
}
132150

133151
const readyNotification: McpUiSandboxProxyReadyNotification["method"] =
134152
"ui/notifications/sandbox-proxy-ready";

examples/basic-host/src/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,10 +264,10 @@ function AppIFramePanel({ toolCallInfo, isDestroying, onTeardownComplete }: AppI
264264
useEffect(() => {
265265
const iframe = iframeRef.current!;
266266

267-
// First get CSP from resource, then load sandbox with CSP in query param
268-
// This ensures CSP is set via HTTP headers (tamper-proof)
269-
toolCallInfo.appResourcePromise.then(({ csp }) => {
270-
loadSandboxProxy(iframe, csp).then((firstTime) => {
267+
// First get CSP and permissions from resource, then load sandbox
268+
// CSP is set via HTTP headers (tamper-proof), permissions via iframe allow attribute
269+
toolCallInfo.appResourcePromise.then(({ csp, permissions }) => {
270+
loadSandboxProxy(iframe, csp, permissions).then((firstTime) => {
271271
// The `firstTime` check guards against React Strict Mode's double
272272
// invocation (mount → unmount → remount simulation in development).
273273
// Outside of Strict Mode, this `useEffect` runs only once per

examples/basic-host/src/sandbox.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ try {
4343
const inner = document.createElement("iframe");
4444
inner.style = "width:100%; height:100%; border:none;";
4545
inner.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
46-
// Allow microphone and clipboard for apps that need audio input or copy functionality
47-
inner.setAttribute("allow", "microphone; clipboard-write");
46+
// Note: allow attribute is set later when receiving sandbox-resource-ready notification
47+
// based on the permissions requested by the app
4848
document.body.appendChild(inner);
4949

5050
const RESOURCE_READY_NOTIFICATION: McpUiSandboxResourceReadyNotification["method"] =

examples/transcript-server/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ transcript-server/
7676

7777
## Notes
7878

79-
- **Microphone Permission**: Requires `allow="microphone"` on the sandbox iframe (configured in `basic-host/src/sandbox.ts`)
79+
- **Microphone Permission**: Requires `allow="microphone"` on the sandbox iframe (configured via `permissions: { microphone: {} }` in the resource `_meta.ui`)
8080
- **Browser Support**: Web Speech API is well-supported in Chrome/Edge, with Safari support. Firefox has limited support.
8181
- **Continuous Mode**: Recognition automatically restarts when it ends, for seamless transcription
8282

examples/transcript-server/server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ export function createServer(): McpServer {
7070
uri: RESOURCE_URI,
7171
mimeType: RESOURCE_MIME_TYPE,
7272
text: html,
73+
_meta: {
74+
ui: {
75+
// Request microphone for Web Speech API, clipboard for copy button
76+
permissions: { microphone: {}, clipboardWrite: {} },
77+
},
78+
},
7379
},
7480
],
7581
};

0 commit comments

Comments
 (0)