Owns the product
Question and answer contract, API client, status wording, fonts, branding, attribution treatment, HTTP configuration and platform entry points.
diff --git a/apps/mcp/Dockerfile b/apps/mcp/Dockerfile index a6ff0511..e5227bee 100644 --- a/apps/mcp/Dockerfile +++ b/apps/mcp/Dockerfile @@ -5,8 +5,6 @@ RUN corepack enable && corepack prepare pnpm@10.33.0 --activate COPY package.json pnpm-lock.yaml pnpm-workspace.yaml LICENSE ./ COPY packages packages COPY apps/mcp/package.json apps/mcp/ -# Until SAGA 1.0.0-rc.1 is on the registry, the install resolves it from these private tarballs. -COPY apps/mcp/candidates apps/mcp/candidates RUN pnpm install --strict-peer-dependencies --frozen-lockfile --filter ansari-mcp... COPY apps/mcp apps/mcp RUN pnpm mcp build diff --git a/apps/mcp/README.md b/apps/mcp/README.md index a2d619fe..1de7f68e 100644 --- a/apps/mcp/README.md +++ b/apps/mcp/README.md @@ -32,15 +32,14 @@ User-provided capture of the Dev answer card in Claude, showing Arabic text, RTL ## Dependencies -SAGA is the 1.0 release candidate, `0.0.0-candidate.g8b4d987dd233`: the stack `@saga-a2ui/react-mcp-stack` plus its exact-version -siblings `@saga-a2ui/core`, `@saga-a2ui/components`, `@saga-a2ui/mcp` and `@saga-a2ui/react`, packed from the SDK's source at commit -`8b4d987` and committed under `candidates/` until `1.0.0-rc.1` is on the npm registry. The manifest pins that exact version; the root -`pnpm-workspace.yaml` overrides the five siblings to those tarballs, and the lockfile records their integrity. No private registry or -credential is involved. Moving to the registry release replaces the pin and the overrides and deletes `candidates/`. +SAGA is installed from the public npm registry at one exact release, `1.0.0-rc.1` (the `next` tag): the stack `@saga-a2ui/react-mcp-stack` +plus its exact-version siblings `@saga-a2ui/core`, `@saga-a2ui/components`, `@saga-a2ui/mcp` and `@saga-a2ui/react`. No private registry, +credential, SDK tarball, SDK workspace link or SDK-package override is involved, and the manifest pins an exact version rather than a tag or range. +The root `pnpm-workspace.yaml` keeps the SDK's documented `dompurify` and `@hono/node-server` overrides. The card's address names its compatibility line, `ui://views/answer-card.v1.html` (`VIEW_MAJOR` in `src/server/view-artifact.ts`), not its bytes: every compatible release is served at the same address, and only a change an already-loaded card cannot handle increases it. -`pnpm check:release --target beta` checks the pinned release against the registry and the lockfile, and refuses the candidate; `--target production` additionally refuses a -prerelease. The Beta and Production deploy scripts run the corresponding gate before any build or deployment, so neither deploys until the release is published. Dev accepts the candidate. +`pnpm check:release --target beta` checks the pinned release against the registry and the lockfile; `--target production` additionally refuses a +prerelease, so Production waits for the stable SAGA release. The Beta and Production deploy scripts run the corresponding gate before any build or deployment. The application depends on one SAGA package, the stack `@saga-a2ui/react-mcp-stack`, plus React and React DOM, and imports only its subpaths: `/faithful-card` (`FaithfulCardApp`, `startFaithfulCardApp`, and the definition's one `.result(...)` reader that reads the whole delivered result, diff --git a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-components-0.0.0-candidate.g8b4d987dd233.tgz b/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-components-0.0.0-candidate.g8b4d987dd233.tgz deleted file mode 100644 index 0a01a17a..00000000 Binary files a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-components-0.0.0-candidate.g8b4d987dd233.tgz and /dev/null differ diff --git a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-core-0.0.0-candidate.g8b4d987dd233.tgz b/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-core-0.0.0-candidate.g8b4d987dd233.tgz deleted file mode 100644 index 33512d44..00000000 Binary files a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-core-0.0.0-candidate.g8b4d987dd233.tgz and /dev/null differ diff --git a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-mcp-0.0.0-candidate.g8b4d987dd233.tgz b/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-mcp-0.0.0-candidate.g8b4d987dd233.tgz deleted file mode 100644 index 66bcad02..00000000 Binary files a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-mcp-0.0.0-candidate.g8b4d987dd233.tgz and /dev/null differ diff --git a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-react-0.0.0-candidate.g8b4d987dd233.tgz b/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-react-0.0.0-candidate.g8b4d987dd233.tgz deleted file mode 100644 index 038cb5a1..00000000 Binary files a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-react-0.0.0-candidate.g8b4d987dd233.tgz and /dev/null differ diff --git a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-react-mcp-stack-0.0.0-candidate.g8b4d987dd233.tgz b/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-react-mcp-stack-0.0.0-candidate.g8b4d987dd233.tgz deleted file mode 100644 index 36b64d67..00000000 Binary files a/apps/mcp/candidates/saga-0.0.0-candidate.g8b4d987dd233/saga-a2ui-react-mcp-stack-0.0.0-candidate.g8b4d987dd233.tgz and /dev/null differ diff --git a/apps/mcp/docs/show-me-ansari-mcp.html b/apps/mcp/docs/show-me-ansari-mcp.html index 13b7e95d..b88f22b3 100644 --- a/apps/mcp/docs/show-me-ansari-mcp.html +++ b/apps/mcp/docs/show-me-ansari-mcp.html @@ -42,21 +42,21 @@ @media(max-width:900px){.tabs{grid-template-columns:repeat(3,1fr)}.responsibility,.lifecards{grid-template-columns:1fr 1fr}.journey{grid-template-columns:220px 1fr}} @media(max-width:600px){.tabs{grid-template-columns:repeat(2,1fr);gap:7px}.tabs button{min-height:46px}.top{flex-wrap:wrap}.theme{margin-left:auto}.top .titleblock{order:2;width:100%}.top>.mark{width:46px}.journey{grid-template-columns:1fr}.minirow,.scopegrid,.responsibility,.lifecards,.sourceindex{grid-template-columns:1fr}.questionbar img{width:48px;height:48px}.steps{grid-template-columns:1fr 1fr}.step{font-size:12px;padding:8px;min-height:44px}.detailgrid{grid-template-columns:1fr}.groupbuttons{grid-template-columns:1fr 1fr}.map{grid-template-columns:1fr}.map>.arrow{display:none}.map>.mapbox:nth-child(5){grid-column:auto}.mapbox{margin-bottom:7px}.runtime-counter{min-width:0}.runtime-counter strong{font-size:23px}.source-exact{line-height:1.6}header,main,footer{padding-left:18px;padding-right:18px}pre{font-size:11px}.detail{padding:18px}} .whycards{grid-template-columns:repeat(3,1fr)}@media(max-width:850px){.whycards{grid-template-columns:1fr}}@media(min-width:951px){.tabs{grid-template-columns:repeat(7,minmax(0,1fr))}}
-From a question in a conversation to a complete, attributed answer in a branded card. Explore the whole application: its data flow, rendering contract, hosting boundaries and release lifecycle.
From a question in a conversation to a complete, attributed answer in a branded card. Explore the whole application: its data flow, rendering contract, hosting boundaries and release lifecycle.
The answer service owns the answer. Ansari owns the application. The SAGA stack connects that meaning to an MCP card.
The application supplies the presentation and every user-facing status sentence. The stack supplies the shared rendering and interaction mechanics.
Kitab Regular and Bold are embedded as bytes and loaded through FontFace. Font readiness has its own failure sentence; no device font is silently substituted for an answer.
readingProfile comes from the stack. Ansari maps host colors, reading tokens and direction without changing answer data or applying a second root scale.
The card renders the full answer once. Ansari watches the rendered closing block to style the attribution; the optional framework credit is separate.
The browser imports one SAGA stack. Hosting adapters inject their platform-specific inputs into one fetch application. Explore a module to see its real source and its boundary.
Question and answer contract, API client, status wording, fonts, branding, attribution treatment, HTTP configuration and platform entry points.
Host bridge, held/latest results, Markdown content, document policy, image consent, host links, own sizing and teardown. Server exports own view linkage; its fetch app owns /mcp admission, request budgets and lifetimes.
Tool selection, permissions, discovery/cache behavior, card sandbox and external-navigation experience. The app does not dictate host refresh timing.
The reusable object is the immutable card document. Each MCP request gets a fresh server and budget. Platform shutdown and iframe teardown are separate lifecycles.
This small model illustrates ownership; it does not send MCP traffic.
Ready. No active request.
The declared tool bound and each request budget agree. Closing its server releases the lifetime even if no transport was connected. The same lifetime signal aborts the upstream fetch and response-body read; remote backend processing may still continue.
SSE can carry client-requested progress. The operation still returns one complete answer. JSON response mode sends the final result; older-host compatibility can use SSE.
The card and server are built together. Node supplies a listener and filesystem access at startup; Workers supplies a fetch entry and bindings. Neither changes the answer contract.
@saga-a2ui/react-mcp-stack - 0.0.0-candidate.g8b4d987dd233 +- One card. Two hosting artifacts.
The card and server are built together. Node supplies a listener and filesystem access at startup; Workers supplies a fetch entry and bindings. Neither changes the answer contract.
Source + dependenciesInstall the public stack
@saga-a2ui/react-mcp-stack 1.0.0-rc.1 react 19.3.1 react-dom 19.3.1 -All five SDK siblings: same version - apps/mcp/candidates/ (SDK 8b4d987) -@a2ui/markdown-it override: 0.2.1The app imports only stack subpaths. The manifest pins the SAGA 1.0 release candidate, packed from the SDK’s source at commit
8b4d987and committed underapps/mcp/candidates/until1.0.0-rc.1is on the npm registry. The rootpnpm-workspace.yamloverrides the five siblings to those tarballs, and the root’s one committed lockfile records their integrity. No private registry or credential is involved.→Build-time content policyProduce self-contained HTML
Vite + single-file plugin +All five SDK siblings: 1.0.0-rc.1 +SDK overrides: dompurify 3.4.17 + @hono/node-server 2.1.5 +@a2ui/markdown-it override: 0.2.1The app imports only stack subpaths. SAGA is installed from the public npm registry at one exact release,
1.0.0-rc.1(thenexttag), and the repository root’s one committed lockfile records its registry integrity. No private registry, credential, SDK tarball, SDK workspace link or SDK-package override is involved. The rootpnpm-workspace.yamlkeeps the SDK’s documenteddompurifyand@hono/node-serveroverrides.→Build-time content policyProduce self-contained HTML
Vite + single-file plugin React + card code + CSS embedded Kitab + floral assets application + SDK identity @@ -81,10 +81,10 @@ pnpm --filter ansari-mcp verify # Uncached root-context container qualification -bash apps/mcp/scripts/verify-container.shThe root lockfile is authoritative. Frozen workspace installation and a root-context Docker smoke check protect packaging; they do not establish an actual host connection.
Setting Where it belongs Why it matters Upstream URL, client label, optional auth Node environment / Worker bindings Same client contract; secrets stay out of source. Allowed Host and Origin Named deployment environment Validation matches the selected public endpoint. Dev / Beta / Production Distinct native Wrangler environment configuration Select an explicit target and keep release/rollback decisions separate. Application, SDK and resource identity Built and served card metadata; a candidate SDK is data-sdk-kind="candidate"with its source commit and tarball sha256Inspect the card actually loaded by the host, rather than assuming its cache is current. +bash apps/mcp/scripts/verify-container.sh Four lifecycles meet at one card
A server release, a host catalog, a request and an embedded view each have a different owner and lifetime. Keeping them distinct explains refreshes, retries and teardown.
01 · Release Build & serve
Install exact packages, build artifacts, configure a named target, start the application and prepare immutable view bytes.
02 · Host catalog Discover & refresh
Register an MCP endpoint and discover its tool and UI association. After a release, verify the card the host loaded carries the current release.
03 · Request Invoke & finish
Send a question, call the API, return one full result and release the per-request server lifetime. Calls remain independent.
04 · View Mount & retire
Connect the bridge, wait for fonts, present the latest result and apply host context. Teardown releases owned browser resources.
What this walkthrough establishes
It explains checked application source, using real names and exact excerpts. The interactive diagrams and card examples are synthetic and make no network requests.
- Runtime and view composition are grounded in the complete application source of app
0.17.0as it is committed in this folder.- SAGA is the 1.0 release candidate
0.0.0-candidate.g8b4d987dd233, packed from SDK commit8b4d987and committed underapps/mcp/candidates/; the root lockfile records the tarballs’ integrity.- This document is a fresh explanation of the full architecture, not a summary of changes from an earlier version.
What must be verified in a host
- A real tool invocation and an actual embedded answer card.
- Matching application revision, SDK identity and served resource bytes.
- Fonts, language direction, attribution, sizing and the relevant consent interactions.
- First-load success distinguished from recovery after Retry.
Rendering inside ChatGPT, Claude and Alexa+ is a separate host acceptance. Neither this document nor this repository’s automated checks establish it.
Read the source offline
Each path links to its exact source commit on GitHub. The embedded source remains available offline through “Read embedded source”.
The root lockfile is authoritative. Frozen workspace installation and a root-context Docker smoke check protect packaging; they do not establish an actual host connection.
| Setting | Where it belongs | Why it matters |
|---|---|---|
| Upstream URL, client label, optional auth | Node environment / Worker bindings | Same client contract; secrets stay out of source. |
| Allowed Host and Origin | Named deployment environment | Validation matches the selected public endpoint. |
| Dev / Beta / Production | Distinct native Wrangler environment configuration | Select an explicit target and keep release/rollback decisions separate. |
| Application, SDK and resource identity | Built and served card metadata: data-sdk-kind="registry" and the SDK version 1.0.0-rc.1 | Inspect the card actually loaded by the host, rather than assuming its cache is current. |
A server release, a host catalog, a request and an embedded view each have a different owner and lifetime. Keeping them distinct explains refreshes, retries and teardown.
Install exact packages, build artifacts, configure a named target, start the application and prepare immutable view bytes.
Register an MCP endpoint and discover its tool and UI association. After a release, verify the card the host loaded carries the current release.
Send a question, call the API, return one full result and release the per-request server lifetime. Calls remain independent.
Connect the bridge, wait for fonts, present the latest result and apply host context. Teardown releases owned browser resources.
It explains checked application source, using real names and exact excerpts. The interactive diagrams and card examples are synthetic and make no network requests.
0.17.1 as it is committed in this folder.1.0.0-rc.1 is installed from the public npm registry as one exact release; the root lockfile records its registry integrity.Rendering inside ChatGPT, Claude and Alexa+ is a separate host acceptance. Neither this document nor this repository’s automated checks establish it.
Each path links to its exact source commit on GitHub. The embedded source remains available offline through “Read embedded source”.
Ansari could build this card directly on the MCP Apps SDK. Building on SAGA instead means Ansari starts from A2UI’s published protocol and a complete answer component, so its own code deals with answers and branding rather than MCP Apps wiring.
A2UI specifies how component properties bind to data-model paths and how model updates converge. SAGA uses the upstream A2UI processor and React surface rather than a second binding engine. Ansari’s card binds two fields: the contract version and the answer Markdown.
FaithfulCard renders the whole answer: nested lists, quotes, code, mixed-direction text, labelled scrollable tables, and links that open through the host with their hostname shown. It also handles answer replacement and view readiness. Ansari styles its documented surface with its own theme.css.
An MCP App has two ends of one iframe channel: the view’s App and the host’s AppBridge, with mirrored methods. A view can ask with requestTeardown(), but only the host’s AppBridge.teardownResource() tears the view down. The specification describes wire messages, with host and view rules side by side, and names neither class. AppBridge’s host role appears only in the overview diagram and its API reference, so a view author has to work out which half applies. Ansari’s source imports nothing from @modelcontextprotocol/ext-apps.
Rough estimate for the same card built on ext-apps, React and a maintained Markdown renderer:
Tests are not counted. No direct version has been built, so these figures are order-of-magnitude estimates, not measured savings.
In exchange, fixes to parsing, the card and delivery arrive from upstream with their regression tests (saga-a2ui, Apache-2.0).