Add remote MCP server for AI-assisted access to tracker data#117
Open
matthelm wants to merge 1 commit intoBuildCanada:mainfrom
Open
Add remote MCP server for AI-assisted access to tracker data#117matthelm wants to merge 1 commit intoBuildCanada:mainfrom
matthelm wants to merge 1 commit intoBuildCanada:mainfrom
Conversation
The Build Canada tracker already makes government accountability data publicly available through a REST API. This PR adds a Model Context Protocol (MCP) server so AI assistants can query that data directly. Anyone using Claude Desktop, Claude Code, Cursor, or any MCP client can connect with a single URL: https://www.buildcanada.com/mcp ## MCP server Uses the official `mcp` Ruby SDK with stateless Streamable HTTP transport. Thirteen read-only tools, no auth required: - list_policy_areas — discover valid policy area slugs - list_commitments / get_commitment — search and inspect commitments - list_promises / get_promise — platform promises with progress scores - list_bills / get_bill — parliamentary bills with stage tracking - list_departments / get_department — departments with ministers - list_ministers — cabinet officials by portfolio - list_activity — chronological feed of government activity - get_commitment_summary — status overview by policy area - get_commitment_progress — time-series for trend analysis Each tool dispatches internally to the existing REST endpoints via Rack — zero duplicated query or serialization logic. The entire MCP surface is a single controller with a declarative config array. Adding a new tool is ~8 lines. No existing files are modified — this is purely additive (new controller, new policy_areas endpoint, routes, and tests). ## Testing locally 1. Start Rails: `bin/rails server -p 3099` 2. Verify: `curl -X POST http://localhost:3099/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'` 3. To test with Claude Desktop, expose via ngrok: ngrok http 3099 4. In Claude Desktop: Settings > Connectors > Add custom connector, then enter the ngrok URL with /mcp appended. You may need to allow the ngrok host in Rails: # config/initializers/allow_tunnel.rb (do not commit) Rails.application.config.hosts.clear 5. Run tests: `bin/rails test test/controllers/mcp_controller_test.rb`
xrendan
reviewed
Mar 26, 2026
Member
xrendan
left a comment
There was a problem hiding this comment.
Can you also update the docs and CLAUDE.md to mention these things.
The mcp server will be available at buildcanada.com/tracker/mcp instead of buildcanada.com/mcp.
| properties: { id: { type: "integer", description: "The commitment ID" } }, | ||
| required: [ "id" ] | ||
| }, | ||
| { |
Member
There was a problem hiding this comment.
This is really not clear in the code (I need to clean things up), but commitments now replace promises:
list_promises
get_promise
Can you remove these?
| path_template = config[:path] | ||
| path_params = path_template.scan(/:(\w+)/).flatten.map(&:to_sym) | ||
|
|
||
| klass = Class.new(MCP::Tool) do |
Member
There was a problem hiding this comment.
It's slightly more code, but I'd rather this as their own models instead of the dynamic generation.
Can you make this POROs and put them under models/mcp_tools with a concern that has the shared logic here with the properties as methods/class methods on the tool classes directly?
{
name: "get_commitment_progress",
path: "/api/burndown/:government_id",
description: <<~DESC.strip,
Commitment progress over time — daily time-series of how many commitments have been
scoped, started, completed, or broken throughout a government's mandate. Useful for
trend analysis and charting. Returns { date, scope, started, completed, broken } per
day, plus mandate_start/end dates. Tracks COMMITMENTS (not promises).
The government_id for the current Government of Canada is 1.
DESC
properties: {
government_id: { type: "integer", description: "Government ID (1 = current Government of Canada)" },
source_type: { type: "string", description: "Filter by source type" },
policy_area_slug: { type: "string", description: "Filter by policy area slug" },
department_slug: { type: "string", description: "Filter by lead department slug" }
},
required: [ "government_id" ]
}
Becomes something like:
description= <<~DESC.strip,
Commitment progress over time — daily time-series of how many commitments have been
scoped, started, completed, or broken throughout a government's mandate. Useful for
trend analysis and charting. Returns { date, scope, started, completed, broken } per
day, plus mandate_start/end dates. Tracks COMMITMENTS (not promises).
The government_id for the current Government of Canada is 1.
DESC
...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds a Model Context Protocol (MCP) server so AI assistants can query Build Canada tracker data directly. Anyone using Claude Desktop, Claude Code, Cursor, or any MCP client can connect with a single URL:
https://www.buildcanada.com/mcp
In Claude Desktop: Settings → Connectors → Add custom connector → paste the URL.
What's included
12 read-only tools that mirror the existing REST API — no duplicated query logic. Each tool dispatches internally to the existing controllers via Rack, so when a controller or view changes, the MCP output updates automatically.
list_commitmentsGET /commitmentsget_commitmentGET /commitments/:idlist_promisesGET /promisesget_promiseGET /promises/:idlist_billsGET /billsget_billGET /bills/:idlist_departmentsGET /departmentsget_departmentGET /departments/:idlist_ministersGET /ministerslist_activityGET /feedget_commitment_summaryGET /api/dashboard/:id/at_a_glanceget_commitment_progressGET /api/burndown/:idImplementation
The entire MCP surface is a single controller (
mcp_controller.rb) with a declarative config array. Adding a new tool is ~8 lines — just a name, path, description, and schema. The controller dynamically generatesMCP::Toolsubclasses and dispatches tool calls to existing endpoints viaRack::MockRequest.Uses the official
mcpRuby SDK (v0.9.1) with stateless Streamable HTTP transport. No session management needed, works on multi-node deploys out of the box.Testing
29 integration tests covering all 12 tools, filters, not-found paths, and protocol handshake.
To test locally with Claude Desktop: