Comprehensive verification checklists for MCP Apps. Tests with basic-host reference, validates handler-before-connect, text fallback, resource URI linking, single-file bundling, host styling, CSP, and legacy pattern detection.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add a5c-ai/babysitter --skill mcp-app-verification --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Mcp App Verification?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/a5c-ai-mcp-app-verification)More formats (shields.io, HTML) on the badges page.
---
name: mcp-app-verification
description: Comprehensive verification checklists for MCP Apps. Tests with basic-host reference, validates handler-before-connect, text fallback, resource URI linking, single-file bundling, host styling, CSP, and legacy pattern detection.
allowed-tools: Read, Bash, Glob, Grep
graph:
domains: [domain:software-engineering]
specializations: [specialization:ai-agents-conversational]
skillAreas: [skill-area:mcp-server-implementation, skill-area:agent-simulation-testing]
roles: [role:backend-engineer, role:fullstack-engineer]
workflows: [workflow:feature-development]
topics: [topic:api-design]
---
# mcp-app-verification
Run comprehensive verification checklists for MCP Apps covering correctness, compatibility, and migration completeness.
## Overview
MCP Apps have several critical invariants that must be verified before deployment. This skill provides systematic verification across multiple dimensions:
1. **Runtime verification**: App loads and functions in basic-host reference
2. **Pattern verification**: Critical code patterns are correct (handler-before-connect, text fallback)
3. **Build verification**: Single-file bundle is valid and complete
4. **Styling verification**: Host theming applies correctly
5. **CSP verification**: All origins declared, no silent failures
6. **Migration verification**: No remaining legacy patterns (OpenAI, old MIME types, snake_case)
## Capabilities
### basic-host Test Execution
- Build the MCP App
- Start the server
- Launch basic-host reference implementation against the server
- Verify app loads without console errors
- Verify handlers fire correctly
### Handler-Before-Connect Invariant
- Search source code for `app.connect()` call
- Verify ALL handlers (`ontoolinput`, `ontoolresult`, `onhostcontextchanged`, `onteardown`) are registered BEFORE connect
- Flag violations -- handlers registered after connect will silently not work
### Text Fallback Verification
- Search all tool handlers for `content` array in return value
- Verify each tool returns at least one `{ type: 'text', text: '...' }` entry
- Flag tools that only return `structuredContent` without text fallback
### Resource URI Link Integrity
- Extract all `resourceUri` values from `registerAppTool` calls
- Extract all URIs from `registerAppResource` calls
- Verify every tool `resourceUri` has a matching registered resource
- Flag orphaned resources (registered but not referenced)
### Single-File Bundle Verification
- Build the project
- Verify `dist/mcp-app.html` (or equivalent) exists
- Check the HTML file is self-contained (no external `<script src>`, `<link href>`, `<img src>` to relative paths)
- Verify `vite-plugin-singlefile` is in dev dependencies
### Host Styling Verification
- Search CSS for `var(--color-*`, `var(--font-*`, `var(--border-radius-*` patterns
- Verify fallback values are present: `var(--color-background-primary, #ffffff)` not just `var(--color-background-primary)`
- Check `onhostcontextchanged` handler exists and applies styling
### CSP Verification
- Build and search output for network origins
- Compare against CSP configuration in `registerAppResource`
- Flag origins present in code but missing from CSP
- Verify conditional origins match between runtime and CSP config
### Legacy Pattern Detection (Migration)
- Search for remaining OpenAI patterns: `window.openai.toolInput`, `window.openai.toolOutput`, `window.openai`
- Search for old metadata paths: `openai/`
- Search for old MIME types: `text/html+skybridge`
- Search for hardcoded MIME type: `text/html;profile=mcp-app` (should use `RESOURCE_MIME_TYPE`)
- Search for snake_case CSP: `_domains"` or `_domains:` (should be camelCase)
## Usage
### Full Verification Workflow
```bash
# Step 1: Build the project
npm run build
# Step 2: Verify single-file bundle
ls -la dist/mcp-app.html
# Should be a single file with all assets inlined
# Step 3: Check for external references in bundle
grep -E '<script src="|<link.*href="|<img src="(?!data:)' dist/mcp-app.html
# Should return NOTHING (all assets inlined)
# Step 4: Start server
npm run serve &
SERVER_PID=$!
# Step 5: Test with basic-host
cd /tmp/mcp-ext-apps/examples/basic-host
SERVERS='["http://localhost:3001/mcp"]' npm run start
# Verify: app loads, handlers fire, styling applies
# Step 6: Stop server
kill $SERVER_PID
```
### Pattern Verification Commands
```bash
# Handler-before-connect check
# Find app.connect() and verify handlers are above it
grep -n 'app\.connect\|\.ontoolinput\|\.ontoolresult\|\.onhostcontextchanged\|\.onteardown' src/main.ts
# Text fallback check
# Every tool handler should return content array
grep -A5 'return {' src/server.ts | grep -c 'content:'
# Resource URI linking
grep 'resourceUri' src/server.ts
grep "registerAppResource" src/server.ts
# RESOURCE_MIME_TYPE usage (not hardcoded)
grep 'RESOURCE_MIME_TYPE' src/server.ts
grep "text/html;profile" src/server.ts # Should NOT match
# CSS variable fallbacks
grep -c 'var(--.*,' src/global.css # Count with fallbacks
grep 'var(--' src/global.css | grep -v ',' # Flag missing fallbacks
```
### Migration Verification (OpenAI -> MCP)
```bash
# Server-side legacy patterns
grep -rn 'openai/' src/ # Old metadata paths
grep -rn 'text/html+skybridge' src/ # Old MIME type
grep -rn "text/html;profile=mcp-app" src/ # Hardcoded (use RESOURCE_MIME_TYPE)
grep -rn '_domains"' src/ # Snake_case CSP
grep -rn "_domains:" src/ # Snake_case CSP
# Client-side legacy patterns
grep -rn 'window\.openai\.toolInput' src/
grep -rn 'window\.openai\.toolOutput' src/
grep -rn 'window\.openai' src/
# All should return ZERO matches
```
### Automated Verification Script
```bash
#!/bin/bash
# mcp-app-verify.sh - Comprehensive MCP App verification
ERRORS=0
echo "=== MCP App Verification ==="
# 1. Build
echo "[1/8] Building..."
npm run build 2>&1 || { echo "FAIL: Build failed"; ERRORS=$((ERRORS+1)); }
# 2. Single-file bundle
echo "[2/8] Checking single-file bundle..."
if [ ! -f dist/mcp-app.html ]; then
echo "FAIL: dist/mcp-app.html not found"
ERRORS=$((ERRORS+1))
fi
# 3. No external references
echo "[3/8] Checking for external references..."
EXT_REFS=$(grep -cE 'src="(?!data:)[^"]+"|href="(?!data:)[^"]+\.css"' dist/mcp-app.html 2>/dev/null || echo "0")
if [ "$EXT_REFS" -gt 0 ]; then
echo "WARN: Found $EXT_REFS potential external references"
fi
# 4. Handler-before-connect
echo "[4/8] Checking handler-before-connect..."
CONNECT_LINE=$(grep -n 'app\.connect()' src/main.ts* 2>/dev/null | head -1 | cut -d: -f2)
if [ -n "$CONNECT_LINE" ]; then
LATE_HANDLERS=$(grep -n '\.on\(toolinput\|toolresult\|hostcontextchanged\|teardown\)' src/main.ts* 2>/dev/null | awk -F: -v cl="$CONNECT_LINE" '$2 > cl')
if [ -n "$LATE_HANDLERS" ]; then
echo "FAIL: Handlers registered after app.connect()"
ERRORS=$((ERRORS+1))
fi
fi
# 5. Text fallback
echo "[5/8] Checking text fallback..."
# (manual review needed for complex cases)
# 6. RESOURCE_MIME_TYPE
echo "[6/8] Checking RESOURCE_MIME_TYPE usage..."
HARDCODED=$(grep -rn "text/html;profile=mcp-app" src/ 2>/dev/null | wc -l)
if [ "$HARDCODED" -gt 0 ]; then
echo "FAIL: Hardcoded MIME type found (use RESOURCE_MIME_TYPE)"
ERRORS=$((ERRORS+1))
fi
# 7. CSS fallbacks
echo "[7/8] Checking CSS variable fallbacks..."
NO_FALLBACK=$(grep 'var(--' src/*.css 2>/dev/null | grep -v ',' | wc -l)
if [ "$NO_FALLBACK" -gt 0 ]; then
echo "WARN: $NO_FALLBACK CSS variables without fallback values"
fi
# 8. Legacy patterns (migration)
echo "[8/8] Checking for legacy patterns..."
LEGACY=$(grep -rn 'window\.openai\|text/html+skybridge\|_domains"' src/ 2>/dev/null | wc -l)
if [ "$LEGACY" -gt 0 ]; then
echo "FAIL: $LEGACY legacy patterns found"
ERRORS=$((ERRORS+1))
fi
echo ""
if [ "$ERRORS" -eq 0 ]; then
echo "PASS: All verification checks passed"
else
echo "FAIL: $ERRORS verification errors found"
fi
exit $ERRORS
```
## Verification Checklist (Summary)
### Core Invariants
- [ ] App builds without errors
- [ ] `dist/mcp-app.html` exists and is self-contained single file
- [ ] `vite-plugin-singlefile` in devDependencies
- [ ] ALL handlers registered BEFORE `app.connect()`
- [ ] Every tool returns `content[]` with text fallback
- [ ] Every tool's `resourceUri` matches a registered resource
- [ ] `RESOURCE_MIME_TYPE` used (not hardcoded string)
### Styling
- [ ] CSS variables use `var(--name, fallback)` pattern
- [ ] `onhostcontextchanged` handler registered
- [ ] App looks correct with host styling (test in basic-host)
- [ ] App looks correct without host styling (standalone fallbacks)
### CSP (if applicable)
- [ ] All network origins discovered in build output
- [ ] All origins declared in `resourceDomains` / `connectDomains` / `frameDomains`
- [ ] CSP in correct location: `contents[]` of `registerAppResource` read callback
- [ ] Conditional origins: config controls both runtime URL and CSP entry
### Migration (if applicable)
- [ ] Zero `window.openai` references
- [ ] Zero `text/html+skybridge` references
- [ ] Zero `openai/` metadata paths
- [ ] Zero snake_case CSP properties (`_domains`)
- [ ] Zero hardcoded `text/html;profile=mcp-app` (use `RESOURCE_MIME_TYPE`)
### Runtime
- [ ] App loads in basic-host without console errors
- [ ] `ontoolinput` fires with tool arguments
- [ ] `ontoolresult` fires with tool result
- [ ] Host styling (theme, fonts, colors) applies correctly
- [ ] Teardown handler fires on app close
## Task Definition
```javascript
const mcpAppVerificationTask = defineTask({
name: 'mcp-app-verification',
description: 'Run comprehensive MCP App verification',
inputs: {
projectDir: { type: 'string', required: true },
isMigration: { type: 'boolean', default: false },
migrationSource: { type: 'string', default: '' },
checkCsp: { type: 'boolean', default: true }
},
outputs: {
passed: { type: 'boolean' },
errors: { type: 'array' },
warnings: { type: 'array' },
artifacts: { type: 'array' }
},
async run(inputs, taskCtx) {
return {
kind: 'skill',
title: `Verify MCP App: ${inputs.projectDir}`,
skill: {
name: 'mcp-app-verification',
context: {
projectDir: inputs.projectDir,
isMigration: inputs.isMigration,
migrationSource: inputs.migrationSource,
checkCsp: inputs.checkCsp,
instructions: [
'Build the application',
'Verify single-file bundle integrity',
'Check handler-before-connect invariant',
'Verify text fallback in all tools',
'Validate resource URI linking',
'Check CSS variable fallbacks',
inputs.checkCsp ? 'Verify CSP completeness' : null,
inputs.isMigration ? 'Search for legacy patterns' : null,
'Test with basic-host reference'
].filter(Boolean)
}
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/result.json`
}
};
}
});
```
## Applicable Processes
- create-mcp-app.js
- add-app-to-mcp-server.js
- convert-web-app-to-mcp.js
- migrate-openai-app-to-mcp.js
## External Dependencies
- basic-host reference implementation (from SDK repo clone)
- Build toolchain for the target application
- grep/ripgrep for pattern searching
## References
- [MCP Apps Testing Guide](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/testing-mcp-apps.md)
- [MCP Apps SDK - basic-host Example](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host)
- [MCP Apps Patterns](https://github.com/modelcontextprotocol/ext-apps/blob/main/docs/patterns.md)
## Related Skills
- mcp-tool-resource-pattern
- mcp-host-styling-integration
- mcp-csp-investigation
- single-file-bundling
- mcp-app-scaffolding
## Related Agents
- mcp-app-architect
- mcp-ui-developer
- csp-security-auditor
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!