Skip to content

Conversation

@hybrist
Copy link
Contributor

@hybrist hybrist commented Nov 26, 2025

This removes some of the workarounds to make the sandboxing work to allow for a more straight-forward vite dev experience when looking at the host.

Downside: It no longer inlines the sandbox script in sandbox.html for the output. But that seems less relevant for the host than it is for the tool UIs. It's likely possible to bring that back but for a simple demo it might be fine to leave as is?

@hybrist hybrist force-pushed the simpler-simple-host branch from fde9bf2 to 643fd8f Compare November 26, 2025 17:50
Copy link
Collaborator

@ochafik ochafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @hybrist, thanks for this, looks neater, but I wonder if there's a way to keep serving CSP dynamically?

Ideally would need to show how to pass down the various CSP settings defined in the resource's _meta.ui.csp and the only safe way to do so is as query params on the sandbox GET handler so they're returned as http headers.

I was incubating the following change for serve.ts:

// CSP for sandbox content, with customizable script-src and connect-src via query params
sandboxApp.get(["/", "/sandbox.html"], (req, res) => {
  // Allow customizing script-src and connect-src via query params
  const scriptSrc = req.query["script-src"] as string | undefined;
  const connectSrc = req.query["connect-src"] as string | undefined;

  // Build CSP with defaults, allowing query param overrides
  const csp = [
    "default-src 'self'",
    "img-src * data: blob: 'unsafe-inline'",
    "style-src * blob: data: 'unsafe-inline'",
    `script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' blob: data:${scriptSrc ? ` ${scriptSrc}` : ""}`,
    `connect-src 'self'${connectSrc ? ` ${connectSrc}` : " 'self'"}`,
    "font-src * blob: data:",
    "media-src * blob: data:",
    `frame-src 'self' blob: data:`,
    "base-uri 'self'",
  ].join("; ");

  res.setHeader("Content-Security-Policy", csp);
  res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  res.setHeader("Pragma", "no-cache");
  res.setHeader("Expires", "0");
  res.sendFile(join(DIRECTORY, "sandbox.html"));
});

ochafik added a commit that referenced this pull request Dec 1, 2025
Replace dual-server Express setup with Vite-only approach:
- Add vite-plugin-sandbox-csp for dynamic CSP headers via query params
  (supports script-src and connect-src customization for _meta.ui.csp)
- Use [::1] vs localhost for cross-origin sandbox isolation
- Simplify package.json scripts (dev, start, preview, build)
- Remove serve.ts, express, cors, concurrently, bun deps
- Add index.html redirect to example-host-react.html

Based on PR #33 approach but preserves dynamic CSP header capability.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Collaborator

@ochafik ochafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found a way to do the CSP as a vite plugin, will merge this PR then introduce the csp stuff.

@ochafik ochafik merged commit a7d73c7 into modelcontextprotocol:main Dec 1, 2025
1 check failed
@hybrist
Copy link
Contributor Author

hybrist commented Dec 1, 2025

Thanks! Sorry for the lack of follow-up. I think there's also some neat opportunity to leverage vite environments for the widget setup. But I still have to try it out to see if it makes things easier or just more convoluted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants