Protecting India's medicine supply, one batch at a time. Google Cloud Rapid Agent Hackathon Β· Track: MongoDB
Roughly 1 in 4 drugs sold in developing markets are counterfeit or substandard. A chemist in tier-2/tier-3 India has no practical tool to verify a drug batch before dispensing it. The official CDSCO verification route is inaccessible at the last mile, and individual suspicious reports are never connected β so counterfeit clusters spread silently.
MedShield is an AI agent a chemist can use to verify any medicine by entering a drug name and/or batch number. The agent:
- Validates the batch-number format.
- Looks the medicine up in a real verified drug database (253K+ Indian medicines).
- Cross-checks the manufacturer and runs a production-capacity anomaly check (impossible batch sizes).
- Checks whether the batch is already CDSCO NSQ-flagged.
- Positively verifies the batch against the registry β authenticity must be proven, never assumed from the absence of red flags.
- Detects nearby counterfeit clusters using MongoDB geospatial + vector search.
- Scores supplier credibility.
- Returns a clear verdict β GENUINE / UNVERIFIED / SUSPICIOUS / LIKELY COUNTERFEIT / NSQ-FLAGGED β with a confidence score, plain-English reasons, and an auto-generated CDSCO complaint PDF when needed.
Security posture. A real, well-known drug name printed on a box proves nothing β a forger can print anything. MedShield only returns GENUINE when the exact batch is positively confirmed in the verified registry and is consistent with the claimed drug, manufacturer, serial range and dates. A batch it cannot confirm is UNVERIFIED ("we cannot confirm this is genuine"), never a silent pass.
βββββββββββββββββββββββββββββββββββββββββββββββ
β Streamlit UI (app.py) β
β Verify tab β’ Map tab β’ metrics/badges β
βββββββββββββββββ¬βββββββββββββββββββ¬ββββββββββββ
β β (read-only)
agent/runner.py β
(ADK Runner + sessions) β
β β
βββββββββββββββββΌββββββββββββββββ β
β ADK Agent (Gemini 2.5 Flash- β β
β Lite) agent/agent.py β β
β instruction = prompts.py β β
βββββ¬ββββββββββββββββββββββββ¬βββββ β
β β β
ββββββββββββββββΌββββββββββ βββββββββββΌβββββββββββ
β MongoDB MCP server β β 8 custom tools β
β (npx, find/aggregate/ β β agent/tools.py β
β vector search) β β validators / β
ββββββββββββββββ¬βββββββββββ β pdf_generator β
β βββββββββββ¬βββββββββββ
β β
βββββββΌβββββββββββββββββββββββββΌββββββ
β utils/repositories.py β β all queries
β Medicine / Report / Mfr / Supplier β
βββββββββββββββββββ¬ββββββββββββββββββββ
β utils/mongodb.py (pooled client)
βββββββββββββΌβββββββββββββ
β MongoDB Atlas β
β medicines Β· suppliers β
β manufacturers Β· β
β suspicious_reports β
β (+2dsphere +vector idx)β
βββββββββββββββββββββββββββ
Embeddings: Google gemini-embedding-001 (768-dim) β agent/embeddings.py
Design principles. Configuration is centralised (agent/config.py,
single source of truth). All persistence goes through a repository layer
(utils/repositories.py) β tools and scripts never write raw pymongo.
Domain entities and the Verdict/ReportSource enums live in
agent/models.py (defined once, used everywhere). Pure logic (batch
validation, risk scoring) is I/O-free and unit-testable. This keeps the
codebase SRP/DIP/DRY-aligned and easy to extend.
| Layer | Technology |
|---|---|
| Agent framework | Google ADK (google-adk) |
| LLM | Gemini 2.5 Flash-Lite (gemini-2.5-flash-lite) |
| Embeddings | Google gemini-embedding-001 (768-dim, cosine) |
| Database | MongoDB Atlas (M0) + Vector Search + 2dsphere |
| DB tool surface | mongodb-mcp-server (via npx, MCP/stdio) |
| UI | Streamlit + pydeck |
| PDF generation | ReportLab |
| PDF parsing | PyMuPDF (fitz) |
| Deployment | Google Cloud Run (Docker) |
| Language | Python 3.11+ |
- Indian Medicine Dataset β 253,973 real medicines with manufacturer, composition, price, pack size. https://github.com/junioralive/Indian-Medicine-Dataset
- CDSCO NSQ alerts β monthly "Not of Standard Quality / spurious" drug alert PDFs, parsed for real flagged batches. https://cdsco.gov.in/opencms/opencms/en/Notifications/nsq-drugs/
- Kaggle AZ Medicine Dataset of India β supplementary composition/usage. https://www.kaggle.com/datasets/shudhanshusingh/az-medicine-dataset-of-india
Generated fields (batch numbers, manufacturing/expiry dates, serial ranges, manufacturer capacities, distributor records, and the 15 demo suspicious cases) are clearly produced by the ingestion/seed scripts and are reproducible via a fixed RNG seed.
- Python 3.11+
- Node.js + npm (the agent launches the MongoDB MCP server via
npx) - A Google Gemini API key β https://aistudio.google.com/app/apikey
- A free MongoDB Atlas account
- Create an M0 cluster. Atlas β Build a Database β M0 (Free) β pick a cloud/region β Create.
- Create a database user. Database Access β Add New Database User β username + password (Atlas auth). Save these.
- Whitelist your IP. Network Access β Add IP Address β
0.0.0.0/0(allow-all is fine for the hackathon; tighten for prod). - Get the connection string. Database β Connect β Drivers β copy
the
mongodb+srv://β¦string. Insert your username/password and append the database namemedshield:mongodb+srv://<user>:<pass>@<cluster>.mongodb.net/medshield?retryWrites=true&w=majority - The
medshielddatabase + collections are created automatically the first timescripts/ingest_data.pywrites to them β no manual creation needed. - Standard indexes are created automatically by ingestion
(
ensure_all_indexes()): the compound{batch_number, manufacturer_name}onmedicines, atextindex onmedicines.name, and the 2dsphere index onsuspicious_reports.location. - Create the Vector Search index manually (Atlas UI):
Atlas Search β Create Search Index β JSON Editor β choose the
suspicious_reportscollection β Vector Search type β name itreport_vector_indexβ paste:(Atlas Search indexes cannot be created reliably via pymongo on M0, which is why this one step is manual.){ "fields": [ { "type": "vector", "path": "embedding", "numDimensions": 768, "similarity": "cosine" } ] }
cd medshield
python3.11 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # then edit .env with real values.env:
MONGODB_URI=mongodb+srv://<user>:<pass>@<cluster>.mongodb.net/medshield?retryWrites=true&w=majority
MONGODB_DB=medshield
GOOGLE_API_KEY=your_gemini_api_key
GOOGLE_CLOUD_PROJECT=your_gcp_project_id # only for Cloud Run
# Full ingest (downloads 253K-row CSV + CDSCO PDFs, builds all collections).
python -m scripts.ingest_data --drop
# Quick subset while testing (no embeddings, ~2k medicines):
python -m scripts.ingest_data --limit 2000 --skip-embeddings
# Seed the 15 curated demo suspicious cases (needs the vector index for
# semantic search; geospatial works regardless).
python -m scripts.seed_suspicious --reset
# Seed the 2 genuine, in-registry demo medicines (so the GENUINE demo has a
# real verified batch with future expiry β Augmentin GSK-202503-00421).
python -m scripts.seed_demo_medicines# Fast, no LLM β exercises every tool directly against MongoDB:
python -m scripts.test_agent --tools-only
# Full end-to-end through Gemini + MCP (runs the 3 demo scenarios):
python -m scripts.test_agentstreamlit run app.py
# open http://localhost:8501| # | Input | Expected verdict |
|---|---|---|
| 1 | Augmentin 625 Duo Tablet, batch GSK-202503-00421, city Mumbai |
GENUINE β exact batch positively verified in the registry, no nearby flags |
| 2 | Augmentin 625 Duo Tablet, batch GSK-202403-99999, city Mumbai |
UNVERIFIED β real drug name but the batch is not in the registry; cannot confirm |
| 3 | Telmisartan 40mg Tablet, batch GSK-202503-00421, city Delhi |
LIKELY_COUNTERFEIT β that batch belongs to a different product (cloned batch number) |
| 4 | Telmisartan 40mg Tablet, batch TEL-202309-04412, mfr Hetero Labs, city Hyderabad |
NSQ_FLAGGED β matches a seeded CDSCO NSQ flag; complaint generated |
| 5 | Ranitidine 150mg Tablet, batch RAN-202310-03021, mfr JB Chemicals, supplier Surya Pharma Supplies, city Mumbai |
LIKELY_COUNTERFEIT β 3-city cluster within 200 km; complaint generated |
(Scenario 1 relies on scripts/seed_demo_medicines.py; scenarios 3β5 rely on
scripts/seed_suspicious.py + seed_demo_medicines.py having been run.)
gcloud run deploy medshield \
--source . \
--region asia-south1 \
--allow-unauthenticated \
--set-env-vars "MONGODB_URI=...,MONGODB_DB=medshield,GOOGLE_API_KEY=..."The image bundles Node.js so the MCP server runs inside the container.
medshield/
βββ app.py # Streamlit UI (Verify + Map tabs)
βββ agent/
β βββ agent.py # ADK agent (Gemini + MCP + custom tools)
β βββ runner.py # shared ADK Runner / streaming helpers
β βββ tools.py # 8 custom tools
β βββ prompts.py # system prompt (11-step reasoning)
β βββ embeddings.py # gemini-embedding-001 wrapper
β βββ config.py # single source of truth (settings/weights)
β βββ models.py # domain entities + Verdict/ReportSource enums
βββ utils/
β βββ mongodb.py # pooled client + health check
β βββ repositories.py # data-access layer (one repo per collection)
β βββ pdf_generator.py # CDSCO complaint PDF (ReportLab)
β βββ validators.py # batch-format validation (pure)
βββ scripts/
β βββ ingest_data.py # download + parse real data β MongoDB
β βββ seed_suspicious.py # 15 curated demo suspicious cases
β βββ seed_demo_medicines.py # 2 genuine, in-registry demo medicines
β βββ test_agent.py # CLI test harness
βββ data/ # downloaded datasets + generated PDFs (gitignored)
βββ requirements.txt
βββ Dockerfile
βββ .env.example
βββ README.md
MedShield is a decision-support tool, not a legal authority. Verdicts are guidance to help a chemist decide. Final confirmation of a spurious or substandard drug rests with CDSCO and State Drug Control authorities. When in doubt, do not dispense β verify.