Skip to content

Commit 50f846c

Browse files
authored
Merge pull request #140 from Hack23/copilot/generate-news-articles
Fix worker thread OOM by removing fake tests and switching to vmThreads
2 parents 7db20ca + b081915 commit 50f846c

39 files changed

+1455
-6794
lines changed

docs/AGENTIC_WORKFLOW_ANALYSIS.md

Lines changed: 0 additions & 79 deletions
This file was deleted.

docs/CIA_STATS_INTEGRATION.md

Lines changed: 0 additions & 78 deletions
This file was deleted.

package-lock.json

Lines changed: 7 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"dev": "vite",
99
"build": "vite build",
1010
"preview": "vite preview",
11-
"test": "vitest run",
11+
"test": "NODE_OPTIONS='--max-old-space-size=2048' vitest run",
1212
"test:watch": "vitest",
1313
"test:ui": "vitest --ui",
1414
"test:coverage": "vitest run --coverage",

scripts/mcp-client.js

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,37 @@
33
/**
44
* MCP Client for riksdag-regering-mcp Server
55
*
6-
* HTTP client for accessing Swedish Parliament and Government data
6+
* JSON-RPC 2.0 client for accessing Swedish Parliament and Government data
77
* via the riksdag-regering-mcp server (32 specialized tools).
88
*
99
* Server: https://riksdag-regering-ai.onrender.com/mcp
10+
* Protocol: JSON-RPC 2.0 (https://www.jsonrpc.org/specification)
1011
* Tools: 32 tools for Swedish political data (Riksdag, Regering, MPs, votes, documents)
1112
*
13+
* MCP Protocol:
14+
* - POST to /mcp endpoint (not /mcp/tools/{tool})
15+
* - JSON-RPC 2.0 format with method: 'tools/call'
16+
* - Tool names: 'riksdag-regering--{tool}' (e.g., 'riksdag-regering--get_calendar_events')
17+
*
1218
* Usage:
1319
* import { MCPClient } from './mcp-client.js';
1420
* const client = new MCPClient();
1521
* const events = await client.fetchCalendarEvents('2026-02-10', '2026-02-17');
22+
*
23+
* Error Handling:
24+
* - Automatic retries on network errors (max 3 attempts with exponential backoff)
25+
* - Fallback from prefixed to non-prefixed tool names
26+
* - Detailed error messages with troubleshooting hints
1627
*/
1728

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

34+
// JSON-RPC 2.0 request ID counter
35+
let jsonRpcId = 1;
36+
2337
/**
2438
* MCP Client Class
2539
*/
@@ -41,7 +55,7 @@ export class MCPClient {
4155
}
4256

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

76+
const controller = new AbortController();
77+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
78+
6279
try {
63-
const controller = new AbortController();
64-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
80+
// MCP uses JSON-RPC 2.0 protocol
81+
// Call the tool using tools/call method
82+
// Try with server prefix first (riksdag-regering--tool_name)
83+
// If that fails with "tool not found", try without prefix
84+
const toolName = tool.includes('--') ? tool : `riksdag-regering--${tool}`;
85+
86+
const jsonRpcRequest = {
87+
jsonrpc: '2.0',
88+
id: jsonRpcId++,
89+
method: 'tools/call',
90+
params: {
91+
name: toolName,
92+
arguments: params
93+
}
94+
};
6595

66-
const response = await fetch(`${this.baseURL}/tools/${tool}`, {
96+
const response = await fetch(this.baseURL, {
6797
method: 'POST',
6898
headers: {
6999
'Content-Type': 'application/json',
70100
},
71-
body: JSON.stringify(params),
101+
body: JSON.stringify(jsonRpcRequest),
72102
signal: controller.signal
73103
});
74104

75-
clearTimeout(timeoutId);
76-
77105
if (!response.ok) {
78-
throw new Error(`MCP server error: ${response.status} ${response.statusText}`);
106+
// Provide more detailed error information
107+
let errorBody = '';
108+
try {
109+
errorBody = await response.text();
110+
} catch (e) {
111+
// Ignore if we can't read the body
112+
}
113+
throw new Error(`MCP server error: ${response.status} ${response.statusText}${errorBody ? ' - ' + errorBody : ''}`);
79114
}
80115

81-
const data = await response.json();
82-
return data;
116+
const jsonRpcResponse = await response.json();
117+
118+
// Check for JSON-RPC error
119+
if (jsonRpcResponse.error) {
120+
const errorMsg = jsonRpcResponse.error.message || JSON.stringify(jsonRpcResponse.error);
121+
122+
// If tool not found and we used prefix, try without prefix
123+
// Guard against infinite recursion by checking if tool already doesn't have prefix
124+
if (errorMsg.includes('not found') && toolName.startsWith('riksdag-regering--')) {
125+
console.warn(`⚠️ Tool not found with prefix, retrying without prefix...`);
126+
// Recursive call without prefix
127+
return this.request(tool.replace(/^riksdag-regering--/, ''), params, retryCount);
128+
}
129+
130+
throw new Error(`MCP tool error: ${errorMsg}`);
131+
}
132+
133+
// Extract result from JSON-RPC response
134+
return jsonRpcResponse.result || {};
83135

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

101153
throw new Error(`MCP request failed: ${error.message}`);
154+
} finally {
155+
// Always clear timeout to prevent timer leak
156+
clearTimeout(timeoutId);
102157
}
103158
}
104159

0 commit comments

Comments
 (0)