Skip to main content
Glama
Anandb71

CargoMind MCP Server

by Anandb71

CargoMind

A self-healing, multi-model, multi-agent logistics control tower. Built on Google ADK, OpenAI GPT-6.1 Sol / GPT-6 Luna, Google Maps and MCP. Ask it about any shipment, lane, truck or SKU in an India-wide 3PL network. It plans, checks its own work and keeps answering when models, APIs or servers fail. The previous Gemini/Anthropic chain remains available with CARGOMIND_PROVIDER=multi.

▶ Open the live demo · Run it locally in 60 s · Criteria → proof · See it in action · Submission overview

CI Python 3.12 Google ADK 2.12 Gemini Google Maps Claude / OpenAI MCP Cloud Run Zero keys License: MIT

Quickstart · Criteria map · Architecture · Demo script · Self-healing · Feedback loops · Deploy

CargoMind mission control: Watchtower alerts, network KPIs, live shipment lanes on the India map, and the ops agent

23

3 vendors

11 + 2

18 / 18

0

Google ADK agents using AgentTool, Sequential, Parallel and Loop patterns

Gemini → Claude / GPT → offline engine, failover per request

MCP tools + resources in our own server

eval scenarios pass, also under 30% injected chaos

API keys needed to run it

Self-healing, seen live. One real request from our live runs, straight from the healing journal: gemini-3.8-flash → 429 RESOURCE_EXHAUSTED · gemini-3.6-flash → 429 · anthropic/claude-sonnet-5-5 → error · served by openai/gpt-5-mini — recovered. The operator just got their answer. Across those runs the journal recorded 19 automatic cross-vendor model recoveries.


Judge fast path: 90-second demo · criterion-by-criterion evidence · reproducible eval report. Verified: 106 tests pass, 18/18 offline scenarios succeed, routing F1 0.974. The timed demo uses the offline engine; live provider usage is visible in the trace.

Resilience evidence: 30% chaos eval also completed 18/18 scenarios, with 14 injected faults, 12 retries and 12 recorded recoveries. Both eval runs use the offline model and in-process MCP; CI separately exercises stdio MCP and Docker.

Simulation readiness

Open /simulation on the running server for a configurable logistics graph, explicit multi-agent handoffs, independently verified route decisions, and a forged-output rejection demo. The interface accepts operational IDs and numeric constraints; personal/contact fields are rejected. Proposals remain read-only.

Run uv run cargomind simulate --input scenario.json or POST a scenario to /api/simulation/run. See simulation schema and protocol, evaluation method, and raw-count benchmark report. The protocol is custom JSON handoff 1.0 over JSON-RPC 2.0; existing ADK agents invoke the same optimizer. Deterministic routing scores are distinct from LLM hallucination measurements.

Related MCP server: Logistics AI MCP

Contents

More depth: docs/ARCHITECTURE.md (sequence diagrams), docs/JUDGING.md (criteria checklist with file links), docs/DEMO_SCRIPT.md (3-minute live demo run-sheet).


See it in action

Disruption Autopilot: one sentence starts a multi-agent incident response. "Cyclone alert in Chennai, severity high: run disruption autopilot." The map draws the impact zone around Chennai and lights up every exposed lane. Then impact_assessor hands off to a ParallelAgent (reroute planner + notice drafter), and autopilot_commander writes the incident action plan for the operator to approve. In this capture the journal on the right shows Gemini hitting its quota and the run continuing on the next model.

Disruption Autopilot: cyclone impact ring over Chennai with live model failover in the journal

Multimodal proof-of-delivery inspection that knows when not to guess. Attach a delivery photo and Gemini vision returns damage_detected, severity, findings and the next action. In this capture Gemini vision was over quota, so the local Pillow fallback answered instead: it marked the result degraded, explained why and asked for human review rather than inventing a verdict.

Watchtower acts before you ask. It scans SLAs, exceptions, stock, fleet and hub weather every 60 s. Each alert is a one-click question to the agent; weather alerts launch the autopilot.


The 30-second pitch

The problem. A logistics control tower runs on a dozen screens: a TMS for shipments, a WMS for stock, maps for routes, weather for risk, spreadsheets for dispatch, WhatsApp for customers. Most AI assistants for this job stop working the moment an API rate-limits, a model returns a 429 or a backend crashes. In operations that is the moment you need them.

CargoMind puts the whole control tower behind one conversation:

  • One concierge, many specialists. A conversational Gemini agent sends each request to 8 specialist agents: tracking, routes and risk, inventory, dispatch, customer comms, photo inspection, the ops briefing and the Disruption Autopilot. The graph has 23 ADK agents in total and uses every ADK workflow pattern: LlmAgent + AgentTool, SequentialAgent, ParallelAgent and LoopAgent.

  • Multi-model by design. Conversation, planning and vision tiers prefer Gemini models. The independent dispatch critic prefers Claude or GPT. The trace records which provider actually ran after any fallback. A deterministic offline rule-engine is the last link of every model chain.

  • It checks its own work. The dispatch planner and an independent critic run in a feedback loop. The critic checks truck capacity, driver hours, cold-chain and hazmat handling, SLA and weather. The planner revises until the plan is valid. Nothing is written to the database until you say "commit the plan".

  • It acts before you ask. The Watchtower scans the network every 60 s for SLA breach risk, exceptions, low stock, fleet problems and hub weather, and turns each alert into a one-click question. For a cyclone, flood, strike or closure, the Disruption Autopilot assesses the impact, plans reroutes and drafts customer notices in parallel, then hands the operator an incident action plan to approve.

  • It learns from the operator. Thumbs up or down, comments and "remember: always..." are turned into lessons. Those lessons are added to the agent's instructions on every turn.

  • It heals itself. The system has five layers of self-healing:

    • model failover across vendors

    • tool-argument repair, retries and circuit breakers

    • API fallbacks: Google Maps → OpenStreetMap → offline math

    • MCP-server failover

    • a Chaos Monkey slider so you can break things live and watch it recover

    Every fault and recovery is written to a live healing journal.

  • Runs with zero keys. git clone → uv sync → uv run cargomind web gives you a working system with no API keys at all. Add keys to switch on Gemini, Claude/OpenAI and Google Maps.


How CargoMind meets the judging criteria

Criterion

How CargoMind meets it

Where to look

Verify in under a minute

Tool set: Agent SDK

Built on Google ADK 2.12. It uses LlmAgent, AgentTool, SequentialAgent, ParallelAgent, LoopAgent, exit_loop, output_key state passing, App + BasePlugin, Runner + sessions, McpToolset and a custom BaseLlm

agent.py, runtime.py, healing/plugin.py

uv run adk web src, then pick cargomind

Tool set: Google products

Gemini in 4 roles (fast, pro, backup, multimodal vision). Google Maps Routes API v2 and Geocoding API. Cloud Run, Cloud Build and Secret Manager for deployment. The ADK Dev UI. Gemini CLI as an MCP client

config.py, tools/geo.py, tools/vision.py, deploy/cloudrun.sh

uv run cargomind doctor lists the live providers

"No half-baked information: A–Z instructions"

This README covers a zero-key quickstart, setup for each OS, every API key step by step, every env var and command, the dashboard, ADK UI, MCP, Docker, Cloud Run and troubleshooting. cargomind doctor runs a self-check

this file, docs/

Follow Quickstart on a clean machine

Multi-model agents

4 model tiers across Gemini + Claude/OpenAI + an offline engine. When a Claude or OpenAI key is set, the critic deliberately runs on a different vendor from the planner. Images go to Gemini multimodal. Each answer is tagged with the model that served it (served_by)

healing/resilient_llm.py (build_model), agent.py

Dashboard trace and CLI show [served_by] on every answer

Conversational agents

The cargomind_concierge agent keeps sessions and remembers the conversation ("commit the plan" refers to the plan from 3 turns ago). It asks clarifying questions and handles requests that span several domains in one turn. Available in the CLI, the dashboard and the ADK UI

agent.py (root_agent), runtime.py

uv run cargomind chat

API calls

Google Routes v2 (computeRoutes, waypoint optimisation), Google Geocoding, Open-Meteo forecasts, OSRM and Nominatim fallbacks, Gemini vision, and Anthropic/OpenAI through LiteLLM. CargoMind also exposes its own REST + SSE API

tools/, web/server.py

Ask "Any weather risk on Mumbai → Pune today?"

MCP integration

CargoMind ships its own MCP server (cargomind-logistics, 11 tools + 2 resources over stdio). Its own agents use it through ADK McpToolset, wrapped in a self-healing ResilientMcpToolset. The same server plugs into Claude Desktop, Claude Code, Gemini CLI or the MCP Inspector

mcp_server/server.py, mcp_tools.py, deploy/claude_desktop_config.example.json

uv run cargomind mcp (see MCP server)

Feedback loop

(1) Planner and critic run in a LoopAgent until the plan is valid. (2) Operator thumbs and comments, plus remember_operator_feedback, become lessons that are added to the instruction on every turn. (3) Human-in-the-loop commit. (4) Reflect-and-retry hints let the LLM correct its own tool calls

agent.py, tools/planning.py, feedback/store.py

"Plan today's dispatch from Mumbai", then a thumbs-down with a comment

Self-healing agents

5 layers: a resilient model chain with rollback, the SelfHealingPlugin (argument repair, retries, breakers, reflection), API fallback chains, MCP failover, and Chaos Monkey + a healing journal

healing/, mcp_tools.py

Set the dashboard Chaos slider to 30% and keep chatting

Submission: GitHub repo + README

MIT-licensed repo with CI (lint, tests, offline end-to-end demo under chaos, Docker build), a Dockerfile and a one-command Cloud Run deploy

.github/workflows/ci.yml, Dockerfile

CI badge at the top

A per-criterion checklist with file and symbol pointers is in docs/JUDGING.md.


Architecture

System view

flowchart TB
    subgraph IF["Interfaces"]
        WEB["Mission-control dashboard<br/>FastAPI + SSE + Leaflet"]
        CLI["CLI<br/>chat / demo / doctor<br/>autopilot / watchtower / eval"]
        ADKUI["ADK Dev UI<br/>adk web src"]
        EXT["External MCP clients<br/>Claude Desktop / Claude Code / Gemini CLI"]
    end

    subgraph RT["Google ADK runtime"]
        RUN["Runner + session service<br/>stream_chat event stream"]
        PLUG["SelfHealingPlugin<br/>arg repair / retries / breakers / reflect"]
        CON["cargomind_concierge<br/>conversational orchestrator"]
        SPEC["8 specialist agents<br/>exposed as AgentTools"]
        WT["Watchtower<br/>background scan every 60 s"]
    end

    subgraph MODELS["ResilientLlm chain - one per agent"]
        G["Gemini tier model"] --> GB["Gemini backup"] --> V2["Claude or GPT<br/>via LiteLLM"] --> OFF["Offline rule-engine"]
    end

    subgraph TOOLS["Tools"]
        MCPT["ResilientMcpToolset<br/>MCP client with in-process failover"]
        GEO["geo: routes, multi-stop, geocode"]
        WX["weather: risk, route disruptions"]
        PLAN["planning: quotes, ETA,<br/>propose / validate / commit plan"]
        VIS["vision: proof-of-delivery photo"]
        COMMS["comms: draft + simulated send"]
        FB["feedback: remember_operator_feedback"]
    end

    MCPS["cargomind-logistics MCP server<br/>11 tools + 2 resources, stdio"]
    DB[("SQLite<br/>shipments / fleet / stock / POs / outbox<br/>healing_events / feedback / preferences")]

    WEB & CLI & ADKUI --> RUN --> CON --> SPEC
    PLUG -.-|"wraps every tool and model call"| RUN
    CON -.- MODELS
    SPEC -.- MODELS
    SPEC --> MCPT & GEO & WX & PLAN & VIS & COMMS
    CON --> FB & PLAN
    MCPT -- "stdio JSON-RPC" --> MCPS --> DB
    EXT -- "stdio JSON-RPC" --> MCPS
    PLAN & COMMS & FB --> DB
    WEB --> WT --> DB

    GEO --> GM["Google Routes v2 + Geocoding"] -.->|fallback| OSM["OSRM / Nominatim"] -.->|fallback| HAV["Haversine + gazetteer"]
    WX --> OM["Open-Meteo"] -.->|fallback| SEAS["Seasonal heuristic"]
    VIS --> GV["Gemini multimodal"] -.->|fallback| PIL["Pillow heuristic"]

Agent graph

flowchart LR
    U(["Operator"]) --> CON["cargomind_concierge<br/>LlmAgent - fast tier"]
    CON -->|AgentTool| TRK["tracking_agent"]
    CON -->|AgentTool| RRK["route_risk_agent"]
    CON -->|AgentTool| INV["inventory_agent"]
    CON -->|AgentTool| DP
    CON -->|AgentTool| COM["comms_agent"]
    CON -->|AgentTool| VI["vision_inspector<br/>vision tier"]
    CON -->|AgentTool| OB
    CON -->|AgentTool| AP
    CON -->|tool| CMT["commit_dispatch_plan"]
    CON -->|tool| REM["remember_operator_feedback"]

    subgraph DP["dispatch_pipeline - SequentialAgent"]
        direction TB
        subgraph LOOP["plan_critique_loop - LoopAgent, max 4"]
            PL["dispatch_planner<br/>pro tier"] --> CR["dispatch_critic<br/>critic tier = other vendor"]
            CR -->|"issues -> revise"| PL
        end
        LOOP -->|"exit_loop when valid"| REP["dispatch_reporter"]
    end

    subgraph OB["ops_briefing - SequentialAgent"]
        direction TB
        subgraph FAN["signal_fanout - ParallelAgent"]
            K["kpi_watch"]
            R["risk_watch"]
            S["stock_watch"]
        end
        FAN -->|"output_key state"| BW["briefing_writer"]
    end

    subgraph AP["disruption_autopilot - SequentialAgent"]
        direction TB
        IA["impact_assessor"] --> MF
        subgraph MF["mitigation_fanout - ParallelAgent"]
            RP["reroute_planner<br/>pro tier"]
            ND["notice_drafter<br/>drafts only"]
        end
        MF -->|"output_key state"| AC["autopilot_commander<br/>incident action plan"]
    end

Model chain (one per agent, built by build_model(tier))

Tier

Used by

Chain, tried in order (links without a key are left out)

fast

concierge, tracking, route/risk, inventory, comms, reporter, watchers, briefing writer, impact assessor, notice drafter, autopilot commander

gemini-3.8-flash → gemini-2.5-flash → Claude or GPT → offline/rule-engine

pro

dispatch planner, autopilot reroute planner

gemini-4-argon → gemini-2.5-flash → Claude or GPT → offline/rule-engine

critic

dispatch critic

Claude or GPT first (independent vendor) → gemini-4-argon → gemini-2.5-flash → offline/rule-engine

vision

vision inspector

CARGOMIND_GEMINI_VISION (default: the fast model) → gemini-2.5-flash → Claude or GPT → offline/rule-engine

The second vendor is Claude (anthropic/claude-sonnet-5-5) when ANTHROPIC_API_KEY is set. Otherwise it is GPT (openai/gpt-5-mini) when OPENAI_API_KEY is set. Both run through LiteLLM. Every model id can be overridden: see Configuration reference.

Sequence diagrams for a dispatch request and a self-healing fallback are in docs/ARCHITECTURE.md.


Feature tour

Agent

ADK pattern

Model tier

What it does

Tools

cargomind_concierge

LlmAgent that orchestrates AgentTools

fast

Conversational front door. Understands the goal, calls one or more specialists in a single turn, asks clarifying questions and applies learned lessons

8 specialist AgentTools, commit_dispatch_plan, remember_operator_feedback

tracking_agent

LlmAgent

fast

Status, location, history, ETA and SLA risk. Updates status only when asked

MCP: track_shipment, list_shipments, shipment_history, update_shipment_status. Native: estimate_eta

route_risk_agent

LlmAgent

fast

Road distance and time, multi-stop optimisation, weather and disruption risk, carrier quotes

get_route, optimize_multi_stop_route, geocode, get_weather_risk, check_route_disruptions, quote_carriers

inventory_agent

LlmAgent

fast

Stock levels, low-stock alerts, reorders (POs only when asked), warehouse info

MCP: check_inventory, create_reorder, list_warehouses

dispatch_pipeline

SequentialAgent[LoopAgent[planner, critic], reporter]

pro + critic + fast

Assigns pending shipments to own-fleet trucks. An independent critic checks capacity, driver hours, cold chain, hazmat, SLA, weather and duplicates. The planner revises (weather deferrals, driver swaps, team drivers, carrier hand-offs, corridor splits) until the plan is valid, then a report is written

propose_dispatch_plan, validate_dispatch_plan, exit_loop

comms_agent

LlmAgent

fast

Empathetic, specific delay or delivery notices (email, SMS, WhatsApp). Sends only after approval, to a simulated outbox

draft_customer_notification, send_notification, MCP track_shipment

vision_inspector

LlmAgent

vision

Proof-of-delivery photo inspection: damage, severity, findings, recommended action

inspect_delivery_photo (Gemini multimodal, JSON output)

ops_briefing

SequentialAgent[ParallelAgent[3 watchers], writer]

fast

Morning briefing: KPIs, weather at 6 hubs and low stock are gathered in parallel, then written up as Headline, Risks and Actions

MCP get_kpis, hub_weather_sweep, MCP check_inventory

disruption_autopilot

SequentialAgent[impact_assessor, ParallelAgent[reroute_planner, notice_drafter], autopilot_commander]

fast + pro

City-level disruption (cyclone, flood, strike, bandh, closure, landslide): finds exposed shipments, vehicles and warehouses, proposes reroutes/holds via an alternate hub, drafts customer notices (never sends) and writes an incident action plan. Nothing executes until the operator approves

assess_disruption_impact, propose_disruption_reroutes, draft_disruption_notices

The seeded network. On first run CargoMind creates data/cargomind.db with a deterministic India-wide network. It contains 25 shipments (SHP-1001…SHP-1025, with real-world problems such as checkpost holds, monsoon delays, crushed cartons and hazmat), 5 warehouses (WH-BOM, WH-DEL, WH-BLR, WH-MAA, WH-HYD), 20 SKUs (some below their reorder point), 10 trucks (TRK-101…TRK-110, including a cold-chain reefer), 5 carriers (CR-01…CR-05) and a 20-city gazetteer. The gazetteer knows aliases such as Bangalore, Bombay, Madras and Gurgaon, and IATA-style codes such as BLR and BOM.


Quickstart

Works on Windows, macOS and Linux with zero API keys. You need Git and uv. uv installs Python 3.12 for you if you don't have it.

git clone https://github.com/Anandb71/cargomind
cd cargomind
uv sync
cp .env.example .env          # Windows: copy .env.example .env
uv run cargomind web          # open http://localhost:8080

Then try uv run cargomind doctor (self-check), uv run cargomind demo (scripted 8-step scenario) or uv run cargomind chat (terminal chat).

With no keys, every answer comes from the offline rule-engine and the free or offline API fallbacks. The answers are structured and correct but templated. Add a free Gemini key to .env (2 minutes) to get the full multi-model experience.


Full setup (Windows, macOS, Linux)

Prerequisites

Need

Why

Install

Git

clone the repo

https://git-scm.com/downloads (or use GitHub's Code → Download ZIP)

uv

Python + dependency manager (reads uv.lock, installs Python 3.12 automatically)

see below

Internet on first run

download dependencies; live APIs are optional

–

(optional) Docker

container run

https://docs.docker.com/get-docker/

(optional) Google Cloud SDK

Cloud Run deploy

https://cloud.google.com/sdk/docs/install

(optional) Node.js 18+

MCP Inspector only

https://nodejs.org

Windows 10/11 (PowerShell)

# 1. Install uv, then CLOSE and REOPEN the terminal so `uv` is on PATH
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# 2. Get the code (two separate lines: Windows PowerShell 5.1 has no `&&`)
git clone https://github.com/Anandb71/cargomind
cd cargomind

# 3. Install Python 3.12 + all dependencies into .venv
uv sync

# 4. Create your settings file (all keys optional)
copy .env.example .env
notepad .env

# 5. Check, then run
uv run cargomind doctor
uv run cargomind web

Open http://localhost:8080. If you hit "path too long" errors or garbled characters, see Troubleshooting.

macOS

curl -LsSf https://astral.sh/uv/install.sh | sh     # or: brew install uv
exec $SHELL -l                                      # reload PATH
git clone https://github.com/Anandb71/cargomind && cd cargomind
uv sync
cp .env.example .env && open -e .env
uv run cargomind doctor
uv run cargomind web                                # http://localhost:8080

Linux

curl -LsSf https://astral.sh/uv/install.sh | sh
source "$HOME/.local/bin/env" 2>/dev/null || export PATH="$HOME/.local/bin:$PATH"
git clone https://github.com/Anandb71/cargomind && cd cargomind
uv sync
cp .env.example .env && ${EDITOR:-nano} .env
uv run cargomind doctor
uv run cargomind web                                # http://localhost:8080

Verify the install

uv run cargomind doctor prints which providers are live, the configured model ids, live KPIs from the seeded database, and a smoke-test answer to "Where is SHP-1003?". The first line tells you the mode:

CargoMind | live: none | offline rule-engine fallback: on                       <- zero keys
CargoMind | live: gemini, claude, google_maps | offline rule-engine fallback: on <- keys picked up

Getting the API keys

Every key is optional. Put keys in .env at the repo root. Empty values and placeholders that start with PASTE_, YOUR_ or < are ignored. Environment variables already set in your shell take precedence over .env. After changing .env, restart the command and re-run uv run cargomind doctor.

What each key unlocks

You set

Conversation and planning

Dispatch critic

Routes and geocoding

Weather

Photo inspection

(nothing)

offline rule-engine

offline rule-engine

OSRM / Nominatim (free, no key) → haversine

Open-Meteo (free, no key) → seasonal heuristic

Pillow heuristic

GOOGLE_API_KEY

Gemini fast / pro + backup

Gemini Pro

same as above

same

Gemini multimodal

+ ANTHROPIC_API_KEY or OPENAI_API_KEY

+ cross-vendor failover

Claude / GPT (independent vendor)

same

same

same

+ GOOGLE_MAPS_API_KEY

–

–

Google Routes API v2 + Geocoding

same

same

CARGOMIND_OFFLINE=1

forces offline everywhere (no network at all)

offline

gazetteer + haversine

seasonal heuristic

Pillow heuristic

1. Gemini API key (the brain)

  1. Open https://aistudio.google.com/apikey and sign in with a Google account.

  2. Click Create API key. Pick an existing Google Cloud project or let AI Studio create one.

  3. Copy the key and paste it into .env:

    GOOGLE_API_KEY=AIza...your-key...
  4. Run uv run cargomind doctor. The first line should now include gemini.

The free tier has per-minute limits. When you hit one, Gemini returns HTTP 429. CargoMind retries, then fails over to the backup Gemini model, then to the second vendor, then to the offline engine, automatically (see Self-healing).

GOOGLE_GENAI_USE_VERTEXAI=true
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=us-central1

Then authenticate once with gcloud auth application-default login. Enable the Vertex AI API on the project, and check that the model ids you configure are available in that location.

2. Google Maps API key (Routes API + Geocoding API)

  1. Open https://console.cloud.google.com/ and select (or create) a project. Google Maps Platform requires a billing account on the project. It includes a monthly free usage allowance; check current pricing on the Maps Platform site.

  2. Go to APIs & Services → Library. Search for Routes API and click Enable. Then search for Geocoding API and click Enable. CargoMind calls the Routes API v2 endpoint routes.googleapis.com/directions/v2:computeRoutes, not the legacy Directions API.

  3. Open https://console.cloud.google.com/google/maps-apis/credentials and choose Create credentials → API key.

  4. Recommended: click the key, open API restrictions, choose Restrict key and select Routes API and Geocoding API.

  5. Paste it into .env:

    GOOGLE_MAPS_API_KEY=AIza...your-maps-key...
  6. Run uv run cargomind doctor. The first line should now include google_maps. Route answers now show source: google_routes. Before this they showed osrm or haversine with degraded: true.

3. Second-vendor model: Claude or OpenAI (pick one)

Anthropic Claude (preferred when both are set):

  1. Open https://console.anthropic.com/settings/keys. Add credits under Billing if the account is new.

  2. Click Create Key, name it cargomind and copy it.

  3. Add ANTHROPIC_API_KEY=sk-ant-... to .env.

OpenAI:

  1. Open https://platform.openai.com/api-keys. The account needs billing or credits.

  2. Click Create new secret key and copy it.

  3. Add OPENAI_API_KEY=sk-... to .env.

The second vendor runs the dispatch critic first, so the plan is reviewed by a different model family from the one that wrote it. It is also the cross-vendor failover link for every other agent. If both keys are set, Claude is used.


Configuration reference

Every setting is an environment variable, read in src/cargomind/config.py. Put them in .env or export them in your shell. Shell values win.

Variable

Default

Purpose

GOOGLE_API_KEY (alias GEMINI_API_KEY)

(empty)

Gemini API key. Enables all Gemini tiers and Gemini vision

GOOGLE_GENAI_USE_VERTEXAI

false

true uses Vertex AI (with GOOGLE_CLOUD_PROJECT / GOOGLE_CLOUD_LOCATION and ADC) instead of an API key

GOOGLE_MAPS_API_KEY

(empty)

Google Routes API v2 + Geocoding API

ANTHROPIC_API_KEY

(empty)

Claude as the second vendor (critic + failover)

OPENAI_API_KEY

(empty)

GPT as the second vendor if no Anthropic key is set

CARGOMIND_GEMINI_FAST

gemini-3.8-flash

Fast conversational / routing model

CARGOMIND_GEMINI_PRO

gemini-4-argon

Deep-reasoning model for the planner (and critic fallback)

CARGOMIND_GEMINI_BACKUP

gemini-2.5-flash

Same-vendor backup, tried before switching vendors

CARGOMIND_GEMINI_VISION

value of CARGOMIND_GEMINI_FAST

Multimodal model for proof-of-delivery photos

CARGOMIND_CLAUDE_MODEL

anthropic/claude-sonnet-5-5

LiteLLM model id for Claude

CARGOMIND_OPENAI_MODEL

openai/gpt-5-mini

LiteLLM model id for OpenAI

CARGOMIND_OFFLINE

false

1/true forces the offline rule-engine and network-free tool fallbacks

CARGOMIND_MCP_LOCAL

false

1/true serves the MCP tool set in-process instead of spawning the stdio server

CARGOMIND_CHAOS

0

Chaos Monkey fault-injection rate, 0…1 (also adjustable live in the dashboard)

CARGOMIND_TOOL_RETRIES

2

Retries per failed tool call (exponential backoff 0.3 s, 0.6 s, …)

CARGOMIND_BREAKER_THRESHOLD

3

Consecutive failures that open a circuit breaker

CARGOMIND_BREAKER_COOLDOWN

60

Seconds a breaker stays open before a half-open trial

CARGOMIND_HTTP_TIMEOUT

8

Seconds per external HTTP call before falling back

CARGOMIND_DATA_DIR

./data

Where the SQLite DB and uploaded photos live

CARGOMIND_DB

<data dir>/cargomind.db

SQLite database file

CARGOMIND_WATCHTOWER

1

0/false/no/off stops the dashboard from starting the Watchtower background scan

PORT

8080

Dashboard port (Cloud Run sets it automatically)

HOST

0.0.0.0

Dashboard bind address

LOG_LEVEL

info

Uvicorn log level for the dashboard

PYTHONUTF8

set to 1 by CargoMind

UTF-8 I/O for LiteLLM and child processes on Windows


CLI reference

All commands run from the repo root with uv run …. They are installed as console scripts by uv sync.

Command

What it does

uv run cargomind doctor

Shows live providers and model ids, prints KPIs from the DB and runs a one-question smoke test

uv run cargomind web [--port 8080]

Starts the mission-control dashboard and API at http://localhost:8080 (API docs at /docs)

uv run cargomind chat

Interactive multi-turn chat in the terminal. Shows tool calls (↳), healing events (⚕) and which model served each answer. Type exit to quit

uv run cargomind demo

Runs the scripted 8-step scenario from Demo script in one session, then prints self-healing stats

uv run cargomind demo --chaos 0.3

Same, with 30% of tool and model calls failing on the first try, to show self-healing

uv run cargomind mcp

Runs the cargomind-logistics MCP server on stdio, for Claude Desktop or any MCP client. It waits silently for a client

uv run cargomind reset-db

Re-seeds the demo database (shipments, fleet, stock, POs, outbox)

uv run cargomind autopilot Chennai --event cyclone --severity high

Runs the Disruption Autopilot steps directly (impact → reroutes → notice drafts) and prints the actions. --event: cyclone, flood, strike, closure, … (default cyclone). --severity: low/medium/high (default high)

uv run cargomind autopilot Chennai --event cyclone --agent

Same, but through the full multi-agent concierge (disruption_autopilot workflow, LLMs, self-healing, journal)

uv run cargomind watchtower

One Watchtower scan: prints every active alert with severity, detail and the suggested question

uv run cargomind eval --mode offline --out docs/EVAL_REPORT.md

Runs the 18-scenario eval harness and writes the report. All flags pass through to evals/runner.py

uv run adk web src

Google ADK Dev UI at http://localhost:8000. Pick cargomind

uv run adk run src/cargomind

ADK's own terminal runner for the same agent

uv run cargomind-web / uv run cargomind-mcp

Aliases for cargomind web / cargomind mcp

uv run pytest

Test suite


Mission-control dashboard

uv run cargomind web        # http://localhost:8080   (other port: --port 8090)

Area

What you see and do

Top bar

Status pills showing which models and APIs are live, the network clock (IST), and the Chaos Monkey slider (0–60%), which injects transient faults into tool and model calls live

Watchtower

Proactive alerts (critical first) from the background scan. Click an alert to send its suggested question to the agent, or scan on demand

Network pulse

KPI tiles: on-time rate, delayed and critical shipments, fleet utilisation, open POs and more. Refreshes every 10 s

Shipments

Filterable table (ID, customer, city) of the seeded network

Network map

Leaflet + OpenStreetMap map of hubs, cities and shipment lanes, coloured by status

Ops agent chat

Streaming conversation with suggested questions, a new session button and photo attach (button, paste or drag-and-drop). The trace shows agent hand-offs, every tool call with its arguments and results, and the model that served each answer. Thumbs up/down + comment on answers feed the learning loop

Self-healing journal

Live SSE feed of fault, retry, fallback, arg_repair, reflect, breaker_open/close and recovered events, circuit-breaker states, and Lessons learned distilled from operator feedback

Method

Path

Purpose

POST

/api/chat

{session_id, message, image_ref?} → SSE stream of agent, tool_call, tool_result, text, healing, error and done events

POST

/api/upload

Upload a delivery photo (multipart file, or JSON {filename, data_b64}). PNG/JPEG/GIF/WebP/BMP, max 8 MB, checked by magic bytes + Pillow. Returns image_ref

GET

/api/uploads/{name}

Serve an uploaded photo

POST

/api/feedback

{session_id, rating: up/down/±1, comment, message, agent} → stored, returns the current lessons

GET

/api/feedback/lessons

Distilled lessons + satisfaction stats

GET

/api/kpis, /api/shipments, /api/vehicles, /api/warehouses, /api/inventory, /api/outbox, /api/cities

Network data (filters: status, city, priority, low_stock_only, …)

GET

/api/healing

Recent journal events, stats, circuit-breaker states, chaos rate

GET

/api/healing/stream

SSE live healing journal

GET/POST

/api/chaos

Read or set the chaos rate ({rate: 0.3} or {rate: 30})

GET

/api/alerts

Active Watchtower alerts (id, kind, severity, title, detail, suggested_prompt, raised_at)

POST

/api/alerts/scan

Run a Watchtower scan now; returns new and active alerts

GET

/api/health

Mode, live providers, component presence, library versions

Example:

curl -N -X POST http://localhost:8080/api/chat -H "Content-Type: application/json" \
     -d '{"session_id":"curl-1","message":"Where is SHP-1003?"}'

ADK Dev UI

Google ADK ships a developer UI. CargoMind's package exposes its ADK App (agent graph + SelfHealingPlugin) in src/cargomind/__init__.py, so the dev UI runs exactly the same system.

uv run adk web src                 # http://localhost:8000
uv run adk web --port 8001 src     # if 8000 is busy
  1. Open http://localhost:8000. In the agent dropdown (top left), select cargomind.

  2. Chat as usual, for example "Plan today's dispatch from Mumbai".

  3. Use the Events list to inspect every model request and response, function call and function response, including the planner ⇄ critic iterations.

  4. Use State to see the session state the agents pass to each other, such as current_plan, plan_issues, dispatch_plan_summary, kpi_snapshot, risk_snapshot and stock_snapshot.

Notes: adk web also reads the repo-root .env through config.py. On Windows, ADK turns off auto-reload by itself, because reload is incompatible with the MCP stdio subprocess. For a terminal-only ADK session use uv run adk run src/cargomind.


MCP server

cargomind-logistics is a real Model Context Protocol server built with the Python MCP SDK. It exposes the operational database over stdio.

Tool

Arguments

Purpose

track_shipment

shipment_id

Status, location, carrier/vehicle, ETA, promised date

list_shipments

status, city, priority, limit

Filtered list, critical first

shipment_history

shipment_id

Event timeline

update_shipment_status

shipment_id, status, city, note

Write a status change + event

check_inventory

sku, warehouse_id, low_stock_only

Stock vs reorder points

create_reorder

sku, warehouse_id, qty

Raise a purchase order

list_vehicles

status, city

Fleet, capacity, driver hours, refrigeration

assign_vehicle

shipment_id, vehicle_id

Assign a truck to a shipment

list_carriers

–

Carriers, modes, rates, reliability

list_warehouses

–

Warehouse directory

get_kpis

–

Network KPIs

Resources: logistics://kpis and logistics://warehouses (JSON). A look-up that misses never throws. It returns {"status": "error", "error": ..., "suggestions": [closest valid IDs]} so the calling LLM can correct itself.

How CargoMind's own agents use it

The specialists get their MCP tools from mcp("track_shipment", ...) in mcp_tools.py. That creates a ResilientMcpToolset wrapping ADK's McpToolset, which spawns python -m cargomind.mcp_server.server over stdio. If the server can't start or stops answering, the toolset journals the fault, opens a breaker and serves the same tool names in-process, so the conversation never breaks. After the cooldown it reconnects and journals recovered.

Use it from Claude Desktop

  1. Find the absolute path to your clone and to uv (where uv on Windows, which uv on macOS/Linux).

  2. Open Claude Desktop → Settings → Developer → Edit Config. That opens claude_desktop_config.json:

    • Windows: %APPDATA%\Claude\claude_desktop_config.json

    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  3. Paste the contents of deploy/claude_desktop_config.example.json and replace the path:

    {
      "mcpServers": {
        "cargomind-logistics": {
          "command": "uv",
          "args": ["--directory", "/ABSOLUTE/PATH/TO/cargomind", "run", "cargomind", "mcp"],
          "env": { "PYTHONUTF8": "1" }
        }
      }
    }

    On Windows, escape backslashes ("C:\\Users\\you\\cargomind"). If Claude Desktop can't find uv, put the full path to uv in "command".

  4. Fully quit Claude Desktop (including the tray icon) and reopen it. Then ask: "Using cargomind-logistics, which critical shipments are delayed?"

The MCP server and the dashboard share data/cargomind.db. A status change made from Claude Desktop appears on the dashboard's next refresh.

Use it from Claude Code, Gemini CLI or the MCP Inspector

# Claude Code
claude mcp add cargomind-logistics -- uv --directory /ABSOLUTE/PATH/TO/cargomind run cargomind mcp

# MCP Inspector (needs Node.js 18+): browse tools, call them by hand
npx @modelcontextprotocol/inspector uv --directory /ABSOLUTE/PATH/TO/cargomind run cargomind mcp

Gemini CLI: add this to ~/.gemini/settings.json, then run /mcp inside Gemini CLI to see the tools.

{
  "mcpServers": {
    "cargomind-logistics": {
      "command": "uv",
      "args": ["--directory", "/ABSOLUTE/PATH/TO/cargomind", "run", "cargomind", "mcp"]
    }
  }
}

Demo script

These are the same 8 prompts uv run cargomind demo runs, in one session. Type them into the dashboard or cargomind chat to drive them yourself. Wording varies by model. In zero-key mode the rule-engine answers with structured markdown tables.

#

Prompt

Routed to

What happens / what to look for

1

Morning ops briefing

ops_briefing

A ParallelAgent runs kpi_watch (MCP get_kpis), risk_watch (hub_weather_sweep over 6 hubs at once) and stock_watch (MCP check_inventory, low stock only) concurrently. Then briefing_writer turns their output_key snapshots into Headline / Risks / Actions

2

Where is SHP-1003 and will it make its SLA?

tracking_agent

MCP track_shipment + estimate_eta. SHP-1003 is a critical Delhi → Jaipur PPE consignment held at a checkpost over an e-way-bill mismatch. Expect delayed and an SLA-risk verdict

3

Any weather risk on Mumbai → Pune today?

route_risk_agent

check_route_disruptions samples the Open-Meteo forecast at the origin, an en-route midpoint and the destination. It returns an overall risk level, a recommendation, a suggested ETA buffer, and sources / degraded flags

4

Quote carriers Delhi → Bengaluru for 800kg express

route_risk_agent

quote_carriers compares the partner carriers for the lane. It returns the top 3 quotes (cost in INR, ETA, reliability, score, reasons), the excluded carriers with the reason, and a recommendation. Express pricing is ×1.35

5

Plan today's dispatch from Mumbai

dispatch_pipeline

The feedback loop: the planner (pro tier) calls propose_dispatch_plan. The critic (other vendor) calls validate_dispatch_plan and flags blockers such as capacity, driver hours, cold chain, hazmat, SLA and weather. The planner revises. When the plan is valid the critic calls exit_loop and the reporter shows the final table and asks you to reply "commit the plan"

6

Which SKUs need reordering?

inventory_agent

check_inventory(low_stock_only=true). Several SKU/warehouse pairs are seeded below their reorder point, including insulin pens (SKU-107) at WH-BLR. No PO is raised unless you ask

7

Draft a WhatsApp delay notice for SHP-1007

comms_agent

track_shipment + draft_customer_notification(channel="whatsapp"). Nothing is sent until you approve ("send it"). Sending goes to a simulated outbox (/api/outbox)

8

Remember: always prioritise critical pharma shipments over express ones

concierge → remember_operator_feedback

Stored as a lasting PREFERENCE. It appears under Lessons learned and is added to the concierge's instruction from the next turn on

Bonus prompts that show off specific features:

Prompt

Shows

commit the plan (after #5)

Human-in-the-loop: commit_dispatch_plan assigns trucks in the DB. Watch the dashboard update

Attach a photo + Inspect this delivery photo for SHP-1004

Gemini multimodal inspection: damage_detected, severity, findings, recommended_action. SHP-1004 is seeded as an exception with crushed cartons

where is shp 1003 / route bangalore to madras

Argument repair: watch for arg_repair events (shp 1003 → SHP-1003, bangalore → Bengaluru)

Optimise a milk run from Mumbai via Pune, Surat and Ahmedabad

Google Routes waypoint optimisation (or the OSRM / haversine fallback)

Show delayed critical shipments and draft notices for them

A multi-domain turn: the concierge calls several specialists in one go

Reorder SKU-107 for WH-BLR

Write action through MCP create_reorder

Cyclone alert in Chennai, severity high - run disruption autopilot

The Disruption Autopilot: impact → reroutes + notice drafts in parallel → incident action plan awaiting approval

The timed, click-by-click version for presenting is in docs/DEMO_SCRIPT.md.


Self-healing deep dive

CargoMind assumes things will break: free-tier quotas, flaky public APIs, hallucinated arguments, crashed subprocesses. It has five layers of defence, and every action is written to a journal you can watch live.

#

Layer

Where

What it heals

1

ResilientLlm model chain

healing/resilient_llm.py

Each agent's model is a chain: Gemini tier → Gemini backup → Claude/GPT → offline rule-engine. Transient errors (408/429/5xx, timeouts) are retried in place (0.4 s, 1.0 s). Other errors, such as a bad key or an unknown model, fall straight through. The request is rolled back to a pristine snapshot before every attempt. Each model has a circuit breaker (3 failures → skipped for 60 s). Every response is stamped served_by, fallbacks and tier

2

SelfHealingPlugin (runner-wide ADK plugin)

healing/plugin.py

before_tool: argument repair ("shp 1003" → SHP-1003, "trk101" → TRK-101, "wh blr" → WH-BLR, "bangalore" → Bengaluru) and a per-tool breaker short-circuit. on_tool_error: retries transient errors with backoff (chaos suppressed during retries). Otherwise it returns a structured error with a healing_hint so the LLM reflects and retries with corrected arguments. after_tool: adds hints to tool-reported errors. on_model_error: returns a graceful message instead of crashing the turn

3

API provider fallback chains

tools/geo.py, tools/weather.py, tools/vision.py

Routes: Google Routes v2 → OSRM → haversine × road factor. Geocoding: Google → Nominatim → gazetteer. Weather: Open-Meteo → seasonal heuristic. Vision: Gemini → Pillow heuristic. Every result carries source and degraded, and agents are told to say when data is degraded

4

ResilientMcpToolset

mcp_tools.py

If the MCP server can't be spawned or crashes, it serves identical tools in-process, opens a breaker and reconnects after the cooldown

5

Chaos Monkey + healing journal

healing/chaos.py, healing/journal.py, healing/breaker.py

maybe_fail("tool:<name>") / maybe_fail("model:<id>") inject transient faults at a configurable rate. Every fault, retry, fallback, breaker transition, repair, reflection and recovery goes to an in-memory ring (500 events) and a SQLite healing_events table, and is pushed to subscribers (dashboard SSE, CLI)

Journal event kinds: fault (injected or real failure) · retry · fallback (next model or provider) · breaker_open / breaker_close · arg_repair · reflect (hint handed to the LLM) · recovered.

How to demo it

Recipe

Steps

What you'll see

Chaos slider (best on stage)

Dashboard → drag Chaos Monkey to 30% → ask "Morning ops briefing"

The journal fills with fault → retry → recovered. The answer is still complete

Scripted chaos run

uv run cargomind demo --chaos 0.3

⚕ healing lines between steps, then Self-healing stats: {...}

Chaos for a whole session

Add CARGOMIND_CHAOS=0.3 to .env, or for one shell: $env:CARGOMIND_CHAOS="0.3" (PowerShell) / export CARGOMIND_CHAOS=0.3 (bash)

Every interface runs under chaos

Kill Gemini, deterministically

uv run python -c "from cargomind.healing import chaos; chaos.force_fail('model:gemini', 50); import asyncio; from cargomind.runtime import ask; print(asyncio.run(ask('Where is SHP-1003?', verbose=True)))"

Every Gemini call fails. Watch retry → fallback → breaker_open, then the answer is served_by Claude/GPT, or by the offline engine if you have no second key

Bad API key

Set GOOGLE_API_KEY=invalid in .env and run uv run cargomind chat

Non-transient error → immediate fallback down the chain. The breaker then skips Gemini for 60 s

Broken arguments

Ask "where is shp 1003" or "route bangalore to madras"

arg_repair events with before/after values

No internet

CARGOMIND_OFFLINE=1

All tools serve offline estimates flagged degraded: true, and the offline rule-engine answers


Feedback loops deep dive

CargoMind runs four feedback loops, two driven by agents and two by humans.

1. Agent ⇄ agent: plan → critique → revise (LoopAgent). dispatch_planner (pro tier) calls propose_dispatch_plan. That builds a quick greedy plan assigning pending shipments to available own-fleet trucks, and writes state["current_plan"].

dispatch_critic runs on the critic tier: a different vendor when a Claude or OpenAI key is set. It calls validate_dispatch_plan, which checks every assignment for:

  • vehicle capacity

  • driver duty hours

  • cold-chain and dangerous-goods handling

  • weather risk

  • SLA for critical and express loads

  • diverging multi-drop routes, duplicate assignments, and critical shipments left without transport

Each issue has a severity (blocker / warning) and a suggested fix. The critic writes them to state["plan_issues"] and returns {valid, issues, score}.

If there are blockers, the next loop iteration runs the planner again. propose_dispatch_plan then revises the plan to fix them, using weather deferrals, driver-hour swaps, team drivers, carrier hand-offs for priority loads or corridor splits. When the plan is valid, the critic calls exit_loop. The loop is capped at 4 iterations. dispatch_reporter then summarises the revisions, fixed issues, assignments and deferred shipments. commit_dispatch_plan refuses an unvalidated revision unless the operator insists. Carrier hand-offs come back as booking actions; they are never auto-booked.

2. Human → agent: ratings, corrections and preferences that persist.

  • In the dashboard, the thumbs up/down + comment on each answer goes to POST /api/feedback, which writes the SQLite feedback table.

  • In conversation, when the operator corrects the agent or says "remember / always / never / prefer…", the concierge calls remember_operator_feedback. Text with always / never / prefer / avoid is also saved as a lasting preference.

  • lessons() distils all of this into at most 8 deduplicated lines: PREFERENCE: …, then AVOID: … (from negative comments), then KEEP: … (from positive ones).

  • render_lessons_block() appends those lines to the concierge's instruction. The instruction is a function (concierge_instruction), so ADK re-evaluates it on every turn. Feedback changes behaviour on the very next message, with no restart.

  • The dashboard's Lessons learned panel shows what is currently being applied.

Check it from the terminal:

uv run python -c "from cargomind.feedback.store import render_lessons_block, stats; print(render_lessons_block()); print(stats())"

3. Human-in-the-loop gates. Plans are only applied after "commit the plan". Notifications are only sent after approval. POs are raised and statuses changed only when explicitly asked. The agent proposes and the operator decides.

4. Tool → LLM: reflect and retry. Failed or not-found tool calls come back as structured errors with suggestions (closest valid IDs) and a healing_hint. Agents are instructed to correct their arguments and retry once before asking the user.


Disruption Autopilot and Watchtower

Disruption Autopilot (autopilot.py) handles a city-level event (cyclone, flood, strike, bandh, road closure, landslide) as one workflow. The concierge calls it as AgentTool(disruption_autopilot) when a message mentions autopilot, cyclone, flood, strike, bandh, closure, disruption, landslide or "alert in".

Step

Agent

Tool

Output (output_key)

1

impact_assessor (fast)

assess_disruption_impact

Exposed shipments, vehicles and warehouses, plus hub weather → autopilot_impact

2a (parallel)

reroute_planner (pro)

propose_disruption_reroutes

Reroute / hold actions via an alternate hub, critical first, and vehicle moves out of the zone → autopilot_reroutes

2b (parallel)

notice_drafter (fast)

draft_disruption_notices

Customer notice drafts for urgent shipments (sent: false) → autopilot_notices

3

autopilot_commander (fast)

–

Incident action plan: Situation, Exposure, Actions (owner + deadline), Customer comms, Approval needed

The workflow produces proposals and drafts for operator review. Reroutes and vehicle moves must be applied manually in the transport system. To queue an approved notice in the simulated outbox, name its shipment, channel and message when asking the comms agent; there is no batch approval command. run_autopilot(city, event, severity) runs the three tools directly without an LLM; the CLI and tests use it.

uv run cargomind autopilot Chennai --event cyclone --severity high           # direct tools, instant
uv run cargomind autopilot Chennai --event cyclone --severity high --agent   # full multi-agent workflow

Watchtower (watchtower.py) raises alerts before the operator asks. uv run cargomind web starts run_watchtower(60) as a background task in the server lifespan (set CARGOMIND_WATCHTOWER=0 to disable it). Each scan (scan_once() → {"new": [...], "active": [...]}) checks:

Check

Alert kind

Raised when

SLA breach risk

sla_risk

ETA is later than promised_by, or the shipment is marked delayed (critical priority → critical severity)

Exceptions

exception

Shipment status is exception

Low stock

low_stock

SKU below its reorder point with no open PO

Fleet

fleet

Fuel below 20% or driver hours below 1 h

Hub weather

weather

Medium or high 24 h weather risk at one of 6 hubs. The suggested prompt runs the Disruption Autopilot

Every alert has id, kind, severity, title, detail and a suggested_prompt. Existing alerts from a failing source are retained with stale: true until a healthy scan updates or resolves them. Other checks continue. See the alerts in the dashboard's Watchtower panel, at GET /api/alerts / POST /api/alerts/scan, or with uv run cargomind watchtower.


Evaluation

uv run cargomind eval --mode offline --out docs/EVAL_REPORT.md runs 18 scenarios (one fresh session each) through the real agent graph and scores routing, answer content, latency and healing events. Latest run, in offline rule-engine mode (no API keys, chaos 0%, in-process MCP). Full report: docs/EVAL_REPORT.md.

Metric

Value

Task success

18/18 (100%)

Answer pass rate

100%

Routing precision / recall / F1 (micro)

0.950 / 1.000 / 0.974

Routing exact-set match

94.4%

Latency p50 / p95

0.47 s / 2.85 s

Runtime errors

0

Every scenario reached the right specialist (recall 1.0). Precision is below 1.0 because one prompt is routed to an extra agent: the feedback prompt also calls tracking_agent. The harness caught two real rule-engine bugs, and both are fixed with regression tests: "Which" was parsed as warehouse WH-ICH, and "SKUs" was parsed as SKU-S. These numbers are for the offline rule-engine; live-model results will differ, so rerun with your keys (--mode online) to measure them.


Project structure

cargomind/
├── src/cargomind/
│   ├── __init__.py              # exposes `app` / `root_agent` lazily, so `adk web src` finds the agent
│   ├── agent.py                 # the agent graph: concierge + 8 specialists (23 ADK agents), App + plugin
│   ├── autopilot.py             # Disruption Autopilot: impact -> parallel reroutes + notice drafts -> action plan
│   ├── watchtower.py            # proactive monitor: SLA, exception, stock, fleet and weather alerts
│   ├── runtime.py               # ADK Runner, sessions, stream_chat() merging agent + healing events
│   ├── cli.py                   # `cargomind` command: chat / demo / web / mcp / doctor / reset-db / autopilot / watchtower / eval
│   ├── config.py                # every env var, model ids, key detection, mode summary
│   ├── mcp_tools.py             # ResilientMcpToolset: MCP client with in-process failover
│   ├── data/
│   │   ├── gazetteer.py         # 20 Indian cities, aliases, IATA codes, haversine
│   │   └── db.py                # SQLite schema, deterministic seed, queries, KPIs
│   ├── mcp_server/
│   │   └── server.py            # cargomind-logistics MCP server: 11 tools + 2 resources (stdio)
│   ├── tools/
│   │   ├── geo.py               # Google Routes v2 / Geocoding -> OSRM / Nominatim -> haversine
│   │   ├── weather.py           # Open-Meteo -> seasonal heuristic
│   │   ├── planning.py          # carrier quotes, ETA, dispatch propose / validate / commit
│   │   ├── vision.py            # Gemini multimodal proof-of-delivery inspection -> Pillow heuristic
│   │   └── comms.py             # customer notification drafts + simulated outbox
│   ├── healing/
│   │   ├── resilient_llm.py     # cross-vendor model chain with retries, breakers, request rollback
│   │   ├── offline_llm.py       # deterministic rule-engine that speaks the ADK model protocol
│   │   ├── plugin.py            # SelfHealingPlugin (ADK BasePlugin)
│   │   ├── breaker.py           # circuit breakers
│   │   ├── chaos.py             # Chaos Monkey fault injection
│   │   └── journal.py           # healing journal: ring buffer + SQLite + pub/sub
│   ├── evals/
│   │   ├── scenarios.py         # 18 eval scenarios with expected routing and answer checks
│   │   └── runner.py            # eval harness -> docs/EVAL_REPORT.md
│   ├── feedback/
│   │   └── store.py             # ratings, preferences, lessons learned, remember_operator_feedback
│   └── web/
│       ├── server.py            # FastAPI: REST + SSE chat / healing stream / alerts; starts the Watchtower
│       └── static/              # no-build dashboard (HTML/CSS/JS, Leaflet)
├── tests/                       # pytest suite (data, MCP server, healing, feedback, offline LLM, autopilot, ...)
├── docs/                        # ARCHITECTURE.md, JUDGING.md, DEMO_SCRIPT.md, CONTRACTS.md, EVAL_REPORT.md
├── deploy/
│   ├── cloudrun.sh              # one-command Cloud Run deploy (Secret Manager aware)
│   └── claude_desktop_config.example.json
├── .github/workflows/ci.yml     # lint + tests + offline e2e demo under chaos + Docker smoke test
├── Dockerfile / .dockerignore
├── pyproject.toml / uv.lock / .python-version
├── .env.example                 # copy to .env; every key optional
└── LICENSE                      # MIT

Testing and CI

uv run pytest                    # full suite
uv run pytest -q tests/test_healing.py
uv run ruff check src            # lint

The tests are deterministic and need no keys. To match CI exactly (no network, real MCP stdio server):

CARGOMIND_OFFLINE=1 CARGOMIND_MCP_LOCAL=0 uv run pytest -q                    # macOS / Linux
$env:CARGOMIND_OFFLINE="1"; $env:CARGOMIND_MCP_LOCAL="0"; uv run pytest -q    # PowerShell

GitHub Actions runs on every push and PR, with zero keys:

  1. uv sync

  2. ruff check (advisory)

  3. pytest -q

  4. cargomind doctor

  5. the full 8-step demo under 30% chaos, end to end through the real MCP stdio server

  6. a separate job that builds the Docker image, runs it and waits for /api/health


Docker

docker build -t cargomind .
docker run -p 8080:8080 --env-file .env cargomind      # with your keys
docker run -p 8080:8080 cargomind                      # zero keys

Open http://localhost:8080. Useful variations:

docker run -p 8080:8080 --env-file .env -v cargomind-data:/app/data cargomind   # keep DB + feedback across restarts
docker run -p 9000:9000 -e PORT=9000 cargomind                                  # different port
docker run -p 8080:8080 -e CARGOMIND_CHAOS=0.3 cargomind                        # start with chaos on

The image is python:3.12-slim + uv. It installs exactly what uv.lock pins (uv sync --frozen --no-dev), runs as a non-root user, respects $PORT, and has a health check on /api/health. .env is never copied into the image.


Deploy to Google Cloud Run

Hosted Vercel demo

The public demo runs at https://cargomind-lilac.vercel.app with live provider keys configured through the Vercel CLI. Anonymous dashboard, health, KPI and static-asset requests returned HTTP 200. A live SSE tracking request completed with tool results and a final answer; its trace recorded Gemini backup and OpenAI providers.

Vercel uses temporary per-instance SQLite storage, uploads and session memory. Data can reset on a cold start. Click Scan now for Watchtower alerts; the continuous background scanner is disabled on serverless hosting. Local and Cloud Run operation retain the background scanner.

The deployment entrypoint is app.py; vercel.json selects FastAPI and a 300-second function limit. From a linked project, deploy with vercel --prod. Set provider keys through vercel env add NAME production and redeploy; keep secrets out of source control.

Google Cloud Run

Prerequisites: a Google Cloud project with billing enabled, and the gcloud CLI. Or use Cloud Shell, which already has everything.

gcloud auth login
gcloud config set project YOUR_PROJECT_ID
./deploy/cloudrun.sh            # Windows: run from Git Bash, WSL or Cloud Shell

deploy/cloudrun.sh does the following:

  1. Enables Cloud Run, Cloud Build and Artifact Registry.

  2. Reads your keys from .env without executing it.

  3. Stores each key in Secret Manager (cargomind-google-api-key, …) and grants the runtime service account secretAccessor.

  4. Runs gcloud run deploy --source . in asia-south1 (Mumbai).

  5. Prints the URL.

Options:

  • --plain-env passes the keys as plain env vars instead of secrets.

  • --no-keys does a zero-key deploy.

  • REGION=, SERVICE=, PROJECT_ID=, ENV_FILE= and SERVICE_ACCOUNT= override the defaults.

Manual equivalent (plain env vars, quickest):

gcloud run deploy cargomind --source . --region asia-south1 --allow-unauthenticated \
  --memory 1Gi --timeout 3600 --max-instances 1 --session-affinity \
  --set-env-vars GOOGLE_API_KEY=your-gemini-key,GOOGLE_MAPS_API_KEY=your-maps-key,ANTHROPIC_API_KEY=your-claude-key

Manual equivalent with Secret Manager (recommended for anything public):

printf '%s' "your-gemini-key" | gcloud secrets create cargomind-google-api-key --data-file=-
PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format='value(projectNumber)')
gcloud secrets add-iam-policy-binding cargomind-google-api-key \
  --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"
gcloud run deploy cargomind --source . --region asia-south1 --allow-unauthenticated \
  --memory 1Gi --timeout 3600 --max-instances 1 --session-affinity \
  --set-secrets GOOGLE_API_KEY=cargomind-google-api-key:latest

Why these flags:

  • --max-instances 1 + --session-affinity keep chat sessions, the journal and the demo SQLite DB on one instance.

  • --timeout 3600 keeps dashboard SSE streams open.

  • Cloud Run's filesystem is in-memory, so the demo DB re-seeds on each cold start. That is by design for a demo. See the Roadmap for managed persistence.


Troubleshooting and FAQ

Close and reopen the terminal after installing uv. If it still fails, add uv's directory to PATH: %USERPROFILE%\.local\bin on Windows, ~/.local/bin on macOS and Linux. On Windows, if the installer is blocked, run exactly powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex".

Run git config --global core.longpaths true. Enable Win32 long paths once, from an Administrator PowerShell: New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force Then restart. Or simply clone to a short path such as C:\src\cargomind.

CargoMind already sets PYTHONUTF8=1 for child processes and switches the CLI to UTF-8 output. If your console still complains, set it for the shell before running: $env:PYTHONUTF8="1" (PowerShell) or set PYTHONUTF8=1 (cmd). Windows Terminal renders the output best. Python 3.12+ is required, and uv sync installs it.

Windows PowerShell 5.1 doesn't support &&. Run the commands on separate lines (as in the Windows setup above), or use PowerShell 7+.

Use uv run cargomind web --port 8090, or set PORT=8090. For the ADK UI use uv run adk web --port 8001 src. To find the process holding the port, run netstat -ano | findstr :8080 (Windows) or lsof -i :8080 (macOS/Linux).

Nothing to do: this is exactly what the self-healing chain is for. CargoMind retries twice, fails over to CARGOMIND_GEMINI_BACKUP, then to Claude/GPT, then to the offline engine. The model's breaker skips it for 60 s, and the journal shows each step. For sustained heavy use, enable billing on the AI Studio project or point CARGOMIND_GEMINI_FAST at a model with more quota.

Your key may not have access to a default model id. The chain falls back automatically. To fix it permanently, set CARGOMIND_GEMINI_FAST, CARGOMIND_GEMINI_PRO or CARGOMIND_GEMINI_BACKUP (or the Claude / OpenAI ids) in .env to models your account lists. uv run cargomind doctor prints the ids in use.

No usable model key was found, or every online model failed. Run uv run cargomind doctor. If live: is none, your .env is missing, empty or still has placeholders. It must be at the repo root and named exactly .env. Otherwise, check the healing journal for the fallback reason, for example an invalid key.

Google Maps isn't configured or rejected the call. Check that Routes API and Geocoding API are both enabled on the key's project, that billing is on, and that the key restrictions allow those APIs. Until then, the free OSRM or offline estimates are used and clearly flagged.

Use absolute paths: the repo path in --directory and, if needed, the full path to uv in "command". Fully quit Claude Desktop, including the tray icon, before reopening. Logs are in %APPDATA%\Claude\logs\mcp*.log (Windows) or ~/Library/Logs/Claude/mcp*.log (macOS). Run uv sync once in the repo first.

That's correct. It is a stdio server waiting for an MCP client. Press Ctrl+C, then connect it from Claude Desktop, Claude Code, Gemini CLI or the MCP Inspector.

uv run cargomind reset-db re-seeds the operational data. For a completely clean slate, including feedback, lessons and the journal history, stop all CargoMind processes and delete data/cargomind.db*. It is recreated on the next start.

Set CARGOMIND_OFFLINE=1. No network calls are made, and the whole demo, including dispatch planning, briefings and self-healing, still works on seeded data.

No. send_notification writes to a simulated outbox table (GET /api/outbox). Nothing leaves the machine.

On newer projects, source deploys build with the Compute Engine default service account. Grant it the Cloud Run Builder role: gcloud projects add-iam-policy-binding YOUR_PROJECT_ID --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" --role="roles/run.builder" If you switch between --plain-env and Secret Manager on an existing service and see "already set with a different type", remove the old variables first, for example gcloud run services update cargomind --region asia-south1 --remove-env-vars GOOGLE_API_KEY.


Roadmap

  • Real integrations: carrier and TMS APIs, India's e-way bill and ULIP data, live GPS telematics in place of the seeded fleet.

  • Managed persistence: Firestore or Cloud SQL for operations data, sessions and the healing journal, so the system can scale beyond one instance.

  • Vertex AI Agent Engine deployment, and live-model runs of the eval harness in CI.

  • Optimisation: an OR-Tools VRP solver behind propose_dispatch_plan, with time windows and cold-chain constraints.

  • Learning: embedding-based retrieval of past feedback and incidents, plus per-operator preference profiles.

  • A2A protocol so carrier and warehouse partners' agents can negotiate directly with CargoMind.

  • Voice control-tower mode with the Gemini Live API. Auth / RBAC on the dashboard. OpenTelemetry → Cloud Trace.


Team

Name

Role

GitHub

Anand B

Core platform, agents, self-healing and dashboard

@Anandb71

Junaid Ahamed

Disruption Autopilot, Watchtower, CLI and evaluations

@JunaidAhamed-7777


License

MIT © 2026 CargoMind contributors.

Built with Google ADK, Gemini, Google Maps Platform, the Model Context Protocol, LiteLLM, FastAPI, Leaflet, OpenStreetMap / OSRM and Open-Meteo. Map data © OpenStreetMap contributors.

Available Tools

11 tools
assign_vehicleA

Assign an own-fleet vehicle to a shipment. Checks capacity and maintenance status; idempotent.

Args:
    shipment_id: Shipment ID such as "SHP-1007".
    vehicle_id: Vehicle ID such as "TRK-105".

Returns:
    {"status": "ok", "warnings": [...], "vehicle": {...}} - warnings flag soft issues (vehicle in another
    city, driver near the 10h limit, low fuel, cold-chain mismatch). Errors suggest alternative vehicles.
ParametersJSON Schema
NameRequiredDescriptionDefault
vehicle_idYes
shipment_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses validation behavior (capacity and maintenance checks), idempotency, warning semantics (cross-city vehicle, driver hours, fuel, cold-chain mismatch), and that errors propose alternatives. It stops short of stating permission requirements or whether a failed validation rejects the assignment outright.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose, then compact Args and Returns blocks. The Returns block is somewhat redundant given an output schema exists, but it is short and information-dense rather than padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers purpose, parameters, validation behavior, warning categories, and failure behavior. Since an output schema exists, the return explanation is a minor duplication rather than a gap; only explicit permission/precondition details are missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the two parameters are named clearly and the description compensates with concrete example formats ("SHP-1007", "TRK-105") that the bare string schema lacks. More than adequate for a two-parameter tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Assign an own-fleet vehicle to a shipment') and scopes it to own-fleet, which implicitly separates it from the carrier-related siblings (list_carriers). An agent can distinguish this from track_shipment or list_vehicles immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the verb and the own-fleet scoping, but there is no explicit when-to-use or when-not guidance, and no routing to an alternative for non-own-fleet assignments. It never says what to do when capacity or maintenance checks fail beyond 'errors suggest alternative vehicles'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

check_inventoryA

Check stock levels per SKU and warehouse, including low-stock flags and any open purchase order.

Args:
    sku: SKU ID ("SKU-107") or product name fragment ("insulin", "gloves"). Empty = all SKUs.
    warehouse_id: Warehouse ID ("WH-BLR"), code ("BLR") or city ("Bangalore"). Empty = all warehouses.
    low_stock_only: If true, only lines at or below their reorder point.

Returns:
    {"status": "ok", "count", "low_stock_count", "items": [{sku, name, warehouse_id, qty_on_hand,
    reorder_point, shortfall, low_stock, open_po, ...}]}.
ParametersJSON Schema
NameRequiredDescriptionDefault
skuNo
warehouse_idNo
low_stock_onlyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full load. It discloses that this is a read-style lookup and describes the shape and semantics of the returned item fields (shortfall, low_stock, open_po), but says nothing about permissions, scoping restrictions, or behavior when the result set is large. An output schema exists, so reciting return fields adds less value than it appears.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose sentence followed by structured Args/Returns blocks, so the agent can parse it quickly. The Returns block restates information already carried by the output schema, which is mild redundancy, but nothing is bloated or buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a three-optional-parameter read tool with an output schema available, the description covers invocation semantics and result contents adequately. It omits anything about permission requirements, rate/cost behavior, or result-size limits when all SKUs and warehouses are requested, which are the remaining gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does: each of the three parameters is explained with concrete value formats ('SKU-107', 'WH-BLR'/'BLR'/'Bangalore'), a fragment-matching convention for product names, and the meaning of empty/default values. This is exactly what the schema fails to provide.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Check stock levels per SKU and warehouse') and enumerates the payload of the result (low-stock flags, open purchase order), which clearly separates it from write-side siblings like create_reorder. It does not explicitly name a sibling it replaces, so it stops short of the top band.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives invocation context for each parameter (empty string means all SKUs / all warehouses) and defines low_stock_only operationally, which is useful. However, it never says when to prefer this tool over alternatives such as get_kpis or create_reorder, and there are no exclusions or prerequisites stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_reorderA

Raise a purchase order to replenish a SKU at a warehouse. Safe to retry: if an open PO already exists for the line it is returned with duplicate=true instead of creating another.

Args:
    sku: SKU ID such as "SKU-107".
    warehouse_id: Warehouse ID ("WH-BLR"), code or city.
    qty: Units to order; 0 = automatic (tops stock up to 2x reorder point, min standard reorder qty).

Returns:
    {"status": "ok", "duplicate": bool, "purchase_order": {id, qty, supplier, expected_by, ...}, "message"}.
ParametersJSON Schema
NameRequiredDescriptionDefault
qtyNo
skuYes
warehouse_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses idempotent/retry-safe behavior, the duplicate-detection contract, and the auto-quantity side effect (qty=0 tops stock to 2x reorder point, min standard reorder qty). It omits permission requirements and any downstream effects (supplier notification, inventory mutation), keeping it short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and the retry-safety guarantee, then cleanly sectioned into Args and Returns. The Returns block restates what the output schema already declares, which is mild redundancy, but nothing else is padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter mutation tool with no annotations, the description covers action, idempotency, duplicate handling, and every parameter's semantics. An output schema exists, so the Returns summary is a bonus rather than a necessity, and no information an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does for all three parameters: sku format example, warehouse_id accepting ID/code/city, and detailed qty semantics including the default 0 = automatic behavior. This is meaningfully more than the bare integer/string types in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Raise a purchase order to replenish a SKU at a warehouse") with the scope of the action. It is clearly distinguishable from every sibling, none of which create purchase orders; the closest siblings (check_inventory, list_shipments) are read operations on adjacent resources.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives real usage context: it is safe to retry, and an existing open PO is returned with duplicate=true rather than double-ordering. It stops short of explicitly naming when-not-to-use or a competing alternative tool, but no sibling overlaps this action, so the routing gap is minor.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_kpisB

Network health snapshot: on_time_rate (%), in_transit, delayed, exceptions, delivered_today, low_stock_skus, fleet_utilization (%), open_pos, critical_active and counts by status.

Returns:
    KPI dict with status="ok".
ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden, but it only lightly implies a safe read via 'snapshot' and states the return wrapper 'KPI dict with status="ok"'. It says nothing about permissions, caching/refresh semantics, or whether the snapshot is real-time — gaps that matter for a monitoring tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The metric list is front-loaded and the description is short. The 'Returns: KPI dict with status="ok"' line is largely redundant given an output schema exists, which is the one wasted fragment.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter read tool with a full output schema and no annotations, the description is nearly sufficient: it tells the agent what metrics come back and that the call is a snapshot. The missing piece is when to prefer this aggregate over the sibling list/status tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. There is nothing for the description to disambiguate, and the enumerated metric names usefully pre-announce the shape of the payload the agent will receive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific resource (network health KPIs) and enumerates the exact metrics returned (on_time_rate, in_transit, delayed, exceptions, delivered_today, low_stock_skus, fleet_utilization, open_pos, critical_active). This is far more concrete than a tautology, though it never contrasts itself with siblings like check_inventory or track_shipment, which also surface operational status.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use guidance, no prerequisites, and no mention of alternatives. An agent must infer that this is the dashboard/aggregate call rather than one of the shipment- or inventory-specific siblings; the description does nothing to route that decision.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_carriersA

List carrier partners with modes, INR rate card (base_rate_per_km, per_kg_rate), reliability (0-1) and average speed - use for quoting and choosing a carrier.

Returns:
    {"status": "ok", "count", "carriers": [...]}.
ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It discloses the return envelope ('status', 'count', 'carriers') and field meanings, which is useful, but says nothing about auth requirements, rate limits, caching/freshness, or whether the carrier set is static — mild gaps for a read-only listing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and usage are front-loaded in the first sentence, and the return block is compact. The 'Returns:' section partially duplicates the existing output schema, keeping it just short of maximally efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, parameterless listing tool with an output schema already present, the description covers what the tool is, what it returns, and why an agent would call it. Nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so there is nothing to disambiguate; the baseline for a 0-param tool is 4. The description instead spends its detail budget on the returned fields, which is the right trade-off.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List carrier partners') and enumerates the exposed fields (modes, INR rate card, reliability, average speed). No sibling tool deals with carriers, so the scope is unambiguous against list_shipments, list_warehouses, list_vehicles, etc.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'use for quoting and choosing a carrier' gives a concrete use context, which is more than most list tools offer. It stops short of naming alternatives or stating when not to use it, but for a zero-param read tool the guidance is adequate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_shipmentsA

List shipments, filtered and sorted (active first, critical first, then earliest promised delivery).

Args:
    status: One of created, picked_up, in_transit, delayed, out_for_delivery, delivered, exception,
        or "active" (anything not yet delivered/exception). Empty = all.
    city: City name or alias (e.g. "Bangalore", "Bombay"); matches origin, destination or current city.
    priority: standard, express or critical. Empty = all.
    limit: Maximum rows to return (default 25).

Returns:
    {"status": "ok", "count": n, "shipments": [...]} each with is_late / delay_hours.
ParametersJSON Schema
NameRequiredDescriptionDefault
cityNo
limitNo
statusNo
priorityNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the burden. It discloses the sort order and the response shape with is_late/delay_hours, which is useful. However, it doesn't state read-only nature explicitly, permission requirements, or pagination behavior (only a limit default).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and sort order, then organized Args/Returns. Slightly verbose with the multi-line enum listing but every element is informative.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Even without an output schema-explain requirement (it has one, though the description still describes the wrapper), the description covers filtering, sorting, and return shape adequately. Missing only explicit safety/read-only statement and cross-sibling guidance.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate – and it does well: it enumerates status values including the 'active' pseudo-value, explains city alias matching across origin/destination/current city, and gives the limit default. Only priority lacks an explicit default note (empty = all is stated).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('List shipments') and adds the implicit sort order (active first, critical first, then earliest promised delivery), which distinguishes it from track_shipment and update_shipment_status.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the filtering args (status/city/priority/limit), but there is no explicit 'when to use vs track_shipment/shipment_history' guidance or exclusions. The agent can infer this is a filtered list, but the sibling routing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_vehiclesA

List own-fleet vehicles with capacity, current load, driver hours remaining and fuel.

Args:
    status: available, on_route or maintenance. Empty = all.
    city: Current city of the vehicle (name or alias). Empty = all.

Returns:
    {"status": "ok", "count", "vehicles": [{id, type, capacity_kg, remaining_capacity_kg, status,
    current_city, driver_name, driver_hours_today, driver_hours_remaining, fuel_pct, refrigerated}]}.
ParametersJSON Schema
NameRequiredDescriptionDefault
cityNo
statusNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries the full behavioral burden. It implies a safe read and enumerates returned fields, but says nothing about pagination, result limits, permissions, or ordering for a list tool, leaving real gaps against the no-annotation baseline.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded purpose sentence followed by clean Args/Returns blocks with no filler. The Returns block largely restates the existing output schema, which is mild redundancy, but it is not verbose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With only two optional parameters and an output schema already covering the return shape, the description supplies everything an agent needs to invoke the tool correctly. Only the missing behavioral details (limits, ordering) leave a small residual gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the schema contributes only titles and defaults. The description compensates by enumerating the status values (available, on_route, maintenance) and clarifying that city accepts a name or alias, which is meaningful added semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List own-fleet vehicles') plus the exact data surfaced (capacity, load, driver hours, fuel). 'Own-fleet' implicitly separates it from list_carriers, though no sibling is named explicitly, keeping it just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Filter semantics are implied through the Args block ('Empty = all' for status and city), which tells the agent how to broaden results, but there is no statement of when to choose this over list_carriers, track_shipment, or other listing tools. Usage is inferable but not guided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_warehousesA

List warehouses with city, coordinates, utilisation %, cold-chain capability and low-stock line count.

Returns:
    {"status": "ok", "count", "warehouses": [...]}.
ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. 'List' implies a safe read, and the return shape is disclosed, but there is no statement about pagination, ordering, result-size limits, or freshness of the utilisation/low-stock figures. Adequate but thin for an unannotated tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short lines, front-loaded with the resource and its fields. The 'Returns:' block is arguably redundant with the output schema, but it is compact and does not bury the purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-param read tool with an output schema already present, the description supplies what is needed: the resource and the fields each warehouse carries. Only the return-shape line duplicates the output schema, and nothing about result volume or pagination is stated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so per the rubric the baseline is 4; there is no parameter semantics to explain beyond the implicit 'no filtering available' constraint, which the description does not state but the empty schema makes obvious.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('List') and resource ('warehouses') and even enumerates the fields returned (city, coordinates, utilisation %, cold-chain capability, low-stock line count). It is clearly distinct from the shipment/vehicle/carrier siblings, though it never explicitly says so.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No when-to-use statement, no alternatives, no exclusions. For a zero-parameter listing tool the intended use is largely self-evident, so the guidance is implied rather than absent, but nothing in the text routes the agent here versus check_inventory or get_kpis.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

shipment_historyA

Full chronological event trail (scans, checkpoints, delays, assignments) for one shipment.

Args:
    shipment_id: Shipment ID such as "SHP-1003".

Returns:
    {"status": "ok", "shipment_id": ..., "events": [{ts, status, city, note, source}, ...]}.
ParametersJSON Schema
NameRequiredDescriptionDefault
shipment_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden. It discloses the semantic content of the response and implies a read-only operation, but says nothing about ordering guarantees, pagination or result caps for long trails, or any permission requirements — meaningful gaps for a history endpoint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose sentence is front-loaded with the key scope words, and the Args/Returns blocks are terse and scannable. No filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter read tool with an output schema present, the description covers purpose, the input format, and a sample response, so it is largely self-sufficient. The only shortfall is the absence of any note on result size or paging for long event trails.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description has to compensate and it does: it gives the expected ID format example ("SHP-1003"), which the bare string schema does not. Only one parameter exists and no additional qualifiers (date range, filters) are hinted at.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource and scope: a full chronological event trail (scans, checkpoints, delays, assignments) for one shipment. This clearly differs from a current-status tool like track_shipment, but the description never names or contrasts with any sibling, so the agent must infer the boundary.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied by the words 'full chronological event trail', which suggests historical/audit retrieval versus a live-tracking sibling. There is no explicit when-to-use, when-not-to-use, or named alternative, so the routing guidance is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

track_shipmentA

Track a shipment: current status, location, ETA vs promised delivery, carrier, vehicle and recent events.

Use this first whenever the user mentions a specific shipment or order.

Args:
    shipment_id: Shipment ID such as "SHP-1001" (also accepts "shp1001" or "1001").

Returns:
    The shipment (status, priority, origin_city, destination_city, last_city, eta, promised_by,
    is_late, delay_hours, hours_to_promise, carrier_name, vehicle, customer, weight_kg, notes)
    plus ``recent_events`` (latest 5, newest last). On a miss: status="error" with suggestions.
ParametersJSON Schema
NameRequiredDescriptionDefault
shipment_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries the full load, and it does disclose real behavior: accepted ID variants ('SHP-1001', 'shp1001', '1001'), that recent_events returns only the latest 5 newest-last, and the miss behavior (status="error" with suggestions). It omits auth/permission needs and rate limits, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose and usage trigger before the Args/Returns blocks, and every sentence carries information. The Returns block restating 15 output fields is somewhat redundant given an output schema exists, mildly diluting conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter read tool with an output schema, the description supplies the identifier format, the priority trigger, and error behavior. It is essentially complete, with only minor gaps around permissions and any pagination/limit nuance for recent_events.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the schema provides no parameter meaning at all. The description compensates fully for the single parameter by giving a concrete example ID and documenting that normalized/short forms are also accepted, which is information an agent could not get from the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Track a shipment') and enumerates the exact facets returned: status, location, ETA vs promised delivery, carrier, vehicle, recent events. It implicitly separates itself from list_shipments via 'a specific shipment or order' but never names a sibling, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Use this first whenever the user mentions a specific shipment or order' is an explicit trigger condition with a priority ordering relative to other tools. It gives no when-not guidance or named alternative (e.g. list_shipments for broad queries), so it is clear context without exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_shipment_statusA

Change a shipment's status and log a history event (e.g. mark delayed, delivered, exception).

Args:
    shipment_id: Shipment ID such as "SHP-1002".
    status: New status: created, picked_up, in_transit, delayed, out_for_delivery, delivered or exception.
    city: Optional city where the update happened (defaults to the current/destination city).
    note: Short human-readable reason, e.g. "Rain on NH-48, 6h delay". Stored as the shipment note.

Returns:
    {"status": "ok", "previous_status": ..., "shipment": {...}, "event": {...}} or an error with valid values.
ParametersJSON Schema
NameRequiredDescriptionDefault
cityNo
noteNo
statusYes
shipment_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does reasonably well: it discloses a side effect beyond the status change (a history event is logged), specifies that the note is persisted as the shipment note, and that invalid input returns an error listing valid values. It stops short of stating permissions, reversibility, or idempotency, which matters for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The one-line purpose is front-loaded, and the Args/Returns block is tightly formatted with one line per parameter and no filler. Every sentence carries information an agent needs to invoke the tool correctly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a four-parameter mutation tool with no annotations, the definition covers purpose, all parameters, the side effect, and the return shape, so an agent has what it needs. The only gap is the absence of any explicit routing guidance against the many read-oriented sibling tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and it does: it documents all four parameters with an ID format example ('SHP-1002'), the complete closed set of valid status values (absent from the schema, which has no enums), and the default behavior of city when omitted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Change a shipment's status') and adds the important side effect that a history event is logged, which an agent cannot infer from the name alone. It does not explicitly differentiate itself from siblings such as track_shipment or shipment_history, but the mutation-plus-audit purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: the examples ('mark delayed, delivered, exception') show the intended scenario, but there is no explicit when-to-use vs sibling guidance (e.g. vs track_shipment, which appears read-only) and no stated prerequisites or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 11 tool updatesv0.1.0
    • First observedassign_vehicle
    • First observedcheck_inventory
    • First observedcreate_reorder
    • First observedget_kpis
    • First observedlist_carriers
    • First observedlist_shipments
    • First observedlist_vehicles
    • First observedlist_warehouses
    • First observedshipment_history
    • First observedtrack_shipment
    • First observedupdate_shipment_status

TDQS

A3.9/5.0

Scored across 11 tools

Disambiguation5/5

Tools are cleanly separated by resource and action: shipment tracking/list/history/status update, inventory/warehouses/reorders, fleet assignment, carriers, and KPIs. Overlaps are minimal; track_shipment and shipment_history differ in scope (current status plus recent events vs full event trail).

Naming Consistency4/5

Names are consistently snake_case and mostly follow a verb_noun pattern such as track_shipment, list_shipments, update_shipment_status, check_inventory, create_reorder, assign_vehicle, and get_kpis. The main deviation is shipment_history, which is noun_noun rather than action-first, but it remains readable and predictable.

Tool Count5/5

11 tools is well-scoped for a logistics control tower, covering shipments, inventory, warehouses, fleet, carriers, and KPIs without obvious filler. Each tool maps to a clear operational task.

Completeness4/5

Core operational workflows are covered: track/update shipments, inspect inventory, raise reorders, assign vehicles, and monitor KPIs. Gaps remain around shipment creation/cancellation and purchase-order or vehicle lifecycle management, but these are minor for a monitoring and dispatch-oriented server.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    The first logistics/WMS MCP server for AI agents. Rate shopping, inventory management, order tracking, fleet logistics, AI-powered route optimization, demand forecasting, and supply chain analytics. 18 tools across 3 tiers.
    20
    7 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides supply chain and shipping tools including shipment tracking, route optimization, warehouse inventory management, delivery ETA estimation, and customs documentation. Enables logistics operations through natural language interactions with Claude.
    6 npm
    58 PyPI
    MIT