euparliamentmonitor 0.8.24 → 0.8.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "euparliamentmonitor",
3
- "version": "0.8.24",
3
+ "version": "0.8.25",
4
4
  "type": "module",
5
5
  "description": "European Parliament Intelligence Platform - Monitor political activity with systematic transparency",
6
6
  "main": "scripts/index.js",
@@ -170,7 +170,7 @@
170
170
  "node": ">=25"
171
171
  },
172
172
  "dependencies": {
173
- "european-parliament-mcp-server": "1.2.1"
173
+ "european-parliament-mcp-server": "1.2.2"
174
174
  },
175
175
  "optionalDependencies": {
176
176
  "worldbank-mcp": "1.0.1"
@@ -14,6 +14,15 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
14
14
  private readonly _failedTools;
15
15
  /** Tracks tools that have been called (attempted) in the current session */
16
16
  private readonly _calledTools;
17
+ /**
18
+ * Record a tool failure and log a warning.
19
+ *
20
+ * @param toolName - MCP tool name that failed
21
+ * @param errorText - Raw error text from the failure
22
+ * @param fallbackText - JSON text for the fallback result
23
+ * @returns Fallback MCPToolResult
24
+ */
25
+ private _recordToolFailure;
17
26
  /**
18
27
  * Generic error-safe wrapper around {@link callToolWithRetry}.
19
28
  * Retries transient failures (timeouts, connection drops) with a bounded
@@ -22,6 +31,12 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
22
31
  * Catches any error thrown by the tool (or by the args factory), logs a warning,
23
32
  * and returns a fallback payload.
24
33
  *
34
+ * Also inspects the tool result for the MCP protocol `isError` flag. When
35
+ * `isError === true`, the first content item's text is passed through
36
+ * {@link classifyToolError} for diagnostic categorization, and the tool is
37
+ * recorded as failed via {@link _recordToolFailure}. This handles EP MCP
38
+ * Server error responses that are returned (not thrown) as structured results.
39
+ *
25
40
  * Accepts either a plain args object or a factory function `() => object`.
26
41
  * Using a factory ensures that options normalization/destructuring runs inside
27
42
  * the try/catch so invalid runtime inputs fall back gracefully.
@@ -27,21 +27,36 @@ const PROCEDURE_EVENT_FALLBACK = '{"event": null}';
27
27
  /** Fallback payload for server health status */
28
28
  const SERVER_HEALTH_FALLBACK = '{"server": null, "feeds": []}';
29
29
  /**
30
- * Classify an error message into a diagnostic error category, aligned with
31
- * EP MCP Server v1.2.1 standardized error categories.
30
+ * Classify an error message into a diagnostic error category.
32
31
  *
33
- * Priority:
34
- * 1. Gateway 5xx SERVER_ERROR (not TIMEOUT, even for 504 "Gateway Timeout")
35
- * 2. 429 / rate-limit → RATE_LIMIT
36
- * 3. 404 NOT_FOUND
37
- * 4. Client-side timeout TIMEOUT
38
- * 5. Everything else UNKNOWN
32
+ * Maps EP MCP Server v1.2.2 structured error codes and generic HTTP/network
33
+ * errors into one of six broad categories used for logging and retry decisions:
34
+ *
35
+ * Returned categories (priority order):
36
+ * 1. `INTERNAL_ERROR` — EP MCP `INTERNAL_ERROR` (catch-all for DNS, TLS, unclassified upstream failures)
37
+ * 2. `SERVER_ERROR` — EP MCP `UPSTREAM_500`/`UPSTREAM_503`/`SERVER_ERROR`, or gateway 5xx patterns
38
+ * 3. `TIMEOUT` — EP MCP `UPSTREAM_TIMEOUT`, or generic "timeout" strings
39
+ * 4. `RATE_LIMIT` — EP MCP `RATE_LIMITED`, HTTP 429, or "rate limit"/"too many requests" strings
40
+ * 5. `NOT_FOUND` — EP MCP `UPSTREAM_404`, or generic "404" strings
41
+ * 6. `UNKNOWN` — everything else
39
42
  *
40
43
  * @param message - Raw error message
41
44
  * @returns Diagnostic error category string
42
45
  */
43
46
  function classifyToolError(message) {
44
47
  const lowerMsg = message.toLowerCase();
48
+ // EP MCP Server v1.2.2 structured error codes (matched case-insensitively)
49
+ if (lowerMsg.includes('internal_error')) {
50
+ return 'INTERNAL_ERROR';
51
+ }
52
+ if (lowerMsg.includes('upstream_500') ||
53
+ lowerMsg.includes('upstream_503') ||
54
+ lowerMsg.includes('server_error')) {
55
+ return 'SERVER_ERROR';
56
+ }
57
+ if (lowerMsg.includes('upstream_timeout')) {
58
+ return 'TIMEOUT';
59
+ }
45
60
  if (lowerMsg.includes('gateway timeout') ||
46
61
  lowerMsg.includes('gateway error 500') ||
47
62
  lowerMsg.includes('gateway error 502') ||
@@ -51,10 +66,11 @@ function classifyToolError(message) {
51
66
  }
52
67
  if (lowerMsg.includes('429') ||
53
68
  lowerMsg.includes('rate limit') ||
54
- lowerMsg.includes('too many requests')) {
69
+ lowerMsg.includes('too many requests') ||
70
+ lowerMsg.includes('rate_limited')) {
55
71
  return 'RATE_LIMIT';
56
72
  }
57
- if (lowerMsg.includes('404'))
73
+ if (lowerMsg.includes('404') || lowerMsg.includes('upstream_404'))
58
74
  return 'NOT_FOUND';
59
75
  if (lowerMsg.includes('timeout'))
60
76
  return 'TIMEOUT';
@@ -69,6 +85,20 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
69
85
  _failedTools = new Map();
70
86
  /** Tracks tools that have been called (attempted) in the current session */
71
87
  _calledTools = new Set();
88
+ /**
89
+ * Record a tool failure and log a warning.
90
+ *
91
+ * @param toolName - MCP tool name that failed
92
+ * @param errorText - Raw error text from the failure
93
+ * @param fallbackText - JSON text for the fallback result
94
+ * @returns Fallback MCPToolResult
95
+ */
96
+ _recordToolFailure(toolName, errorText, fallbackText) {
97
+ const errorType = classifyToolError(errorText);
98
+ this._failedTools.set(toolName, `${errorType}: ${errorText.slice(0, 200)}`);
99
+ console.warn(`⚠️ ${toolName} failed [${errorType}]:`, errorText.slice(0, 200));
100
+ return { content: [{ type: 'text', text: fallbackText }] };
101
+ }
72
102
  /**
73
103
  * Generic error-safe wrapper around {@link callToolWithRetry}.
74
104
  * Retries transient failures (timeouts, connection drops) with a bounded
@@ -77,6 +107,12 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
77
107
  * Catches any error thrown by the tool (or by the args factory), logs a warning,
78
108
  * and returns a fallback payload.
79
109
  *
110
+ * Also inspects the tool result for the MCP protocol `isError` flag. When
111
+ * `isError === true`, the first content item's text is passed through
112
+ * {@link classifyToolError} for diagnostic categorization, and the tool is
113
+ * recorded as failed via {@link _recordToolFailure}. This handles EP MCP
114
+ * Server error responses that are returned (not thrown) as structured results.
115
+ *
80
116
  * Accepts either a plain args object or a factory function `() => object`.
81
117
  * Using a factory ensures that options normalization/destructuring runs inside
82
118
  * the try/catch so invalid runtime inputs fall back gracefully.
@@ -91,16 +127,19 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
91
127
  try {
92
128
  const resolvedArgs = typeof args === 'function' ? args() : args;
93
129
  const result = await this.callToolWithRetry(toolName, resolvedArgs);
130
+ // Inspect the result for structured error responses from the EP MCP server.
131
+ // The server may return isError: true with JSON content containing errorCode
132
+ // (e.g., INTERNAL_ERROR, UPSTREAM_500) instead of throwing an exception.
133
+ if (result.isError === true) {
134
+ return this._recordToolFailure(toolName, result.content?.[0]?.text ?? '', fallbackText);
135
+ }
94
136
  // Clear from failed tools on success
95
137
  this._failedTools.delete(toolName);
96
138
  return result;
97
139
  }
98
140
  catch (error) {
99
141
  const message = error instanceof Error ? error.message : String(error);
100
- const errorType = classifyToolError(message);
101
- this._failedTools.set(toolName, `${errorType}: ${message}`);
102
- console.warn(`⚠️ ${toolName} failed [${errorType}]:`, message);
103
- return { content: [{ type: 'text', text: fallbackText }] };
142
+ return this._recordToolFailure(toolName, message, fallbackText);
104
143
  }
105
144
  }
106
145
  /**
@@ -483,11 +483,18 @@ export class MCPConnection {
483
483
  const command = isJavaScriptFile ? process.execPath : this.serverPath;
484
484
  const args = isJavaScriptFile ? [this.serverPath] : [];
485
485
  // Ensure EP_REQUEST_TIMEOUT_MS is propagated to the MCP server subprocess.
486
- // The EP MCP server defaults to only 10 seconds; we need 90+ seconds for
487
- // slow EP API feed endpoints (events, procedures, documents, etc.).
486
+ // The EP MCP server defaults to only 10 seconds (v1.1.x) or 60 seconds (v1.2.x);
487
+ // we need 90+ seconds for slow EP API feed endpoints (events, procedures, documents, etc.).
488
488
  const childEnv = { ...process.env };
489
- if (!childEnv['EP_REQUEST_TIMEOUT_MS']) {
490
- childEnv['EP_REQUEST_TIMEOUT_MS'] = String(REQUEST_TIMEOUT_MS);
489
+ const effectiveTimeoutMs = childEnv['EP_REQUEST_TIMEOUT_MS']
490
+ ? Number(childEnv['EP_REQUEST_TIMEOUT_MS'])
491
+ : REQUEST_TIMEOUT_MS;
492
+ childEnv['EP_REQUEST_TIMEOUT_MS'] = String(effectiveTimeoutMs);
493
+ // Pass --timeout as CLI arg (highest precedence in EP MCP server).
494
+ // This guarantees the timeout is applied even when the env var is not
495
+ // read at module load time (e.g. due to import ordering in some versions).
496
+ if (!isJavaScriptFile) {
497
+ args.push('--timeout', String(effectiveTimeoutMs));
491
498
  }
492
499
  this.process = spawn(command, args, {
493
500
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -25,6 +25,8 @@ export interface MCPContentItem {
25
25
  /** MCP tool call result */
26
26
  export interface MCPToolResult {
27
27
  content?: MCPContentItem[] | undefined;
28
+ /** Set to `true` by the MCP server when the tool invocation produced an error response */
29
+ isError?: boolean | undefined;
28
30
  }
29
31
  /** JSON-RPC 2.0 request */
30
32
  export interface JSONRPCRequest {