mcp-csp-investigation ยท diff
git:20260404.bc2ce97 to git:20260507.313363d
253 added, 250 removed. Audit A to A.
- ---
- name: mcp-csp-investigation
- description: Comprehensive Content Security Policy audit for MCP Apps in sandboxed iframes. Discovers all network origins, traces them to source, and generates CSP configuration for registerAppResource.
- allowed-tools: Read, Bash, Glob, Grep
- ---
-
- # mcp-csp-investigation
-
- Perform exhaustive Content Security Policy audits for MCP Apps running in sandboxed iframes where all network requests fail SILENTLY without proper CSP declarations.
-
- ## Overview
-
- MCP Apps run in sandboxed iframes with no same-origin server. This means:
- - **ALL network requests fail silently** without CSP -- no errors, no warnings, just silent failure
- - Every external origin (CDN, API, font, image, WebSocket) must be declared in CSP
- - CSP is configured in the `contents[]` return from the `registerAppResource` read callback
- - Missing even ONE origin causes that resource to silently not load
-
- This skill provides a systematic methodology for discovering every network origin an app uses, tracing each to its source, and generating the correct CSP configuration.
-
- ## Capabilities
-
- ### Build Output Analysis
- - Build the application and capture all output files (HTML, CSS, JS, assets)
- - Search every file for network origin references
- - Identify fetch/XHR targets, script sources, link hrefs, image sources, font URLs, iframe sources, WebSocket endpoints
-
- ### Origin Tracing
- - Trace each discovered origin to its source in the codebase
- - Classify as: hardcoded constant, environment variable, or conditional logic
- - Document whether origin is universal, dev-only, or prod-only
- - Check third-party libraries for hidden network requests (analytics, telemetry, CDN fallbacks)
-
- ### CSP Domain Categorization
- - **resourceDomains**: Scripts, stylesheets, images, fonts (maps to `script-src`, `style-src`, `img-src`, `font-src`)
- - **connectDomains**: fetch/XHR targets, WebSocket endpoints (maps to `connect-src`)
- - **frameDomains**: Nested iframes (maps to `frame-src`)
-
- ### CSP Configuration Generation
- - Generate the CSP object for `registerAppResource` read callback
- - Handle environment-specific origins with proper conditional logic
- - Verify conditional origins have matching runtime URL and CSP entry
-
- ## Usage
-
- ### Step 1: Build the Application
-
- ```bash
- # Build to produce final output files
- npm run build
-
- # Identify all output files
- find dist/ -type f \( -name "*.html" -o -name "*.js" -o -name "*.css" \)
- ```
-
- ### Step 2: Search for ALL Network Origins
-
- ```bash
- # Search for URL patterns in build output
- grep -rEoh 'https?://[a-zA-Z0-9._-]+[a-zA-Z0-9._/-]*' dist/ | sort -u
-
- # Search for protocol-relative URLs
- grep -rEoh '//[a-zA-Z0-9._-]+\.[a-zA-Z]{2,}' dist/ | sort -u
-
- # Search for fetch/XHR patterns in source
- grep -rn 'fetch\|XMLHttpRequest\|axios\|\.get\(\|\.post\(' src/
-
- # Search for WebSocket connections
- grep -rn 'new WebSocket\|wss://\|ws://' src/
-
- # Search for dynamic imports and lazy loading
- grep -rn 'import(\|require(\|loadScript' src/
- ```
-
- ### Step 3: Trace Each Origin to Source
-
- For each discovered origin, determine:
-
- | Origin | Source Type | Environment | Category |
- |--------|-----------|-------------|----------|
- | `https://cdn.example.com` | Hardcoded constant | Universal | resourceDomains |
- | `https://api.example.com` | Environment variable (`API_URL`) | Conditional | connectDomains |
- | `https://fonts.googleapis.com` | Third-party library | Universal | resourceDomains |
- | `wss://realtime.example.com` | Conditional (feature flag) | Prod-only | connectDomains |
-
- ### Step 4: Check Third-Party Libraries
-
- ```bash
- # Check node_modules for hidden network requests
- grep -rn 'fetch\|XMLHttpRequest\|beacon\|sendBeacon' node_modules/<lib>/dist/ 2>/dev/null
-
- # Common hidden request sources:
- # - Analytics (Google Analytics, Segment, Mixpanel)
- # - Error tracking (Sentry, Bugsnag, Datadog)
- # - Font loading (Google Fonts, Adobe Fonts)
- # - CDN fallbacks (jQuery CDN, unpkg)
- # - Map tiles (Mapbox, Google Maps, Leaflet)
- ```
-
- ### Step 5: Generate CSP Configuration
-
- The CSP object goes in the `contents[]` return from `registerAppResource`:
-
- ```typescript
- import { registerAppResource, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps';
-
- registerAppResource(server, {
- uri: 'app:///my-app',
- name: 'My App',
- mimeType: RESOURCE_MIME_TYPE,
- async read() {
- return {
- contents: [{
- uri: 'app:///my-app',
- mimeType: RESOURCE_MIME_TYPE,
- text: bundledHtml,
- // CSP configuration goes HERE, in contents[]
- resourceDomains: [
- 'https://cdn.example.com',
- 'https://fonts.googleapis.com',
- 'https://fonts.gstatic.com',
- ],
- connectDomains: [
- 'https://api.example.com',
- ...(process.env.NODE_ENV === 'development'
- ? ['http://localhost:3000']
- : []),
- ],
- frameDomains: [],
- }],
- };
- },
- });
- ```
-
- ### Step 6: Verify Conditional Origins
-
- For every origin that depends on configuration or environment:
-
- ```typescript
- // WRONG: Runtime URL uses config but CSP doesn't include it
- const apiUrl = process.env.API_URL || 'https://api.example.com';
- fetch(apiUrl); // Works for default, fails for custom API_URL
-
- // RIGHT: Same config controls both runtime URL and CSP entry
- const apiUrl = process.env.API_URL || 'https://api.example.com';
- // In registerAppResource:
- connectDomains: [apiUrl], // CSP matches runtime URL
- ```
-
- ## Common Pitfalls
-
- 1. **Silent failures**: Missing CSP origin = resource silently doesn't load. No console error, no network error.
- 2. **CSP in wrong location**: CSP goes in `contents[]` from `registerAppResource` read callback, NOT in `_meta` on the tool.
- 3. **Forgetting localhost**: Even `http://localhost:3000` needs CSP in dev mode.
- 4. **Third-party surprises**: Libraries like Google Maps, Sentry, or analytics SDKs make network requests you may not be aware of.
- 5. **Conditional origins not in CSP**: If `API_URL` can be customized, the CSP must include whatever value it resolves to.
- 6. **Snake_case CSP properties**: Use camelCase (`resourceDomains`, `connectDomains`, `frameDomains`), NOT snake_case.
-
- ## Verification Checklist
-
- - [ ] Application built and all output files identified
- - [ ] Every network origin in build output discovered
- - [ ] Each origin traced to source (constant / env var / conditional)
- - [ ] Third-party libraries checked for hidden requests
- - [ ] Origins categorized (resourceDomains / connectDomains / frameDomains)
- - [ ] Environment annotations applied (universal / dev-only / prod-only)
- - [ ] CSP configuration placed in `contents[]` of `registerAppResource` read callback
- - [ ] Conditional origins verified: config controls both runtime URL and CSP entry
- - [ ] camelCase property names used (not snake_case)
-
- ## Task Definition
-
- ```javascript
- const mcpCspInvestigationTask = defineTask({
- name: 'mcp-csp-investigation',
- description: 'Perform CSP audit for MCP App in sandboxed iframe',
-
- inputs: {
- projectDir: { type: 'string', required: true },
- buildCommand: { type: 'string', default: 'npm run build' },
- outputDir: { type: 'string', default: 'dist' }
- },
-
- outputs: {
- resourceDomains: { type: 'array' },
- connectDomains: { type: 'array' },
- frameDomains: { type: 'array' },
- conditionalOrigins: { type: 'array' },
- artifacts: { type: 'array' }
- },
-
- async run(inputs, taskCtx) {
- return {
- kind: 'skill',
- title: `CSP investigation for ${inputs.projectDir}`,
- skill: {
- name: 'mcp-csp-investigation',
- context: {
- projectDir: inputs.projectDir,
- buildCommand: inputs.buildCommand,
- outputDir: inputs.outputDir,
- instructions: [
- 'Build the application',
- 'Search all output for network origins',
- 'Trace each origin to source',
- 'Check third-party libraries for hidden requests',
- 'Categorize domains (resource, connect, frame)',
- 'Generate CSP configuration for registerAppResource',
- 'Verify conditional origins have matching CSP entries'
- ]
- }
- },
- io: {
- inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
- outputJsonPath: `tasks/${taskCtx.effectId}/result.json`
- }
- };
- }
- });
- ```
-
- ## Applicable Processes
-
- - convert-web-app-to-mcp.js
- - migrate-openai-app-to-mcp.js
-
- ## External Dependencies
-
- - Build toolchain for the target application
- - grep/ripgrep for origin discovery
- - Access to third-party library source code or documentation
-
- ## References
-
- - [MCP Apps CSP/CORS Guide](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/csp-cors.md)
- - [MCP Apps Testing Guide](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/testing-mcp-apps.md)
- - [MDN Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- - [MCP Apps Patterns - CSP Section](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/patterns.md)
-
- ## Related Skills
-
- - mcp-app-verification
- - mcp-tool-resource-pattern
- - single-file-bundling
-
- ## Related Agents
-
- - csp-security-auditor
- - mcp-app-architect
+ ---
+ name: mcp-csp-investigation
+ description: Comprehensive Content Security Policy audit for MCP Apps in sandboxed iframes. Discovers all network origins, traces them to source, and generates CSP configuration for registerAppResource.
+ allowed-tools: Read, Bash, Glob, Grep
+ graph:
+ domains: [domain:software-engineering]
+
+ ---
+
+ # mcp-csp-investigation
+
+ Perform exhaustive Content Security Policy audits for MCP Apps running in sandboxed iframes where all network requests fail SILENTLY without proper CSP declarations.
+
+ ## Overview
+
+ MCP Apps run in sandboxed iframes with no same-origin server. This means:
+ - **ALL network requests fail silently** without CSP -- no errors, no warnings, just silent failure
+ - Every external origin (CDN, API, font, image, WebSocket) must be declared in CSP
+ - CSP is configured in the `contents[]` return from the `registerAppResource` read callback
+ - Missing even ONE origin causes that resource to silently not load
+
+ This skill provides a systematic methodology for discovering every network origin an app uses, tracing each to its source, and generating the correct CSP configuration.
+
+ ## Capabilities
+
+ ### Build Output Analysis
+ - Build the application and capture all output files (HTML, CSS, JS, assets)
+ - Search every file for network origin references
+ - Identify fetch/XHR targets, script sources, link hrefs, image sources, font URLs, iframe sources, WebSocket endpoints
+
+ ### Origin Tracing
+ - Trace each discovered origin to its source in the codebase
+ - Classify as: hardcoded constant, environment variable, or conditional logic
+ - Document whether origin is universal, dev-only, or prod-only
+ - Check third-party libraries for hidden network requests (analytics, telemetry, CDN fallbacks)
+
+ ### CSP Domain Categorization
+ - **resourceDomains**: Scripts, stylesheets, images, fonts (maps to `script-src`, `style-src`, `img-src`, `font-src`)
+ - **connectDomains**: fetch/XHR targets, WebSocket endpoints (maps to `connect-src`)
+ - **frameDomains**: Nested iframes (maps to `frame-src`)
+
+ ### CSP Configuration Generation
+ - Generate the CSP object for `registerAppResource` read callback
+ - Handle environment-specific origins with proper conditional logic
+ - Verify conditional origins have matching runtime URL and CSP entry
+
+ ## Usage
+
+ ### Step 1: Build the Application
+
+ ```bash
+ # Build to produce final output files
+ npm run build
+
+ # Identify all output files
+ find dist/ -type f \( -name "*.html" -o -name "*.js" -o -name "*.css" \)
+ ```
+
+ ### Step 2: Search for ALL Network Origins
+
+ ```bash
+ # Search for URL patterns in build output
+ grep -rEoh 'https?://[a-zA-Z0-9._-]+[a-zA-Z0-9._/-]*' dist/ | sort -u
+
+ # Search for protocol-relative URLs
+ grep -rEoh '//[a-zA-Z0-9._-]+\.[a-zA-Z]{2,}' dist/ | sort -u
+
+ # Search for fetch/XHR patterns in source
+ grep -rn 'fetch\|XMLHttpRequest\|axios\|\.get\(\|\.post\(' src/
+
+ # Search for WebSocket connections
+ grep -rn 'new WebSocket\|wss://\|ws://' src/
+
+ # Search for dynamic imports and lazy loading
+ grep -rn 'import(\|require(\|loadScript' src/
+ ```
+
+ ### Step 3: Trace Each Origin to Source
+
+ For each discovered origin, determine:
+
+ | Origin | Source Type | Environment | Category |
+ |--------|-----------|-------------|----------|
+ | `https://cdn.example.com` | Hardcoded constant | Universal | resourceDomains |
+ | `https://api.example.com` | Environment variable (`API_URL`) | Conditional | connectDomains |
+ | `https://fonts.googleapis.com` | Third-party library | Universal | resourceDomains |
+ | `wss://realtime.example.com` | Conditional (feature flag) | Prod-only | connectDomains |
+
+ ### Step 4: Check Third-Party Libraries
+
+ ```bash
+ # Check node_modules for hidden network requests
+ grep -rn 'fetch\|XMLHttpRequest\|beacon\|sendBeacon' node_modules/<lib>/dist/ 2>/dev/null
+
+ # Common hidden request sources:
+ # - Analytics (Google Analytics, Segment, Mixpanel)
+ # - Error tracking (Sentry, Bugsnag, Datadog)
+ # - Font loading (Google Fonts, Adobe Fonts)
+ # - CDN fallbacks (jQuery CDN, unpkg)
+ # - Map tiles (Mapbox, Google Maps, Leaflet)
+ ```
+
+ ### Step 5: Generate CSP Configuration
+
+ The CSP object goes in the `contents[]` return from `registerAppResource`:
+
+ ```typescript
+ import { registerAppResource, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps';
+
+ registerAppResource(server, {
+ uri: 'app:///my-app',
+ name: 'My App',
+ mimeType: RESOURCE_MIME_TYPE,
+ async read() {
+ return {
+ contents: [{
+ uri: 'app:///my-app',
+ mimeType: RESOURCE_MIME_TYPE,
+ text: bundledHtml,
+ // CSP configuration goes HERE, in contents[]
+ resourceDomains: [
+ 'https://cdn.example.com',
+ 'https://fonts.googleapis.com',
+ 'https://fonts.gstatic.com',
+ ],
+ connectDomains: [
+ 'https://api.example.com',
+ ...(process.env.NODE_ENV === 'development'
+ ? ['http://localhost:3000']
+ : []),
+ ],
+ frameDomains: [],
+ }],
+ };
+ },
+ });
+ ```
+
+ ### Step 6: Verify Conditional Origins
+
+ For every origin that depends on configuration or environment:
+
+ ```typescript
+ // WRONG: Runtime URL uses config but CSP doesn't include it
+ const apiUrl = process.env.API_URL || 'https://api.example.com';
+ fetch(apiUrl); // Works for default, fails for custom API_URL
+
+ // RIGHT: Same config controls both runtime URL and CSP entry
+ const apiUrl = process.env.API_URL || 'https://api.example.com';
+ // In registerAppResource:
+ connectDomains: [apiUrl], // CSP matches runtime URL
+ ```
+
+ ## Common Pitfalls
+
+ 1. **Silent failures**: Missing CSP origin = resource silently doesn't load. No console error, no network error.
+ 2. **CSP in wrong location**: CSP goes in `contents[]` from `registerAppResource` read callback, NOT in `_meta` on the tool.
+ 3. **Forgetting localhost**: Even `http://localhost:3000` needs CSP in dev mode.
+ 4. **Third-party surprises**: Libraries like Google Maps, Sentry, or analytics SDKs make network requests you may not be aware of.
+ 5. **Conditional origins not in CSP**: If `API_URL` can be customized, the CSP must include whatever value it resolves to.
+ 6. **Snake_case CSP properties**: Use camelCase (`resourceDomains`, `connectDomains`, `frameDomains`), NOT snake_case.
+
+ ## Verification Checklist
+
+ - [ ] Application built and all output files identified
+ - [ ] Every network origin in build output discovered
+ - [ ] Each origin traced to source (constant / env var / conditional)
+ - [ ] Third-party libraries checked for hidden requests
+ - [ ] Origins categorized (resourceDomains / connectDomains / frameDomains)
+ - [ ] Environment annotations applied (universal / dev-only / prod-only)
+ - [ ] CSP configuration placed in `contents[]` of `registerAppResource` read callback
+ - [ ] Conditional origins verified: config controls both runtime URL and CSP entry
+ - [ ] camelCase property names used (not snake_case)
+
+ ## Task Definition
+
+ ```javascript
+ const mcpCspInvestigationTask = defineTask({
+ name: 'mcp-csp-investigation',
+ description: 'Perform CSP audit for MCP App in sandboxed iframe',
+
+ inputs: {
+ projectDir: { type: 'string', required: true },
+ buildCommand: { type: 'string', default: 'npm run build' },
+ outputDir: { type: 'string', default: 'dist' }
+ },
+
+ outputs: {
+ resourceDomains: { type: 'array' },
+ connectDomains: { type: 'array' },
+ frameDomains: { type: 'array' },
+ conditionalOrigins: { type: 'array' },
+ artifacts: { type: 'array' }
+ },
+
+ async run(inputs, taskCtx) {
+ return {
+ kind: 'skill',
+ title: `CSP investigation for ${inputs.projectDir}`,
+ skill: {
+ name: 'mcp-csp-investigation',
+ context: {
+ projectDir: inputs.projectDir,
+ buildCommand: inputs.buildCommand,
+ outputDir: inputs.outputDir,
+ instructions: [
+ 'Build the application',
+ 'Search all output for network origins',
+ 'Trace each origin to source',
+ 'Check third-party libraries for hidden requests',
+ 'Categorize domains (resource, connect, frame)',
+ 'Generate CSP configuration for registerAppResource',
+ 'Verify conditional origins have matching CSP entries'
+ ]
+ }
+ },
+ io: {
+ inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
+ outputJsonPath: `tasks/${taskCtx.effectId}/result.json`
+ }
+ };
+ }
+ });
+ ```
+
+ ## Applicable Processes
+
+ - convert-web-app-to-mcp.js
+ - migrate-openai-app-to-mcp.js
+
+ ## External Dependencies
+
+ - Build toolchain for the target application
+ - grep/ripgrep for origin discovery
+ - Access to third-party library source code or documentation
+
+ ## References
+
+ - [MCP Apps CSP/CORS Guide](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/csp-cors.md)
+ - [MCP Apps Testing Guide](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/testing-mcp-apps.md)
+ - [MDN Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
+ - [MCP Apps Patterns - CSP Section](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/patterns.md)
+
+ ## Related Skills
+
+ - mcp-app-verification
+ - mcp-tool-resource-pattern
+ - single-file-bundling
+
+ ## Related Agents
+
+ - csp-security-auditor
+ - mcp-app-architect