SEAO procurement-intelligence MCP server

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.

Category
访问服务器

README

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.

Example prompt

What's in this repo

File Role
fetch_seao_opendata.py Downloads the weekly OCDS JSON dumps from the open.canada.ca Atom feed into DATA/.
build_raw_json_db.py Filters those dumps (title regex + UNSPSC codes) into DATA/filtered_records.json.
build_work_sqlite_db.py Normalizes the filtered records into DATA/seao.db (relational schema + FTS5 + views + a schema_doc data dictionary).
seao_mcp_server.py The hybrid MCP server (FastMCP, read-only) exposing 9 tools over DATA/seao.db.
test_mcp_tools.py Smoke-tests every tool against the built DB.
.mcp.json Project-scoped MCP registration for Claude Code.

The data is not committed. The raw weekly dumps are multiple GB and the built DATA/seao.db is 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).

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 supplier_name badly undercounts. Every supplier metric resolves a name → party.neq and aggregates on the NEQ.
Win / loss Not stored. A bidder won a process iff its (ocid, party_id) is in award_supplier. Win-rate, head-to-head and margins derive it, counting distinct processes (bids span multiple lots).
gré à gré direct awards are sole-sourced and have no bids. Win-rate / margin tools restrict to open + limited tenders.

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.txt

Build 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.py

The 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.db

Register 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).

  1. 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.

  2. Expose it over HTTPS so ChatGPT (cloud) can reach your machine:

    ngrok http 8000          # or cloudflared / any tunnel; copy the https URL
    
  3. Add the connector in ChatGPTSettings → 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.

  4. 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_sql rejects writes/DDL and opens the DB mode=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_unit meanings, tags, procurement methods). Call it first.
  • run_sql(sql, limit?) — single read-only SELECT/WITH; writes/DDL/PRAGMA rejected, connection opened mode=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?" incumbent("Marguerite-Bourgeoys", "securite")
"Average winning bid for guarding (90152100) in Montreal?" price_benchmark(unspsc="90152100", delivery_area="6")
"Garda's win rate on open tenders?" supplier_profile("Garda")
"Commissionnaires vs Neptune — who wins more?" head_to_head("Commissionnaires", "Neptune")
"Does the Sûreté du Québec rotate suppliers?" buyer_profile("Surete du Quebec")
"Top-3 firms' share of the market?" market_overview(top_n=3)

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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选