LabOps Agent MCP Server
by Marianooss
README.md
# LabOps Agent π¬
> **Slack Agent for Good** β Slack Agent Builder Challenge 2026 (Salesforce/Devpost)
**Predict. Alert. Act. All from Slack.**
LabOps Agent is a Slack-native AI agent that predicts reagent stockouts in clinical laboratories **before they happen**, based on historical demand patterns by test type β and enables lab staff to act directly from Slack without switching to external LIMS systems.
---
## The Problem
Clinical laboratories run on reagents. When a critical reagent runs out mid-operation, testing stops. Current solutions (Quartzy, Scispot) only send passive threshold alerts **after** stock is already low. No product today predicts stockouts based on test-type demand patterns.
**Example:** TSH demand spikes every autumn in Argentina (MarβMay, 2.5x ratio). A lab with 680 units, at a projected demand of ~147 units/day, runs out in **~6 days** β inside the 7-day reorder window. Without prediction, they find out when the analyzer throws an error.
---
## What LabOps Agent Does
1. **Predicts** stockouts using Prophet, calibrated with patterns derived from anonymized demand analysis in Argentine clinical labs
2. **Alerts** lab staff in `#labops-alerts` with interactive Block Kit messages β before the stockout happens
3. **Acts** β staff orders reagents, assigns tasks, and updates inventory without leaving Slack
### Why LabOps Agent is Different
No existing product (Quartzy, Scispot, Benchling) combines:
- **Prediction by test type** β TSH spikes in autumn (MarβMay), Hemograma is stable; each reagent gets its own Prophet model
- **Native Slack agent** β not a webhook or Zapier bridge; Bolt Python with Socket Mode, interactive Block Kit buttons, and modals
- **Domain expertise** β built by someone with 4 years of B2B KAM experience in clinical diagnostics (Argentina), not a generic inventory template
---
## Technologies Used
| Technology | How It's Used | Platform |
|---|---|---|
| **MCP Server** | Exposes 4 lab tools: `get_inventory`, `get_forecast`, `create_order`, `update_canvas` | Anthropic/Slack |
| **Claude Tool-Use Agent** | LLM selects and invokes MCP tools via `agent_router.py` on every @mention | Anthropic |
| **Slack Channel History API** | Queries #labops-alerts message history for past reagent incidents (works with the bot token) | Slack |
| **Claude API Summarization** | Generates natural language summaries of reagent alert history | Anthropic |
> **Design decision note:** `search.messages` (Slack Search API) was evaluated
> but replaced by `conversations_replies` (Channel History API) for thread history
> retrieval. `search.messages` requires a workspace **user token** (`xoxp-`) which
> is not available in standard bot-token deployments; the bot token returns empty
> results. Thread-scoped history via `conversations_replies` provides the relevant
> alert context without requiring a user token.
---
## MCP Server
LabOps Agent exposes a **real MCP Server** using the official Anthropic MCP Python SDK:
```bash
# Run MCP Server (stdio transport)
cd backend
python mcp_server.py
```
Tools available:
- `get_inventory` β Query current reagent stock
- `get_forecast` β Prophet demand forecast
- `create_order` β Create reagent order
- `update_canvas` β Update inventory Canvas
---
## Architecture
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SLACK WORKSPACE β
β #labops-alerts β Canvas Inventario β App Home β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β Bolt Python (Socket Mode)
ββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β FASTAPI BACKEND β
β βββββββββββββββββββ ββββββββββββββββββββββββββββ β
β β MCP SERVER β β PREDICTION ENGINE β β
β β get_inventory β β Prophet + seasonality β β
β β get_forecast β β params from 414K B2B β β
β β create_order β β CV MAPE 8-11% (synth) β β
β β update_canvas β ββββββββββββββββββββββββββββ β
β βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββΌββββββββββββββββββββ
β SUPABASE β
β inventory β demand_history β orders β alerts_log β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββΌββββββββββββββββββββ
β CLAUDE API β
β Natural language explanations of predictions β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
---
## Demo Flow (3 minutes)
**0:00β0:45** β Agent detects TSH will run out in ~6 days (demand forecast exceeds stock)
**0:45β1:30** β Alert fires in `#labops-alerts` with Block Kit buttons:
- π Ver proyecciΓ³n
- π Ordenar reactivo
- π€ Asignar al equipo
**1:30β2:15** β User clicks "Ordenar reactivo" β modal opens β one click confirms β Canvas auto-updates
**2:15β3:00** β Agent explains WHY TSH is at risk (seasonal demand pattern) β Claude API summarizes past incidents
---
## Try it Live
> **Demo video (β€3 min):** [Watch on YouTube](https://youtu.be/TApbp2AE3JQ) β full demo with voiceover narration.
> **Live deploy:** `https://labops-agent.onrender.com` (Render persistent host β Socket Mode + FastAPI co-hosted)
> **Sandbox workspace:** `https://labopsespacio.slack.com` (invite-only sandbox).
### Reproduce the live deploy (judge-reachable, chart renders)
Slack fetches forecast-chart images from its own servers, so the backend must be
reachable over **public HTTPS**. Socket Mode also needs a **persistent host**, so
this deploys to Render/Railway/Fly β **not** Vercel serverless (see
[Deployment](#deployment-render--railway--fly)).
1. Join the [Slack Developer Program](https://api.slack.com/developer-program) and create a Developer Sandbox workspace.
2. Create a new app from [`slack-manifest.json`](slack-manifest.json), install it, and copy the Bot + App tokens.
3. Deploy with the included [`render.yaml`](render.yaml) blueprint (one persistent web service hosts both the chart endpoint and the Socket Mode websocket).
4. After the first deploy, set `BACKEND_URL=https://labops-agent.onrender.com` (or your service URL) and redeploy so the forecast chart renders in Slack.
**Local + tunnel (for recording the demo on your machine):**
```bash
docker-compose up --build # backend + slack client + DB
cloudflared tunnel --url http://localhost:8000 # public HTTPS for the chart
# set BACKEND_URL=https://<random>.trycloudflare.com in .env, restart slack_client
```
Without a public `BACKEND_URL`, the forecast still shows as native Block Kit
fields β the chart image is simply omitted, never broken.
---
## Setup Instructions
### Prerequisites
- Python 3.11+
- Supabase account
- Slack Developer Sandbox (via [Slack Developer Program](https://api.slack.com/developer-program))
- Anthropic API key
### 1. Clone and install
```bash
git clone https://github.com/Marianooss/labops-agent
cd labops-agent
pip install -r requirements.txt
```
### 2. Configure environment
```bash
cp .env.example .env
# Fill in your keys:
# SUPABASE_URL, SUPABASE_KEY
# SLACK_BOT_TOKEN, SLACK_APP_TOKEN, SLACK_SIGNING_SECRET
# SLACK_USER_TOKEN (optional β only for Slack Search API, not used in current implementation)
# ANTHROPIC_API_KEY
# LABOPS_ALERTS_CHANNEL=#labops-alerts
```
### 3. Set up Supabase
```bash
# Run in Supabase SQL Editor:
# 1. data/create_tables.sql
# 2. data/seed_data.sql
```
### 4. Create Slack App (from manifest)
1. Go to [api.slack.com/apps](https://api.slack.com/apps) β **Create New App**
2. Select **From an app manifest**
3. Choose your Developer Sandbox workspace
4. Paste the contents of [`slack-manifest.json`](slack-manifest.json)
5. Click **Create**
6. Go to **OAuth & Permissions** β **Install to Workspace**
7. Copy the **Bot User OAuth Token** and **App-Level Token** into your `.env`
Manifest includes all required scopes: `chat:write`, `channels:read`, `channels:history`, `groups:read`, `groups:history`, `im:write`, `users:read`, `app_mentions:read`, `canvases:read`, `canvases:write`.
### 5. One-Click Docker Setup (recommended for judges)
```bash
# Clone and start everything (PostgreSQL + backend + slack client + auto-seed)
git clone https://github.com/Marianooss/labops-agent
cd labops-agent
docker-compose up --build
# Wait ~30s for DB init, then test:
curl "http://localhost:8000/alert/trigger?reagent_name=TSH"
```
This runs the full stack locally without needing a cloud Supabase account:
- `db` β PostgreSQL with auto-created schema and seed data
- `backend` β FastAPI on http://localhost:8000
- `slack_client` β Bolt Python Socket Mode (waits gracefully if Slack tokens are missing)
> To enable Slack integration, copy `.env.example` to `.env` and fill in `SLACK_BOT_TOKEN`, `SLACK_APP_TOKEN`, and `SLACK_SIGNING_SECRET` before running `docker-compose up`.
### 6. Local Setup without Docker (one-script)
```bash
# Single command β starts backend + Slack client + seeds DB
python scripts/start_local.py
```
Or manually in separate terminals:
```bash
# Terminal 1: FastAPI backend
cd backend && uvicorn main:app --reload --port 8000
# Terminal 2: Slack agent
cd backend && python slack_client.py
```
> **Note:** Backend commands must be run from the `backend/` directory because modules use relative imports.
---
## Deployment (Render / Railway / Fly)
**Why not Vercel?** The agent uses Slack **Socket Mode**, which holds a persistent
websocket. Vercel serverless functions are short-lived and cannot keep that
connection open. (If you must use Vercel, you would have to switch to HTTP Events
mode and expose `/slack/events` β a different integration path this repo does not
use.) Instead, deploy to a persistent host.
The included [`render.yaml`](render.yaml) provisions **one** web service that hosts
both:
1. the FastAPI HTTP app (serves the public `/chart/forecast/{reagent}` PNG that
Slack image blocks fetch β needs public HTTPS), and
2. the Socket Mode websocket, started in a background thread via
`RUN_SOCKET_MODE=1` (see [`backend/main.py`](backend/main.py) startup).
```
Render β New β Blueprint β select this repo β fill the secret env vars
β after first deploy, set `BACKEND_URL=https://labops-agent.onrender.com` β redeploy
```
Railway and Fly.io work identically with the same [`Dockerfile`](Dockerfile)
(it honors the host-injected `$PORT`).
---
## Project Structure
```
labops-agent/
βββ backend/
β βββ main.py # FastAPI entry point
β βββ mcp_server.py # MCP tools (4 lab tools)
β βββ prediction.py # Prophet demand forecasting
β βββ slack_client.py # Bolt Python + event handlers
β βββ database.py # Dual backend: Supabase or PostgreSQL
β βββ claude_client.py # Claude API wrapper
β βββ blocks_loader.py # Block Kit / Canvas template loader
βββ blocks/
β βββ alert.json # Stockout alert Block Kit template
β βββ modal_order.json # Order reagent modal template
β βββ canvas.json # Inventory canvas template
βββ data/
β βββ create_tables.sql # Database schema
β βββ seed_data.sql # Demo data (DEMO badge)
βββ docs/
β βββ architecture.md # Technical architecture
β βββ impact.md # Impact metrics
β βββ demo_script.md # 3-minute demo script
βββ scripts/
β βββ init_db.py # Auto-seed PostgreSQL on Docker startup
β βββ start_local.py # One-script local startup (backend + slack + seed)
β βββ holdout_backtest.py # Monthly hold-out backtest (illustrative)
β βββ cross_validation.py # Rolling-origin CV (headline accuracy metric)
βββ notebooks/
β βββ cv_metrics.json # Rolling-origin CV results (real, reproducible)
β βββ holdout_metrics.json # Monthly hold-out results (caveated)
β βββ prophet_metrics.json # Consolidated metrics summary
βββ tests/
β βββ test_mcp.py # MCP tool unit tests
β βββ test_prediction.py # Prophet engine tests
β βββ test_integration.py # Bolt handler integration tests
βββ models/ # Prophet serialized models (.pkl)
βββ render.yaml # Render Blueprint (persistent host + Socket Mode)
βββ docker-compose.yml # One-click local stack
βββ Dockerfile # Backend container
βββ LICENSE # MIT License
βββ AGENTS.md # Development operating system
βββ BIBLE.md # Immutable declarations
βββ CLAUDE.md # Claude Code instructions
```
---
## UiPath Components Used
None β this project uses **Slack platform APIs** (Channel History API, Canvas API) for messaging and surfaces, **Anthropic MCP Server** for tool exposure, and **Claude API** for natural language generation.
---
## Demo Screenshots
Extracted from the final demo video ([YouTube](https://youtu.be/TApbp2AE3JQ)).
### Block Kit Alert

Real-time Block Kit alert fired when Prophet predicts a stockout within the reorder lead time window.
### Order Modal

Pre-filled order modal with model-suggested quantity and supplier dropdown β one click creates the order in Supabase.
### Demand Forecast Chart

Prophet-generated forecast chart with 80% confidence interval, rendered live and embedded in the Slack thread.
### Canvas Update

Inventory Canvas updates in real time after an order is confirmed.
**Verified numbers from live deploy:**
- TSH demand: ~144 u/dΓa (hΓ‘bil), ~79 u/dΓa (fin de semana) β Prophet forecast, output real `/alert/trigger`, out-of-peak (Jul-Sep)
- Stockout projection: 6 dΓas con 680 u stock (projected_stockout_date: 2026-07-08)
- Forecast chart: renders from `https://labops-agent.onrender.com/chart/forecast/TSH`
- Canvas: persists via `lab_config` table in Supabase
## Data & Privacy
All data in this project is **synthetic and clearly labeled with DEMO badges**. No real patient data, no PHI. The prediction model's seasonal parameters (peak months, peak/trough ratios per test type) were **derived from analysis of 414,289 real B2B demand records** (Labmedicina, 2025-2026, anonymized β raw data not included, see [`data/DATASET_PROVENANCE.md`](data/DATASET_PROVENANCE.md)); Prophet trains on a synthetic daily series encoding those parameters. Reported CV metrics (MAPE ~8-11%) are a synthetic-benchmark validation of the pipeline, not a real-consumption accuracy claim β see [`docs/architecture.md`](docs/architecture.md).
---
## License
MIT License β see [LICENSE](LICENSE)
---
## Builder
**Mariano Adrian Oss** Β· [DevelopOss](https://developoss.com) Β· Buenos Aires, Argentina
B2B KAM in clinical diagnostics (4 years) + AI Builder. This project applies real insider knowledge of clinical laboratory operations to a problem that existing software hasn't solved: uninterrupted diagnostic access for vulnerable patients inside the tools labs already use daily.
---
*LabOps Agent Β· Slack Agent Builder Challenge 2026 Β· Track: Slack Agent for Good*This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues