Three cooperating agents — Greenhouse, Transport, Merchant — sharing one MongoDB blackboard. No agent calls another in process; each reads the documents it depends on and writes its own. Gemini does the reasoning, MongoDB is the shared state, accessed through the MongoDB MCP server.
🔗 Live demo: https://agent-farm-582643636508.us-central1.run.app
- Greenhouse Agent monitors a pixel-art greenhouse and publishes its harvestable
yield as a
farmsdocument. - Transport Agent reads the live
farms, reasons about co-loadability, calls an OR-Tools optimizer, and writes a consolidatedtransport_plansdoc. - Merchant Agent reads the committed
transport_plans(which farms actually got routed) joined withfarms(their crops), allocates the arriving supply across competing buyers by priority, prices the shortfall, and writes amarket_ordersdoc.
A storm cascades through all three: the Transport Agent's storm button blocks roads +
reduces yields via a world_events doc → the Greenhouse Agent's farm_publish reacts
and lowers its yield → the Transport Agent re-plans against the new picture → the
Merchant Agent auto-reallocates (it watches the blackboard) and applies scarcity
pricing. The Coordination tab tracks all three in real time, and a Gemini ↔
MongoDB-MCP situation report narrates the whole chain.
┌────────────────────────────────────────────────────────────┐
│ MongoDB (shared blackboard) │
│ farms · world_events · transport_plans · market_orders │
│ buyers · agent_logs · live_telemetry · incidents │
└──▲────────────▲─────────────────▲────────────────▲──────────┘
farms / │ │ plans / │ plans (read) │ market_orders /
yield │ │ world_events │ │ buyers
│ │ │ │
┌────────┴───────┐ ┌──┴─────────────┐ ┌─┴────────────┐ ┌─┴───────────────┐
│ Greenhouse │ │ Transport │ │ Route │ │ Merchant │
│ port 8000 │←│ port 8001 │→│ Optimizer │ │ port 8002 │
│ + serves UI │ │ │ │ port 8080 │ │ (auto-watcher) │
│ + Gemini↔MCP │ │ │ │ (pure math) │ │ + Gemini │
└────────────────┘ └────────────────┘ └──────────────┘ └─────────────────┘
The unified UI (served by 8000) has four tabs:
🛰️ COORDINATION — live Greenhouse→Transport→Merchant flow + MCP situation report
🌱 GREENHOUSE — pixel-art scene + agent reasoning + sliders
🚛 LOGISTICS — farms map + reasoning trace + transport plan
🏪 MARKET — buyer demand book + allocation table + scarcity pricing
- Gemini (via Vertex AI) — agent reasoning, plant-health vision, situation report
- MongoDB Atlas — the shared blackboard, accessed through the MongoDB MCP server
- Google OR-Tools — vehicle-routing optimization
- FastAPI services + a vanilla HTML5-canvas frontend, deployed on Google Cloud Run
- Python 3.12 (the pinned dependencies — pydantic-core, Pillow, OR-Tools — ship prebuilt wheels for 3.12; newer Python versions may try to build from source and fail)
- Node.js 18+ (the MongoDB MCP server runs via
npx mongodb-mcp-server) - A MongoDB Atlas cluster (connection string)
- A Google Cloud project with Vertex AI enabled (for Gemini)
# 1. Create venv with Python 3.12 & install deps
py -3.12 -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
# 2. Authenticate for Vertex AI / Gemini (uses your own GCP project)
gcloud auth application-default login
gcloud config set project YOUR_GCP_PROJECT_ID
# 3. Copy the env template and fill in your values
copy .env.example .envThen edit .env with your own values (see .env.example for the full list). The key ones:
MONGODB_URI=<your Atlas connection string> # the shared blackboard
MONGODB_DB=agent_greenhouse
GCP_PROJECT=YOUR_GCP_PROJECT_ID # for Gemini via Vertex
GCP_REGION=us-central1
GEMINI_MODEL=gemini-2.5-flash
GOOGLE_GENAI_USE_VERTEXAI=true
GOOGLE_CLOUD_PROJECT=<your GCP project id>
GOOGLE_CLOUD_LOCATION=us-central1
Note: with
MONGODB_URIset, all agents share state through Atlas (the blackboard the cascade depends on). Without it, each process falls back to an in-memory store — the UI still works inside each tab, but the cascade won't bridge across processes.
Start the four services, each in its own terminal (venv activated). They connect to the
Atlas cluster from your .env:
python -m uvicorn optimizer.main:app --port 8080$env:OPTIMIZER_URL="http://localhost:8080"
python -m uvicorn app.transport.main:app --port 8001python -m uvicorn app.merchant.main:app --port 8002python -m uvicorn app.greenhouse.main:app --port 8000Then open http://localhost:8000 — the unified UI loads on the Greenhouse tab. Click
🛰️ COORDINATION to watch all three agents, or 🚛 LOGISTICS / 🏪 MARKET for each in detail.
The included
run.ps1/run.batscripts start a local mongod instead of Atlas and are intended for offline development. For Atlas (the deployed configuration), run the services directly as shown above.
- Greenhouse tab — drag temperature to 44 °C, humidity to 22%. Plants wilt,
the incident pipeline fires, the plan auto-approves, actuators recover the scene.
Behind the scenes, every cycle the Greenhouse upserts its
farmsdoc with the live yield (scaled by harvest-zone health). - Logistics tab — see all farms on the blackboard. The greenhouse's farm
shows up flagged with
▣and is highlighted on the map in cyan. ClickBUILD PLANto consolidate them into multi-stop routes. - Market tab — the Merchant Agent has already allocated the routed supply across four buyers (hospital → school → market → exporter). See per-buyer fill rates, the allocation/pricing table, and a Gemini-written rationale.
- ⚡ FIRE STORM (Logistics tab) — writes an active
world_eventsdoc, blocks two peer farms, drops yields. The Greenhouse Agent reads the event and republishes a reduced farm doc; the Transport Agent re-plans; the Merchant Agent auto-reallocates (it watches the blackboard) — fill rate falls, essentials are protected, scarce crops get re-priced. The before/after diffs in both Logistics and Market are the headline. - Coordination tab — the live flow diagram shows the cascade rippling through all
three nodes. Click ASK GEMINI for a MongoDB-MCP situation report that reads every
collection and summarizes the whole chain (
CHAIN: HEALTHY/DISRUPTED). - ↺ CLEAR STORM — resolves the world event, reopens roads, re-plans clean; the Merchant reallocates back to full fulfillment.
app/
greenhouse/ greenhouse FastAPI service (port 8000), serves web/index.html
farm_publish.py ← the bridge to the blackboard's `farms` collection
coordination.py ← cross-agent snapshot + Gemini↔MCP situation report
transport/ transport FastAPI service (port 8001)
merchant/ merchant FastAPI service (port 8002) — allocation + scarcity pricing
agent.py ← priority fair-share allocation brain
pipeline.py ← the blackboard watcher that makes the cascade automatic
mcp_mongo.py Gemini ↔ MongoDB MCP bridge (shared)
optimizer/ OR-Tools microservice (port 8080) — pure math, no DB
orchestrator/ standalone script that fires the storm cascade
shared/schemas/ JSON schemas for the blackboard contracts (incl. market_orders)
web/ unified UI (one index.html: Coordination + Greenhouse + Logistics + Market)
assets/ pixel-art plant images
docs/ greenhouse_change.md, transport_setup.md (design notes)
Dockerfile Cloud Run container (all services + nginx + Node for the MCP server)
deploy/ nginx.conf + start.sh used by the container
The app deploys as a single Cloud Run service (all four services + nginx in one
container; the MCP server runs via Node inside it). Configuration is passed as
environment variables (see .env.example for the list).
gcloud run deploy agent-farm --source . --region us-central1 `
--allow-unauthenticated --port 8080 --memory 2Gi --timeout 3600 `
--env-vars-file env.yamlThe Cloud Run service account needs the Vertex AI User role, and the Atlas cluster's
Network Access must allow connections from anywhere (0.0.0.0/0) so the container can reach it.
Greenhouse (8000)
POST /api/sensor— sliders post here, pipeline runs in the backgroundGET /api/status,/api/history,/api/incident/latest,/api/agent/tracePOST /api/incident/{id}/approve,/dismiss,/api/resetGET /api/stream/logs— SSEGET /api/farm/state,POST /api/farm/publish— blackboard introspection
Plus the cross-agent coordination layer (served by 8000):
GET /api/coordination/state— one blackboard snapshot across all three agentsPOST /api/coordination/narrate— Gemini reads every collection via MongoDB MCP and writes a situation report on the cascade
Transport (8001)
POST /api/transport/plan,/replan,/seedGET /api/transport/farms,/plan/latest,/traceGET /api/transport/world,POST /api/transport/world/clearGET /api/transport/stream/logs— SSE
Merchant (8002)
GET /api/merchant/orders/latest,/buyers,/supply,/trace,/worldPOST /api/merchant/allocate— run an allocation now;/seed— reset the demand bookGET /api/merchant/stream/logs— SSE- Runs a background watcher: a new committed
transport_planor a storm auto-triggers a reallocation — no manual step needed.
Optimizer (8080)
POST /solve— called by the Transport Agent; takes a CVRP problem, returns routesGET /health
MIT — see LICENSE.