---
title: Where's my sofa?
tagline: A customer care agent on MuleSoft Agent Fabric, with its own MCP server
description: Where's my sofa? is a customer care agent for a made-to-order furniture shop, built on MuleSoft Agent Fabric: an Agent Script broker behind Omni Gateway, a Mule MCP server and a Mule System API that holds the rules.
date: 2026-10-18
repo: https://github.com/brbousnguar/wheres-my-sofa
stack: MuleSoft Agent Fabric · Agent Script · MCP · A2A · Mule 4 · CloudHub 2.0
status: Ran on CloudHub 2.0 in an Anypoint trial org, October 2026
video: /assets/video/wheres-my-sofa-demo.mp4
video_square: /assets/video/wheres-my-sofa-demo-square.mp4
poster: /assets/img/demo/wheres-my-sofa.jpg
video_caption: The repo's demo page talking to the Care Desk broker deployed on my Anypoint trial org, with the System API's own data on the right. Every reply is a live A2A call. Voiceover in my own voice, cloned with AI. Waits for the model sped up, and labelled.
video_duration: 65.1
install_video: /assets/video/wheres-my-sofa-config.mp4
install_video_square: /assets/video/wheres-my-sofa-config-square.mp4
install_poster: /assets/img/demo/wheres-my-sofa-config.jpg
install_video_label: How it's configured
install_video_caption: A read-only tour of the Anypoint trial org the agent ran in: agent-network.yaml on GitHub, then the network, the MCP server and the A2A card in Exchange, the instances and their policies in API Manager, and the Omni Gateway and the apps in Runtime Manager. The org's name and IDs are blurred. Voiceover in my own voice, cloned with AI. Page loads cut, scrolling up to 1.5x.
install_video_duration: 89.7
---

Where's my sofa? is a care desk agent for a furniture shop that makes most pieces to order. Customers ask it where their order is, move a booked delivery or report damage, over A2A. It runs on MuleSoft Agent Fabric, and everything it touches goes through Mule: an MCP server in front of a System API.

My day job is MuleSoft integration, and there's no agent in it yet. MuleSoft is moving fast on agents (Agent Fabric, Agent Script, an MCP connector), so I built the smallest real use case I could think of, end to end, the API-led way. The shop, its products and its customers are invented.

## The model classifies, the graph decides

Furniture made to order is a good test for an agent. "Where is my sofa?" means joining the order, the workshop's production stage and the delivery booking. And some calls should never be left to a model: whose order it is, which slots are free, and that a delivery under 48 hours away can't move.

So the broker is an Agent Script graph, and the model only gets the parts that need language:

- `understandRequest` makes one LLM call with no tools: intent, order number, email, preferred slot.
- A router, with no LLM, picks the path and checks that the order number and email are there.
- `orderStatus` gets only the two tools its path needs, with the email bound by the graph, so the model can't point a tool at someone else's order.
- To move a delivery, an executor reads the free slots, one LLM call picks the slot that matches, and another executor moves it. The model picks a slot but never calls the tool, and when it has to ask which slot suits the customer, the question is a fixed reply.
- Complaints, and deliveries too close to move, go to executors that open a care case with fixed arguments. No model decides whether a case gets opened.

The rules live in the System API. A wrong email gets the same 404 as a missing order, so the API never confirms that an order number exists.

## What's in it

- **sofa-orders-sapi**: a Mule System API over a mock ERP, with its rules in DataWeave.
- **sofa-care-mcp**: a Mule app on the MCP Connector 1.7, five tools over the System API, and an API key checked when an MCP session opens.
- **The Care Desk broker**, deployed by Agent Fabric behind Omni Gateway: client ID enforcement, 60 requests an hour per client to cap the LLM bill, and user context so each caller gets their own conversation memory.
- `gpt-5-mini` through OpenRouter, with low reasoning effort.

## What broke

The agent sometimes answered customers with its own reasoning. Agent Fabric runs its LLM nodes with human-in-the-loop: when the model decides it needs more input, the task stops to wait for the customer, and the model's reasoning becomes the reply. The fix was to take that decision away from the model: it fills in fields, the graph decides, and every question to the customer is a fixed reply. And MCP Connector 1.6 held an SSE stream open for every response, so the broker's tool discovery timed out behind the gateway. Connector 1.7 answers with one JSON body.

## The eval checks the system, not the reply

A reply can sound right and do the wrong thing. `scripts/eval.py` sends eight customer messages and snapshots every delivery slot and care case in the System API before and after each one. A move passes only if that one order moved to that one slot and nothing else changed. A prompt injection on someone else's order passes only if nothing moved at all.

The whole story, with the eval runs: [A customer care agent on MuleSoft Agent Fabric, where the model only talks](/notes/mulesoft-agent-fabric-care-desk.html).

## Run it

The repo has both Mule apps, the agent network and the deploy scripts. With an Anypoint org that has Agent Fabric, a connected app and an OpenRouter key:

```bash
scripts/deploy-apps.sh                                                     # both Mule apps to CloudHub 2.0
SETUP_GATEWAY=1 SOFA_CARE_MCP_URL=https://... scripts/deploy-network.sh    # gateway, publish, broker
scripts/ask.py                                                             # talk to it over A2A
```

`demo/server.py` serves the page from the video: the chat next to the System API's own data.
