DEV Community

Evan Lin for Google Developer Experts

Posted on • Originally published at evanlin.com on

Building Interoperable AI Business Agents with UCP: DevBooks Agent Implementation Analysis

Google Chrome 2026-01-31 21.09.06

Previous Article Recap

In the previous article, we explored how to implement Agentic Vision using LINE Bot. Today, we'll shift our focus to another important area of AI Agents: E-commerce and Interoperability.

Most current AI Agents are "islands." If you want to buy a book, you might need a dedicated bookstore Bot; to buy groceries, you'll need another grocery Bot. These Agents don't communicate with each other, and the user experience is fragmented.

To solve this problem, the Universal Commerce Protocol (UCP) was born. It's like HTML for the commerce world, defining a set of standard languages that allow different AI Agents (buyer agents and seller agents) to "communicate" with each other and complete complex commercial transactions.

In this article, I will take you deep into the code of devbooks_agent, a UCP-based technical bookstore Agent, to demonstrate how it works.

What is UCP and A2A?

Before diving into the code, let's briefly understand two core concepts:

  1. UCP (Universal Commerce Protocol): A standardized commerce protocol. It defines data structures (Schemas) such as "Product", "Order", and "Checkout", ensuring that the "Product" your Agent says is the same as the "Product" my Agent understands.
  2. A2A (Agent-to-Agent): The communication model between Agents. Here, we will have two roles:
    • User Agent (Client): Represents the user, responsible for sending requests (e.g., "I want to buy a React book").
    • Business Agent (Merchant): Represents the merchant (like DevBooks), responsible for providing product information and processing orders.

Our focus today is this Business Agentdevbooks_agent.

Project Structure Overview

devbooks_agent is a standard Python project that uses Google's Agent Development Kit (ADK).

devbooks_agent/
├── src/devbooks_agent/
│ ├── agent.py # Agent's brain: defines tools and behaviors
│ ├── ucp_profile_resolver.py # UCP handshake protocol: confirms each other's capabilities
│ ├── store.py # Simulated database and business logic
│ ├── data/
│ │ ├── ucp.json # UCP capability declaration
│ │ ├── products.json # Book catalog
│ │ └── agent_card.json # Agent's business card
│ └── main.py # Program entry point
Enter fullscreen mode Exit fullscreen mode

1. Defining Agent Capabilities (ucp.json)

First, the Agent needs to tell the world what it "can do." This is defined through ucp.json. This is like the Agent's resume.

{
  "ucp": {
    "version": "2026-01-11",
    "capabilities": [
      {
        "name": "dev.ucp.shopping.checkout",
        "version": "2026-01-11",
        "spec": "https://ucp.dev/specs/shopping/checkout"
      },
      {
        "name": "dev.ucp.shopping.fulfillment",
        "version": "2026-01-11",
        "extends": "dev.ucp.shopping.checkout"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This configuration declares that the DevBooks Agent supports the 2026 version of the UCP protocol and has the capabilities of "shopping checkout" and "logistics delivery."

2. Agent's Brain and Tools (agent.py)

This is the most core part. We use google.adk.agents.Agent to define the Agent and give it various tools (Tools).

# src/devbooks_agent/agent.py

root_agent = Agent(
    name="devbooks_agent",
    model="gemini-2.5-flash", # Uses the latest Gemini model
    description="Agent to help with shopping for technical books",
    instruction=(
        "You are a helpful agent who assists developers in finding and purchasing"
        " technical books..."
        # ... Detailed Prompt instructions ...
    ),
    tools=[
        search_shopping_catalog, # Search for books
        preview_book, # Preview (DevBooks exclusive feature)
        add_to_checkout, # Add to cart
        start_payment, # Start checkout
        complete_checkout, # Complete order
        # ... Other tools
    ],
    # ... callback settings
)
Enter fullscreen mode Exit fullscreen mode

Featured Tool: preview_book

Unlike general grocery stores, selling books usually requires a "preview." This is where the benefits of Agent toolification come in handy, allowing us to easily add custom features:

def preview_book(tool_context: ToolContext, book_id: str) -> dict:
  """Gets a preview/sample chapter of a book."""
  try:
    preview = store.get_book_preview(book_id)
    if preview is None:
        # Handle the case where there is no preview
        return _create_error_response(...)

    return {
        "preview": preview.model_dump(mode="json"),
        "status": "success"
    }
  except Exception:
    # Error handling
    return _create_error_response(...)
Enter fullscreen mode Exit fullscreen mode

3. UCP Handshake Protocol (ucp_profile_resolver.py)

When the User Agent connects to the Business Agent, both parties need to "tune in" first to confirm the UCP versions they support. This is handled by ProfileResolver.

# src/devbooks_agent/ucp_profile_resolver.py

def resolve_profile(self, client_profile_url: str, user_id: str | None = None) -> dict:
    # 1. Get the Client's Profile
    profile = self._fetch_profile(client_profile_url, headers=headers)

    # 2. Check version compatibility
    client_version = profile.get("ucp").get("version")
    merchant_version = self.merchant_profile.get("ucp").get("version")

    # If the Client version is too new, and the Merchant doesn't support it, then report an error
    if client_version > merchant_version:
      raise ServerError(...)

    return profile
Enter fullscreen mode Exit fullscreen mode

This ensures that both parties in the transaction are on the same channel and that there is no miscommunication.

Practical Demo

After understanding the architecture, let's run a complete UCP test flow. This Demo will simulate a developer purchasing a technical book.

Environment Preparation

Make sure you have started the following services:

  1. Business Agent (DevBooks): http://localhost:11000
  2. Chat Client: http://localhost:3000

Test Script

Google Chrome 2026-01-31 21.19.00

Please open the Chat Client (http://localhost:3000) in your browser and follow these steps:

1. Book Search

User Input:

"I looking for some books about React to learn."

Behind the Scenes: The Agent will call the search_shopping_catalog tool and return a list of matching books (e.g., "Learning React", "React Design Patterns").

Expected Result: You will see book cards with cover images and prices.

2. Preview Content

This is a DevBooks exclusive feature.

User Input:

"Can I see a preview of the first one?"

Behind the Scenes: The Agent identifies the user's intent and calls the preview_book tool to get the preview chapter content.

Expected Result: The Agent returns the first chapter excerpt or a preview link of the book.

3. Add to Cart

User Action: Click the "Add to Checkout" button on the card, or enter "Add Learning React to my cart".

Behind the Scenes: Calls add_to_checkout. At this point, the Agent creates a UCP Checkout Session (ADK_USER_CHECKOUT_ID) in the background.

4. Checkout Info

User Input:

"My email is dev@example.com, ship to 456 Tech Blvd, San Francisco, CA 94107"

Behind the Scenes: The Agent parses the address information and calls update_customer_details to fill in the information in the UCP Checkout object.

5. Payment

User Action: Click "Complete Payment" -> Select a payment method (Mock Pay) -> "Confirm Purchase".

Behind the Scenes: Calls complete_checkout. The Agent interacts with MockPaymentProcessor, verifies the payment, and finally calls store.place_order to complete the order.

Expected Result: Receive order confirmation message: "Order Confirmed! Order ID: ORDER-12345".

Google Chrome 2026-01-31 21.19.48

Technical Development Experience

1. The Importance of State Management

In agent.py, you can see a lot of tool_context.state usage. Because the Agent's interaction is a multi-turn conversation, we must preserve checkout_id between conversations.

def add_to_checkout(tool_context: ToolContext, ...):
    # Read or create Checkout ID from Context
    checkout_id = tool_context.state.get(ADK_USER_CHECKOUT_ID)
    if not checkout_id:
        # ... Create new Checkout
        tool_context.state[ADK_USER_CHECKOUT_ID] = checkout.id
Enter fullscreen mode Exit fullscreen mode

This is similar to the concept of Session in traditional Web development, but in Agent development, this is maintained jointly by the LLM's Context Window and an external State Store.

2. The Power of UCP: Structured Data

Pay attention to the after_tool_modifier function:

def after_tool_modifier(..., tool_response: Dict) -> Optional[Dict]:
    # ...
    # Inject structured UCP data into the response
    if UcpExtension.URI in extensions:
        tool_context.state[ADK_LATEST_TOOL_RESULT] = tool_response
Enter fullscreen mode Exit fullscreen mode

This allows the Agent to return not just a piece of text ("Okay, added to cart"), but a complete, machine-readable JSON object. The Client side (front-end UI) receives this JSON and can render beautiful product cards or checkout buttons, instead of just displaying plain text. This is the essence of A2A: Not just chatting, but data exchange.

Conclusion

Through the implementation of devbooks_agent, we see how UCP elevates AI Agents from simple chatbots to "digital clerks" capable of handling complex business logic.

  • Standardization: UCP allows Agents written by different developers to communicate with each other.
  • Modularity: Through ADK Tools, we can easily extend functionality (such as previews).
  • Interoperability: The front-end UI can automatically generate interfaces based on standard protocols, without customizing the screen for each Agent.

The future of AI Agents is definitely not a solo fight, but an interconnected world.

References

Top comments (0)