Skip to content

feat(dev): add mcp-server-examples with the reference view and form flows - #349

Merged
dsklyar merged 10 commits into
mainfrom
danielsklyar/zel-8132-mcp-server-examples
Aug 7, 2026
Merged

dsklyar merged 10 commits into
mainfrom
danielsklyar/zel-8132-mcp-server-examples

Conversation

@dsklyar

@dsklyar dsklyar commented Aug 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds dev/mcp-server-examples, a private package that is the first real consumer of everything below it in this stack — and the thing that proves the render path actually works, since the pipeline and the serving code both landed with no view to exercise them.

Two worked examples:

  • example_hello_app takes all three paths: plain text for a scripted client, a form for an elicitation host, and the interactive hello view plus an app-only refresh tool for an MCP Apps host. The view covers what a static document cannot — React state, a tools/call round trip from inside the iframe, and re-rendering from the result the host pushes back.
  • example_elicitation is form collection on its own, and deliberately has no MCP App variant. Precedence is app, then form, then baseline, so a tool offering both resolves to its view on every host worth testing against — form-only is what keeps this flow reachable from a host that renders views instead of only from one that cannot. It covers every field shape the spec allows, including the titled oneOf select and anyOf multi-select that are the only nested structures permitted, and reports a distinct outcome for each way a request can end. Declining and cancelling stay separate because a refusal should not be retried while an abandoned dialog reasonably can be.

Why dev/ and not a published package

A view ships as one self-contained document — React, the view's CSS, and the design tokens all inlined — at roughly 550 KB. While this demo lived in @transcend-io/mcp-server-docs it was 94% of that package's published bytes, downloaded by everyone installing the umbrella server or the CLI, for a tool no end user has a reason to call. private: true in dev/ makes that structural instead of a rule someone has to remember, and it is why pnpm-workspace.yaml gains dev/* outside packages/.

Being the first package to inline a built view, it is also the first to need tsdown's .html text loader. Both settings live in tsdown.config.mcp.ts, which the MCP servers extend: rolldown's own sourcemapExcludeSources makes their maps reference sources instead of embedding them, taking the maps across those packages from ~817 kB to ~174 kB — a size win over the ordinary TypeScript these servers are built from, since no published server inlines a view today. Scoped there rather than in the base config so published libraries keep their embedded sources — and since Node reads a source from disk when the map omits it, a server debugged beside its own src still produces an identical stack trace.

Ticket: ZEL-8132. Ninth of eleven PRs splitting the MCP Apps branch. Carries a patch changeset for the eleven MCP servers, whose published sourcemaps change; the examples package itself is private and publishes nothing.

Two deviations from the split plan, worth knowing

  • scripts/mcp-app-views.test.ts moved to the next PR. It imports discoverMcpPackages, which is part of the Inspector lib rather than this package, so it cannot compile here.
  • src/apps/hello.ts inlines the built document directly rather than calling viewHtml(...). That helper arrives with the Inspector loop in the next PR, which switches this over.

Where this sits in the stack

Eleven layers, each targeting the one below it, registered as a GitHub stack — so this diff is only its own commits, and merging a lower layer rebases and retargets everything above it automatically.

  1. fix(design-tokens): resolve tokens.css from source inside the monorepo #341 — design-tokens: resolve tokens.css from source inside the monorepo
  2. feat(mcp-server-base): negotiate client capabilities from the handshake #342 — negotiate client capabilities from the handshake
  3. feat(mcp-server-base): attribute stdio sessions to the detected host #343 — attribute stdio sessions to the detected host
  4. feat(mcp): serve ui:// views and per-capability tool variants #344 — serve ui:// resources and per-capability tool variants
  5. feat(mcp-server-base): add a browser-only ./ui subpath with useMcpApp #345 — browser-only ./ui subpath with useMcpApp
  6. feat(mcp-server-base): publish the Tailwind view theme #346 — publish the Tailwind view theme
  7. feat(mcp): build MCP App views with React and Vite #347 — build views into one self-contained document with Vite
  8. fix(mcp): restore the Inspector's missing app sandbox document #348 — restore the Inspector's missing app sandbox document
  9. feat(dev): add mcp-server-examples with the reference view and form flows #349 — dev/mcp-server-examples, the first consumer of all of it ← this PR
  10. feat(mcp): add pnpm mcp:inspect with per-request dev view HTML #350 — pnpm mcp:inspect with per-request dev view HTML
  11. feat(mcp): scaffold a tool, an app, or a form with pnpm mcp:new #351 — pnpm mcp:new-view scaffolder

Depends on #347 for the view build and #344 for the serving code — this is the first consumer of both. Everything below that is ordering, not need.

Test plan

  • format, lint, typecheck, typecheck:ui, and the full test run are green; the examples package's two test files all run
  • A real host reads the built artifact. @modelcontextprotocol/inspector@2 --cli reports {"hasApp":true,"toolName":"example_hello_app","resourceUri":"ui://transcend-examples/hello","resourceMimeType":"text/html;profile=mcp-app"} and {"hasApp":false,"toolName":"example_elicitation"}, with the server logging host: mcp-inspector, capabilities: ["MCP_APP"]. resources/read returns the document, and the app-only refresh tool is correctly absent from tools/list.
  • Calling example_elicitation from that CLI returns "outcome": "unavailable" with "echo": "ping ping" — the baseline branch echoing the agent's own arguments rather than inventing any, since the CLI declares no elicitation support

Demo(s)

Running pnpm mcp:inspect

Screen.Recording.2026-08-02.at.23.14.23.mov

Running pnpm mcp:inspect --examles

Screen.Recording.2026-08-02.at.23.14.51.mov

@linear-code

linear-code Bot commented Aug 2, 2026

Copy link
Copy Markdown

ZEL-8132

@pkg-pr-new

pkg-pr-new Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@transcend-io/airgap.js-types

pnpm add https://pkg.pr.new/@transcend-io/airgap.js-types@349
yarn add https://pkg.pr.new/@transcend-io/airgap.js-types@349.tgz

@transcend-io/cli

pnpm add https://pkg.pr.new/@transcend-io/cli@349
yarn add https://pkg.pr.new/@transcend-io/cli@349.tgz

@transcend-io/design-tokens

pnpm add https://pkg.pr.new/@transcend-io/design-tokens@349
yarn add https://pkg.pr.new/@transcend-io/design-tokens@349.tgz

@transcend-io/internationalization

pnpm add https://pkg.pr.new/@transcend-io/internationalization@349
yarn add https://pkg.pr.new/@transcend-io/internationalization@349.tgz

@transcend-io/privacy-types

pnpm add https://pkg.pr.new/@transcend-io/privacy-types@349
yarn add https://pkg.pr.new/@transcend-io/privacy-types@349.tgz

@transcend-io/sdk

pnpm add https://pkg.pr.new/@transcend-io/sdk@349
yarn add https://pkg.pr.new/@transcend-io/sdk@349.tgz

@transcend-io/type-utils

pnpm add https://pkg.pr.new/@transcend-io/type-utils@349
yarn add https://pkg.pr.new/@transcend-io/type-utils@349.tgz

@transcend-io/utils

pnpm add https://pkg.pr.new/@transcend-io/utils@349
yarn add https://pkg.pr.new/@transcend-io/utils@349.tgz

@transcend-io/mcp

pnpm add https://pkg.pr.new/@transcend-io/mcp@349
yarn add https://pkg.pr.new/@transcend-io/mcp@349.tgz

@transcend-io/mcp-server-admin

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-admin@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-admin@349.tgz

@transcend-io/mcp-server-assessment

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-assessment@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-assessment@349.tgz

@transcend-io/mcp-server-base

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-base@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-base@349.tgz

@transcend-io/mcp-server-consent

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-consent@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-consent@349.tgz

@transcend-io/mcp-server-discovery

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-discovery@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-discovery@349.tgz

@transcend-io/mcp-server-docs

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-docs@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-docs@349.tgz

@transcend-io/mcp-server-dsr

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-dsr@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-dsr@349.tgz

@transcend-io/mcp-server-inventory

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-inventory@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-inventory@349.tgz

@transcend-io/mcp-server-preferences

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-preferences@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-preferences@349.tgz

@transcend-io/mcp-server-workflows

pnpm add https://pkg.pr.new/@transcend-io/mcp-server-workflows@349
yarn add https://pkg.pr.new/@transcend-io/mcp-server-workflows@349.tgz

commit: 3ee04af

@dsklyar
dsklyar force-pushed the danielsklyar/zel-8132-mcp-server-examples branch from 4d97e09 to 096123f Compare August 3, 2026 06:02
@dsklyar
dsklyar changed the base branch from danielsklyar/zel-8129-vite-view-build-pipeline to danielsklyar/zel-8113-inspector-sandbox-proxy August 3, 2026 06:03
@dsklyar
dsklyar force-pushed the danielsklyar/zel-8132-mcp-server-examples branch from 096123f to e96624f Compare August 3, 2026 23:47
@dsklyar
dsklyar force-pushed the danielsklyar/zel-8132-mcp-server-examples branch from e96624f to 51d5158 Compare August 4, 2026 20:24
@dsklyar
dsklyar force-pushed the danielsklyar/zel-8132-mcp-server-examples branch from 51d5158 to c001913 Compare August 5, 2026 22:33
giacaglia
giacaglia previously approved these changes Aug 5, 2026

@giacaglia giacaglia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good

import { HELLO_APP_RESOURCE } from '../apps/hello.js';

/** Prompt shown above the elicitation form. */
const HELLO_ELICIT_MESSAGE = 'Who should this greeting be addressed to?';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this standard?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

WDYM, this is an example for what configuration could look like.

@dawson-turechek-transcend dawson-turechek-transcend left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Love having these examples!

Base automatically changed from danielsklyar/zel-8113-inspector-sandbox-proxy to main August 6, 2026 22:21
@dsklyar
dsklyar requested a review from a team as a code owner August 6, 2026 22:21
@dsklyar
dsklyar force-pushed the danielsklyar/zel-8132-mcp-server-examples branch from c001913 to 06dcd2e Compare August 6, 2026 22:22
dsklyar added 9 commits August 7, 2026 10:11
Replaces the bespoke generateBundle plugin with rolldown's own
`sourcemapExcludeSources`, in a new `tsdown.config.mcp.ts` that the MCP
servers extend. The `.html` loader moves there too, since both settings
exist for the same fact: a prebuilt view becomes one large string literal
that a sourcemap would otherwise embed a second time.

Scoped to MCP servers rather than set in the base config, so published
libraries keep their embedded sources. Node reads sources from disk when
they are absent from a map, so a server debugged beside its own `src`
produces an identical stack trace. Maps across the MCP packages drop from
~817 KB to ~174 KB, and `tsdown.config.base.ts` returns to what main has.
The root config taught every package's tests to read `.html` as text, but the
only module-level `.html` import in the repo is a generated MCP App view. Move
the loader to `vitest.config.mcp.ts`, which MCP servers opt into through their
`test` script, and restore the root config to its `.svg`-only form.

Enforced rather than documented: a package that builds a view but tests on the
root config would fail only once a test imported that view, reporting invalid JS
syntax in an HTML file. The new convention test keys off `build:ui` so it also
covers `dev/*`, which the publishable-package conventions skip.
The tsdown rewiring touches eleven publishable packages and changes what
their published maps contain, so the release check is right to ask for one.
Removes the arbitrary color and length scan along with the README sentence
that promised it, leaving the styling rules as documented convention.
Review fixes for the examples server and the configs around it.

example_elicitation parsed a host's answer with the all-optional tool schema,
so `{ action: 'accept', content: {} }` reported `answered` with nothing in it —
indistinguishable from a filled-in form, in the one file whose purpose is
keeping those endings apart. Answers are now parsed against a schema that
requires what the form required, with the mask checked against REQUIRED_FIELDS
so the two cannot drift.

Also from review:
- Drop the README clause citing `scripts/mcp-app-views.test.ts`, which lands
  with the Inspector loop rather than here.
- Remove the examples `typecheck` -> `build` override. The `.html` ambient
  declaration is a wildcard, so tsc resolves it without the document on disk;
  only `test`, which reads the file, needs the build.
- Cover `dev/*` in `check:deps:root`, so a catalog drift there is caught.
- Reframe the sourcemap change as the size win it is. No published server
  inlines a view today, so a doubly-embedded view was never the cause.
- Name the command when a view has not been built, instead of a bare ENOENT.
Same facts, fewer lines: each block keeps the constraint a reader cannot get from
the code and drops the restatement around it. Net 62 comment lines.
The measured sizes and the "no published server inlines a view today" claim were
both true when written and wrong the moment a package changes. The durable reason
stays in the comment; the measurement belongs to the changeset, which describes a
single release.
Naming the examples package dates the comment as soon as another server inlines a
view, which is the point of putting the loader in a shared config.
@dsklyar
dsklyar added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 6d2b56d Aug 7, 2026
5 of 6 checks passed
@dsklyar
dsklyar deleted the danielsklyar/zel-8132-mcp-server-examples branch August 7, 2026 20:11
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.

3 participants