SEAO procurement-intelligence MCP server
Click on "Install 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., "@SEAO procurement-intelligence MCP servershow me Garda's win rate in security tenders for 2024"
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.
SEAO procurement-intelligence MCP server
A read-only Model Context Protocol server that
lets an LLM analyst interrogate Quebec's SEAO public-procurement open data
(security-services subset) in plain language. A small pipeline downloads the raw
OCDS feed, filters it to security tenders, and normalizes it into a query-ready
SQLite database (DATA/seao.db); the MCP server then turns that database into an
analyst's assistant rather than a raw SQL endpoint.

What's in this repo
File | Role |
| Downloads the weekly OCDS JSON dumps from the open.canada.ca Atom feed into |
| Filters those dumps (title regex + UNSPSC codes) into |
| Normalizes the filtered records into |
| The hybrid MCP server (FastMCP, read-only) exposing 9 tools over |
| Smoke-tests every tool against the built DB. |
| Project-scoped MCP registration for Claude Code. |
The data is not committed. The raw weekly dumps are multiple GB and the built
DATA/seao.dbis a generated artifact — both are.gitignored. Build the database yourself with the pipeline below (a few minutes on a normal connection, dominated by the download step).
Related MCP server: GraphDB MCP Server
Why not just point a generic SQLite MCP at the file?
Three things in this data quietly break naive SQL, so the curated tools handle them for you:
Trap | Reality | How the tools handle it |
Supplier identity | The same firm has many name spellings (Garda = 9 NEQs / 6 spellings). Grouping by | Every supplier metric resolves a name → |
Win / loss | Not stored. A bidder won a process iff its | Win-rate, head-to-head and margins derive it, counting distinct processes (bids span multiple lots). |
gré à gré |
| Win-rate / margin tools restrict to |
Setup
Requires Python 3.10+.
git clone <your-repo-url> SEAO_MCP
cd SEAO_MCP
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtBuild the database
Run the pipeline once to populate DATA/:
# 1. Download the weekly OCDS dumps (writes DATA/week_*.json — several GB).
python fetch_seao_opendata.py
# 2. Filter to security-services tenders (writes DATA/filtered_records.json).
python build_raw_json_db.py
# 3. Normalize into the query-ready SQLite DB (writes DATA/seao.db).
python build_work_sqlite_db.pyThe filter rules (title keywords + UNSPSC codes) live at the bottom of
build_raw_json_db.py — edit them to target a different sector. build_work_sqlite_db.py
accepts optional input/output paths: python build_work_sqlite_db.py [input.json] [output.db].
Verify the build:
python test_mcp_tools.py # smoke-tests every tool against DATA/seao.dbRegister it with an MCP client
The server speaks stdio. Its DB path defaults to DATA/seao.db next to the
script; override with the SEAO_DB_PATH environment variable.
Claude Code (this repo)
A project-scoped .mcp.json is committed — open the project in
Claude Code and it will prompt to enable the seao server. It launches the
server via the project's .venv, so make sure you created the venv and built the
DB first. Or add it from the CLI:
claude mcp add seao -- "$PWD/.venv/bin/python" "$PWD/seao_mcp_server.py"Claude Desktop
Add to claude_desktop_config.json (macOS:
~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), replacing
/ABS/PATH with this repo's absolute path:
{
"mcpServers": {
"seao": {
"command": "/ABS/PATH/SEAO_MCP/.venv/bin/python",
"args": ["/ABS/PATH/SEAO_MCP/seao_mcp_server.py"],
"env": { "SEAO_DB_PATH": "/ABS/PATH/SEAO_MCP/DATA/seao.db" }
}
}
}Restart the client.
ChatGPT
ChatGPT only connects to remote MCP servers (HTTP/SSE) added as custom connectors — it can't spawn a local stdio process the way Claude does. So you wrap this stdio server in an HTTP bridge and expose it with a tunnel. You'll need a ChatGPT plan that supports custom connectors / Developer Mode (Plus, Pro, or a Business/Enterprise workspace where an admin has enabled them).
Bridge stdio → HTTP/SSE with
mcp-proxy:source .venv/bin/activate pip install mcp-proxy SEAO_DB_PATH="$PWD/DATA/seao.db" \ mcp-proxy --sse-port 8000 --sse-host 127.0.0.1 -- \ "$PWD/.venv/bin/python" "$PWD/seao_mcp_server.py"This serves the server at
http://127.0.0.1:8000/sse.Expose it over HTTPS so ChatGPT (cloud) can reach your machine:
ngrok http 8000 # or cloudflared / any tunnel; copy the https URLAdd the connector in ChatGPT → Settings → Connectors → Advanced → Developer mode, then Create. Give it a name, set the MCP server URL to
https://<your-tunnel>/sse, and (since there's no auth) choose "No authentication". Save and enable it for the conversation.Start a chat, pick the seao connector, and ask in plain language.
⚠️ The tunnel makes your local DB reachable from the internet for as long as it runs. This server is read-only (
run_sqlrejects writes/DDL and opens the DBmode=ro), but the data is still exposed — keep the tunnel up only while you're using it, and prefer an authenticated tunnel if your tooling supports it.
Tool catalogue
Mapped to four analyst objectives:
Flexible core
describe_schema(table?)— data dictionary + the code lookups (delivery-area names,value_unitmeanings, tags, procurement methods). Call it first.run_sql(sql, limit?)— single read-onlySELECT/WITH; writes/DDL/PRAGMA rejected, connection openedmode=ro, auto-LIMIT. The long-tail escape hatch.search_processes(text?, unspsc?, buyer?, delivery_area?, status?, procurement_method?)— find the relevant tenders (FTS + filters). The usual starting point.
1 — Tactical intelligence (evaluating a new tender)
incumbent(buyer, service?, unspsc?)— who holds / held a buyer's contract, with awarded and contract values, most recent first.price_benchmark(unspsc?, text?, delivery_area?, value_unit?)— awarded-value and winning-bid distributions, plus the winner-vs-next-lowest-rival margin.
2 — Competitor analysis
supplier_profile(supplier)— win rate, total won, method mix, top buyers, areas targeted, and most-frequent rivals (resolved by NEQ).head_to_head(supplier_a, supplier_b)— how often two firms bid the same tender and who won (incl. both-won-different-lots).
3 — Buyer behaviour
buyer_profile(buyer)— open vs gré à gré mix, total spend, supplier loyalty/rotation (top-supplier share), volume by year.
4 — Macro market trends
market_overview(year?, unspsc?, text?, top_n?)— total value by year (TAM), top-N concentration (consolidation), method trend, geographic spread.
Anything not covered → run_sql, guided by describe_schema.
Examples (analyst → tool)
Question | Call |
"Who held security at CSS Marguerite-Bourgeoys, and for how much?" |
|
"Average winning bid for guarding (90152100) in Montreal?" |
|
"Garda's win rate on open tenders?" |
|
"Commissionnaires vs Neptune — who wins more?" |
|
"Does the Sûreté du Québec rotate suppliers?" |
|
"Top-3 firms' share of the market?" |
|
Data source & license
Source data: SEAO open data published via open.canada.ca in OCDS format. This repository's code is released under the MIT License; the underlying procurement data remains subject to its original publisher's terms.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/E112222/SEAO_MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server