Skip to content

Detect UCP version drift and explain stale-CLI failures - #45

Draft
gil-- wants to merge 2 commits into
Shopify:mainfrom
gil--:ucp-version-drift
Draft

Detect UCP version drift and explain stale-CLI failures#45
gil-- wants to merge 2 commits into
Shopify:mainfrom
gil--:ucp-version-drift

Conversation

@gil--

@gil-- gil-- commented Sep 3, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #44. Many agents install shop-cli once and never update, and UCP servers drop old releases after a few months. Today a stale CLI fails with an opaque MCP search_catalog returned an error, and nothing in the CLI tells the agent (or user) that the fix is to upgrade. This PR surfaces UCP version drift using only data the servers already publish, with no new network calls on the hot path and no npm registry lookups.

  • shop --version now prints the UCP release too: 0.1.2 (UCP 2026-08-25).
  • New shop version [--check] [--shop-domain <domain>] prints { cli, ucp }. With --check it fetches /.well-known/ucp from the global catalog (or a merchant) and classifies the pinned release against the server's version / supported_versions as current, outdated, unsupported, ahead, or unknown, with a message and the upgrade command. A failed manifest fetch degrades to unknown rather than erroring.
  • Actionable MCP errors. JSON-RPC errors now include the server's message/data (e.g. Invalid params: Tool not found: search_catalog). A "Tool not found" for a tool this CLI is built against is checked against the host's manifest (non-fatal) and explained as a dropped release with the upgrade command, or, when a custom --profile-url is in use, attributed to that profile.
  • Negotiated-version notice. When a server answers with a different ucp.version than the CLI requested, a one-time # Notice goes to stderr. stdout stays clean JSON/markdown.
  • SKILL.md tells agents to run shop version --check at session start and on "Tool not found", and what each status means. README lists the new command. Minor changeset.

Why this design

UCP negotiation always goes down to the release named in the agent profile, so a successful response can never reveal that the CLI is behind; only the server's discovery manifest can (ucp.version + ucp.supported_versions). Fetching that manifest on every call would add latency to every command, so the check is explicit (shop version --check) or triggered only on the failure it explains ("Tool not found"). Checking npm for a newer package was considered and rejected: it is an extra third-party call on every run and it cannot say whether the installed release still works.

Verification

Live against production:

$ shop --version
0.1.2 (UCP 2026-08-25)

$ shop version --check          # catalog.shopify.com → status "current", supported 2026-08-25, 2026-04-08
$ shop version --shop-domain shopify.supply   # → "current", supported 2026-08-25, 2026-04-08, 2026-01-23

# 2026-04-08 profile → stderr:
# Notice
catalog.shopify.com answered search_catalog with UCP 2026-04-08, older than the 2026-08-25 shop-cli requested. ...

# 2026-01-23 profile (dropped by the catalog) → stderr:
# Error
MCP search_catalog returned an error: Invalid params: Tool not found: search_catalog. catalog.shopify.com still supports UCP 2026-08-25, so this is not a version mismatch. A custom --profile-url is in use (...); the server may have dropped the UCP release that profile declares.
  • pnpm typecheck, pnpm test (128 tests, 20 new in tests/ucp-version-check.test.ts), pnpm build all pass.
  • Existing tests are unaffected: mocks return no ucp block, so no notices fire, and only "Tool not found" errors trigger a manifest fetch.

Notes

🤖 Generated with Claude Code

gil-- and others added 2 commits September 3, 2026 18:51
Both profiles the CLI sends in meta["ucp-agent"].profile
(valid-with-capabilities for the global catalog, personal_agent for
merchant checkout) move from 2026-04-08 to 2026-08-25, pinned via a
single UCP_VERSION constant in src/constants.ts. The skill reference
docs use the new profile URLs, and the shipping destination example
carries the explicit "type": "shipping_address" discriminator that
2026-08-25 introduced.

Request shapes are unchanged between the two releases: MCP method
names, the meta.ucp-agent / idempotency-key definitions, the message
and payment schemas, and the checkout status enum all diff clean.
Catalog responses gain optional actions[] and policies[] fields.
Shopify's global catalog extension now requires protocol >= 2026-08-25.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
UCP servers negotiate down to the release named in the agent profile, so a
stale shop-cli never learns it is behind until the server drops that release
and every call fails with "Tool not found". Surface drift using the data the
servers already publish, without adding network calls to ordinary commands:

- `shop --version` prints the UCP release alongside the package version.
- New `shop version [--check] [--shop-domain]` compares the pinned release
  against a host's /.well-known/ucp manifest (current / outdated /
  unsupported / ahead / unknown) and prints the upgrade command.
- MCP JSON-RPC errors include the server's message/data; a "Tool not found"
  is checked against the manifest and explained as a dropped release (or
  attributed to a custom --profile-url).
- A one-time stderr `# Notice` when a server negotiates a different release.
- SKILL.md guidance to run `shop version --check` at session start.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

1 participant