Diagnose a stuck shipment
A shipment hasn't moved in a while. An ops user — or a customer-support agent — wants to understand why, and what to do about it.
This is the most common multi-tool MCP pattern we see. The agent reads state from three angles (Carriyo-internal, activity, carrier-side system logs), narrates a diagnosis, and proposes (or takes) the next action.
When to use
- Customer service: "Where's order 8842?"
- Internal ops: "Why is SHP-12345 stuck on
READY_FOR_PICKUPfor two days?" - Bulk triage: "Find all shipments stuck >24 hours and tell me what's
wrong." (Use
get_shipment_listfiltered bycreation_date_*and status to find candidates, then loop the diagnosis pattern.)
Tool sequence
get_shipment (current state)
│
▼
get_shipment_activity (Carriyo-internal events: status changes, rules, notifications)
│
▼
list_shipment_system_logs (carrier-side log entries — find errors)
│
▼
get_shipment_system_log (full request/response for the most relevant entry)
│
▼
diagnosis
│
├── (carrier never picked up) → reprocess_shipment
├── (carrier rejected — capacity / address / weight) → reassign_shipment
├── (label issue — bad PDF, expired URL) → refresh_label
└── (delivery failed, can retry) → schedule_delivery
Example agent prompt
"SHP-12345 hasn't moved in two days. What's going on, and what should I do about it?"
Walkthrough
Step 1 — pull the shipment
get_shipment(tenantId="…", shipment_id="SHP-12345")
Returns the current status, status timestamp, carrier, promised dates,
addresses. The agent now knows what state the shipment is in and
for how long — but not why.
Step 2 — pull the activity timeline
get_shipment_activity(tenantId="…", shipment_id="SHP-12345")
Activity is everything Carriyo-internal that's happened on the shipment record: creation, status transitions, rule assignments, notifications sent, customer feedback. If the issue is on the Carriyo side — a stuck rule, a notification that didn't fire, a user action that broke flow — it shows up here.
Step 3 — pull carrier-side system logs
list_shipment_system_logs(
tenantId="…",
shipment_id="SHP-12345",
responseCode="5xx"
)
System logs are the carrier-side view: every request Carriyo made to
the carrier, the response code, and the response body. Filtering on
responseCode="5xx" (or 4xx) narrows to errors. If the carrier
rejected a booking, returned a malformed label, or hit a transient
error, it lives here.
Step 4 — pull the full log body
get_shipment_system_log(
tenantId="…",
shipment_id="SHP-12345",
identifier="…" (the identifier of the most relevant entry from step 3)
)
Full request / response details. The agent reads the carrier's error message and uses it to decide what's wrong.
Step 5 — narrate the diagnosis
The agent now has three views of the same shipment. Common patterns:
| Symptom | Likely cause | Recommended action |
|---|---|---|
| Confirmed, no carrier success logs, no tracking events | Carrier didn't pick up | reprocess_shipment (re-book with same carrier) |
| Carrier returned an error response in system logs | Carrier rejected (capacity, address, weight) | reassign_shipment (different carrier) |
| Multiple failed delivery attempts in activity | Customer unavailable | schedule_delivery (new slot) |
| Label issue — generation failed, URL expired | Bad label state | refresh_label, then physical reconciliation |
The agent narrates the diagnosis in plain English ("Looks like Aramex rejected this one — their API returned a 500 on the booking call. Address might be the issue. I'd reassign to a different carrier.") and either prompts the human for confirmation or takes the action directly.
Step 6 — take the action
Based on the diagnosis:
reprocess_shipment(tenantId="…", shipment_id="SHP-12345")
or
reassign_shipment(tenantId="…", shipment_id="SHP-12345", carrier_id="dhl-uae-2")
In a customer-facing agent, always confirm before invoking a write tool. The agent's diagnosis can be wrong, and a wrong reassignment costs real money (label fees, capacity reservations). See Limits & security → recommended guardrails.
Variations
- Bulk triage — open with
get_shipment_listfiltered bycreation_date_from/creation_date_toplus a status filter, then loop the diagnosis pattern over each result. Mind the rate limits (see Limits & security). - Customer-facing version — same pattern but the action step is
always "create a support ticket" rather than
reprocess_*/reassign_*. Restrict the agent's tool catalog accordingly (see Limits & security → Tool-catalog restriction). - Routing-decision variant — when the question is "why was this
routed to carrier X?" rather than "why is it stuck?" — pair
get_shipmentwithget_automation_rulesetandlist_automation_rulesto explain the routing decision.
Related patterns
- Reschedule a delivery on customer request — similar shape, different action.
- Daily ops standup — runs at the aggregate (analytics) level rather than per-shipment.