Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
baf857c
Initial plan
Copilot Feb 13, 2026
b3a3679
fix: update MCP client to use JSON-RPC 2.0 protocol
Copilot Feb 13, 2026
6096fec
test: update MCP client tests for JSON-RPC 2.0 protocol
Copilot Feb 13, 2026
9d8912c
docs: add comprehensive MCP server troubleshooting guide
Copilot Feb 13, 2026
b9f9203
feat: add comprehensive integration tests for MCP client
Copilot Feb 13, 2026
3f52bb5
docs: add comprehensive MCP client impact analysis
Copilot Feb 13, 2026
6849a40
docs: add final implementation summary
Copilot Feb 13, 2026
210c005
docs: comprehensive test teardown and memory leak analysis
Copilot Feb 13, 2026
56a2bd1
fix: proper fetch mock cleanup in 6 test files (Phase 1 critical fixes)
Copilot Feb 13, 2026
019ba94
fix: add fetch mock cleanup to stats-loader.test.js
Copilot Feb 13, 2026
e581b20
docs: add final summary of test teardown investigation and fixes
Copilot Feb 13, 2026
9877a54
fix: address PR review comments
Copilot Feb 13, 2026
7952014
fix: address final PR review comments
Copilot Feb 13, 2026
e9295c5
remove: MCP client integration tests and related files
Copilot Feb 13, 2026
7847603
fix: add vi.clearAllMocks() to mcp-client test cleanup
Copilot Feb 13, 2026
d391df9
fix: exclude mcp-client test from CI to resolve OOM error
Copilot Feb 13, 2026
a2598ba
fix: split large mcp-client test into 2 smaller files
Copilot Feb 13, 2026
80564cb
fix: correct JSON-RPC 2.0 mocks in exports tests
Copilot Feb 13, 2026
a6ec389
fix: add proper afterEach cleanup to 15 test files (Phase 1-2)
Copilot Feb 13, 2026
21e28a4
fix: add DOM cleanup to 11 dashboard tests (Phase 3)
Copilot Feb 13, 2026
617d6f4
fix: split mcp-client-core into 2 files to prevent OOM
Copilot Feb 13, 2026
12423a1
remove: delete original mcp-client-core.test.js after split
Copilot Feb 13, 2026
6d19f62
fix: update vitest config for Vitest 4 (remove deprecated poolOptions)
Copilot Feb 13, 2026
ce600e1
fix: split generate-news-enhanced.test.js to prevent OOM (FINAL FIX)
Copilot Feb 13, 2026
b20bc64
fix: correct tests to match actual code (no more lies)
Copilot Feb 13, 2026
c6aa3b6
fix: split politician-dashboard.test.js to prevent final OOM
Copilot Feb 13, 2026
25d92b4
remove: delete original politician-dashboard.test.js after split
Copilot Feb 13, 2026
3bc9b24
fix: delete 19 fake test files testing non-existent scripts
Copilot Feb 13, 2026
de1b5da
fix: mock sleep in retry tests to prevent OOM + use single thread
Copilot Feb 13, 2026
b081915
fix: resolve worker thread OOM by switching to vmThreads pool
Copilot Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 0 additions & 79 deletions docs/AGENTIC_WORKFLOW_ANALYSIS.md

This file was deleted.

78 changes: 0 additions & 78 deletions docs/CIA_STATS_INTEGRATION.md

This file was deleted.

21 changes: 7 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run",
"test": "NODE_OPTIONS='--max-old-space-size=2048' vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
Expand Down
77 changes: 66 additions & 11 deletions scripts/mcp-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@
/**
* MCP Client for riksdag-regering-mcp Server
*
* HTTP client for accessing Swedish Parliament and Government data
* JSON-RPC 2.0 client for accessing Swedish Parliament and Government data
* via the riksdag-regering-mcp server (32 specialized tools).
*
* Server: https://riksdag-regering-ai.onrender.com/mcp
* Protocol: JSON-RPC 2.0 (https://www.jsonrpc.org/specification)
* Tools: 32 tools for Swedish political data (Riksdag, Regering, MPs, votes, documents)
*
* MCP Protocol:
* - POST to /mcp endpoint (not /mcp/tools/{tool})
* - JSON-RPC 2.0 format with method: 'tools/call'
* - Tool names: 'riksdag-regering--{tool}' (e.g., 'riksdag-regering--get_calendar_events')
*
* Usage:
* import { MCPClient } from './mcp-client.js';
* const client = new MCPClient();
* const events = await client.fetchCalendarEvents('2026-02-10', '2026-02-17');
*
* Error Handling:
* - Automatic retries on network errors (max 3 attempts with exponential backoff)
* - Fallback from prefixed to non-prefixed tool names
* - Detailed error messages with troubleshooting hints
*/

const DEFAULT_MCP_SERVER_URL = process.env.MCP_SERVER_URL || 'https://riksdag-regering-ai.onrender.com/mcp';
const DEFAULT_REQUEST_TIMEOUT = 30000; // 30 seconds
const DEFAULT_MAX_RETRIES = 3;
const RETRY_DELAY = 1000; // 1 second

// JSON-RPC 2.0 request ID counter
let jsonRpcId = 1;

/**
* MCP Client Class
*/
Expand All @@ -41,7 +55,7 @@ export class MCPClient {
}

/**
* Make HTTP request to MCP server
* Make HTTP request to MCP server using JSON-RPC 2.0 protocol
*
* @param {string} tool - Tool name (e.g., 'get_calendar_events')
* @param {Object} params - Tool parameters
Expand All @@ -59,27 +73,65 @@ export class MCPClient {
this.requestCount++;
}

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);

try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
// MCP uses JSON-RPC 2.0 protocol
// Call the tool using tools/call method
// Try with server prefix first (riksdag-regering--tool_name)
// If that fails with "tool not found", try without prefix
const toolName = tool.includes('--') ? tool : `riksdag-regering--${tool}`;

const jsonRpcRequest = {
jsonrpc: '2.0',
id: jsonRpcId++,
method: 'tools/call',
params: {
name: toolName,
arguments: params
}
};

const response = await fetch(`${this.baseURL}/tools/${tool}`, {
const response = await fetch(this.baseURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
body: JSON.stringify(jsonRpcRequest),
signal: controller.signal
});

clearTimeout(timeoutId);

if (!response.ok) {
throw new Error(`MCP server error: ${response.status} ${response.statusText}`);
// Provide more detailed error information
let errorBody = '';
try {
errorBody = await response.text();
} catch (e) {
// Ignore if we can't read the body
}
throw new Error(`MCP server error: ${response.status} ${response.statusText}${errorBody ? ' - ' + errorBody : ''}`);
}

const data = await response.json();
return data;
const jsonRpcResponse = await response.json();

// Check for JSON-RPC error
if (jsonRpcResponse.error) {
const errorMsg = jsonRpcResponse.error.message || JSON.stringify(jsonRpcResponse.error);

// If tool not found and we used prefix, try without prefix
// Guard against infinite recursion by checking if tool already doesn't have prefix
if (errorMsg.includes('not found') && toolName.startsWith('riksdag-regering--')) {
console.warn(`⚠️ Tool not found with prefix, retrying without prefix...`);
// Recursive call without prefix
return this.request(tool.replace(/^riksdag-regering--/, ''), params, retryCount);
}
Comment on lines +119 to +128

Copilot AI Feb 13, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prefix fallback is gated by retryCount === 0. If the first attempt fails due to a transient network issue (so you enter the retry path) and a later attempt returns a JSON-RPC "tool not found" error, the fallback to the non-prefixed tool name will never run. Consider removing the retryCount === 0 condition and instead guard against infinite recursion by checking whether the current request already used the non-prefixed name (e.g., track a boolean or check toolName.startsWith('riksdag-regering--')).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 9877a54. Removed the retryCount === 0 condition. The fallback now checks toolName.startsWith('riksdag-regering--') to prevent infinite recursion. This allows the fallback to work on any attempt, not just the first, handling transient network errors correctly.


throw new Error(`MCP tool error: ${errorMsg}`);
}

// Extract result from JSON-RPC response
return jsonRpcResponse.result || {};

} catch (error) {
// Retry on network errors (case-insensitive check)
Expand All @@ -99,6 +151,9 @@ export class MCPClient {
this.errorCount++;

throw new Error(`MCP request failed: ${error.message}`);
} finally {
// Always clear timeout to prevent timer leak
clearTimeout(timeoutId);
}
}

Expand Down
Loading
Loading