Native Host Requirements for Implementing the UCP Embedded Protocol

Native hosts must inject global JavaScript objects named Embedded{Capability}ProtocolConsumer and Embedded{Capability}Protocol into the web-view context to enable JSON-RPC 2.0 message exchange between the host application and embedded business UIs.

The Universal-Commerce-Protocol (UCP) Embedded Protocol defines how native applications—such as iOS/Android apps or desktop clients—host business UIs within web-views while maintaining secure, bidirectional communication. According to the specification in docs/specification/embedded-protocol.md, native hosts must implement a specific bridging layer that exposes standardized global objects to the embedded JavaScript context. This article details the mandatory technical requirements for building a compliant native host implementation.

Mandatory Bridging Layer Components

Native hosts must provide two distinct global JavaScript objects that serve as the communication bridge between the host runtime and the embedded business UI.

Consumer Object Injection

The host must inject a consumer object into the iframe or web-view before the business UI loads. According to docs/specification/embedded-protocol.md, this object must be named following the pattern window.Embedded{Capability}ProtocolConsumer (recommended) or window.webkit.messageHandlers.Embedded{Capability}ProtocolConsumer for WebKit-based environments.

The consumer object must implement a single method:

  • postMessage(message: string): void – Accepts a JSON-stringified JSON-RPC 2.0 request that the host parses and acts upon.

For example, the Checkout capability requires EmbeddedCheckoutProtocolConsumer, while the Cart capability uses EmbeddedCartProtocolConsumer as defined in embedded-checkout.md and embedded-cart.md respectively.

Producer Object Exposure

The host must also expose a producer object that allows the native side to send messages back to the embedded context. The host injects JavaScript that calls window.Embedded{Capability}Protocol.postMessage(jsonMessage), where the embedded context listens for these calls immediately upon loading.

Handshake Timing and Message Flow

Proper initialization sequencing is critical for protocol compliance.

Ready Handshake Requirements

As specified in the Native Hosts section of embedded-protocol.md, the host must complete the following steps before the embedded context loads:

  1. Register the global consumer and producer objects in the JavaScript context.
  2. Prepare to receive the initial ready request (method name ec.ready for checkout contexts).
  3. Validate the incoming handshake and reply with a JSON-RPC response containing the matching id.

The embedded UI broadcasts its ready request immediately upon loading. If the global objects are not present, the handshake fails, preventing subsequent communication.

Message Format Compliance

All messages exchanged via the injected globals must conform to the JSON-RPC 2.0 envelope defined in the Message Format section of the specification. Each message must include:

  • "jsonrpc": "2.0"
  • "method" – The procedure name being invoked.
  • "params" – Arguments for the method.
  • "id" – Optional for notifications, required for requests.

The host must parse the JSON string, validate the envelope structure, and reply with matching id values for requests.

Security and Error Handling Constraints

Native implementations must enforce strict security policies and standardized error responses.

Origin Validation

As documented in the Security section of embedded-protocol.md, hosts utilizing postMessage-based bridges must validate the origin of all incoming postMessage calls. Failure to validate origins constitutes a security_error. The bridge must not be exposed to untrusted frames or contexts outside the UCP ecosystem.

Error Response Standards

Transport-level failures—such as malformed JSON or unknown methods—must return using the JSON-RPC error field. Application-level errors must be wrapped within the result.ucp.status payload. This dual-layer error model ensures consistent error handling across native and web host implementations.

Capability-Specific Naming Conventions

Each UCP capability defines exact global names that native hosts must implement:

  • Checkout: EmbeddedCheckoutProtocolConsumer / EmbeddedCheckoutProtocol
  • Cart: EmbeddedCartProtocolConsumer / EmbeddedCartProtocol

The capability-specific bindings in docs/specification/embedded-checkout.md and docs/specification/embedded-cart.md define the exact method contracts and event sequences for each context. The OpenRPC schema at source/services/shopping/embedded.openrpc.json provides the complete method definitions useful for code generation and validation.

Implementation Examples

iOS WKWebView Consumer Injection

When implementing a host using iOS WKWebView, inject the consumer object before loading the business UI:

// Swift → WKUserContentController injection
let script = """
window.EmbeddedCheckoutProtocolConsumer = {
  postMessage: function(message) {
    // Forward the JSON-RPC string to the native layer
    window.webkit.messageHandlers.checkoutConsumer.postMessage(message);
  }
};
"""
webView.evaluateJavaScript(script, completionHandler: nil)

Native Message Handler

The following Python pseudocode demonstrates handling incoming EP messages and responding via the producer bridge:

def handle_checkout_message(json_str: str):
    msg = json.loads(json_str)               # Parse JSON-RPC envelope

    assert msg["jsonrpc"] == "2.0"
    method = msg["method"]

    if method == "ec.ready":
        # Process handshake, return optional upgrade or credentials

        response = {
            "jsonrpc": "2.0",
            "id": msg.get("id"),
            "result": {
                "ucp": {"version": "1.0", "status": "success"},
                # ... additional fields ...

            }
        }
        # Send back via the producer bridge

        webview.evaluate_javascript(
            f'window.EmbeddedCheckoutProtocol.postMessage({json.dumps(response)})')

Host-to-Embedded Notifications

After the ready handshake succeeds, the host can send state updates to the embedded context:

// Host-initiated notification
const notification = {
  jsonrpc: "2.0",
  method: "ec.start",
  params: {
    checkout: { /* current checkout state */ }
  }
};
window.EmbeddedCheckoutProtocol.postMessage(JSON.stringify(notification));

Summary

  • Native hosts must inject Embedded{Capability}ProtocolConsumer (for incoming messages) and Embedded{Capability}Protocol (for outgoing messages) before the embedded UI loads.
  • All messages must use JSON-RPC 2.0 envelopes with proper jsonrpc, method, params, and optional id fields.
  • The host must handle the ec.ready handshake immediately upon receiving it from the embedded context.
  • Security requires origin validation on all postMessage calls to prevent exposure to untrusted frames.
  • Transport errors use the JSON-RPC error field; application errors use result.ucp.status.
  • Capability-specific bindings in embedded-checkout.md and embedded-cart.md define exact global names like EmbeddedCheckoutProtocolConsumer.

Frequently Asked Questions

What happens if the host injects the global objects after the embedded UI loads?

The embedded business UI sends its ready request immediately upon loading. If window.Embedded{Capability}ProtocolConsumer is not present, the request fails, the handshake never completes, and the embedded UI remains in a non-functional state. According to embedded-protocol.md, hosts must register these globals before loading the web-view content.

Can native hosts use MessageChannel instead of injected globals?

Yes, after the initial handshake completes, hosts may upgrade to a MessageChannel for subsequent communication. However, the host must still provide the initial bridging layer via injected globals to handle the ready request and establish the session. The specification allows for transport upgrades but requires the global object bridge for initialization.

How should native hosts handle session errors?

When receiving error or session_error notifications via the consumer object, the host must tear down the embedded web-view and optionally redirect the buyer using the continue_url provided in the error payload. This ensures secure session termination and proper buyer experience flow as defined in the Response Handling section of the specification.

Where can developers find the complete method definitions for code generation?

Developers should reference source/services/shopping/embedded.openrpc.json in the Universal-Commerce-Protocol/ucp repository. This OpenRPC schema defines the complete set of EP methods, parameters, and structures, enabling automated client generation and request validation for native host implementations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →