comprasal-mcp
# COMPRASAL MCP
Query **El Salvador's public procurement data** straight from your AI assistant.
This is a [Model Context Protocol](https://modelcontextprotocol.io) server that wraps the public COMPRASAL API (`comprasal.gob.sv`) — the country's electronic public-purchasing system — so you can ask Claude (or any MCP client) things like:
> *"What did the Ministry of Health award in 2025, and for how much?"*
> *"Show me the latest public contracts and their suppliers."*
> *"Pull the full detail and stage calendar for process 83743."*
All data is public information published by the State under the LACAP procurement law. This server is **read-only**.
---
## What you can ask
Once connected, your assistant gains these tools:
| Tool | What it does |
|------|--------------|
| `search_procurement` | Search awarded processes by institution, year, modality, or free text. Returns supplier, amount, dates, code. |
| `get_process_detail` | Full detail of one process: amounts, dates, and the complete stage calendar. |
| `get_award_report` | Richest view: bidders, planned vs certified amounts, budget codes, signature date. |
| `get_supplier_contracts` | A supplier's public-sector track record: contracts won, by company name (bounded recent scan). |
| `list_institutions` | Resolve an institution name (e.g. "salud") into its id. |
| `list_modalities` / `list_states` / `list_years` | Catalogs for building precise filters. |
---
## Install
Requires [Node.js](https://nodejs.org) 18+.
```bash
git clone https://github.com/glassbit/comprasal-mcp.git
cd comprasal-mcp
npm install
npm run build
npm test
```
## Connect to Cursor
This repo ships a project-level MCP config at `.cursor/mcp.json`. After `npm run build`:
1. Open this folder in Cursor.
2. Go to **Settings → MCP** and enable the **comprasal** server (or restart Cursor).
3. The assistant can call the COMPRASAL tools in Agent mode.
The config runs `node dist/index.js` against the live COMPRASAL API (cache off by default). To override globally, add the same block to your user MCP settings.
## Connect to Claude Desktop
Add this to your `claude_desktop_config.json`:
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"comprasal": {
"command": "node",
"args": ["/absolute/path/to/comprasal-mcp/dist/index.js"]
}
}
}
```
Restart Claude Desktop. You'll see the COMPRASAL tools available. Ask it about El Salvador's public spending.
---
## Local cache (opt-in)
By default every request goes to the live COMPRASAL API — a fresh clone behaves identically for everyone. You may opt in to a **local, per-user** response cache to speed up repeated queries within a session:
| Variable | Default | Description |
|----------|---------|-------------|
| `COMPRASAL_CACHE_ENABLED` | `false` | Set to `true` to cache raw API responses on disk |
| `COMPRASAL_CACHE_DIR` | `.comprasal-cache` | Cache directory (gitignored, never committed) |
| `COMPRASAL_CACHE_TTL_MS` | `3600000` (1 hour) | Entry lifetime; expired entries are deleted on read |
The cache is **not** a shared or committed database — it is created at runtime on your machine only. After TTL expiry, the next request fetches live data again. Disable it anytime with `COMPRASAL_CACHE_ENABLED=false`.
---
## Tests
```bash
npm test # unit + mocked integration only (no network)
npm run test:live # optional smoke test against real COMPRASAL API (~7s)
```
Tests cover client-side filters, Zod argument validation, file cache behaviour, and mocked HTTP flows.
---
## Notes & caveats
**The upstream government API only filters reliably by institution.** This is the single most important thing to understand about this tool:
- `id_institucion` is the **only** server-side filter that works. Year, free-text search, and date range are **silently ignored** by the COMPRASAL backend.
- This server compensates by filtering **client-side** (over the real award date `fecha_adjudicacion` and text fields), scanning newest-first pages up to a bounded limit.
- **Consequence:** deep historical queries are not practical. Asking for "all contracts from supplier X since January 2025" or "institution Y in 2024" can require scanning thousands of records at ~7s/page — so those tools return a **bounded recent window** and clearly say so in a `coverage` / `filtering` block. A bounded result is never presented as exhaustive.
- **Backend latency is ~7s per request** (this is the government server, not the MCP). Tools make one request per page and cap how many pages they scan, so a call may take 10-50s.
- `get_supplier_contracts` cannot filter by supplier upstream, so it scans the most-recent global records and matches locally. Pass `id_institucion` when you know it — the scan becomes far faster and deeper.
- **Date filters** apply to the award date (`fecha_adjudicacion`) in this server, even though the raw upstream API filters on the process start date.
- `get_award_report` uses a different id than the other endpoints (a known upstream quirk). Prefer `get_process_detail` unless you specifically need the bidder/budget breakdown.
- The dataset holds ~100k+ awarded processes, current through 2026. Each process may appear as several rows (one per lot/supplier).
- Tool arguments are validated with **Zod** before any upstream call; invalid inputs return a clear error without hitting the API.
### Why no local database / committed cache?
This server queries the **live** public API so that **anyone who clones it gets identical results with zero setup** — no pre-scraped dataset, no shared state in the repo. A committed database would mean either hosting a central server (defeating the "run it yourself" model) or asking every user to scrape COMPRASAL first (hours of work).
An **optional, opt-in file cache** is available for convenience: it lives in `.comprasal-cache/` on your machine only (gitignored), expires after 1 hour, and is **off by default**. It does not change what a fresh clone returns on first run. A full local-database variant for deep historical analysis remains a possible separate, opt-in project.
## Data, source & legality
Data comes from the public COMPRASAL portal (`comprasal.gob.sv`), accessed through its public, unauthenticated REST API. Public procurement acts are public by law under the **LACAP** (Ley de Adquisiciones y Contrataciones de la Administración Pública). Public registries are excluded from the scope of El Salvador's Personal Data Protection Law (Decreto 144, 2024, art. 3). This project redistributes already-public information for transparency and research purposes and performs no scraping of authenticated areas.
## License
MIT. Not affiliated with or endorsed by the Government of El Salvador.
TDQS
Scored across 8 tools
Most tools have distinct purposes, but get_award_report and get_process_detail could be confused since both provide process details. The caveat about different IDs and the distinct focus (richest award vs full detail) help clarify, but there is potential for misselection.
All tool names follow a consistent snake_case verb_noun pattern (get_, list_, search_), making the set predictable for an agent.
8 tools is well-scoped for a procurement data retrieval system, covering search, detail views, supplier lookup, and listing of reference entities without unnecessary bloat.
The tool set covers the main read operations (search, detail, supplier contracts, reference lists). A minor gap is the lack of a straightforward 'list all processes' tool, but search can be used broadly. Overall, the surface is adequate for the domain.