Skip to content

fix(python/server): make MCP discovery work (ucp wrapper + minimal /mcp endpoint)#113

Open
dzuluaga wants to merge 4 commits into
Universal-Commerce-Protocol:mainfrom
dzuluaga:fix/python-discovery-ucp-wrapper
Open

fix(python/server): make MCP discovery work (ucp wrapper + minimal /mcp endpoint)#113
dzuluaga wants to merge 4 commits into
Universal-Commerce-Protocol:mainfrom
dzuluaga:fix/python-discovery-ucp-wrapper

Conversation

@dzuluaga

@dzuluaga dzuluaga commented Jun 17, 2026

Copy link
Copy Markdown

Summary

Makes the Python reference server's UCP discovery work end-to-end for spec-conformant MCP clients, and keeps the bundled REST happy-path client working with the now-conformant profile. Three related changes:

  1. Discovery profile envelope — serve the spec-required top-level ucp wrapper.
  2. MCP transport endpoint — implement the /mcp endpoint the profile advertises (minimal: enough for discovery).
  3. Client reads the wrapped profile — the happy-path client now reads payment_handlers from inside ucp.

1. Preserve required top-level ucp wrapper (routes/discovery.py)

The /.well-known/ucp handler unwrapped the ucp key from its own template before responding, emitting a flat profile. The UCP discovery-profile schema (discovery/profile.json$defs.base.required = ["ucp"], for both 2026-01-23 and 2026-04-08) requires the wrapper, so conformant clients reject it:

PROFILE_SCHEMA_INVALID: ... ucp: Invalid input: expected object, received undefined

Fix: return the wrapped template as-is; default payment_handlers inside the ucp object. (The Node sample already serves the wrapped shape.)

2. Add a minimal MCP endpoint (routes/mcp.py)

The profile advertises an mcp transport at <endpoint>/mcp, but only REST routes were mounted, so MCP clients got HTTP 404 at that endpoint:

TRANSPORT_HTTP_ERROR: MCP request returned HTTP 404 from .../mcp

Fix: add a JSON-RPC /mcp route handling initialize and tools/list, advertising the five shopping checkout methods (create_checkout, get_checkout, update_checkout, complete_checkout, cancel_checkout) from the UCP shopping OpenRPC. Tool execution (tools/call) bridging to the existing REST checkout handlers is intentionally left as a follow-up.

3. Client reads payment_handlers from the ucp wrapper (client/flower_shop/simple_happy_path_client.py)

The happy-path client read payment_handlers from the document root, which only worked with the (non-conformant) flat profile. Once the server is fixed (#1), payment_handlers lives under ucp, so the client found 0 handlers and aborted at the payment step. Fix: read from ucp.payment_handlers (with a root fallback for legacy/flat profiles).

Verification

# MCP discovery (spec-conformant client):
$ ucp discover --business http://localhost:8182
SUCCESS  status: success
  negotiated: dev.ucp.shopping = (mcp, http://localhost:8182/mcp)
  tools: cancel_checkout, complete_checkout, create_checkout, get_checkout, update_checkout

# REST happy path (bundled client):
$ python simple_happy_path_client.py --server_url=http://localhost:8182
Merchant supports 3 payment handlers
... create -> items -> discount -> fulfillment -> delivery -> shipping ...
Payment Successful!  Checkout Status: completed
Happy Path completed successfully.

Scope

  • Affected: rest/python/server + the bundled rest/python/client/flower_shop client. The Node sample (rest/nodejs) already serves the wrapped profile and is untouched.
  • tools/call execution over MCP is out of scope (left as a follow-up).

🤖 Generated with Claude Code

@google-cla

google-cla Bot commented Jun 17, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

…ery profile

The /.well-known/ucp handler unwrapped the ucp key from its own template before responding, so the served body was a flat object. The UCP discovery profile schema (discovery/profile.json) requires the top-level ucp object ($defs.base.required = ["ucp"], for both 2026-01-23 and 2026-04-08), so spec-conformant clients reject the served profile with PROFILE_SCHEMA_INVALID.

Return the wrapped template as-is and default payment_handlers inside the ucp object rather than at the document root. The Node sample (rest/nodejs/src/api/discovery.ts) already serves the wrapped shape.
@dzuluaga dzuluaga force-pushed the fix/python-discovery-ucp-wrapper branch from 1887122 to 2f65401 Compare June 17, 2026 22:09
@dzuluaga dzuluaga marked this pull request as ready for review June 17, 2026 22:11
@dzuluaga dzuluaga requested review from a team as code owners June 17, 2026 22:11
@dzuluaga dzuluaga requested review from jingyli and westeezy June 17, 2026 22:11
The discovery profile advertises an mcp transport at <endpoint>/mcp, but the server only implemented REST, so MCP clients failed discovery with HTTP 404 on that endpoint.

Add a minimal JSON-RPC /mcp route handling initialize and tools/list (advertising the five shopping checkout methods from the UCP shopping OpenRPC), so spec-conformant MCP clients can complete discovery. tools/call bridging to the REST checkout handlers is left as a follow-up.
@dzuluaga dzuluaga changed the title fix(python/server): preserve required top-level ucp wrapper in /.well-known/ucp fix(python/server): make MCP discovery work (ucp wrapper + minimal /mcp endpoint) Jun 17, 2026
The flower_shop happy-path client read payment_handlers from the document root, which only worked with the non-conformant flat discovery profile. Now that the server serves the spec-required {ucp: {...}} wrapper, read payment_handlers from inside ucp (with a root fallback for flat/legacy profiles). Without this, the happy path aborts at the payment step with 0 payment handlers.

@igrigorik igrigorik left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ty for catching and fixing this!

Wrap the JSONResponse error payload and the notifications guard to satisfy ruff-format (line-length 80, indent-width 2).
@dzuluaga

Copy link
Copy Markdown
Author

Friendly nudge @westeezy / @jingyli 🙏 This one's approved by @igrigorik and all checks are green, just needs a 2nd approval to land. Would either of you mind taking a quick look?

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