From 0a553f76f1e9f1ac67a3b5162b56404d700b2c92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:59:02 +0000 Subject: [PATCH 1/6] Initial plan From 5781e05d76d0eb19c90d86a2b3f14c2f00092bd6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:08:39 +0000 Subject: [PATCH 2/6] Enhance evening news workflow: Add explicit data freshness checks, date filtering, and cross-referencing examples Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/aw/actions-lock.json | 5 + .../workflows/news-article-generator.lock.yml | 14 +- .../workflows/news-evening-analysis.lock.yml | 14 +- .github/workflows/news-evening-analysis.md | 189 +++++++++++++++--- 4 files changed, 175 insertions(+), 47 deletions(-) diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index 08870a86..7d2b045a 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -34,6 +34,11 @@ "repo": "github/gh-aw/actions/setup", "version": "v0.45.4", "sha": "ac090214a48a1938f7abafe132460b66752261af" + }, + "github/gh-aw/actions/setup@v0.45.5": { + "repo": "github/gh-aw/actions/setup", + "version": "v0.45.5", + "sha": "852cb06ad52958b402ed982b69957ffc57ca0619" } } } diff --git a/.github/workflows/news-article-generator.lock.yml b/.github/workflows/news-article-generator.lock.yml index d788a8d1..fd8e0108 100644 --- a/.github/workflows/news-article-generator.lock.yml +++ b/.github/workflows/news-article-generator.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.45.4). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.45.5). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -62,7 +62,7 @@ jobs: comment_repo: "" steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Checkout .github and .agents folders @@ -269,7 +269,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -324,7 +324,7 @@ jobs: model: "claude-opus-4.6", version: "", agent_version: "0.0.410", - cli_version: "v0.45.4", + cli_version: "v0.45.5", workflow_name: "News Article Generator", experimental: false, supports_tools_allowlist: true, @@ -898,7 +898,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1001,7 +1001,7 @@ jobs: success: ${{ steps.parse_results.outputs.success }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent artifacts @@ -1114,7 +1114,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-evening-analysis.lock.yml b/.github/workflows/news-evening-analysis.lock.yml index 2ee07003..29b2b625 100644 --- a/.github/workflows/news-evening-analysis.lock.yml +++ b/.github/workflows/news-evening-analysis.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.45.4). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.45.5). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -62,7 +62,7 @@ jobs: comment_repo: "" steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Checkout .github and .agents folders @@ -261,7 +261,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -316,7 +316,7 @@ jobs: model: "claude-opus-4.6", version: "", agent_version: "0.0.410", - cli_version: "v0.45.4", + cli_version: "v0.45.5", workflow_name: "News Evening Analysis", experimental: false, supports_tools_allowlist: true, @@ -890,7 +890,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -993,7 +993,7 @@ jobs: success: ${{ steps.parse_results.outputs.success }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent artifacts @@ -1106,7 +1106,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-evening-analysis.md b/.github/workflows/news-evening-analysis.md index e42a782f..111cb195 100644 --- a/.github/workflows/news-evening-analysis.md +++ b/.github/workflows/news-evening-analysis.md @@ -205,17 +205,16 @@ Generate article versions for each requested language with culturally appropriat **IMPORTANT:** Call the tools using their simple names directly: ```javascript -// Calendar events -get_calendar_events({ from: "2026-02-16", tom: "2026-02-16", limit: 50 }) - -// Recent votes -search_voteringar({ rm: "2025/26", limit: 50 }) +// STEP 1: ALWAYS check data freshness first +get_sync_status({}) // Returns last_updated timestamp -// Committee reports -get_betankanden({ rm: "2025/26", limit: 20 }) +// STEP 2: Query with explicit date ranges where supported +get_calendar_events({ from: "2026-02-16", tom: "2026-02-16", limit: 50 }) +search_regering({ from_date: "2026-02-16", to_date: "2026-02-17", limit: 30 }) -// Government documents -search_regering({ from_date: "2026-02-16", limit: 30 }) +// STEP 3: For tools without date filters, use rm + limit and filter results by date +get_betankanden({ rm: "2025/26", limit: 20 }) // Then filter by publicerad date +search_voteringar({ rm: "2025/26", limit: 50 }) // Then filter by datum ``` **Tool Naming:** Use simple names like `get_calendar_events()`, `search_voteringar()` - the framework handles routing automatically. @@ -233,6 +232,9 @@ search_regering({ from_date: "2026-02-16", limit: 30 }) - ✅ Use simple tool names: `get_calendar_events({ params })`, `search_voteringar({ params })` - ✅ Let the framework handle all routing, authentication and session management - ✅ Trust the automatic retry logic for cold starts +- ✅ **CRITICAL**: Check `get_sync_status()` first to verify data freshness +- ✅ **CRITICAL**: Use explicit date parameters where available (from_date, to_date, from, tom) +- ✅ **CRITICAL**: Filter results by date in your analysis when tools don't support date params **✅ For running Node.js scripts via bash:** - ✅ Set `export MCP_SERVER_URL="http://host.docker.internal:80/mcp/riksdag-regering"` BEFORE running script @@ -244,56 +246,138 @@ search_regering({ from_date: "2026-02-16", limit: 30 }) The MCP server may take 30-60 seconds on first request (cold start). **The framework handles this automatically with retries.** Just make your call normally and wait. -**Best Practice:** Start with a simple query to warm up the server, then batch multiple queries. +**Best Practice:** +1. Call `get_sync_status()` first to warm up the server AND check data freshness +2. Batch multiple queries after warmup ### 🐛 If You Get Errors | Error | Cause | Fix | |-------|-------|-----| | Tool not found | Wrong tool name | Use exact names: `get_calendar_events`, `search_voteringar` | -| Empty results | No data in timeframe | Check `get_sync_status`, widen date range | +| Empty results | No data in timeframe | Check `get_sync_status`, widen date range, verify rm parameter | +| Stale data | Last sync >48h ago | Note in analysis, use available data with disclaimer | | Timeout | Cold start (30-60s) | Wait - framework retries automatically | | Swedish-only results | Riksdag API returns Swedish | YOU must translate to target languages | +| Too broad results | No date filtering | Add from_date/to_date params OR filter results by date in code | ### 📋 32 Available MCP Tools **Riksdag (Parliament) Tools (15):** - `get_ledamoter` / `search_ledamoter` - MPs and member search -- `get_motioner` / `search_motioner` - Parliamentary motions -- `get_propositioner` / `search_propositioner` - Government proposals +- `get_motioner` / `search_motioner` - Parliamentary motions (filter by inlämnad date) +- `get_propositioner` / `search_propositioner` - Government proposals (filter by publicerad date) - `get_dokument` / `search_dokument` / `search_dokument_fulltext` - Documents -- `get_voteringar` / `search_voteringar` - Voting records -- `get_anforanden` / `search_anforanden` - Speeches and debates -- `get_fragor` / `get_interpellationer` - Questions and interpellations -- `get_calendar_events` - Parliamentary schedule -- `get_betankanden` - Committee reports +- `get_voteringar` / `search_voteringar` - Voting records (filter by datum) +- `get_anforanden` / `search_anforanden` - Speeches and debates (filter by datum) +- `get_fragor` / `get_interpellationer` - Questions and interpellations (filter by inlämnad date) +- `get_calendar_events` - Parliamentary schedule (**supports from/tom date params**) +- `get_betankanden` - Committee reports (filter by publicerad date) **Government (Regering) Tools (7):** -- `search_regering` - Government document search +- `search_regering` - Government document search (**supports from_date/to_date params**) - `get_regering_document` - Retrieve specific government doc - `get_g0v_document_content` - Get document in Markdown format - `summarize_regering_document` - AI summarization -- `analyze_g0v_by_department` - Department analysis +- `analyze_g0v_by_department` - Department analysis (**supports dateFrom/dateTo params**) - `get_g0v_document_types` - List document categories **Metadata & Statistics (5):** - `get_utskott` - Committee information - `get_voting_group` - Voting analysis by party/constituency - `fetch_report` - Statistical reports -- `get_sync_status` - Data freshness check +- `get_sync_status` - **Data freshness check (CALL THIS FIRST)** - `get_data_dictionary` - Schema definitions **Utility (5):** - `batch_fetch_documents` - Efficient bulk retrieval - `fetch_paginated_documents` - Pagination support - `list_reports` - Available report types -- `get_latest_update` - Last data sync timestamp +- `get_latest_update` - Last data sync timestamp (alias for get_sync_status) - `enhanced_government_search` - Combined Riksdag + Government search +### 🔗 Cross-Referencing Strategy (Use Multiple Tools Together) + +For richer analysis, combine data from multiple tools: + +**Example 1: Committee Report Deep Dive** +```javascript +// 1. Get recent committee reports +const betankanden = get_betankanden({ rm: "2025/26", limit: 20 }); +const recentBet = betankanden.filter(b => new Date(b.publicerad) >= new Date(fromDate)); + +// 2. For each report, get full details +const reportDetails = recentBet.map(bet => + get_dokument({ dok_id: bet.dok_id, include_full_text: false }) +); + +// 3. Check related votes +const relatedVotes = search_voteringar({ rm: "2025/26", limit: 50 }) + .filter(v => recentBet.some(bet => v.bet === bet.beteckning)); + +// 4. Find committee members' speeches +const committeeSpeeches = search_anforanden({ rm: "2025/26", limit: 100 }) + .filter(a => recentBet.some(bet => a.dokument_hangar_samman === bet.dok_id)); +``` + +**Example 2: Government Activity Analysis** +```javascript +// 1. Get government documents in date range +const govDocs = search_regering({ from_date: fromDate, to_date: today, limit: 30 }); + +// 2. Get related propositions +const propositions = get_propositioner({ rm: "2025/26", limit: 20 }) + .filter(p => new Date(p.publicerad) >= new Date(fromDate)); + +// 3. Check ministerial questions on same topics +const questions = get_fragor({ rm: "2025/26", limit: 50 }) + .filter(q => new Date(q.inlämnad) >= new Date(fromDate)); + +// 4. Department analysis +const deptAnalysis = analyze_g0v_by_department({ + dateFrom: fromDate, + dateTo: today +}); +``` + +**Example 3: Party Behavior Analysis** +```javascript +// 1. Get voting patterns +const voteGroups = get_voting_group({ rm: "2025/26", groupBy: "parti" }); + +// 2. Get recent votes +const recentVotes = search_voteringar({ rm: "2025/26", limit: 100 }) + .filter(v => new Date(v.datum) >= new Date(fromDate)); + +// 3. Get party motions +const partyMotions = get_motioner({ rm: "2025/26", limit: 50 }) + .filter(m => new Date(m.inlämnad) >= new Date(fromDate)); + +// 4. Get speeches by party members +const partySpeeches = search_anforanden({ rm: "2025/26", limit: 100 }) + .filter(a => new Date(a.datum) >= new Date(fromDate)); +``` + ## Analysis Workflow ### Step 1: Gather Data +**🚨 CRITICAL: Always check data freshness first** + +```javascript +// === DATA FRESHNESS CHECK === +// ALWAYS start by checking when MCP server last synced data +const syncStatus = get_sync_status({}); +console.log("MCP Data Sync Status:", syncStatus); + +// If data is stale (>48 hours), note this in the analysis +const lastSync = new Date(syncStatus.last_updated); +const hoursSinceSync = (Date.now() - lastSync.getTime()) / 3600000; +if (hoursSinceSync > 48) { + console.warn(`⚠️ Data may be stale: ${hoursSinceSync.toFixed(1)} hours since last sync`); +} +``` + Determine the lookback period based on day of week: ```javascript @@ -304,37 +388,55 @@ const dayOfWeek = new Date().getUTCDay(); // 0=Sunday, 6=Saturday const lookbackHours = dayOfWeek === 6 ? 120 : (github.event.inputs.lookback_hours || 12); const fromDate = dayOfWeek === 6 ? new Date(Date.now() - 5 * 86400000).toISOString().split('T')[0] // Monday - : today; + : new Date(Date.now() - lookbackHours * 3600000).toISOString().split('T')[0]; // === PARLIAMENTARY ACTIVITY === -// Calendar events (today for daily, Mon-Fri for weekly) +// Calendar events (explicit date range - DO NOT rely on implicit "latest") get_calendar_events({ from: fromDate, tom: today, limit: 100 }) -// Votes (weekly wrap-up gets higher limit for full week) -search_voteringar({ rm: "2025/26", limit: dayOfWeek === 6 ? 100 : 50 }) +// Votes (IMPORTANT: Use from_date parameter when searching, not just rm) +// The riksdag-regering-mcp server returns votes sorted by date DESC +// but we should be explicit about our date range +search_voteringar({ + rm: "2025/26", + limit: dayOfWeek === 6 ? 100 : 50 + // Note: search_voteringar doesn't support from_date, so we rely on rm + limit + // and then filter results by date in our analysis +}) + +// Party voting patterns (contextual data - no date filter available) get_voting_group({ rm: "2025/26", groupBy: "parti" }) -// Committee reports published +// Committee reports published (specify riksmöte explicitly) get_betankanden({ rm: "2025/26", limit: dayOfWeek === 6 ? 50 : 20 }) +// Note: Filter results by publicerad date >= fromDate in analysis -// Speeches and debates +// Speeches and debates (specify riksmöte explicitly) search_anforanden({ rm: "2025/26", limit: dayOfWeek === 6 ? 100 : 50 }) +// Note: Filter results by datum >= fromDate in analysis // === GOVERNMENT ACTIVITY === -// Government documents published (press releases, SOU, crisis, etc.) -search_regering({ from_date: fromDate, limit: dayOfWeek === 6 ? 50 : 30 }) +// Government documents published (CRITICAL: Always use from_date parameter) +search_regering({ + from_date: fromDate, + to_date: today, + limit: dayOfWeek === 6 ? 50 : 30 +}) -// New propositions +// New propositions (specify riksmöte) get_propositioner({ rm: "2025/26", limit: dayOfWeek === 6 ? 20 : 10 }) +// Note: Filter results by publicerad date >= fromDate in analysis -// Opposition motions +// Opposition motions (specify riksmöte) get_motioner({ rm: "2025/26", limit: dayOfWeek === 6 ? 50 : 20 }) +// Note: Filter results by inlämnad date >= fromDate in analysis -// Ministerial questions and interpellations +// Ministerial questions and interpellations (specify riksmöte) get_fragor({ rm: "2025/26", limit: dayOfWeek === 6 ? 50 : 20 }) get_interpellationer({ rm: "2025/26", limit: dayOfWeek === 6 ? 20 : 10 }) +// Note: Filter results by inlämnad date >= fromDate in analysis // === NEXT WEEK PREVIEW (Saturday) / TOMORROW (weekday) === const nextMonday = dayOfWeek === 6 @@ -343,9 +445,30 @@ const nextMonday = dayOfWeek === 6 const previewEnd = dayOfWeek === 6 ? new Date(Date.now() + 7 * 86400000).toISOString().split('T')[0] // Full next week : nextMonday; + +// Future calendar events (explicit date range) get_calendar_events({ from: nextMonday, tom: previewEnd, limit: 50 }) ``` +**⚠️ IMPORTANT: Date Filtering in Analysis** + +Many riksdag-regering-mcp tools (search_voteringar, get_betankanden, get_propositioner, etc.) don't support `from_date` parameters. They return results sorted by date DESC with a limit. + +**YOU MUST filter results by date in your analysis code:** +```javascript +// Example: Filter betänkanden by publication date +const recentBetankanden = betankanden.filter(bet => + new Date(bet.publicerad) >= new Date(fromDate) +); + +// Example: Filter motions by submission date +const recentMotions = motions.filter(mot => + new Date(mot.inlämnad) >= new Date(fromDate) +); +``` + +This ensures you're analyzing the **specific time period** requested, not just the "latest" documents from the MCP server. + ### Step 2: Synthesize and Analyze Structure the analysis around these editorial pillars: From d8c7730f20476a1263627039d035194b830fb364 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:11:34 +0000 Subject: [PATCH 3/6] Add comprehensive test suite for agentic workflow MCP query patterns Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- tests/agentic-workflow-mcp-queries.test.js | 410 +++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 tests/agentic-workflow-mcp-queries.test.js diff --git a/tests/agentic-workflow-mcp-queries.test.js b/tests/agentic-workflow-mcp-queries.test.js new file mode 100644 index 00000000..6493820c --- /dev/null +++ b/tests/agentic-workflow-mcp-queries.test.js @@ -0,0 +1,410 @@ +/** + * Test Suite for Agentic Workflow MCP Query Patterns + * + * Validates that agentic workflows follow best practices for MCP data querying: + * - Data freshness validation with get_sync_status() + * - Explicit date filtering where supported + * - Post-query date filtering documentation + * - Cross-referencing patterns + * - Error handling for stale data + * + * Prevents regressions where workflows rely on implicit "latest" data + * instead of explicit date-based queries. + * + * @author Hack23 AB + * @license Apache-2.0 + */ + +import { describe, it, expect } from 'vitest'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const WORKFLOWS_DIR = path.join(__dirname, '..', '.github', 'workflows'); + +// Workflows to validate +const WORKFLOWS = [ + 'news-evening-analysis.md', + 'news-article-generator.md', + 'news-realtime-monitor.md' +]; + +describe('Agentic Workflow MCP Query Patterns', () => { + describe('Data Freshness Validation', () => { + WORKFLOWS.forEach(workflow => { + it(`${workflow} should document get_sync_status() call`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Check for get_sync_status() documentation + expect(content).toContain('get_sync_status'); + + // Check for data freshness validation guidance + expect(content.toLowerCase()).toMatch(/data\s+freshness|sync\s+status|stale\s+data/); + }); + + it(`${workflow} should warn about stale data`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should document stale data handling + const hasStaleDataHandling = + content.includes('stale') || + content.includes('last_updated') || + content.includes('hoursSinceSync') || + content.includes('>48') || + content.includes('> 48'); + + expect(hasStaleDataHandling).toBe(true); + }); + }); + }); + + describe('Explicit Date Filtering', () => { + WORKFLOWS.forEach(workflow => { + it(`${workflow} should document explicit date parameters`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should document date parameters + const hasDateParameters = + content.includes('from_date') || + content.includes('to_date') || + content.includes('from:') || + content.includes('tom:') || + content.includes('dateFrom') || + content.includes('dateTo'); + + expect(hasDateParameters).toBe(true); + }); + + it(`${workflow} should NOT rely on implicit "latest" patterns`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Check for problematic implicit patterns + // Look for queries without any date awareness + const lines = content.split('\n'); + let hasProblematicPattern = false; + let problematicLines = []; + + lines.forEach((line, idx) => { + // Check for MCP tool calls without date context + if (line.match(/search_voteringar|get_betankanden|get_motioner|get_propositioner|search_anforanden/)) { + // Look for date-related context in surrounding lines + const contextStart = Math.max(0, idx - 5); + const contextEnd = Math.min(lines.length, idx + 5); + const context = lines.slice(contextStart, contextEnd).join('\n'); + + const hasDateContext = + context.includes('from') || + context.includes('tom') || + context.includes('date') || + context.includes('filter') || + context.includes('Filter') || + context.includes('datum') || + context.includes('publicerad') || + context.includes('inlämnad'); + + if (!hasDateContext && !line.includes('//')) { + problematicLines.push({ line: idx + 1, content: line.trim() }); + } + } + }); + + // For now, just log problematic lines for review + // In future, could make this a hard failure + if (problematicLines.length > 0) { + console.log(`⚠️ ${workflow} has queries without obvious date context:`); + problematicLines.forEach(p => { + console.log(` Line ${p.line}: ${p.content}`); + }); + } + }); + }); + }); + + describe('Post-Query Date Filtering Documentation', () => { + it('news-evening-analysis.md should document post-query filtering', () => { + const filepath = path.join(WORKFLOWS_DIR, 'news-evening-analysis.md'); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should document filtering by date fields + expect(content).toMatch(/filter.*by.*publicerad|filter.*by.*datum|filter.*by.*inlämnad/i); + + // Should have filtering examples + expect(content).toContain('.filter('); + expect(content).toMatch(/new Date.*>=.*new Date|new Date.*>.*fromDate/); + }); + + it('workflows should annotate tools with date support', () => { + const filepath = path.join(WORKFLOWS_DIR, 'news-evening-analysis.md'); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Check for date support annotations + expect(content).toMatch(/supports.*from.*tom|supports.*from_date.*to_date|supports.*dateFrom.*dateTo/); + expect(content).toMatch(/filter by.*datum|filter by.*publicerad|filter by.*inlämnad/); + }); + }); + + describe('Cross-Referencing Strategy', () => { + it('news-evening-analysis.md should have cross-referencing examples', () => { + const filepath = path.join(WORKFLOWS_DIR, 'news-evening-analysis.md'); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should have "Cross-Referencing Strategy" section + expect(content).toMatch(/cross.*referencing.*strategy/i); + + // Should have multi-tool examples + const hasMultiToolExamples = + content.includes('Example 1:') && + content.includes('Example 2:') && + content.includes('// 1.') && + content.includes('// 2.'); + + expect(hasMultiToolExamples).toBe(true); + }); + }); + + describe('Error Handling', () => { + WORKFLOWS.forEach(workflow => { + it(`${workflow} should document error scenarios`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should have error handling table or section + expect(content).toMatch(/error|Error|cause|Cause|fix|Fix/); + + // Should document specific error scenarios + const hasErrorScenarios = + content.includes('Tool not found') || + content.includes('Empty results') || + content.includes('Timeout') || + content.includes('Stale data'); + + expect(hasErrorScenarios).toBe(true); + }); + + it(`${workflow} should document stale data handling`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should document what to do with stale data + const hasStaleDataGuidance = + content.toLowerCase().includes('stale') && + (content.includes('disclaimer') || + content.includes('note in analysis') || + content.includes('48h') || + content.includes('48 hours')); + + expect(hasStaleDataGuidance).toBe(true); + }); + }); + }); + + describe('MCP Tool Documentation Quality', () => { + WORKFLOWS.forEach(workflow => { + it(`${workflow} should list all 32 riksdag-regering tools`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should document tool count + expect(content).toMatch(/32.*tools|32.*riksdag-regering/i); + + // Should list key tools + const keyTools = [ + 'get_calendar_events', + 'search_voteringar', + 'get_betankanden', + 'search_regering', + 'get_sync_status' + ]; + + keyTools.forEach(tool => { + expect(content).toContain(tool); + }); + }); + + it(`${workflow} should emphasize get_sync_status() first`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should emphasize calling get_sync_status first + const emphasizesFirst = + content.includes('ALWAYS check') || + content.includes('STEP 1') || + content.includes('CALL THIS FIRST') || + content.includes('first to warm up'); + + expect(emphasizesFirst).toBe(true); + }); + }); + }); + + describe('Date Filtering Best Practices', () => { + it('workflows should have date calculation examples', () => { + const filepath = path.join(WORKFLOWS_DIR, 'news-evening-analysis.md'); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should show date calculation patterns + expect(content).toMatch(/new Date.*toISOString|Date\.now\(\)|fromDate|today/); + expect(content).toMatch(/86400000|3600000/); // Millisecond calculations + }); + + it('workflows should document riksmöte (rm) parameter usage', () => { + const filepath = path.join(WORKFLOWS_DIR, 'news-evening-analysis.md'); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Should use riksmöte parameter + expect(content).toMatch(/rm:.*"2025\/26"|"2024\/25"/); + }); + }); + + describe('Workflow Compilation', () => { + WORKFLOWS.forEach(workflow => { + it(`${workflow} should compile without errors`, () => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + + // Check file exists + expect(fs.existsSync(filepath)).toBe(true); + + // Check for required YAML frontmatter + const content = fs.readFileSync(filepath, 'utf-8'); + expect(content).toMatch(/^---\n/); + expect(content).toMatch(/\nname:/); + expect(content).toMatch(/\ndescription:/); + expect(content).toMatch(/\nmcp-servers:/); + expect(content).toMatch(/\nengine:/); + + // Check for compiled .lock.yml file + const lockFile = filepath.replace('.md', '.lock.yml'); + expect(fs.existsSync(lockFile)).toBe(true); + }); + }); + }); + + describe('Regression Prevention', () => { + it('evening analysis should maintain enhanced query patterns', () => { + const filepath = path.join(WORKFLOWS_DIR, 'news-evening-analysis.md'); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Check for key enhancements added to prevent regression + const enhancements = [ + 'DATA FRESHNESS CHECK', + 'hoursSinceSync', + 'IMPORTANT: Date Filtering in Analysis', + 'Cross-Referencing Strategy', + 'Example 1: Committee Report Deep Dive', + 'Example 2: Government Activity Analysis', + 'Example 3: Party Behavior Analysis', + 'Too broad results' + ]; + + enhancements.forEach(enhancement => { + expect(content).toContain(enhancement); + }); + }); + + it('workflows should not use ambiguous "latest" language', () => { + WORKFLOWS.forEach(workflow => { + const filepath = path.join(WORKFLOWS_DIR, workflow); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Check for problematic "latest" usage (excluding code comments and documentation) + const lines = content.split('\n'); + const problematicLines = []; + + lines.forEach((line, idx) => { + // Skip if it's a code comment explaining "latest" + if (line.includes('//') && line.includes('Get latest')) { + // This is OK - it's just a comment describing what the code does + return; + } + + // Skip if it's in documentation about what NOT to do + if (line.includes('NOT') || line.includes('DO NOT') || line.includes('❌')) { + return; + } + + // Skip if it's documenting get_latest_update tool + if (line.includes('get_latest_update')) { + return; + } + + // Skip if it's in a tool description + if (line.match(/- `.*latest.*`.*-/)) { + return; + } + + // Flag queries that might rely on implicit "latest" + if (line.match(/latest.*data|get.*latest|fetch.*latest/) && + !line.includes('DO NOT') && + !line.includes('❌')) { + problematicLines.push({ line: idx + 1, content: line.trim() }); + } + }); + + // For now, just warn (not hard failure) + if (problematicLines.length > 0) { + console.log(`⚠️ ${workflow} may have ambiguous "latest" usage:`); + problematicLines.forEach(p => { + console.log(` Line ${p.line}: ${p.content}`); + }); + } + }); + }); + }); +}); + +describe('MCP Tool Date Parameter Support Matrix', () => { + it('should document which tools support date parameters', () => { + const filepath = path.join(WORKFLOWS_DIR, 'news-evening-analysis.md'); + const content = fs.readFileSync(filepath, 'utf-8'); + + // Tools that SUPPORT date parameters + const supportsDateParams = [ + 'get_calendar_events', // from/tom + 'search_regering', // from_date/to_date + 'analyze_g0v_by_department' // dateFrom/dateTo + ]; + + supportsDateParams.forEach(tool => { + expect(content).toContain(tool); + + // Should be annotated with supported parameters + const toolSection = content.split(tool)[1]?.substring(0, 200) || ''; + const hasDateAnnotation = + toolSection.includes('supports') || + toolSection.includes('from') || + toolSection.includes('date'); + + expect(hasDateAnnotation).toBe(true); + }); + + // Tools that REQUIRE post-query filtering + const requiresFiltering = [ + 'search_voteringar', // filter by datum + 'get_betankanden', // filter by publicerad + 'get_motioner', // filter by inlämnad + 'get_propositioner', // filter by publicerad + 'search_anforanden' // filter by datum + ]; + + requiresFiltering.forEach(tool => { + expect(content).toContain(tool); + + // Should be annotated with filter guidance + const toolSection = content.split(tool)[1]?.substring(0, 200) || ''; + const hasFilterAnnotation = + toolSection.includes('filter') || + toolSection.includes('datum') || + toolSection.includes('publicerad') || + toolSection.includes('inlämnad'); + + expect(hasFilterAnnotation).toBe(true); + }); + }); +}); From bf44e44963a8683410eb6d343c25a78687143412 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:24:19 +0000 Subject: [PATCH 4/6] Phase 1 complete: Apply MCP query improvements to news-article-generator.md (100% test pass) Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/workflows/news-article-generator.md | 80 ++++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/.github/workflows/news-article-generator.md b/.github/workflows/news-article-generator.md index d008c571..f0953d40 100644 --- a/.github/workflows/news-article-generator.md +++ b/.github/workflows/news-article-generator.md @@ -241,7 +241,10 @@ get_sync_status({}) **DO:** - ✅ Call tools by simple name: `get_calendar_events()`, `search_dokument()` - ✅ Let the framework handle routing, auth, and retries -- ✅ Start with a lightweight warm-up query (e.g., `get_sync_status({})`) before batching +- ✅ **CRITICAL: ALWAYS call `get_sync_status()` FIRST** to check data freshness and warm up server +- ✅ Check for stale data (>48 hours since last sync) and note in articles with disclaimer +- ✅ Use explicit date parameters where supported (from_date, to_date, from, tom) +- ✅ Filter results by date when tools don't support date parameters - ✅ For Node.js scripts: set `export MCP_SERVER_URL="http://host.docker.internal:80/mcp/riksdag-regering"` before running **DO NOT:** @@ -249,10 +252,42 @@ get_sync_status({}) - ❌ Import `MCPClient` or manage sessions yourself - ❌ Use `mcp["server"]["tool"]` wrapper syntax - ❌ Try to authenticate or handle cold starts manually +- ❌ Rely on implicit "latest" data without checking freshness + +### 🚨 DATA FRESHNESS CHECK (MANDATORY FIRST STEP) + +**ALWAYS start by checking when MCP server last synced data:** + +```javascript +// === DATA FRESHNESS CHECK === +// CALL THIS FIRST - validates data freshness and warms up MCP server +const syncStatus = get_sync_status({}); +console.log("MCP Data Sync Status:", syncStatus); + +// Calculate hours since last sync +const lastSync = new Date(syncStatus.last_updated); +const hoursSinceSync = (Date.now() - lastSync.getTime()) / 3600000; + +// Warn if data is stale (>48 hours) +if (hoursSinceSync > 48) { + console.warn(`⚠️ DATA MAY BE STALE: ${hoursSinceSync.toFixed(1)} hours since last sync`); + console.warn(`⚠️ Include disclaimer in articles about data freshness`); +} +``` + +**Why this matters:** +1. **Data Freshness**: Ensures articles use recent data, not stale information +2. **Server Warmup**: Warms up MCP server (avoids 30-60s cold start on first data query) +3. **User Transparency**: Readers know when data was last updated +4. **Quality Control**: Prevents publishing articles with outdated information ### Cold Start Handling -The riksdag-regering-mcp server runs on Render.com and may take 30-60 seconds on first request (cold start). The gh-aw framework retries automatically. Best practice: call `get_sync_status({})` first to warm the server before making data queries. +The riksdag-regering-mcp server runs on Render.com and may take 30-60 seconds on first request (cold start). The gh-aw framework retries automatically. + +**Best Practice:** +1. Call `get_sync_status()` first (warms server AND checks freshness) +2. Batch subsequent queries after warmup ### Error Recovery @@ -260,8 +295,10 @@ The riksdag-regering-mcp server runs on Render.com and may take 30-60 seconds on |-------|-------|-----| | Tool not found | Wrong tool name | Use exact names from the tool list below | | Empty results | No data in timeframe | Widen date range or check `get_sync_status()` | +| **Stale data** | **Last sync >48h ago** | **Note in articles with disclaimer, use available data** | | Timeout | Cold start (30-60s) | Framework retries automatically — just wait | | Swedish-only results | Riksdag API returns Swedish | YOU must translate in Step 5 | +| Too broad results | No date filtering | Add from_date/to_date params OR filter results by date | ### 📋 Available Tools by Category @@ -269,27 +306,48 @@ You have access to 32 specialized tools for Swedish political data: **Riksdag (Parliament) Tools (15):** - `get_ledamoter` / `search_ledamoter` - MPs and member search -- `get_motioner` / `search_motioner` - Parliamentary motions -- `get_propositioner` / `search_propositioner` - Government proposals +- `get_motioner` / `search_motioner` - Parliamentary motions (filter by inlämnad date post-query) +- `get_propositioner` / `search_propositioner` - Government proposals (filter by publicerad date post-query) - `get_dokument` / `search_dokument` / `search_dokument_fulltext` - Documents -- `get_voteringar` / `search_voteringar` - Voting records -- `get_anforanden` / `search_anforanden` - Speeches and debates -- `get_fragor` / `get_interpellationer` - Questions and interpellations -- `get_calendar_events` - Parliamentary schedule -- `get_betankanden` - Committee reports +- `get_voteringar` / `search_voteringar` - Voting records (filter by datum post-query) +- `get_anforanden` / `search_anforanden` - Speeches and debates (filter by datum post-query) +- `get_fragor` / `get_interpellationer` - Questions and interpellations (filter by inlämnad date post-query) +- `get_calendar_events` - Parliamentary schedule (**supports from/tom date params**) +- `get_betankanden` - Committee reports (filter by publicerad date post-query) **Government (Regering) Tools (7):** -- `search_regering` - Government document search +- `search_regering` - Government document search (**supports from_date/to_date params**) - `get_regering_document` - Retrieve specific government doc - `get_g0v_document_content` - Get document in Markdown format - `summarize_regering_document` - AI summarization -- `analyze_g0v_by_department` - Department analysis +- `analyze_g0v_by_department` - Department analysis (**supports dateFrom/dateTo params**) - `get_g0v_document_types` - List document categories **Metadata & Statistics (5):** - `get_utskott` - Committee information - `get_voting_group` - Voting analysis by party/constituency - `fetch_report` - Statistical reports +- `get_sync_status` - **Data freshness check (CALL THIS FIRST)** +- `get_data_dictionary` - Schema definitions + +**Utility (5):** +- `batch_fetch_documents` - Efficient bulk retrieval +- `fetch_paginated_documents` - Pagination support +- `list_reports` - Available report types +- `get_latest_update` - Last data sync timestamp (alias for get_sync_status) +- `enhanced_government_search` - Combined Riksdag + Government search + +**⚠️ Date Parameter Support:** +- **3 tools support explicit date parameters**: `get_calendar_events`, `search_regering`, `analyze_g0v_by_department` +- **All other tools require post-query filtering** by date fields: `datum`, `publicerad`, `inlämnad` +- **Example filtering**: + ```javascript + const recentBetankanden = betankanden.filter(bet => + new Date(bet.publicerad) >= new Date(fromDate) + ); + ``` +- `get_voting_group` - Voting analysis by party/constituency +- `fetch_report` - Statistical reports - `get_sync_status` - Data freshness check - `get_data_dictionary` - Schema definitions From 2fa9d7d5db307ddf39b6bc05cc0c8afe98088d7f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:25:41 +0000 Subject: [PATCH 5/6] Phase 2 complete: Apply MCP improvements to news-realtime-monitor.md - ALL TESTS PASS (100%) Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .../workflows/news-realtime-monitor.lock.yml | 14 ++-- .github/workflows/news-realtime-monitor.md | 80 ++++++++++++++++--- 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/.github/workflows/news-realtime-monitor.lock.yml b/.github/workflows/news-realtime-monitor.lock.yml index fbe45c77..55c140a8 100644 --- a/.github/workflows/news-realtime-monitor.lock.yml +++ b/.github/workflows/news-realtime-monitor.lock.yml @@ -13,7 +13,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.45.4). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.45.5). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -59,7 +59,7 @@ jobs: comment_repo: "" steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Checkout .github and .agents folders @@ -258,7 +258,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -313,7 +313,7 @@ jobs: model: "claude-opus-4.6", version: "", agent_version: "0.0.410", - cli_version: "v0.45.4", + cli_version: "v0.45.5", workflow_name: "News Realtime Monitor", experimental: false, supports_tools_allowlist: true, @@ -887,7 +887,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -990,7 +990,7 @@ jobs: success: ${{ steps.parse_results.outputs.success }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent artifacts @@ -1103,7 +1103,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@ac090214a48a1938f7abafe132460b66752261af # v0.45.4 + uses: github/gh-aw/actions/setup@852cb06ad52958b402ed982b69957ffc57ca0619 # v0.45.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/news-realtime-monitor.md b/.github/workflows/news-realtime-monitor.md index 758a1d43..08c0a2ff 100644 --- a/.github/workflows/news-realtime-monitor.md +++ b/.github/workflows/news-realtime-monitor.md @@ -222,6 +222,10 @@ search_regering({ from_date: "2026-02-16", limit: 30 }) **✅ For MCP tool calls in prompts, ALWAYS do this:** - ✅ Use simple tool names: `get_calendar_events({ params })`, `search_voteringar({ params })` - ✅ Let the framework handle all routing, authentication and session management +- ✅ **CRITICAL: ALWAYS call `get_sync_status()` FIRST** to check data freshness and warm up server +- ✅ Check for stale data (>48 hours since last sync) and note in articles with disclaimer +- ✅ Use explicit date parameters where supported (from_date, to_date, from, tom) +- ✅ Filter results by date when tools don't support date parameters - ✅ Trust the automatic retry logic for cold starts **✅ For running Node.js scripts via bash:** @@ -229,11 +233,45 @@ search_regering({ from_date: "2026-02-16", limit: 30 }) - ✅ Set `export MCP_CLIENT_TIMEOUT_MS=90000` for cold start tolerance - ✅ Scripts ARE used by agentic workflows and work perfectly +**❌ DO NOT:** +- ❌ Rely on implicit "latest" data without checking freshness +- ❌ Skip data freshness validation +- ❌ Use tools without understanding date parameter support + +### 🚨 DATA FRESHNESS CHECK (MANDATORY FIRST STEP) + +**ALWAYS start by checking when MCP server last synced data:** + +```javascript +// === DATA FRESHNESS CHECK === +// CALL THIS FIRST - validates data freshness and warms up MCP server +const syncStatus = get_sync_status({}); +console.log("MCP Data Sync Status:", syncStatus); + +// Calculate hours since last sync +const lastSync = new Date(syncStatus.last_updated); +const hoursSinceSync = (Date.now() - lastSync.getTime()) / 3600000; + +// Warn if data is stale (>48 hours) +if (hoursSinceSync > 48) { + console.warn(`⚠️ DATA MAY BE STALE: ${hoursSinceSync.toFixed(1)} hours since last sync`); + console.warn(`⚠️ Include disclaimer in articles about data freshness`); +} +``` + +**Why this matters:** +1. **Data Freshness**: Ensures breaking news uses recent data, not stale information +2. **Server Warmup**: Warms up MCP server (avoids 30-60s cold start on first data query) +3. **User Transparency**: Readers know when data was last updated +4. **Quality Control**: Prevents publishing breaking news with outdated information + ### 🚨 Cold Start Handling The MCP server may take 30-60 seconds on first request (cold start). **The framework handles this automatically with retries.** Just make your call normally and wait. -**Best Practice:** Start with a simple query to warm up the server, then batch multiple queries. +**Best Practice:** +1. Call `get_sync_status()` first (warms server AND checks freshness) +2. Batch subsequent queries after warmup ### 📋 32 Available MCP Tools @@ -241,43 +279,61 @@ The MCP server may take 30-60 seconds on first request (cold start). **The frame **Riksdag (Parliament) Tools (15):** - `get_ledamoter` / `search_ledamoter` - MPs and member search -- `get_motioner` / `search_motioner` - Parliamentary motions -- `get_propositioner` / `search_propositioner` - Government proposals +- `get_motioner` / `search_motioner` - Parliamentary motions (filter by inlämnad date post-query) +- `get_propositioner` / `search_propositioner` - Government proposals (filter by publicerad date post-query) - `get_dokument` / `search_dokument` / `search_dokument_fulltext` - Documents -- `get_voteringar` / `search_voteringar` - Voting records -- `get_anforanden` / `search_anforanden` - Speeches and debates -- `get_fragor` / `get_interpellationer` - Questions and interpellations -- `get_calendar_events` - Parliamentary schedule -- `get_betankanden` - Committee reports +- `get_voteringar` / `search_voteringar` - Voting records (filter by datum post-query) +- `get_anforanden` / `search_anforanden` - Speeches and debates (filter by datum post-query) +- `get_fragor` / `get_interpellationer` - Questions and interpellations (filter by inlämnad date post-query) +- `get_calendar_events` - Parliamentary schedule (**supports from/tom date params**) +- `get_betankanden` - Committee reports (filter by publicerad date post-query) **Government (Regering) Tools (7):** -- `search_regering` - Government document search +- `search_regering` - Government document search (**supports from_date/to_date params**) - `get_regering_document` - Retrieve specific government doc - `get_g0v_document_content` - Get document in Markdown format - `summarize_regering_document` - AI summarization -- `analyze_g0v_by_department` - Department analysis +- `analyze_g0v_by_department` - Department analysis (**supports dateFrom/dateTo params**) - `get_g0v_document_types` - List document categories **Metadata & Statistics (5):** - `get_utskott` - Committee information - `get_voting_group` - Voting analysis by party/constituency - `fetch_report` - Statistical reports -- `get_sync_status` - Data freshness check +- `get_sync_status` - **Data freshness check (CALL THIS FIRST)** - `get_data_dictionary` - Schema definitions **Utility (5):** - `batch_fetch_documents` - Efficient bulk retrieval - `fetch_paginated_documents` - Pagination support - `list_reports` - Available report types -- `get_latest_update` - Last data sync timestamp +- `get_latest_update` - Last data sync timestamp (alias for get_sync_status) - `enhanced_government_search` - Combined Riksdag + Government search +**⚠️ Date Parameter Support:** +- **3 tools support explicit date parameters**: `get_calendar_events`, `search_regering`, `analyze_g0v_by_department` +- **All other tools require post-query filtering** by date fields: `datum`, `publicerad`, `inlämnad` +- **Example filtering**: + ```javascript + const recentBetankanden = betankanden.filter(bet => + new Date(bet.publicerad) >= new Date(fromDate) + ); + ``` + ### 🐛 Troubleshooting **Issue: Request times out** - **Cause**: Cold start (30-60s) or server overload - **Solution**: Wait and retry - framework handles retries automatically +**Issue: Data seems stale or outdated** +- **Cause**: MCP server last synced >48 hours ago +- **Solution**: Check `get_sync_status()`, note in articles with disclaimer, use available data + +**Issue: Too many results returned** +- **Cause**: No date filtering applied +- **Solution**: Add from_date/to_date params OR filter results by date in code + **Issue: Tool not found error** - **Cause**: Wrong tool name - **Solution**: Use exact simple names: `get_calendar_events`, `search_voteringar` From 4db68980bc052c8c30f95522438c3b9a7910b9ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:27:25 +0000 Subject: [PATCH 6/6] Phase 3 complete: Enhance riksdag-regering-mcp skill with comprehensive MCP query best practices Co-authored-by: pethers <1726836+pethers@users.noreply.github.com> --- .github/skills/riksdag-regering-mcp/SKILL.md | 235 +++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/.github/skills/riksdag-regering-mcp/SKILL.md b/.github/skills/riksdag-regering-mcp/SKILL.md index 899b5bcb..c9c0af3d 100644 --- a/.github/skills/riksdag-regering-mcp/SKILL.md +++ b/.github/skills/riksdag-regering-mcp/SKILL.md @@ -217,8 +217,243 @@ const allVotes = await riksdag_regering_mcp.fetch_paginated_documents({ }); ``` +## 🔥 MCP Query Best Practices + +### ⚡ CRITICAL: Always Check Data Freshness First + +**RULE #1: Call `get_sync_status()` before any data queries** + +```javascript +// === DATA FRESHNESS CHECK === +// ALWAYS call this FIRST - validates freshness and warms up server +const syncStatus = get_sync_status({}); +console.log("Last data sync:", syncStatus.last_updated); + +// Calculate hours since last sync +const lastSync = new Date(syncStatus.last_updated); +const hoursSinceSync = (Date.now() - lastSync.getTime()) / 3600000; + +// Warn if data is stale (>48 hours) +if (hoursSinceSync > 48) { + console.warn(`⚠️ DATA MAY BE STALE: ${hoursSinceSync.toFixed(1)} hours since last sync`); + // Include disclaimer in analysis/articles +} +``` + +**Why this matters:** +1. **Data Quality**: Ensures analysis uses recent data, not stale information +2. **Server Warmup**: Warms up MCP server (avoids 30-60s cold start on first query) +3. **User Transparency**: Readers/stakeholders know when data was last updated +4. **Quality Control**: Prevents publishing analysis with outdated information + +### 📅 Date Parameter Support Matrix + +**Only 3 of 32 tools support explicit date parameters:** + +| Tool | Date Parameters | Example | +|------|----------------|---------| +| `get_calendar_events` | `from`, `tom` | `{ from: "2026-02-17", tom: "2026-02-23" }` | +| `search_regering` | `from_date`, `to_date` | `{ from_date: "2026-02-01", to_date: "2026-02-17" }` | +| `analyze_g0v_by_department` | `dateFrom`, `dateTo` | `{ dateFrom: "2026-02-01", dateTo: "2026-02-17" }` | + +**All other 29 tools require post-query date filtering by:** +- `datum` - votes, speeches (voting/speech date) +- `publicerad` - committee reports, propositions (publication date) +- `inlämnad` - motions, questions, interpellations (submission date) + +### ✅ Good Pattern: Explicit Date Filtering + +```javascript +// === STEP 1: Check data freshness === +const syncStatus = get_sync_status({}); + +// === STEP 2: Query with explicit dates (where supported) === +const govDocs = search_regering({ + from_date: "2026-02-01", + to_date: "2026-02-17", + limit: 50 +}); + +// === STEP 3: Filter by date for tools without date params === +const betankanden = get_betankanden({ rm: "2025/26", limit: 100 }); + +// Filter results by publication date +const fromDate = new Date("2026-02-01"); +const recentBetankanden = betankanden.filter(bet => + new Date(bet.publicerad) >= fromDate +); + +console.log(`Found ${recentBetankanden.length} committee reports from ${fromDate.toISOString().split('T')[0]}`); +``` + +### ❌ Anti-Pattern: Implicit "Latest" Reliance + +```javascript +// ❌ BAD: Relies on implicit sorting without date awareness +const votes = search_voteringar({ rm: "2025/26", limit: 50 }); +// Problem: No idea when votes occurred, might be months old + +// ✅ GOOD: Explicit date filtering +const votes = search_voteringar({ rm: "2025/26", limit: 50 }); +const recentVotes = votes.filter(v => + new Date(v.datum) >= new Date("2026-02-01") +); +console.log(`Found ${recentVotes.length} votes since 2026-02-01`); +``` + +### 🔗 Cross-Referencing Strategy + +Combine multiple tools for richer analysis: + +#### Pattern 1: Committee Report Deep Dive +```javascript +// 1. Get recent committee reports +const betankanden = get_betankanden({ rm: "2025/26", limit: 20 }); +const recentBet = betankanden.filter(b => + new Date(b.publicerad) >= new Date("2026-02-01") +); + +// 2. Get full details for each report +const reportDetails = recentBet.map(bet => + get_dokument({ dok_id: bet.dok_id }) +); + +// 3. Find related votes +const votes = search_voteringar({ rm: "2025/26", limit: 100 }); +const relatedVotes = votes.filter(v => + recentBet.some(bet => v.bet === bet.beteckning) +); + +// 4. Get committee members' speeches +const speeches = search_anforanden({ rm: "2025/26", limit: 100 }); +const committeeSpeeches = speeches.filter(a => + recentBet.some(bet => a.dokument_hangar_samman === bet.dok_id) +); + +// Now you have: reports + details + votes + speeches +``` + +#### Pattern 2: Party Behavior Analysis +```javascript +// 1. Get voting patterns by party +const voteGroups = get_voting_group({ rm: "2025/26", groupBy: "parti" }); + +// 2. Get recent votes +const votes = search_voteringar({ rm: "2025/26", limit: 200 }); +const recentVotes = votes.filter(v => + new Date(v.datum) >= new Date("2026-02-01") +); + +// 3. Get party motions +const motions = get_motioner({ rm: "2025/26", limit: 100 }); +const partyMotions = motions.filter(m => + new Date(m.inlämnad) >= new Date("2026-02-01") +); + +// 4. Calculate party cohesion +const partyDiscipline = calculateCohesion(voteGroups, recentVotes); +``` + +### 🐛 Troubleshooting Guide + +| Issue | Cause | Solution | +|-------|-------|----------| +| **Tool not found** | Wrong tool name | Use exact names: `get_calendar_events`, `search_voteringar` | +| **Empty results** | No data in timeframe | Check `get_sync_status()`, widen date range, verify `rm` parameter | +| **Stale data** | Last sync >48h ago | Note in analysis with disclaimer, use available data | +| **Timeout** | Cold start (30-60s) | Wait - MCP framework retries automatically | +| **Too broad results** | No date filtering | Add date params OR filter by datum/publicerad/inlämnad | +| **Swedish-only results** | Riksdag API returns Swedish | Must translate to target languages | +| **Missing documents** | Wrong riksmöte (rm) | Verify rm parameter (e.g., "2025/26") | + +### 🚀 Performance Tips + +1. **Batch Queries After Warmup**: Call `get_sync_status()` first, then batch data queries +2. **Use Pagination Wisely**: Use `fetch_paginated_documents` for large datasets +3. **Filter Early**: Apply date filters as early as possible +4. **Cache Results**: MCP server data updates daily, cache results when appropriate +5. **Parallel Queries**: Independent queries can run in parallel after server warmup + +### 📊 Query Pattern Templates + +**Template 1: Daily News Monitoring** +```javascript +const syncStatus = get_sync_status({}); +const today = new Date().toISOString().split('T')[0]; +const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0]; + +// Government activity +const govDocs = search_regering({ + from_date: yesterday, + to_date: today, + limit: 50 +}); + +// Parliamentary calendar +const events = get_calendar_events({ + from: today, + tom: today, + limit: 50 +}); + +// Recent votes (filter by date) +const votes = search_voteringar({ rm: "2025/26", limit: 50 }); +const todayVotes = votes.filter(v => v.datum === today); +``` + +**Template 2: Weekly Analysis** +```javascript +const syncStatus = get_sync_status({}); +const today = new Date(); +const weekAgo = new Date(Date.now() - 7 * 86400000); + +// Committee reports (filter by publicerad) +const reports = get_betankanden({ rm: "2025/26", limit: 100 }); +const weeklyReports = reports.filter(r => + new Date(r.publicerad) >= weekAgo +); + +// Propositions (filter by publicerad) +const props = get_propositioner({ rm: "2025/26", limit: 50 }); +const weeklyProps = props.filter(p => + new Date(p.publicerad) >= weekAgo +); + +// Questions (filter by inlämnad) +const questions = get_fragor({ rm: "2025/26", limit: 100 }); +const weeklyQuestions = questions.filter(q => + new Date(q.inlämnad) >= weekAgo +); +``` + +**Template 3: MP Activity Analysis** +```javascript +const syncStatus = get_sync_status({}); + +// Get MP profile +const mp = get_ledamot({ intressent_id: "XXXXXXXX" }); + +// Get MP's motions +const allMotions = get_motioner({ rm: "2025/26", limit: 200 }); +const mpMotions = allMotions.filter(m => + m.intressent_id === mp.intressent_id +); + +// Get MP's speeches +const speeches = search_anforanden({ + rm: "2025/26", + talare: mp.tilltalsnamn + " " + mp.efternamn, + limit: 100 +}); + +// Get MP's voting record +const votes = search_voteringar({ rm: "2025/26", limit: 200 }); +// Requires cross-referencing with ledamöter data +``` + ## MCP Server Configuration + ### HTTP Endpoint (Production) ```json {