Skip to main content
Glama
northernvariables

FedMCP - Federal Parliamentary Information

AGENTS.md26.6 kB
# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## ⚠️ IMPORTANT: Deploying to Production **Before deploying to production**, read [DEPLOYMENT.md](DEPLOYMENT.md) for: - Pre-deployment checklist - Environment variable configuration - Rollback procedures - Post-deployment verification **Quick validation**: Run `./scripts/validate-deployment.sh` to check for common deployment issues. ## Project Overview CanadaGPT is an AI-powered Canadian government accountability platform built as a TypeScript/Python monorepo with four core packages: - **Frontend** (`packages/frontend`): Next.js 14 application with mobile-first UI, voice features, and i18n support - **Graph API** (`packages/graph-api`): Neo4j GraphQL API using @neo4j/graphql for parliamentary data queries - **FedMCP** (`packages/fedmcp`): Python MCP (Model Context Protocol) server providing Claude Desktop integration with Canadian parliamentary and legal data - **Data Pipeline** (`packages/data-pipeline`): Automated daily ingestion of Hansard debates, votes, committee evidence, and MP data into Neo4j **Tech Stack**: Next.js 14, TypeScript, Neo4j, GraphQL, Python 3.11, Supabase, Google Cloud Run ## Project Structure ``` CanadaGPT/ ├── packages/ │ ├── frontend/ # Next.js web application │ │ ├── src/ │ │ │ ├── app/ # Next.js 14 app router pages │ │ │ ├── components/ # React components (mobile/, voice/, debates/, committees/) │ │ │ ├── hooks/ # Custom React hooks (useMobileDetect, useSwipeGesture) │ │ │ └── lib/ # GraphQL queries, utilities │ │ └── public/ # Static assets, PWA manifest │ ├── graph-api/ # GraphQL API server │ │ └── src/schema.ts # Neo4j GraphQL schema definitions │ ├── fedmcp/ # Python MCP server │ │ └── src/fedmcp/ │ │ ├── clients/ # API clients (openparliament, ourcommons, canlii, lobbying) │ │ └── server.py # MCP protocol server │ ├── data-pipeline/ # Data ingestion │ │ ├── fedmcp_pipeline/ │ │ │ ├── ingest/ # Ingestion scripts (hansard.py, parliament.py, votes_xml_import.py) │ │ │ └── utils/ # Neo4j client utilities │ │ └── scripts/ # Entry point scripts │ └── design-system/ # Shared UI components (future) ├── scripts/ # Deployment & utility scripts │ ├── deploy-*.sh # Cloud Run deployment scripts │ ├── daily-*.py # Daily import scripts │ └── dev-*.sh # Local development helpers ├── supabase/ # Database migrations & config ├── Dockerfile.* # Container definitions for Cloud Run jobs └── cloudbuild-*.yaml # Google Cloud Build configurations ``` ## Quick Start **Prerequisites**: - Node.js 20+, pnpm, Python 3.11+ - Neo4j instance (local or remote) - Supabase project (for auth/storage) ```bash # Install dependencies pnpm install # Setup Python environment for data pipeline cd packages/data-pipeline python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install -e . cd ../.. # Setup environment variables cp packages/frontend/.env.example packages/frontend/.env # Edit .env with your Neo4j, Supabase credentials # Run development servers pnpm dev:frontend # → http://localhost:3000 pnpm dev:api # → http://localhost:4000 (GraphQL) # Run specific package pnpm --filter @canadagpt/frontend dev pnpm --filter @canadagpt/graph-api build # Build all packages pnpm build:all # Builds design-system, graph-api, frontend in order ``` ## Development Commands ```bash # Frontend pnpm dev:frontend # Start Next.js dev server pnpm build:frontend # Production build pnpm --filter @canadagpt/frontend type-check # TypeScript check # Graph API pnpm dev:api # Start GraphQL server pnpm build:api # Compile TypeScript # Data Pipeline (Python) cd packages/data-pipeline source venv/bin/activate python scripts/daily-hansard-import.py # Import latest Hansard python run_mp_ingestion.py # Import MP data python run_votes_ingestion.py # Import votes # Docker (local development) docker-compose up -d # Start Neo4j locally docker-compose logs -f neo4j # View Neo4j logs # GCP Deployment ./scripts/deploy-cloud-run.sh # Deploy Graph API ./scripts/deploy-hansard-importer.sh # Deploy Hansard ingestion job gcloud run jobs execute hansard-daily-import --region=us-central1 # Manual trigger ``` ## Core Package Details ### Frontend (Next.js) **Location**: `packages/frontend/` **Key Features**: - Server-side rendering with Next.js 14 App Router - Internationalization (en/fr) using next-intl - Mobile-first responsive design with PWA support - Voice search/chat using browser Web Speech API - Twitter/Instagram-style debate viewer with swipe gestures - GraphQL client for Neo4j data **Important Files**: - `src/app/[locale]/` - App router pages (debates, committees, MPs, bills) - `src/components/mobile/` - Mobile UI components (MobileStatementCard, MobileDebateViewer) - `src/components/voice/` - Voice features (VoiceSearch, VoiceChat, VoiceNotes) - `src/lib/queries.ts` - GraphQL queries - `src/contexts/UserPreferencesContext.tsx` - User settings (language, theme) **Mobile/Voice System**: See `MOBILE_IMPLEMENTATION_GUIDE.md` for detailed component usage. Key components include: - **Voice**: VoiceSearch, VoiceChat, VoiceNotes (Web Speech API) - **Mobile**: MobileStatementCard, MobileDebateViewer, MobileBottomNav - **Hooks**: useMobileDetect, useSwipeGesture - **Design**: Party-color coding (Liberal: #DC2626, Conservative: #2563EB, NDP: #F59E0B) ### Graph API (Neo4j GraphQL) **Location**: `packages/graph-api/` **Purpose**: GraphQL API layer over Neo4j database using @neo4j/graphql OGM (Object Graph Mapper) **Schema**: `src/schema.ts` - Defines GraphQL types mapped to Neo4j nodes/relationships **Key Types**: - `Parliament`, `Session` - Parliamentary hierarchy - `MP`, `Riding`, `Party` - Politician data - `Document`, `Statement` - Hansard debates - `Vote`, `Ballot` - Voting records - `Committee`, `Meeting`, `CommitteeEvidence`, `CommitteeTestimony` - Committee data - `Bill` - Legislation **Deployment**: - Production: Google Cloud Run (`canadagpt-graph-api`) - Build: `docker build --platform linux/amd64` (required for Cloud Run) - Connects to Neo4j VM at `bolt://10.128.0.3:7687` ### FedMCP (MCP Server) **Location**: `packages/fedmcp/` **Purpose**: Model Context Protocol server providing Claude Desktop with access to Canadian parliamentary and legal data **Installation**: ```bash cd packages/fedmcp pip install -e . ``` **Running**: ```bash python -m fedmcp.server # Or: fedmcp ``` **Claude Desktop Configuration**: ```json { "mcpServers": { "fedmcp": { "command": "python", "args": ["-m", "fedmcp.server"], "env": { "CANLII_API_KEY": "your_key_here" } } } } ``` **Architecture**: All clients in `src/fedmcp/clients/` use `RateLimitedSession` (http.py): - Proactive rate limiting: `min_request_interval` enforces delays between requests - Reactive retry: Automatic exponential backoff for 429/5xx errors - Shared session management **API Clients**: - **OpenParliamentClient** (openparliament.py): `list_debates()`, `get_debate()`, `list_bills()`, `get_bill()`, `list_mps()`, `list_votes()`, `list_committees()` - Pagination-aware iterators, 10 req/s rate limit - **OurCommonsHansardClient** (ourcommons.py): `get_sitting(slug_or_url, parse=True)` - Two-step fetch: HTML → extract XML link → parse XML - **OurCommonsVotesClient** (ourcommons_votes.py): `get_vote_summaries()`, `get_vote(parliament, session, vote_number)` - **OurCommonsCommitteeEvidenceClient** (ourcommons_committee_evidence.py): `get_evidence(committee_code, meeting_number)` - **LegisInfoClient** (legisinfo.py): `get_bill(parliament_session, bill_code)`, `list_bills(chamber=None)` - **CanLIIClient** (canlii.py): `search_cases_by_keyword()`, `get_case()`, `search_legislation()` - Requires `CANLII_API_KEY`, 2 req/s rate limit - **MPExpenditureClient** (expenditure.py): `search_by_name(name, fiscal_year, quarter)` - **PetitionsClient** (petitions.py): `search_petitions(keyword)`, `get_petition(petition_number)` - **LobbyingRegistryClient** (lobbying.py): `search_registrations()`, `search_communications()` - Downloads ~90MB data on first use, caches to `~/.cache/fedmcp/lobbying/` **MCP Tools** (40+ tools exposed via server.py): - Parliamentary: `search_debates`, `search_bills`, `list_mps`, `list_votes`, `get_committee_details` - Accountability: `get_mp_expenses`, `search_mp_expenses`, `get_mp_activity_scorecard`, `analyze_mp_bills` - Petitions: `search_petitions`, `get_petition_details`, `get_mp_petitions` - Lobbying: `search_lobbying_registrations`, `analyze_bill_lobbying`, `detect_conflicts_of_interest` - Legal (CanLII): `search_cases`, `get_case`, `search_legislation` **Testing Clients**: ```python from fedmcp import OpenParliamentClient, OurCommonsHansardClient from fedmcp.clients.expenditure import MPExpenditureClient # OpenParliament op = OpenParliamentClient() for debate in op.list_debates(limit=10): print(debate) # Hansard commons = OurCommonsHansardClient() sitting = commons.get_sitting("latest/hansard", parse=True) print(f"Date: {sitting.date}, Sections: {len(sitting.sections)}") # MP Expenses expenses = MPExpenditureClient() mp_expenses = expenses.search_by_name("Poilievre", fiscal_year=2026, quarter=1) ``` **Key Implementation Notes**: - Bill codes in LEGISinfo should be lowercase (e.g., "c-249" not "C-249") - DocumentViewer slugs can be relative (e.g., "latest/hansard") or full URLs - OpenParliament list methods return iterators - use `list()` to materialize all results - Hansard parsing preserves paragraph structure with double-newline separation - CanLII tools only exposed if `CANLII_API_KEY` is set **Common CanLII Database IDs**: - `csc-scc` - Supreme Court of Canada - `fca-caf` - Federal Court of Appeal - `onca`, `bcca`, `abca`, `qcca` - Provincial Courts of Appeal - `ca` - Federal acts, `car` - Federal regulations - `on`, `bc` - Provincial statutes ### Data Pipeline (Python) **Location**: `packages/data-pipeline/` **Purpose**: Automated daily imports of parliamentary data into Neo4j **Components**: 1. **Python Client** - Fetches data from API/XML (`packages/fedmcp/src/fedmcp/clients/`) 2. **Ingestion Script** - Processing logic (`fedmcp_pipeline/ingest/`) 3. **Cloud Run Job** - Containerized job on GCP 4. **Cloud Scheduler** - Daily cron trigger **Person ID Linking**: All data uses House of Commons `person_db_id`/`parl_mp_id` to link to MP nodes **Ingestion Pipelines**: | Pipeline | Schedule | Purpose | Files | |----------|----------|---------|-------| | **MP Ingestion** | Daily 6:00 AM UTC | Import MP biographical data, ridings, parties, and committee memberships | `run_mp_ingestion.py`, `fedmcp_pipeline/ingest/parliament.py` | | **Hansard** | Daily 4:00 AM ET | Import House debates (7-day lookback) | `scripts/daily-hansard-import.py`, `Dockerfile.hansard-importer` | | **Committee Meetings** | Daily 6:00 AM ET | Discover/import scheduled meetings (7-day lookback) | `scripts/daily-committee-import.py`, `Dockerfile.committee-importer` | | **Votes** | Daily 7:00 AM UTC | Import vote records & ballots | `run_votes_ingestion.py`, `fedmcp_pipeline/ingest/votes_xml_import.py` | | **Committee Evidence** | Daily 8:00 AM UTC | Import witness testimony | `run_committee_evidence_ingestion.py`, `fedmcp_pipeline/ingest/committee_evidence_xml_import.py` | | **Lobbying Registry** | Weekly Sundays 2:00 AM UTC | Full refresh of lobbying data (registrations, communications, organizations, lobbyists) | `run_lobbying_ingestion.py`, `fedmcp_pipeline/ingest/lobbying.py`, `Dockerfile.lobbying-ingestion` | | **MP Expenses** | Daily 5:00 AM UTC | Import MP office & House Officer expenses (quarterly data) | `run_expenses_ingestion.py`, `fedmcp_pipeline/ingest/finances.py`, `Dockerfile.expenses-ingestion` | **Hansard Ingestion Details**: - **Data Source**: Direct XML URLs - `https://www.ourcommons.ca/Content/House/451/Debates/{sitting}/HAN{sitting}-E.XML` - ⚠️ Important: DocumentViewer HTML pages return 404 programmatically - always use direct XML - **XML Structure**: `HansardBody → OrderOfBusiness (h1) → SubjectOfBusiness (h2) → Intervention` - **MP Name Matching**: Sophisticated fuzzy matching with 85-90% success rate - Accent removal, hyphen handling, honorific removal (Hon., Rt. Hon.) - Nickname mapping (Bobby ↔ Robert, Bill ↔ William) - **Publication Pattern**: Hansard published 1-2 days after sitting, typically 8-10 PM ET - **Deployment**: `./scripts/deploy-hansard-importer.sh` (2Gi memory, 30-min timeout) **Committee Meeting Discovery**: - **Data Source**: `https://www.ourcommons.ca/committees/en/FilteredMeetings?meetingDate=YYYY-MM-DD` - **HTML Parsing**: BeautifulSoup extracts meeting ID, committee code, date, time, subject, status, webcast availability - **Idempotent**: Skips meetings already in database (by `ourcommons_meeting_id`) - **Deployment**: `./scripts/deploy-committee-importer.sh` **Committee Membership Ingestion**: - **Data Source**: OurCommons committee pages (HTML scraping via CommitteeMembershipClient) - Scrapes: `https://www.ourcommons.ca/Committees/en/{COMMITTEE_CODE}` - Captures member names and roles (Chair, Vice-Chair, Member) - **Relationship Created**: `(MP)-[:SERVES_ON {role}]->(Committee)` - **Name Matching**: Fuzzy matching with ~80% success rate - Accent removal, honorific removal (Hon., Rt. Hon.) - Multiple name format attempts (first-last, last-first, etc.) - **Expected Volume**: ~213-250 committee memberships for current parliament - **Note**: Cabinet ministers typically do not serve on standing committees - **Deployment**: Part of `./scripts/deploy-mp-ingestion.sh` (2Gi memory, 30-min timeout) - **Integration**: Runs after MP biographical ingestion in same job **Lobbying Registry Ingestion**: - **Data Source**: `https://lobbycanada.gc.ca` (Office of the Commissioner of Lobbying) - Registrations: `https://lobbycanada.gc.ca/media/zwcjycef/registrations_enregistrements_ocl_cal.zip` (~77MB) - Communications: `https://lobbycanada.gc.ca/media/mqbbmaqk/communications_ocl_cal.zip` (~19MB) - ⚠️ Important: Use `source="official"` (lobbycanada.gc.ca), NOT `source="opendata"` (broken URLs) - **Import Strategy**: Full refresh weekly - clears all existing lobbying data before import - **Data Cleanup**: Batched deletions (10,000 nodes at a time) to avoid Neo4j transaction memory limits - **Resource Requirements**: - Memory: 4Gi (handles 163K registrations + 343K communications) - CPU: 2 cores - Runtime: ~5 minutes - **Typical Import Volumes**: - 163,459 lobby registrations - 343,862 lobby communications - 19,639 organization nodes - 13,847 lobbyist nodes - **Deployment**: `./scripts/deploy-lobbying-ingestion.sh` (Cloud Run job + weekly Cloud Scheduler) - **Key Implementation Notes**: - Cleanup runs in batches: `MATCH (n:LobbyRegistration) WITH n LIMIT 10000 DETACH DELETE n` - Downloads cached locally in container during first run - All data indexed by unique `id` property **MP Expenses Ingestion**: - **Data Source**: OurCommons Proactive Disclosure (CSV format, quarterly updates) - MP Expenses: `https://www.ourcommons.ca/proactivedisclosure/en/members/{fiscal_year}/{quarter}` - House Officer Expenses: `https://www.ourcommons.ca/proactivedisclosure/en/house-officers/{fiscal_year}/{quarter}` - **Dual Source Import**: MP office expenses + House Officer expenses (Speaker, Leaders, Whips, etc.) - **MP Name Matching**: Fuzzy matching with 99.9% success rate (nickname mapping, accent removal, compound surnames) - **Idempotent Design**: Safe to run multiple times (uses MERGE, deterministic IDs) - **Resource Requirements**: - Memory: 2Gi, CPU: 1, Timeout: 15 minutes - Runtime: ~1-2 minutes for daily check - **Typical Import Volumes**: - ~1,500 MP expense records per quarter (338 MPs × 4 categories) - ~60 House Officer expense records per quarter (~15 officers × 4 categories) - **Expense Categories**: salaries, travel, hospitality, contracts - **Deployment**: `./scripts/deploy-expenses-ingestion.sh` (Cloud Run job + daily Cloud Scheduler) - **Historical Backfill**: Run locally with `--fiscal-year-start 2020 --fiscal-year-end 2023` - **Documentation**: See `EXPENSES_INGESTION.md` for full details **Common Operations**: ```bash # Deploy pipeline gcloud builds submit --config packages/data-pipeline/cloudbuild-{JOB_NAME}.yaml # Manual trigger gcloud run jobs execute {JOB_NAME} --region=us-central1 # View logs gcloud logging read "resource.type=cloud_run_job AND resource.labels.job_name={JOB_NAME}" --limit=50 # Test locally export NEO4J_URI=bolt://localhost:7687 export NEO4J_USERNAME=neo4j export NEO4J_PASSWORD=your_password python scripts/daily-hansard-import.py ``` **Troubleshooting**: | Issue | Causes | Solutions | |-------|--------|-----------| | 404 errors fetching XML | DocumentViewer URL used, wrong sitting number, cancelled debate | Use direct XML pattern, check Commons calendar, try ±1-2 sitting numbers | | Low MP linking rate (<80%) | New MPs not in DB, non-MP speakers, name variations | Update MP data, check `who_en` field, add nickname mappings | | Duplicate debates | Multiple imports of same date | Process is idempotent (DETACH DELETE), check: `MATCH (d:Document {date: '2025-11-03'}) RETURN count(*)` | ## Neo4j Database Schema **Connection**: - Production: `bolt://10.128.0.3:7687` (internal VM on GCP) - Local dev: `bolt://localhost:7687` **Core Node Types**: ```cypher # Parliament Hierarchy (Parliament {number, ordinal, election_date, is_current}) -[:HAS_SESSION]-> (Session {id, parliament_number, session_number, start_date, is_current}) # MPs and Affiliations (MP {id, name, parl_mp_id, current_party, current_riding}) -[:REPRESENTS {start_date, end_date}]-> (Riding {id, name, province}) (MP)-[:MEMBER_OF {start_date, end_date}]->(Party {id, name}) # Hansard Debates (Document {id, date, session_id, number}) <-[:PART_OF]- (Statement {id, document_id, time, who_en, content_en, h1_en, h2_en, wordcount}) -[:MADE_BY]-> (MP) # Votes (Vote {vote_number, date_time, result, num_yeas, num_nays, subject, bill_number}) <-[:CAST_IN]- (Ballot {id, vote_number, person_id, vote_value, is_yea, is_nay}) -[:CAST_BY]-> (MP) (Vote)-[:CONCERNS]->(Bill) # Committee System (Committee {code, name}) -[:HELD_MEETING]-> (Meeting {ourcommons_meeting_id, committee_code, date, subject, status}) -[:HAS_EVIDENCE]-> (CommitteeEvidence {id, committee_code, meeting_number, date}) <-[:GIVEN_IN]- (CommitteeTestimony {id, speaker_name, text, is_witness, organization, role}) -[:TESTIFIED_BY]-> (MP) # SPOKE_AT Relationships (Direct MP → Document Shortcuts) # Enable efficient queries like "which MPs spoke in this debate?" (MP)-[:SPOKE_AT {timestamp, statement_id, intervention_id, person_db_id}]->(Document) (MP)-[:SPOKE_AT {testimony_id, intervention_id, person_db_id, timestamp_hour, timestamp_minute}]->(CommitteeEvidence) # Note: SPOKE_AT creates multiple relationships per MP-Document pair # (one per Statement/Testimony) for detailed tracking ``` **GraphQL Schema**: `packages/graph-api/src/schema.ts` maps these nodes to GraphQL types with `@neo4j/graphql` directives **Manual Queries**: ```python #!/usr/bin/env python3 from pathlib import Path import sys sys.path.insert(0, str(Path.cwd() / 'packages' / 'data-pipeline')) from fedmcp_pipeline.utils.neo4j_client import Neo4jClient neo4j = Neo4jClient(uri='bolt://10.128.0.3:7687', user='neo4j', password='...') # Check November debates result = neo4j.run_query(""" MATCH (d:Document) WHERE d.date STARTS WITH '2025-11' RETURN d.id, d.date, d.number ORDER BY d.date """) for row in result: print(f"{row['d.date']}: Document {row['d.id']} - {row['d.number']}") ``` ## Infrastructure **Google Cloud Platform**: - Project: `canada-gpt-ca` - Region: `us-central1` - VPC Connector: `canadagpt-vpc-connector` - Artifact Registry: `us-central1-docker.pkg.dev/canada-gpt-ca/canadagpt/` **Production Services**: - Neo4j VM: `bolt://10.128.0.3:7687` (internal, via VPC connector) - Graph API: Cloud Run service `canadagpt-graph-api` - Ingestion Jobs: Cloud Run jobs (hansard-daily-import, mp-ingestion, votes-ingestion, committee-daily-import, committee-evidence-ingestion, lobbying-ingestion) - Supabase: Database, Auth, Storage **Deployment Notes**: - Always compile Graph API with `--platform linux/amd64` for Cloud Run - Cloud Run jobs require VPC connector for Neo4j access - Environment variables set via `--set-env-vars` in gcloud commands - Screenshots located at: `~/Desktop/Screenshot*.png` ## Common Development Tasks ### Running Locally ```bash # Start Neo4j (Docker) docker-compose up -d neo4j # Start frontend + API pnpm dev:frontend & pnpm dev:api & # Or use dev helper script ./scripts/dev-start.sh ``` ### Adding a New MCP Tool 1. Define tool in `packages/fedmcp/src/fedmcp/server.py` → `list_tools()` function 2. Add input schema (JSON Schema) 3. Implement handler in `call_tool()` function 4. Return results as `TextContent` objects 5. Handle errors with try/except blocks 6. Test with Claude Desktop ### Adding a New Data Source 1. Create client in `packages/fedmcp/src/fedmcp/clients/new_source.py` - Extend `RateLimitedSession` from `http.py` - Define rate limiting: `min_request_interval` 2. Create ingestion script in `packages/data-pipeline/fedmcp_pipeline/ingest/new_source.py` 3. Define Neo4j schema in `packages/graph-api/src/schema.ts` 4. Create Dockerfile: `Dockerfile.new-source-importer` 5. Create Cloud Build config: `cloudbuild-new-source.yaml` 6. Create deployment script: `scripts/deploy-new-source-importer.sh` 7. Set up Cloud Scheduler cron job ### Deploying to Production ```bash # Frontend (Cloud Run) cd packages/frontend ./scripts/deploy-frontend-cloudrun.sh # Graph API (Cloud Run) ./scripts/deploy-cloud-run.sh # Ingestion Jobs ./scripts/deploy-hansard-importer.sh ./scripts/deploy-committee-importer.sh ./scripts/deploy-votes-importer.sh # Manual trigger ingestion gcloud run jobs execute hansard-daily-import --region=us-central1 ``` ### Debugging Ingestion ```bash # View Cloud Run job logs gcloud logging read "resource.type=cloud_run_job AND resource.labels.job_name=hansard-daily-import" --limit=50 --format=json # Test locally with production DB (via tunnel) ./scripts/dev-tunnel.sh # Opens SSH tunnel to Neo4j VM export NEO4J_URI=bolt://localhost:7687 export NEO4J_USERNAME=neo4j export NEO4J_PASSWORD=... python scripts/daily-hansard-import.py # Check database status ./scripts/check-dev-status.sh ``` ### Backfilling SPOKE_AT Relationships If SPOKE_AT relationships are missing or need to be recreated: ```bash # Connect to production DB via tunnel ./scripts/dev-tunnel.sh & export NEO4J_URI=bolt://localhost:7687 export NEO4J_USERNAME=neo4j export NEO4J_PASSWORD=canadagpt2024 # Run backfill script cd packages/data-pipeline python scripts/backfill_spoke_at.py # The script will: # 1. Create (MP)-[:SPOKE_AT]->(Document) from Hansard statements # 2. Create (MP)-[:SPOKE_AT]->(CommitteeEvidence) from committee testimonies # 3. Report statistics and verify results # Expected output: # - Hansard: ~1.2M relationships # - Committee: varies based on testimony data # - Script is idempotent (safe to re-run) ``` ### Common Issues **Issue**: Type errors in frontend after schema changes ```bash pnpm --filter @canadagpt/frontend codegen # Regenerate GraphQL types pnpm --filter @canadagpt/frontend type-check ``` **Issue**: Graph API not reflecting Neo4j changes - Restart the server (schema is cached) - Verify Neo4j connection with Cypher query - Check `packages/graph-api/src/schema.ts` for type definitions **Issue**: Hansard import finding no new debates - Check OurCommons calendar: `https://www.ourcommons.ca/` - Verify sitting numbers: debates use sequential numbers (050, 051, 052...) - Check logs for 404 errors (might be wrong sitting number) ## Mobile/Voice System Reference **Relevant when**: Building mobile UI, implementing voice features, creating PWA **Location**: `packages/frontend/src/components/{mobile,voice}/` and `src/hooks/` **Complete Documentation**: See `MOBILE_IMPLEMENTATION_GUIDE.md` (850 lines) **Quick Reference**: **Components**: - `VoiceSearch`, `VoiceChat`, `VoiceNotes` - Browser Web Speech API integration - `MobileStatementCard`, `MobileDebateViewer` - Twitter/Instagram-style UI - `MobileBottomNav`, `MobileHeader`, `SwipeableDrawer` - Navigation - `MobileMPCard`, `MobileDebateCard`, `MobileBillCard` - Card components **Hooks**: - `useMobileDetect()` - Device type, OS, screen size, orientation - `useSwipeGesture()` - Swipe detection for navigation **Design System**: - Party colors: Liberal #DC2626, Conservative #2563EB, NDP #F59E0B, Bloc #3B82F6, Green #10B981 - Touch targets: 44-48px minimum (iOS HIG) - iOS safe areas: `env(safe-area-inset-top)`, `env(safe-area-inset-bottom)` - Voice API: Requires HTTPS in production **PWA**: `packages/frontend/public/manifest.json` **Browser Support**: - Voice: iOS Safari 14.5+, Chrome Android 33+, Edge Android 79+ (HTTPS required) - UI: All modern mobile browsers ## Frontend Bug Fixes **Timezone Issue** (Fixed Nov 2025): Changed date parsing from UTC to local timezone in `packages/frontend/src/app/[locale]/debates/page.tsx` to prevent date display issues across timezones (e.g., Nov 7 showing as Nov 6 in PST/PDT). ## FedMCP Bug Fixes These fixes were ported from the original canfedinfo library: **OpenParliamentClient** (clients/openparliament.py:52-54): - Fixed pagination to handle relative URLs from API responses - Fetcher prepends `base_url` to relative URLs starting with '/' **OurCommonsHansardClient** (clients/ourcommons.py:89-96, 101-149): - Fixed UTF-8 BOM handling by decoding with 'utf-8-sig' encoding - Updated XML parsing to match actual structure using `<ExtractedInformation>`, `<Intervention>`, `<ParaText>` elements

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/northernvariables/FedMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server