docs: add MCP Apps contributor skill - #81
Merged
Merged
Conversation
The Project Status app now uses the official @modelcontextprotocol/ext-apps SDK under the same strict CSP. The earlier "SDK breaks under strict CSP, use a hand-rolled bridge" guidance was wrong: the __require crash was a Bun --format=iife interop bug, fixed by bundling ESM and loading a module script. Update the wire-handshake description, the recipe (SDK App wiring, ESM bundle, bun install before build), the gotchas, and the verification ladder (frozen-lockfile install, iframe-hosted verify.mjs).
chicoxyzzy
force-pushed
the
docs/mcp-apps-skill
branch
from
July 9, 2026 17:11
3ed3fb2 to
745b452
Compare
chicoxyzzy
marked this pull request as ready for review
July 9, 2026 17:25
chicoxyzzy
added a commit
that referenced
this pull request
Jul 9, 2026
…s app (#80) * feat(mcp): add MCP Apps (ui://) extension foundation Add an MCP Apps (SEP-1865, io.modelcontextprotocol/ui) foundation so hosts can render an interactive HTML view for a tool result. - RegisterApps wires a ui:// resource provider/reader that serves app HTML with the text/html;profile=mcp-app mime type, and reactively declares the io.modelcontextprotocol/ui extension only when at least one app is registered. - uiAppMeta tags a tool descriptor with _meta.ui.resourceUri. - Add the embedded, self-contained Project Status view plus a bun build pipeline under internal/app/views; the view is committed to dist/ and embedded via go:embed, so runtime and go test need no JS toolchain. The view implements the MCP Apps postMessage bridge directly instead of the ext-apps SDK, which does not bundle to a self-contained browser IIFE and needs eval that the view's default-deny CSP forbids. * feat(mcp): add read-only Project Status app Attach the Project Status MCP Apps view to the projection tools so hosts that render apps can show project health, the operations brief, and activity. - Tag projects.health, projects.operations_brief, and projects.activity descriptors with _meta.ui.resourceUri pointing at the Project Status app. Adding the tag is descriptor-only: a regression test compares each tool's call result byte-for-byte (timestamps normalized) against an untagged reference server and asserts the results are unchanged. - The single stateless view detects which of the three structuredContent shapes it received and renders that section; action_kind/action_label hints render as inert labels (no tool calls in this batch). - Document MCP Apps authoring and the host-support caveat in the README and the agent host integration guide. * fix(mcp): render Project Status app per project to prevent view bleed The view accumulated tool results in unkeyed module state, so results for two different projects merged into one view. Key accumulated sections on project_id and reset every section when a result for a new project arrives, so one project's health/operations/activity can never bleed into another's. Complete the MCP Apps handshake in spec order: post ui/notifications/initialized only after the host answers ui/initialize. Extend the headless verify harness to answer ui/initialize, assert the readiness notification follows the response, and assert no cross-project bleed after switching project_id. Rebuild the embedded dist bundle. * ci(mcp): guard embedded views bundle freshness and pin bun A source edit under views/src that skipped `bun run build` would ship a stale embedded dist/ bundle undetected. Add a CI job that installs the pinned Bun, rebuilds the bundle, and fails via git diff --exit-code if the committed dist/ drifted from source. Pin the Bun version in .bun-version and package.json (packageManager / engines) for reproducible builds. Stop git-ignoring the lockfile so it is committed if a dependency is ever added; the build currently pulls in no third-party packages, so Bun writes no lockfile and CI needs no network beyond installing Bun itself. * test(mcp): assert projection tools against pre-app golden results The regression test compared each projection tool's call result against a freshly built untagged server, which is near-tautological: it only proves the app _meta tag does not change the result, not that the result body is stable. Replace it with committed golden fixtures capturing the exact text + structuredContent for a fresh project (timestamps and project id normalized). The tool bodies predate the app tag (PR only added descriptor _meta), so any future drift in a result body is now caught, not just the tag. Regenerate with -update. * docs(mcp): correct stale extension-negotiation comments The initialize and ClientCapabilities comments claimed Cairnline declares no extensions of its own; it now declares io.modelcontextprotocol/ui via DeclareExtension when an MCP Apps view is registered. Correct both. * feat(mcp): drive Project Status app with ext-apps SDK Replace the hand-rolled MCP Apps postMessage bridge with the official @modelcontextprotocol/ext-apps App. The earlier "__require is not defined" failure was a Bun IIFE-interop bug, not an SDK defect: bundling the SDK's mixed ESM/CJS graph to an IIFE emits an undefined __require reference. Building to ESM and loading it as an inline <script type="module"> removes that reference and runs under the unchanged strict CSP (default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline') with no unsafe-eval: the App constructor puts zod in jitless mode, so no eval/new Function path is taken at runtime. - add @modelcontextprotocol/ext-apps@1.7.4 as a pinned dependency with a committed bun.lock; the minified bundle is byte-reproducible under the pinned Bun + frozen lockfile - switch build.ts to --format=esm and guard against a stray second chunk - rebuild the embedded dist bundle; view rendering, per-project keying, and inert action labels are unchanged - run verify against a sandboxed iframe host, since the SDK App is a full JSON-RPC peer and cannot answer its own ui/initialize at top level - install views deps in the freshness CI job; update README and the host integration guide * docs: add MCP Apps contributor skill (#81) * docs: add MCP Apps contributor skill * docs: correct MCP Apps skill for ext-apps SDK adoption The Project Status app now uses the official @modelcontextprotocol/ext-apps SDK under the same strict CSP. The earlier "SDK breaks under strict CSP, use a hand-rolled bridge" guidance was wrong: the __require crash was a Bun --format=iife interop bug, fixed by bundling ESM and loading a module script. Update the wire-handshake description, the recipe (SDK App wiring, ESM bundle, bun install before build), the gotchas, and the verification ladder (frozen-lockfile install, iframe-hosted verify.mjs). * chore(mcp): ignore generated views verify screenshot * refactor(mcp): convert views verify script to typescript Rename verify/verify.mjs to verify/verify.ts and convert it to proper TypeScript: Playwright Browser/Page/Frame types, typed fixture payloads, and typed page-injected Window helpers. Behavior is unchanged — same CSP injection, handshake, tool-result delivery, section assertions, and the same default screenshot path (verify/project-status.png). Pin playwright, typescript, and @types/node as devDependencies (updating bun.lock), add a tsconfig.json that type-checks both src/ and verify/, and add a typecheck script. The verify script now runs via bun (bun verify/verify.ts).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before / After
Before. MCP Apps guidance for contributors was spread across the host-facing
docs/agent-host-integration.md, the view build notes ininternal/app/views/README.md, and the code comments. There was no single,task-shaped "how do I add or change a
ui://view without breaking the wirecontract or the CSP" reference, and
AGENTS.mdhad no skills index.After. Adds a contributor skill at
docs/skills/mcp-apps/SKILL.mdand aSkillspointer table inAGENTS.md. The skill is verified against the actualimplementation and covers:
io.modelcontextprotocol/uiextension(
UIExtensionID),ui://resources served astext/html;profile=mcp-app(
UIAppMimeType, no space after;),_meta.ui.resourceUritool tagging viauiAppMeta, the capability declared only when an app is registered (reactiveRegisterApps+capabilities()), and the view↔host handshake(
ui/initialize,ui/notifications/initialized,ui/notifications/tool-resultcarrying
structuredContent) as driven by the official ext-apps SDK.bridge with the official
@modelcontextprotocol/ext-appsApp, add it tobuild.ts(ESM bundle inlined into a<script type="module">), keep the strictdefault-deny CSP,
bun install+ build/commit the embedded bundle, register +tag in Go, and add the required tests.
__require is not definedcrash is a Bun
--format=iifeinterop bug, not an SDK defect — fixed by ESMoutput + a module script; the SDK runs clean under the strict, no-
unsafe-evalCSP because the
Appconstructor defaultsallowUnsafeEval:falseso zod runsjitless), never
innerHTMLrawstructuredContent, keep the tagged tool resultbyte-identical, keep the extension reactive, the exact mime-type string, and
uneven host support.
agent-host-integration.mdhost contract.go build/vet/test,go test -race,bun install+bun run build, and the headless Chromiumrender check via
verify/verify.mjs, which renders the view inside a sandboxediframe host).
Docs-only; no production code changes.
Stacking
This PR is stacked on #80 (
feat/mcp-apps-ui-extension), which introducesthe MCP Apps foundation and the Project Status app this skill documents. It is
based on that branch, not
main, because #80 is not yet merged. Rebase ontomainonce #80 lands.Verification
verification ladder mirrors the commands feat(mcp): add MCP Apps (ui://) extension and read-only Project Status app #80 uses.
Draft — not for merge yet.