Uwazi
Allows the tender/bid evaluation LLM to run fully on-premises by pointing the OpenAI-compatible gateway at a local Ollama endpoint, so procurement documents and model inference never leave the organization's own infrastructure.
Provides the LLM gateway for the agent's bid-evaluation workflow. The five-node LangGraph pipeline (extraction, bid analysis, scoring, risk flagging, orchestration) sends chat-completion requests through the OpenAI SDK to any OpenAI-compatible endpoint — OpenAI models can be used directly, e.g. side-by-side with the default Qwen2.5-72B-Instruct hosted on ModelScope.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Uwaziingest the tender and three bids, then run evaluation and show the report"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Uwazi
Evidence-first tender review for African procurement teams.
Uwazi turns tender and bid documents into a cited review pack designed for a roughly 20-minute committee review. It never awards the tender: the AI proposes and the procurement committee decides.
Built for the Agentic AI for Africa Innovation Challenge — Governance track, Bid Box Challenge, Digital economy / Open contracting theme.
Demo video
A 63-second end-to-end walkthrough of the deployed platform running against a Neon Postgres database on Vercel. Shows every step — ingestion state (via /api/v1/admin/seed-status), the rendered tenders list, ranked report with citations, a live "Run agentic evaluation" click that runs the LangGraph workflow in real time, the audit trail, and the citizen-facing Open Notices layer. The video lives in video/uwazi-demo.mp4 (1.1 MB, H.264, no audio) — see video/README.md for the scene-by-scene breakdown.
Live deployment: https://uwazi-rose.vercel.app
Related MCP server: caseware-ai-procurement-knowledge-platform
Problem statement
Public procurement in African institutions is slow, opaque, and expensive. The challenge brief itself states the number: a single tender evaluation takes 26 days of committee work today — sorting bids, ticking off mandatory documents, checking each bidder, comparing prices, and writing the report. Across the continent, that is billions of USD in deferred decisions, locked-up supplier cash, and lost opportunities for small suppliers who cannot navigate the bureaucracy.
The status quo has three structural failures:
Time. 26 days of committee work per tender. Procurement offices are bottlenecked; small suppliers wait months to be paid; capital sits idle.
Opacity. A small supplier cannot easily find an active tender, understand the eligibility, or prepare the mandatory documents. The pipeline is legible only to insiders — exactly the constituency that doesn't need it.
Auditability. When a tender is contested — by a losing bidder, by an audit office, by a journalist — the procurement office's defence rests on a Word document edited by hand and signed by one person. There is no machine-readable audit trail.
Solution overview
Uwazi is an agentic AI on the Model Context Protocol that turns the 26-day evaluation into a 30-minute officer-reviewed exercise. The agent ingests a tender document and N bid documents, extracts the evaluation criteria, scores every bid against every criterion with paragraph-level citations, flags compliance risks (missing mandatory documents, abnormally low financials), and produces a ranked report with a per-bid recommendation. A procurement officer then records accept / reject / needs_review decisions — the only entity that can write the decision field.
For the Digital economy / Open contracting theme, Uwazi also exposes a citizen-facing layer: an open_notices tool that returns every active tender in plain English (eligibility checklist, mandatory documents, deadlines) — making the procurement pipeline legible to citizens and small suppliers, not just to insiders.
The hard architectural rule, enforced at three layers (SQL CHECK + trigger, MCP tool, test suite):
The agent may propose scores, risk flags, and recommendations. The agent may NEVER record a decision.
Target users
User | What they get from Uwazi |
Procurement committees / evaluation officers | A cited draft review pack in ~20 minutes instead of 26 days. Every score, risk flag, and recommendation is one click from the source paragraph. They retain the legal decision — the agent never awards a tender. |
Citizens & small suppliers | Plain-language tender notices, eligibility checklists, mandatory-document lists, and AI explanations with citations. Reduces the barrier to entry for first-time bidders. |
Audit offices & journalists | A machine-readable audit trail of every AI proposal + human decision, timestamped and attributable. Exportable as a signed PDF for the procurement archive. |
Procurement regulators (PPRA, PPDA, RPPA) | An open-source reference implementation of an MCP-native, human-in-the-loop tender evaluation workflow that they can self-host. Procurement data never leaves their jurisdiction. |
One command
git clone https://github.com/Roy-Wanyoike/uwazi.git
cd uwazi
./run-demo.shThat's it. The script creates a venv, installs Uwazi, seeds a synthetic Makueni County water-procurement scenario with three bids, runs the LangGraph agent, demonstrates the human-in-the-loop boundary, and opens the review UI at http://localhost:8000.
A live deployment is also running on Vercel (Next.js + FastAPI multi-service) backed by a Neon Postgres database: https://uwazi-rose.vercel.app
Architecture

The diagram above shows the full system: users (procurement teams + suppliers/citizens) → MCP interface (FastMCP server, 10 tools, borrowed mcp-server-fetch) → LangGraph agents (5-node pipeline) → models & persistence (LLM gateway, document processing, SQLite/Postgres) → human-in-the-loop accountability gate at the bottom (AI output is draft-only → authorization enforced twice → human final decision + audit trail).
For the one-page architectural contract, see ARCHITECTURE.md. For the deep-dive (rules, ADRs, schema), see docs/architecture-deep-dive.md.
Setup & installation instructions
Prerequisites
Python 3.11+
Node.js 22+ (for the Next.js frontend)
Internet access for the initial
pip installandnpm install
One-command setup (recommended)
git clone https://github.com/Roy-Wanyoike/uwazi.git
cd uwazi
./run-demo.shThe script does everything: creates a Python virtualenv, installs Python + Node deps, seeds the sample Makueni tender + 3 bids, runs the LangGraph agent, demonstrates the human-in-the-loop boundary, and opens the review UI at http://localhost:8000.
Manual setup (if you'd rather see what's happening)
git clone https://github.com/Roy-Wanyoike/uwazi.git
cd uwazi
# Python side
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env # set OPENAI_API_KEY for real Qwen scores; leave blank for STUB
# Frontend side (optional — the Python-only review UI at /web/index.html works too)
cd apps/web && npm install && cd ..
# Seed + run
python scripts/seed.py
python -m uwazi.cli evaluate --tender-id makueni-water-2026
python -m uwazi.cli serve # → http://localhost:8000Configuration (.env)
Every setting has a sensible default — the demo runs without any configuration. For real LLM scores (instead of STUB mode), set:
OPENAI_API_KEY=<your-modelscope-inference-token> # NOT the read-only Studio token — needs inference scope
OPENAI_BASE_URL=https://api-inference.modelscope.ai/v1
OPENAI_MODEL=Qwen-Ambassador/Qwen3.7-MaxUsage instructions
As a procurement officer (via the review UI)
Open http://localhost:8000 (or the Vercel deployment URL).
Click Tenders in the header → select the Makueni County Water Reticulation Project.
If no report is shown, click Run agentic evaluation — the LangGraph orchestrator runs the 5-node workflow (extract criteria → analyze bids → score → flag risks → compose report).
Review the ranked bids. Each card shows the AI recommendation (
award/shortlist/reject), weighted score, risk flags with citations, and an expandable list of scores with paragraph-level citations back to the tender and bid documents.Click Accept / Needs review / Reject on each bid, enter a rationale (≥10 chars), and submit. The decision is persisted with
decided_by='officer-web'and added to the audit trail.Click View audit trail to see every AI proposal and human decision, timestamped and attributable.
As a citizen or small supplier (via the Open Notices page)
Open http://localhost:8000 (or the Vercel deployment URL).
Click Open Notices in the header.
Every active tender is shown in plain English: eligibility checklist, mandatory documents to prepare, and apply instructions. No legalese.
As an MCP client (Claude Desktop, custom agent, etc.)
python -m uwazi.cli mcp # starts the FastMCP server on stdioConnect from any MCP-compatible client. All 10 tools are listed below in the MCP tools / servers section.
Via the CLI
python -m uwazi.cli evaluate --tender-id makueni-water-2026 # run the agent
python -m uwazi.cli report --tender-id makueni-water-2026 # fetch the ranked report
python -m uwazi.cli audit --tender-id makueni-water-2026 # fetch the audit trail
python -m uwazi.cli demonstrate-human-in-the-loop --tender-id ... # show the SQL trigger refusing an agent principal
python -m uwazi.cli serve # start the FastAPI review UI
python -m uwazi.cli mcp # start the MCP server (stdio)Technology stack
Layer | Technology | Why |
MCP server (built) |
| The protocol surface for the entire workflow. |
MCP server (borrowed) | Official | Handles HTTP redirects, gzip, charset detection, HTML stripping, robots.txt, SSRF protection. ~500 LOC saved. |
Orchestration | LangGraph ( | Open-source, Apache 2.0. The de-facto orchestrator for agent workflows with named-tool calls. |
LLM gateway |
| Default: Qwen2.5-72B-Instruct via ModelScope (open-weights). Also supports OpenAI for side-by-side and local Llama via Ollama for fully on-prem. |
Document ingestion |
| Paragraph-level citation tracking — works for both PDFs (page-based) and Markdown (no pages). |
Storage | SQLite (local dev) / Postgres (production) | Dual-backend via |
Backend API | FastAPI (Python 3.12) | Hosts the MCP server, the REST API used by the frontend, and the lifespan auto-seed handler. |
Frontend | Next.js 16 + TypeScript + Tailwind CSS 4 | Server-rendered procurement review UI. Pages: |
Deployment | Vercel (services pattern) |
|
Tests |
| 42 tests, ~1.5s. 5 are negative-path tests that verify the architectural rule refuses agent-originated decisions. |
Agent architecture
Uwazi is a LangGraph state machine with 5 nodes and 1 conditional edge. The agent plans, calls tools, recovers from failure, and stops at a hard human-in-the-loop gate.
┌──────────────────────────────────────────┐
│ UWAZI LANGGRAPH GRAPH │
└──────────────────────────────────────────┘
START → extract_criteria → analyze_bids → score_bids → flag_risks → compose_report → END
│ │ │ │ │
└── (no criteria) ──────────────────────────────→ compose_report
Nodes (each is an async function of state):
extract_criteria ExtractorAgent over tender paragraphs (LLM call)
analyze_bids BidAnalyzerAgent per bid (LLM call)
score_bids ScorerAgent per (bid, criterion) pair (LLM call)
flag_risks RiskAgent per bid (LLM call)
compose_report OrchestratorAgent pure Python (no LLM)State is a single TypedDict (UwaziState) that flows through every node. Each node returns a partial dict that LangGraph merges. The graph is compiled with MemorySaver checkpointer — an interrupted evaluation can be resumed from the last checkpoint (not yet wired into the UI; see Limitations below).
Recovery path: if extract_criteria returns an empty list (the LLM found no criteria), the conditional edge skips analyze_bids → score_bids → flag_risks and jumps to compose_report, which produces a report with errors=["extractor: no criteria"] rather than crashing. Per-bid failures in analyze_bids / score_bids / flag_risks are caught, logged to state["errors"], and the bid is short-circuited — the rest of the bids continue.
Why LangGraph over a hand-rolled loop: it is a state machine. Each node is a function. The edges are conditional. The graph can be visualised (graph.get_graph().draw_*). State persists to SQLite (langgraph-checkpoint-sqlite), so an interrupted evaluation can be resumed. It is the de-facto open-source orchestrator for agent workflows with named-tool calls — strangers reading the code recognise the shape immediately.
MCP implementation
Uwazi is MCP-native. Every capability — ingesting documents, extracting criteria, scoring bids, recording decisions, fetching external tender notices — is exposed as an MCP tool. The orchestrator agent is an MCP client that calls the tools through the protocol.
This means:
Any MCP-compatible client (Claude Desktop, the MCP CLI, a custom agent) can drive Uwazi.
The procurement office can swap the LLM (Qwen, GPT, Claude, local Llama) without touching the agent code.
The human-in-the-loop boundary is enforced at the MCP tool layer:
record_officer_decisionis the only tool that writes to the decision table, and it requires a human principal. The SQLCHECK + triggeris the second line of defense.
The MCP server is implemented in src/uwazi/mcp_server.py using the official mcp Python SDK's FastMCP class. The server runs on stdio transport by default (python -m uwazi.cli mcp), or as part of the FastAPI service on Vercel.
MCP servers built vs borrowed
MCP server | Source | Why |
Uwazi MCP server ( | Built. FastMCP on stdio. 10 tools (see below). | The challenge requires our own server with ≥3 tools; this is the protocol surface for the entire workflow. |
Official MCP fetch server ( | Borrowed. Anthropic / MCP maintainers. | Handles HTTP redirects, gzip, charset detection, HTML stripping, robots.txt respect, and SSRF protection — features that would take ~500 LOC to reproduce. Used by |
MCP tools / servers
Uwazi's FastMCP server exposes 10 tools:
Tool | What it does | Who can call |
| List all ingested tenders (most recent first). | Any MCP client. |
| Pull a tender notice from a public procurement portal URL via the official | Any MCP client. |
| Parse a tender PDF or Markdown into paragraphs, store it. | Any MCP client. |
| Parse a bid PDF or Markdown, store it against a tender. | Any MCP client. |
| Run the LangGraph orchestrator workflow. Returns the ranked report. | Any MCP client. |
| Fetch the latest evaluation report (citations, scores, risk flags). | Any MCP client. |
| Citizen-facing tool — every active tender in plain English with eligibility + mandatory documents. | Any MCP client. |
| AI-generated plain-language summary for a small supplier, citizen, or journalist. Every claim cites a paragraph. Stamped | Any MCP client. |
| Officer records | Requires a human principal ( |
| Full audit trail of AI proposals + human decisions, timestamped and attributable. | Any MCP client. |
Human-in-the-loop workflow
The single most important architectural rule:
The agent may propose scores, risk flags, and recommendations. The agent may NEVER record a decision.
Enforced at three layers:
SQL —
CHECK (decided_by <> 'agent')+BEFORE INSERTtrigger ondecisions. A reviewer can verify this in 60 seconds:sqlite3 uwazi.db "INSERT INTO decisions (id, tender_id, bid_id, decision, rationale, decided_by, decided_at) VALUES ('hack', 't', 'b', 'accept', 'agent hack', 'agent', datetime('now'));" # → Parse error: agent may not record decisions — human-in-the-loop rule (ARCHITECTURE.md §5)On Postgres (Vercel/Neon), the trigger is split into a
CREATE OR REPLACE FUNCTION refuse_agent_decision_writer() RETURNS TRIGGER+CREATE TRIGGER— the same rule, dialect-appropriate syntax.MCP tool —
record_officer_decisionrefuses any request whereprincipal_kind='agent'. Verified bytests/test_human_in_the_loop.py::test_tool_refuses_agent_principal.Verification — focused tests exercise both the tool-layer refusal and the database constraint. The suite runs on every commit.
The workflow in practice
The agent runs the 5-node LangGraph workflow and produces a ranked report with scores, risk flags, and recommendations — all stamped
ai_generated=True.The procurement officer opens the report in the review UI. Every score is one click from the source paragraph it was derived from.
The officer records
accept/reject/needs_reviewper bid, with a rationale (≥10 chars). The decision is persisted withdecided_by='officer-demo'(or the OIDC principal in production).The audit trail captures every AI proposal and every human decision, timestamped and attributable. Exportable as a signed PDF for the procurement archive.
Limitations
Uwazi is honest about what's unfinished. The brief rewards honesty about this.
Run-to-run variance on the real open-weights model is not measured. STUB mode is deterministic; the real Qwen endpoint should be near-deterministic at
temperature=0.0but I have not measured σ per criterion. See EVALS.md — this is the one failure I did not fix, with a concrete next-step plan.LangGraph checkpoint resume is not yet wired into the UI. The graph is compiled with
MemorySaver, so an interrupted evaluation can in principle be resumed, but the review UI does not yet expose the resume button.PDF ingestion is tested only via the Markdown path.
pdf.pysupports PDFs viapdfplumber, but the test suite covers Markdown only. Committing a binary PDF would bloat the repo. To verify manually:python -c "from uwazi.pdf import ingest_pdf; print(len(ingest_pdf('/path/to/real.pdf')))".The external
mcp-server-fetchis only tested manually. The test suite verifies the fallback path (server not available → graceful degradation). The happy path (real URL fetch) requires a running subprocess.The
/api/debugendpoint leaks env var names + safe values. Disable in production by deletingapps/web/app/api/debug/route.tsor guarding behindprocess.env.NODE_ENV !== 'production'.No real LLM API key set in the deployed demo. The current Vercel deployment uses STUB mode (no
OPENAI_API_KEY) so every bid scores 0.0 — the workflow runs end-to-end but scores aren't differentiated. Set a real ModelScope inference token to see non-zero scores.No production auth. The deployment uses
OFFICER_PRINCIPAL_ID=officer-demo. In production, this should come from OIDC (Keycloak) — the code is structured to accept it, but not wired up.No multi-tenant isolation. Single-tenant per deployment. A procurement office runs their own instance.
Future improvements
Quarter | Milestone |
Q4 2026 | Uwazi v1.0 — this submission. MCP server + 10 tools + 4-agent LangGraph workflow + review UI + audit trail + Vercel deploy + Neon Postgres storage. |
Q1 2027 | Kenya pilot — one county procurement office (target: Makueni or Kirinyaga). Real tender data, real officer feedback. First audit-trail PDF exported to a real audit office. Wire LangGraph checkpoint resume into the UI. Add a per-criterion σ measurement harness ( |
Q2 2027 | Supplier SaaS launch — plain-language tender notices, deadline alerts, eligibility checker. Freemium, USD 19/mo. Add per-country adapters (Kenya PPIP, Tanzania PPRA, Uganda PPDA, Rwanda RPPA) mirroring the |
Q3 2027 | EAC expansion — Tanzania, Uganda, Rwanda. Per-country procurement-portal crawlers wired into |
Q4 2027 | Pan-African — 9 OCDS-publishing authorities. Series A. Replace the SQLite/Postgres storage layer with Vercel Postgres + Neon pooled connection for production scale. Add OIDC auth + per-tenant isolation. |
Project structure
uwazi/
├── README.md ← you are here
├── ARCHITECTURE.md ← one-page architectural contract
├── EVALS.md ← 42 tests + 1 known unfixed failure
├── DEMO_SCRIPT.md ← 20-minute live review walkthrough
├── run-demo.sh ← one-command demo
├── pyproject.toml
├── .env.example
├── docs/
│ ├── architecture.png ← the architecture diagram shown above
│ └── architecture-deep-dive.md ← the full ADR + schema contract
├── src/uwazi/
│ ├── config.py ← env-driven settings
│ ├── models.py ← Pydantic domain models
│ ├── db.py ← dual-backend (SQLite/Postgres) schema + trigger
│ ├── pdf.py ← PDF + Markdown ingestion, paragraph-level citations
│ ├── citation.py ← 5-check citation validator (fails closed)
│ ├── gateway.py ← provider-agnostic LLM gateway (STUB mode + real)
│ ├── prompts.py ← every system + user prompt in one module
│ ├── agents.py ← ExtractorAgent, BidAnalyzerAgent, ScorerAgent, RiskAgent
│ ├── orchestrator.py ← LangGraph StateGraph (5 nodes, 1 conditional edge)
│ ├── external_mcp.py ← integration with official mcp-server-fetch
│ ├── mcp_server.py ← FastMCP server: 10 tools
│ ├── api.py ← FastAPI review UI + lifespan auto-seed
│ └── cli.py ← CLI driver
├── apps/web/ ← Next.js 16 frontend (Vercel-deployed)
│ ├── app/ ← App Router pages: /, /tenders, /notices, /audit
│ ├── lib/api.ts ← typed API client (reads UWAZI_API_URL binding)
│ └── public/ ← logo, brand, favicon SVGs
├── samples/
│ ├── tender-makueni-water.md ← synthetic tender (real-shape)
│ ├── tender-makueni-water.ocds.json ← same tender in OCDS (Open Contracting Data Standard)
│ ├── bid-aquaforce.md ← clean bid
│ ├── bid-hydrotech.md ← missing mandatory ISO 9001 (RiskAgent flag)
│ └── bid-sahara.md ← anomalous-low financial (RiskAgent flag)
├── scripts/
│ ├── seed.py ← seeds the sample tender + 3 bids
│ └── demo-video-script.md ← 3-minute unedited video script
└── tests/
├── test_citation.py
├── test_pdf.py
├── test_human_in_the_loop.py
└── test_mcp_tools.pyLicense
MIT. See LICENSE.
AI recommends. The procurement officer decides.
This server cannot be deployed
Maintenance
Related MCP Connectors
Connect your AI assistant to governed tender and RFP dossiers: sources, versions, decisions.
Human-in-the-loop review and approval for AI agents. Audit trail, approval policies, native MCP.
Evidence-readiness MCP server: validate, audit, and score briefs, memos, and evidence packs.
MCP-native AI evaluation: rubric audits, eval suites, and proof reports for AI/LLM output.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP-only server for evidence-based EPC tender screening that enables AI agents to run a full screening workflow, extract requirements, match company profiles, generate compliance matrices, and produce bid/no-bid advisory memos.MIT
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to retrieve, search, and compare procurement documents using hybrid retrieval and MCP integration.-
- FlicenseNot gradedqualityCmaintenanceEnables AI-powered procurement policy compliance, document generation, supplier management, budget verification, and analytics through standardized MCP tools.-
- AlicenseAqualityBmaintenanceEnables reviewers to search Kenyan public procurement awards, run integrity red-flag checks, benchmark prices and profile suppliers. It also lets the agent file sourced flags and draft committee reports, both gated behind approval by a named human.6MIT