vancouver-city-opendata-mcp
# Vancouver City Open Data MCP
An [MCP](https://modelcontextprotocol.io) server that exposes the [City of Vancouver Open Data Portal](https://opendata.vancouver.ca/) (Opendatasoft Explore API v2.1) as tools an LLM can call directly: search datasets, inspect their schema, query/filter records, discover facet values, and export bounded slices of data.
## Features
- **search_datasets** — full-text and ODSQL search across the ~200 datasets in the catalog.
- **get_dataset** — metadata and field schema for a single dataset.
- **query_records** — filter/sort/paginate a dataset's records with ODSQL (`where`, `select`, `order_by`, `group_by`, `q`, `refine`).
- **get_facets** — discover valid filter values (and counts) before writing a `where`/`refine` clause.
- **export_dataset** — bulk export in csv/json/geojson/parquet, capped at a configurable row limit.
- Runs over **stdio** (for Claude Desktop/Claude Code) or **Streamable HTTP** (for remote deployment) from the same codebase.
- No API key required for public read access; an optional key raises Opendatasoft's rate limits.
## Install
```bash
npm install
npm run build
```
## Usage — stdio (Claude Desktop / Claude Code)
stdio is the default transport — running the built binary with no flags starts it directly.
Claude Desktop config (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"vancouver-opendata": {
"command": "node",
"args": ["/absolute/path/to/vancouver-city-opendata-mcp/dist/index.js"]
}
}
}
```
Or explore it interactively with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
```bash
npx @modelcontextprotocol/inspector node dist/index.js
```
## Usage — HTTP
```bash
MCP_TRANSPORT=http PORT=3000 node dist/index.js
# or: node dist/index.js --http
```
This exposes a single `/mcp` endpoint implementing the Streamable HTTP transport (session lifecycle via the `mcp-session-id` header — `POST` to initialize/call tools, `GET` for the SSE stream, `DELETE` to close a session).
```bash
curl -s -X POST http://127.0.0.1:3000/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
```
**Note:** there is no inbound authentication in v1. If deploying the HTTP transport somewhere reachable beyond localhost, put it behind your own auth/reverse proxy, and set `ALLOWED_HOSTS` for DNS-rebinding protection.
## Environment variables
| Variable | Default | Description |
| --- | --- | --- |
| `ODS_BASE_URL` | `https://opendata.vancouver.ca/api/explore/v2.1` | Base URL of the Opendatasoft Explore API v2.1 to query. |
| `ODS_API_KEY` | _(none)_ | Optional Opendatasoft API key, sent as `Authorization: Apikey <key>`. Not required for public data. |
| `MCP_TRANSPORT` | `stdio` | `stdio` or `http` (overridden by the `--stdio`/`--http` CLI flags). |
| `HOST` | `127.0.0.1` | Host to bind the HTTP transport to. |
| `PORT` | `3000` | Port used when `MCP_TRANSPORT=http`. |
| `ALLOWED_HOSTS` | _(none)_ | Comma-separated hostnames allowed to reach the HTTP transport. |
| `EXPORT_ROW_CAP` | `5000` | Hard cap on rows `export_dataset` will return, regardless of the caller's requested limit. |
## Rate limits
Opendatasoft enforces a public per-IP quota (observed at 15,000 requests/day, plus per-dataset limits) surfaced via `X-RateLimit-*` response headers. `export_dataset` and friends will return a clear `isError` message if you're throttled. Set `ODS_API_KEY` to raise your limits.
## Development
```bash
npm run test # unit tests (no network access required)
RUN_LIVE_TESTS=1 npm test # also runs the opt-in suite against the real API
npm run dev # tsc --watch
```
## License
MIT
TDQS
Scored across 5 tools
Each tool targets a distinct stage of the open-data workflow: catalog search, metadata/schema retrieval, record querying, facet discovery, and dataset export. Even though query_records and export_dataset both return rows, their intended use cases are clear and non-conflicting.
All tool names follow a consistent verb_noun snake_case pattern: search_datasets, get_dataset, query_records, get_facets, export_dataset. The pattern is predictable and makes it easy to infer tool behavior from the name alone.
Five tools is well-scoped for an open-data portal MCP server. Each tool earns its place by covering a necessary part of the exploration-and-extraction workflow without unnecessary redundancy.
The server covers the full read-only lifecycle for open data: discover datasets, inspect schema, explore facet values, query records, and export in multiple formats. There are no obvious dead ends or missing actions that would prevent an agent from accomplishing the apparent purpose.