Skip to main content
Glama
dzhunka

mcp-app-boilerplate

by dzhunka

MCP App plugin boilerplate

A deployable starting point for an agent plugin whose core is an MCP App — a remote MCP server that ships an interactive HTML widget the host renders next to the tool result. One repo covers both halves:

Layer

Where

What it is

MCP App

app/mcp/route.ts + app/page.tsx

Next.js app serving a remote MCP server and the widget UI

Plugin package

plugin.json, mcp.json, skills/

Agent Plugins 1.0.0 package that installs into Cursor, Codex, and Claude Code

This is a GitHub template repository — click Use this template, or copy the directory, to start a project from it. Nothing here is bound to a particular deployment: mcp.json and .mcp.json ship with a replace-me.example.com placeholder so a project that forgets to set its own endpoint fails loudly instead of silently talking to someone else's server.

A reference deployment of this boilerplate runs at https://mcp-app-boilerplate.vercel.app (MCP endpoint at /mcp). It exists so pnpm test:client has something to check against; your project should point at its own.

How the MCP App works

MCP Apps is SEP-1865, stable since 2026-01-26 and identified as io.modelcontextprotocol/ui. Three things wire it together:

  1. A resource with a ui:// URI and MIME type text/html;profile=mcp-app returns the widget's HTML.

  2. A tool points at that resource through _meta.ui.resourceUri.

  3. The host renders the HTML in a sandboxed iframe and speaks MCP JSON-RPC to it over postMessage.

The trick that makes this pleasant to work on: the widget is an ordinary Next.js page, and the MCP route fetches its own rendered HTML to serve as the resource. You build the UI with the full framework instead of hand-writing an HTML string.

app/
  mcp/route.ts          MCP server — tools + the ui:// resource
  page.tsx              the widget (also viewable as a normal web page)
  about/page.tsx        proves client-side navigation survives the iframe
  hooks/use-mcp-app.ts  the host bridge: tool input, tool result, callTool
  layout.tsx            iframe hardening (asset URLs, history, fetch patches)
baseUrl.ts              resolves the public origin (local, tunnel, or Vercel)
proxy.ts                CORS headers so the iframe can load cross-origin assets
scripts/test-client.mjs  protocol smoke test
scripts/set-endpoint.mjs writes your deployment URL into both manifests

The two kinds of tool

The demo registers both, because real MCP Apps need both:

  • greet is model-visible. The agent calls it, and the host opens the widget.

  • set_tone is declared visibility: ["app"], so it is hidden from the model and exists only for the widget to call when the user clicks a button. The widget invokes it through callTool from useMcpApp().

Related MCP server: ContinueWith

Rename it for a new project

The boilerplate name survives in five places:

File

What to change

plugin.json

name, description

.claude-plugin/plugin.json

name, description

mcp.json and .mcp.json

the mcpServers key

skills/mcp-app-boilerplate/

directory name and the name frontmatter — they must match, or the skill is rejected

app/mcp/route.ts, package.json

serverInfo.name, name

The endpoint URL is the one thing you can't set until you've deployed, so leave it and run pnpm set-endpoint afterwards.

Build your own plugin

After that, the work happens in three places:

  1. app/mcp/route.ts — replace greet/set_tone with your tools. Bump UI_VERSION whenever the widget changes, or hosts will serve a cached copy.

  2. app/page.tsx — your widget. Read state from useMcpApp().

  3. skills/<name>/SKILL.md — teach agents when to call your tools. The description is all an agent sees before loading the file, so it has to name concrete triggers.

Develop

pnpm install
pnpm dev
pnpm test:client              # verifies the protocol against localhost:3000

pnpm test:client connects as a UI-capable client and asserts the parts that are easy to get subtly wrong: the extension capability, the ui:// metadata on the tool, the resource MIME type, and that the resource is real HTML.

To try it inside a real host while developing, expose a public HTTPS origin and tell the app about it, since hosts must be able to fetch the widget's assets:

cloudflared tunnel --url http://localhost:3000     # or ngrok
BASE_URL=https://your-tunnel.example pnpm dev

Deploy

vercel deploy --prod
pnpm set-endpoint https://your-project.vercel.app    # updates both manifests
pnpm test:client   https://your-project.vercel.app

baseUrl.ts derives the public origin from Vercel's environment variables, so the resource CSP and asset prefix resolve correctly with no configuration — the only URL you state by hand is the one in the manifests, and set-endpoint writes that.

Install the plugin

The plugin root is the repository root, and both manifests describe the same package; distribute it as a git repo. Run pnpm set-endpoint first — installing while the placeholder URL is still in place gives you a plugin whose MCP server never connects.

Cursor natively supports Agent Plugins. Add the repo through Customize → Plugins, which reads the root plugin.json.

Codex (0.147+) reads the portable root plugin.json and mcp.json:

codex plugin marketplace add <your-org>/<your-repo>
codex plugin add <plugin-name>@<marketplace>
codex plugin list --json

Claude Code does not yet parse the Agent Plugins $schema, so it reads the parallel .claude-plugin/plugin.json and .mcp.json committed here. Its skills/ discovery is the same directory, so the skill is shared:

claude plugin marketplace add <your-org>/<your-repo>

To connect only the MCP server without the plugin wrapper, any host that speaks Streamable HTTP can point at the endpoint directly:

claude mcp add --transport http my-plugin https://your-project.vercel.app/mcp

Stack

  • mcp-handler 2 — framework-agnostic MCP HTTP adapter, serves the 2026-07-28 protocol natively with a fallback for 2025-era Streamable HTTP clients

  • @modelcontextprotocol/server 2 and @modelcontextprotocol/ext-apps 2 — MCP SDK v2 plus the MCP Apps helpers (registerAppTool, registerAppResource)

  • Next.js 16 on Vercel Fluid Compute

Derived from vercel-labs/mcp-apps-nextjs-starter, upgraded to the v2 MCP stack and packaged as an Agent Plugin.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for coding agents (Cursor, Claude Code, Codex) to install and configure the ContinueWith AI handoff widget on any website.
    40 npm
    3
    MIT