Skip to content

feat(mcp)!: move to the SAGA 1.0 candidate, with the card at a stable v1 address - #12

Merged
timeleft-- merged 5 commits into
developfrom
claude/saga-1.0-rc-view-major
Nov 5, 2026
Merged

timeleft-- merged 5 commits into
developfrom
claude/saga-1.0-rc-view-major

Conversation

@timeleft--

Copy link
Copy Markdown
Member

Addresses Q1 of iaser-ai#335.

What changes

Card address. The card is served at ui://views/answer-card.v1.html, which names its compatibility line, not a hash of its bytes. Every compatible release keeps this address, including a release stamp or a card change. So a host that still holds a tool descriptor from before a deploy reads a card the server still serves. VIEW_MAJOR in src/server/view-artifact.ts goes up only for a change an already-loaded card can't handle.

SAGA 1.0 release candidate. The SDK is packed from its source at commit 8b4d987 and committed under apps/mcp/candidates/ until 1.0.0-rc.1 is on the npm registry.

  • The manifest pins the exact candidate version, and the root pnpm-workspace.yaml overrides the five siblings to those tarballs.
  • @modelcontextprotocol/server and client move to 2.4.0, hono to 4.13.16 and @hono/node-server to 2.1.5. The root overrides for dompurify and @hono/node-server follow the SDK's documentation; apps/mcp is their only consumer.
  • The Dev release gate accepts the candidate. Beta and Production refuse it, so neither deploys until the release is published.
  • The card's SDK identity names the candidate by its source commit and tarball digests.

Less SAGA glue. SAGA's createMcpFetchApp now serves /mcp: Host and Origin admission, body bounds, the request budget, lifetime attachment and the official stateless handler. Ansari keeps its own routes (/health and the Dev-only diagnostics beacon) and its logs. The server factory receives the request lifetime instead of building budgets, and the budget stays at the tool's 60 s bound. mcp.error carries SAGA's fixed failure kind.

Tests

  • Retired, because they only re-checked what SAGA tests itself: request-timer release when a server closes (two fixtures), and the malformed and oversize body refusals.
  • Kept, because they check Ansari's own wiring: the 60 s budget, deployment allowlists replacing localhost on both targets, the upstream abort on timeout, and the Node listener's SIGTERM shutdown.
  • The packaging test checks a candidate against the committed tarball bytes, and a registry release against its npm integrity.
  • Address tests expect the v1 address. A release changes the card's bytes, never its address.

Also: app version 0.17.0, the SAGA credit links to the SDK repository, and the README and walkthrough describe the above.

Verification

  • pnpm mcp verify passed: lint, typecheck, 145 tests and 78 browser checks.
  • Changing one byte of a committed tarball fails the lockfile-integrity and card-identity checks; restoring it passes them.
  • bash apps/mcp/scripts/verify-container.sh passed. The root-context Docker build installs from the committed tarballs, and the container serves ui://views/answer-card.v1.html.
  • pnpm check:release --target dev passes. --target beta refuses the candidate.

Before upstream

When 1.0.0-rc.1 is on the npm registry:

  1. Replace the candidate pin and the five SAGA overrides with the registry version.
  2. Delete apps/mcp/candidates/.
  3. Add the registry integrity to test/packaging.test.ts.

🤖 Generated with Claude Code

timeleft-- and others added 5 commits November 5, 2026 11:15
…candidate

The card's address now names its compatibility line, ui://views/answer-card.v1.html,
instead of a hash of its bytes. Every compatible release, including a release stamp
or a card change, is served at the same address, so a host that still holds a tool
descriptor from before a deploy reads a card that is served (iaser-ai#335 Q1).

SAGA moves to the 1.0 release candidate, packed from the SDK's source at 8b4d987
and committed under candidates/ until 1.0.0-rc.1 is on the npm registry. The
manifest pins the exact candidate version, and the root pnpm-workspace.yaml
overrides the five siblings to the tarballs. The MCP packages, hono and
@hono/node-server align with the SDK, with its documented dompurify and
@hono/node-server overrides.

- The packaging test checks a candidate against the committed tarball bytes,
  and a registry release against its npm integrity.
- The card's SDK identity names a candidate by its source commit and tarball digests.
- The Dev release gate accepts the candidate under apps/mcp/candidates; Beta and
  Production still refuse it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… duplicated tests

SAGA's fetch app now serves /mcp: Host and Origin admission, body bounds, the
request budget, lifetime attachment and the official stateless handler. Ansari
keeps only its own routes (/health and the Dev-only diagnostics beacon) and its
logs around /mcp. The server factory receives the request lifetime instead of
creating its own budget, and the request budget stays the tool's 60 s bound.
mcp.error now carries SAGA's fixed failure kind.

Tests that only re-checked what SAGA's fetch app tests itself are retired:
- request-timer release when a server closes, with or without a transport
  (the server-close and closed-subscription fixtures);
- malformed and oversize body refusals.

Ansari's own checks stay: the 60 s budget, the deployment allowlists replacing
localhost on both targets, the upstream abort on timeout, and the Node
listener's SIGTERM shutdown.

The app version is 0.17.0: the card address and SAGA version change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r smoke check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ch-app comments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…v1 card address

The walkthrough now describes:
- the SAGA 1.0 candidate install;
- the card at ui://views/answer-card.v1.html;
- /mcp served through SAGA's createMcpFetchApp;
- app 0.17.0.

Its embedded sources match the code at the commit SOURCE_BASE names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@yia-mw-agent yia-mw-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Helmet-Review-Request: #12 (comment)

Reviewed the full merge-base-to-head diff (base develop 7c605bb = merge-base, 28 files) at 21cc907, which was also the live head at start and before submitting. No material defects.

Checks run on that head:

  • pnpm install --frozen-lockfile --filter ansari-mcp... succeeds, so the lockfile and the five root overrides agree.
  • pnpm mcp lint, typecheck and test pass (145 of 145).
  • pnpm test:browser passes (78 checks, no errors).
  • check-release --target dev passes; --target beta and --target production refuse the candidate as the PR says, so neither can deploy it before the registry release.
  • The five committed tarballs all report 0.0.0-candidate.g8b4d987dd233. A scan found no machine paths or credential markers in them. The packaging test checks the lockfile integrity against the committed bytes.
  • The MCP application and frontend CI jobs pass; the api and auth jobs were still pending when I looked, and this PR does not touch them.

What I checked in the code:

  • createMcpFetchApp replaces the hand-built Host/Origin, body and budget wiring. requestMs still defaults to the 60 s HANDLER_BOUND_MS, so the SDK's 10 s default does not clamp the tool.
  • The lifetime is passed into createAnsariMcpServer. The retired fixtures only re-tested behavior the SDK now owns, and the Ansari-specific checks stay: the 60 s budget, the allowlists replacing localhost, upstream abort on timeout, and SIGTERM shutdown.
  • VIEW_MAJOR = 1 gives ui://views/answer-card.v1.html on both the closed and diagnostic cards. The tests and the Docker smoke script now expect the stable address.
  • sdk-identity.mjs, the build stamp and the packaging test compute the same candidate identity, and the check-release regex accepts the apps/mcp/candidates path.

Non-blocking:

  • apps/mcp/README.md line 104 (MCP_VIEW_DIAGNOSTICS_ORIGIN row) says a diagnostics origin "changes the card's bytes, address and permissions", and line 139 says "changes bytes, address and permissions". The address no longer changes (view-artifact.ts and diagnostics.test.ts say so). Dropping "address" from both would make the docs consistent.
  • Before upstream, replace the candidate pin as the PR describes. Until then the tarballs are the only record of what the SDK candidate contained.

@timeleft--
timeleft-- merged commit c0e4ce1 into develop Nov 5, 2026
8 of 9 checks passed
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.

2 participants