euparliamentmonitor 0.9.19 → 0.9.21
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/README.md +2 -2
- package/package.json +4 -3
- package/scripts/aggregator/editorial-brief-resolver.d.ts +38 -0
- package/scripts/aggregator/editorial-brief-resolver.js +32 -0
- package/scripts/aggregator/generator/render-one.js +35 -0
- package/scripts/aggregator/html/localize-body.d.ts +32 -0
- package/scripts/aggregator/html/localize-body.js +69 -0
- package/scripts/aggregator/html/shell.d.ts +10 -0
- package/scripts/aggregator/html/shell.js +11 -1
- package/scripts/aggregator/markdown-renderer.d.ts +23 -24
- package/scripts/aggregator/markdown-renderer.js +39 -25
- package/scripts/aggregator/metadata/artifact-highlight.d.ts +15 -22
- package/scripts/aggregator/metadata/artifact-highlight.js +14 -230
- package/scripts/aggregator/metadata/artifact-walker.d.ts +34 -0
- package/scripts/aggregator/metadata/artifact-walker.js +177 -0
- package/scripts/aggregator/metadata/editorial-highlight.d.ts +15 -0
- package/scripts/aggregator/metadata/editorial-highlight.js +53 -0
- package/scripts/aggregator/metadata/priority-finding-highlight.js +7 -2
- package/scripts/aggregator/metadata/resolve-helpers.js +9 -3
- package/scripts/aggregator/metadata/text-utils.js +7 -0
- package/scripts/aggregator/metadata/translated-sibling.d.ts +23 -0
- package/scripts/aggregator/metadata/translated-sibling.js +39 -0
- package/scripts/aggregator/reader-guide/builder.js +3 -1
- package/scripts/aggregator/reader-guide/labels.d.ts +7 -0
- package/scripts/aggregator/reader-guide/labels.js +22 -0
- package/scripts/aggregator/reader-intelligence-guide.d.ts +1 -1
- package/scripts/aggregator/reader-intelligence-guide.js +1 -1
- package/scripts/aggregator/seo-entity-extractor.d.ts +45 -0
- package/scripts/aggregator/seo-entity-extractor.js +211 -0
- package/scripts/constants/articles/breaking-strings-central.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-central.js +105 -0
- package/scripts/constants/articles/breaking-strings-east.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-east.js +203 -0
- package/scripts/constants/articles/breaking-strings-nordic.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-nordic.js +252 -0
- package/scripts/constants/articles/breaking-strings-west.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-west.js +154 -0
- package/scripts/constants/articles/breaking.d.ts +0 -1
- package/scripts/constants/articles/breaking.js +9 -6
- package/scripts/constants/articles/dashboard/ar.d.ts +8 -0
- package/scripts/constants/articles/dashboard/ar.js +71 -0
- package/scripts/constants/articles/dashboard/da.d.ts +8 -0
- package/scripts/constants/articles/dashboard/da.js +71 -0
- package/scripts/constants/articles/dashboard/de.d.ts +8 -0
- package/scripts/constants/articles/dashboard/de.js +71 -0
- package/scripts/constants/articles/dashboard/en.d.ts +8 -0
- package/scripts/constants/articles/dashboard/en.js +71 -0
- package/scripts/constants/articles/dashboard/es.d.ts +8 -0
- package/scripts/constants/articles/dashboard/es.js +71 -0
- package/scripts/constants/articles/dashboard/fi.d.ts +8 -0
- package/scripts/constants/articles/dashboard/fi.js +71 -0
- package/scripts/constants/articles/dashboard/fr.d.ts +8 -0
- package/scripts/constants/articles/dashboard/fr.js +71 -0
- package/scripts/constants/articles/dashboard/he.d.ts +8 -0
- package/scripts/constants/articles/dashboard/he.js +71 -0
- package/scripts/constants/articles/dashboard/index.d.ts +7 -0
- package/scripts/constants/articles/dashboard/index.js +33 -0
- package/scripts/constants/articles/dashboard/ja.d.ts +8 -0
- package/scripts/constants/articles/dashboard/ja.js +71 -0
- package/scripts/constants/articles/dashboard/ko.d.ts +8 -0
- package/scripts/constants/articles/dashboard/ko.js +71 -0
- package/scripts/constants/articles/dashboard/nl.d.ts +8 -0
- package/scripts/constants/articles/dashboard/nl.js +71 -0
- package/scripts/constants/articles/dashboard/no.d.ts +8 -0
- package/scripts/constants/articles/dashboard/no.js +71 -0
- package/scripts/constants/articles/dashboard/sv.d.ts +8 -0
- package/scripts/constants/articles/dashboard/sv.js +71 -0
- package/scripts/constants/articles/dashboard/zh.d.ts +8 -0
- package/scripts/constants/articles/dashboard/zh.js +71 -0
- package/scripts/constants/articles/dashboard.d.ts +7 -2
- package/scripts/constants/articles/dashboard.js +4 -8
- package/scripts/constants/articles/deep-analysis/ar.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/ar.js +75 -0
- package/scripts/constants/articles/deep-analysis/da.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/da.js +75 -0
- package/scripts/constants/articles/deep-analysis/de.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/de.js +75 -0
- package/scripts/constants/articles/deep-analysis/en.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/en.js +75 -0
- package/scripts/constants/articles/deep-analysis/es.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/es.js +75 -0
- package/scripts/constants/articles/deep-analysis/fi.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/fi.js +75 -0
- package/scripts/constants/articles/deep-analysis/fr.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/fr.js +75 -0
- package/scripts/constants/articles/deep-analysis/he.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/he.js +75 -0
- package/scripts/constants/articles/deep-analysis/index.d.ts +7 -0
- package/scripts/constants/articles/deep-analysis/index.js +33 -0
- package/scripts/constants/articles/deep-analysis/ja.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/ja.js +75 -0
- package/scripts/constants/articles/deep-analysis/ko.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/ko.js +75 -0
- package/scripts/constants/articles/deep-analysis/nl.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/nl.js +75 -0
- package/scripts/constants/articles/deep-analysis/no.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/no.js +75 -0
- package/scripts/constants/articles/deep-analysis/sv.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/sv.js +75 -0
- package/scripts/constants/articles/deep-analysis/zh.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/zh.js +75 -0
- package/scripts/constants/articles/deep-analysis.d.ts +4 -3
- package/scripts/constants/articles/deep-analysis.js +3 -7
- package/scripts/constants/articles/localized-keywords-central.d.ts +8 -0
- package/scripts/constants/articles/localized-keywords-central.js +118 -0
- package/scripts/constants/articles/localized-keywords-nordic.d.ts +8 -0
- package/scripts/constants/articles/localized-keywords-nordic.js +303 -0
- package/scripts/constants/articles/localized-keywords.js +4 -2
- package/scripts/constants/articles/swot-builder-central.d.ts +8 -0
- package/scripts/constants/articles/swot-builder-central.js +90 -0
- package/scripts/constants/articles/swot-builder-nordic.d.ts +8 -0
- package/scripts/constants/articles/swot-builder-nordic.js +216 -0
- package/scripts/constants/articles/swot.js +4 -2
- package/scripts/constants/articles/week-ahead-eu.d.ts +12 -0
- package/scripts/constants/articles/week-ahead-eu.js +278 -0
- package/scripts/constants/articles/week-ahead-global.d.ts +12 -0
- package/scripts/constants/articles/week-ahead-global.js +278 -0
- package/scripts/constants/articles/week-ahead.d.ts +4 -7
- package/scripts/constants/articles/week-ahead.js +11 -535
- package/scripts/constants/world-bank/category-map-analysis.d.ts +9 -0
- package/scripts/constants/world-bank/category-map-analysis.js +204 -0
- package/scripts/constants/world-bank/category-map-legislative.d.ts +9 -0
- package/scripts/constants/world-bank/category-map-legislative.js +130 -0
- package/scripts/constants/world-bank/category-map-periodic.d.ts +9 -0
- package/scripts/constants/world-bank/category-map-periodic.js +176 -0
- package/scripts/constants/world-bank/category-map.d.ts +3 -26
- package/scripts/constants/world-bank/category-map.js +8 -501
- package/scripts/discover-untranslated-briefs.js +123 -4
- package/scripts/generators/news-indexes/per-language.js +21 -7
- package/scripts/generators/political-intelligence/html.js +39 -8
- package/scripts/generators/sitemap/html.js +25 -7
- package/scripts/mcp/ep/client.d.ts +0 -1
- package/scripts/mcp/ep/client.js +0 -65
- package/scripts/mcp/ep/error-classifier.d.ts +2 -2
- package/scripts/mcp/ep/error-classifier.js +2 -2
- package/scripts/mcp/ep/tools-list.d.ts +13 -0
- package/scripts/mcp/ep/tools-list.js +79 -0
- package/scripts/mcp/ep-mcp-client.d.ts +1 -0
- package/scripts/mcp/ep-mcp-client.js +1 -0
- package/scripts/mcp/imf/client.d.ts +3 -64
- package/scripts/mcp/imf/client.js +18 -207
- package/scripts/mcp/imf/http-transport.d.ts +92 -0
- package/scripts/mcp/imf/http-transport.js +232 -0
- package/scripts/mcp/transport/connection.d.ts +25 -53
- package/scripts/mcp/transport/connection.js +90 -250
- package/scripts/mcp/transport/process.d.ts +62 -0
- package/scripts/mcp/transport/process.js +147 -0
- package/scripts/mcp/transport/reconnect.d.ts +73 -0
- package/scripts/mcp/transport/reconnect.js +96 -0
- package/scripts/validate-brief-translations.js +122 -6
- package/scripts/constants/articles/breaking-strings-eu.d.ts +0 -7
- package/scripts/constants/articles/breaking-strings-global.d.ts +0 -7
- package/scripts/constants/articles/dashboard-builder-eu.d.ts +0 -7
- package/scripts/constants/articles/dashboard-builder-global.d.ts +0 -7
- package/scripts/constants/articles/deep-analysis-strings-eu.d.ts +0 -7
- package/scripts/constants/articles/deep-analysis-strings-global.d.ts +0 -7
- package/scripts/constants/articles/localized-keywords-eu.d.ts +0 -7
- package/scripts/constants/articles/swot-builder-eu.d.ts +0 -7
|
@@ -77,40 +77,34 @@ export declare class MCPConnection {
|
|
|
77
77
|
connected: boolean;
|
|
78
78
|
};
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
* Respects `Retry-After` carried by {@link MCPRateLimitError}; otherwise uses
|
|
82
|
-
* exponential back-off (`connectionRetryDelay * 2^(attempt - 1)`).
|
|
83
|
-
*
|
|
84
|
-
* @param error - The error from the failed attempt
|
|
85
|
-
* @param attempt - Number of attempts made so far (1-indexed)
|
|
86
|
-
* @returns Delay in milliseconds
|
|
80
|
+
* Connect to the MCP server with retry logic
|
|
87
81
|
*/
|
|
88
|
-
|
|
82
|
+
connect(): Promise<void>;
|
|
89
83
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* before the next attempt. Throws when the maximum attempts have been exhausted.
|
|
84
|
+
* Advance the attempt counter and compute the next retry delay.
|
|
85
|
+
* Throws immediately for non-retriable errors or when exhausted.
|
|
93
86
|
*
|
|
94
87
|
* @param error - The error from the failed attempt
|
|
95
88
|
* @returns Delay in milliseconds to wait before the next attempt
|
|
96
89
|
*/
|
|
97
|
-
private
|
|
90
|
+
private _nextConnectionDelay;
|
|
98
91
|
/**
|
|
99
|
-
*
|
|
92
|
+
* Build a {@link GatewayContext} adapter for gateway.ts helpers.
|
|
93
|
+
*
|
|
94
|
+
* @returns Context adapter for gateway.ts helpers
|
|
100
95
|
*/
|
|
101
|
-
|
|
96
|
+
private _gatewayContext;
|
|
102
97
|
/**
|
|
103
|
-
* Build a {@link
|
|
98
|
+
* Build a {@link SpawnContext} adapter for process.ts spawn helpers.
|
|
104
99
|
*
|
|
105
|
-
* @returns
|
|
106
|
-
* gateway helpers need (URL, API key, session ID accessors, request-ID
|
|
107
|
-
* counter, connection-state setter).
|
|
100
|
+
* @returns Context adapter for stdio spawn helpers
|
|
108
101
|
*/
|
|
109
|
-
private
|
|
110
|
-
/** Attempt a single connection via MCP Gateway (HTTP transport). */
|
|
111
|
-
private _attemptGatewayConnection;
|
|
102
|
+
private _spawnContext;
|
|
112
103
|
/**
|
|
113
|
-
* Attempt a single connection via stdio (spawns server binary)
|
|
104
|
+
* Attempt a single connection via stdio (spawns server binary).
|
|
105
|
+
* Delegates to {@link attemptStdioConnection} in process.ts.
|
|
106
|
+
*
|
|
107
|
+
* @returns Resolves when the spawn has completed (or rejects on spawn failure)
|
|
114
108
|
*/
|
|
115
109
|
private _attemptConnection;
|
|
116
110
|
/**
|
|
@@ -118,20 +112,12 @@ export declare class MCPConnection {
|
|
|
118
112
|
*/
|
|
119
113
|
disconnect(): void;
|
|
120
114
|
/**
|
|
121
|
-
* Handle incoming messages from MCP server (stdio mode only)
|
|
115
|
+
* Handle incoming messages from MCP server (stdio mode only).
|
|
116
|
+
* Delegates to {@link handleIncomingMessage} in process.ts.
|
|
122
117
|
*
|
|
123
118
|
* @param line - JSON message line from server
|
|
124
119
|
*/
|
|
125
120
|
handleMessage(line: string): void;
|
|
126
|
-
/**
|
|
127
|
-
* Send a request via MCP Gateway (HTTP transport). Delegates to the
|
|
128
|
-
* gateway helper module.
|
|
129
|
-
*
|
|
130
|
-
* @param method - RPC method name
|
|
131
|
-
* @param params - Method parameters
|
|
132
|
-
* @returns Server response result payload
|
|
133
|
-
*/
|
|
134
|
-
private _sendGatewayRequest;
|
|
135
121
|
/**
|
|
136
122
|
* Send a request to the MCP server
|
|
137
123
|
*
|
|
@@ -155,32 +141,18 @@ export declare class MCPConnection {
|
|
|
155
141
|
*/
|
|
156
142
|
callTool(name: string, args?: object): Promise<MCPToolResult>;
|
|
157
143
|
/**
|
|
158
|
-
*
|
|
159
|
-
* Concurrent callers await the same in-flight reconnect instead of no-oping,
|
|
160
|
-
* ensuring the connection is re-established before all waiting callers continue.
|
|
161
|
-
*
|
|
162
|
-
* @returns Promise that resolves when reconnection succeeds or all attempts are exhausted
|
|
163
|
-
*/
|
|
164
|
-
private reconnect;
|
|
165
|
-
/**
|
|
166
|
-
* Internal reconnect helper.
|
|
167
|
-
*
|
|
168
|
-
* Waits for an exponential back-off delay derived from the current
|
|
169
|
-
* `reconnectCount`, then delegates to `connect()` which handles its own
|
|
170
|
-
* retry loop. This avoids composing N×N attempts.
|
|
144
|
+
* Build a {@link ReconnectOps} adapter for reconnect.ts helpers.
|
|
171
145
|
*
|
|
172
|
-
* @returns
|
|
146
|
+
* @returns Context adapter for reconnect/retry helpers
|
|
173
147
|
*/
|
|
174
|
-
private
|
|
148
|
+
private _reconnectOps;
|
|
175
149
|
/**
|
|
176
|
-
*
|
|
150
|
+
* Attempt to reconnect to the MCP server.
|
|
151
|
+
* Deduplicates concurrent calls — only one reconnect runs at a time.
|
|
177
152
|
*
|
|
178
|
-
* @
|
|
179
|
-
* @param attempt - Zero-based current attempt index
|
|
180
|
-
* @param retries - Total retry count
|
|
181
|
-
* @returns Promise that resolves after logging, optional reconnect, and inter-retry delay
|
|
153
|
+
* @returns Resolves when reconnect succeeds or all attempts are exhausted
|
|
182
154
|
*/
|
|
183
|
-
|
|
155
|
+
reconnect(): Promise<void>;
|
|
184
156
|
/**
|
|
185
157
|
* Call an MCP tool with automatic retry on timeout or connection loss.
|
|
186
158
|
* Reconnects automatically if the connection was lost between attempts.
|
|
@@ -1,45 +1,9 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
/**
|
|
4
|
-
* @module MCP/transport/connection
|
|
5
|
-
* @description MCPConnection — JSON-RPC 2.0 transport over stdio or HTTP gateway.
|
|
6
|
-
* Supports two transport modes:
|
|
7
|
-
* - **stdio**: Spawns the EP MCP server binary as a child process (default)
|
|
8
|
-
* - **gateway**: Connects to an MCP Gateway via HTTP (for agentic workflow environments)
|
|
9
|
-
*
|
|
10
|
-
* Gateway mode is activated when `EP_MCP_GATEWAY_URL` env var is set or
|
|
11
|
-
* `gatewayUrl` is provided in options.
|
|
12
|
-
*/
|
|
13
|
-
import { spawn } from 'child_process';
|
|
14
|
-
import { resolve, dirname } from 'path';
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
16
3
|
import { MCPRateLimitError, MCPSessionExpiredError } from './errors.js';
|
|
17
|
-
import { isRetriableError, RECONNECT_MAX_DELAY_MS } from './retry-policy.js';
|
|
18
4
|
import { attemptGatewayConnection, sendGatewayRequest } from './gateway.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/** Platform-specific binary filename (Windows uses .cmd shim) */
|
|
22
|
-
const BINARY_FILE = process.platform === 'win32' ? `${BINARY_NAME}.cmd` : BINARY_NAME;
|
|
23
|
-
/** Default binary resolved from node_modules/.bin relative to this file's compiled location */
|
|
24
|
-
const DEFAULT_SERVER_BINARY = resolve(dirname(fileURLToPath(import.meta.url)), `../../../node_modules/.bin/${BINARY_FILE}`);
|
|
25
|
-
/** Default request timeout in milliseconds — EU Parliament API responses commonly take 30-120+ seconds for large datasets */
|
|
26
|
-
const DEFAULT_REQUEST_TIMEOUT_MS = 180_000;
|
|
27
|
-
/**
|
|
28
|
-
* Effective request timeout, configurable via `EP_REQUEST_TIMEOUT_MS` env var.
|
|
29
|
-
* This keeps the client-side timeout aligned with the MCP server timeout set
|
|
30
|
-
* in workflow configs and copilot-mcp.json.
|
|
31
|
-
*/
|
|
32
|
-
const REQUEST_TIMEOUT_MS = (() => {
|
|
33
|
-
const envVal = process.env['EP_REQUEST_TIMEOUT_MS'];
|
|
34
|
-
if (envVal) {
|
|
35
|
-
const parsed = Number(envVal);
|
|
36
|
-
if (!Number.isNaN(parsed) && parsed > 0)
|
|
37
|
-
return parsed;
|
|
38
|
-
}
|
|
39
|
-
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
40
|
-
})();
|
|
41
|
-
/** Connection startup delay in milliseconds */
|
|
42
|
-
const CONNECTION_STARTUP_DELAY_MS = 500;
|
|
5
|
+
import { REQUEST_TIMEOUT_MS, DEFAULT_SERVER_BINARY, attemptStdioConnection, handleIncomingMessage, } from './process.js';
|
|
6
|
+
import { performReconnect, runWithRetry } from './reconnect.js';
|
|
43
7
|
/**
|
|
44
8
|
* Base MCP connection managing JSON-RPC 2.0 transport over stdio or HTTP gateway.
|
|
45
9
|
* Extended by domain-specific clients to add tool wrapper methods.
|
|
@@ -148,40 +112,6 @@ export class MCPConnection {
|
|
|
148
112
|
connected: this.connected,
|
|
149
113
|
};
|
|
150
114
|
}
|
|
151
|
-
/**
|
|
152
|
-
* Compute the delay before the next connection attempt.
|
|
153
|
-
* Respects `Retry-After` carried by {@link MCPRateLimitError}; otherwise uses
|
|
154
|
-
* exponential back-off (`connectionRetryDelay * 2^(attempt - 1)`).
|
|
155
|
-
*
|
|
156
|
-
* @param error - The error from the failed attempt
|
|
157
|
-
* @param attempt - Number of attempts made so far (1-indexed)
|
|
158
|
-
* @returns Delay in milliseconds
|
|
159
|
-
*/
|
|
160
|
-
_computeConnectionDelay(error, attempt) {
|
|
161
|
-
if (error instanceof MCPRateLimitError && error.retryAfterMs > 0) {
|
|
162
|
-
return error.retryAfterMs;
|
|
163
|
-
}
|
|
164
|
-
return this.connectionRetryDelay * Math.pow(2, attempt - 1);
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Handle a single connection attempt error: re-throw immediately for non-retriable errors
|
|
168
|
-
* (e.g. session expiry), increment the attempt counter, and return the delay to wait
|
|
169
|
-
* before the next attempt. Throws when the maximum attempts have been exhausted.
|
|
170
|
-
*
|
|
171
|
-
* @param error - The error from the failed attempt
|
|
172
|
-
* @returns Delay in milliseconds to wait before the next attempt
|
|
173
|
-
*/
|
|
174
|
-
_handleConnectionAttemptError(error) {
|
|
175
|
-
if (error instanceof MCPSessionExpiredError) {
|
|
176
|
-
throw error;
|
|
177
|
-
}
|
|
178
|
-
this.connectionAttempts++;
|
|
179
|
-
if (this.connectionAttempts >= this.maxConnectionAttempts) {
|
|
180
|
-
console.error('❌ Failed to connect to MCP server after', this.maxConnectionAttempts, 'attempts');
|
|
181
|
-
throw error;
|
|
182
|
-
}
|
|
183
|
-
return this._computeConnectionDelay(error, this.connectionAttempts);
|
|
184
|
-
}
|
|
185
115
|
/**
|
|
186
116
|
* Connect to the MCP server with retry logic
|
|
187
117
|
*/
|
|
@@ -200,7 +130,7 @@ export class MCPConnection {
|
|
|
200
130
|
while (this.connectionAttempts < this.maxConnectionAttempts) {
|
|
201
131
|
try {
|
|
202
132
|
if (this.gatewayUrl) {
|
|
203
|
-
await this.
|
|
133
|
+
await attemptGatewayConnection(this._gatewayContext());
|
|
204
134
|
}
|
|
205
135
|
else {
|
|
206
136
|
await this._attemptConnection();
|
|
@@ -209,18 +139,36 @@ export class MCPConnection {
|
|
|
209
139
|
return;
|
|
210
140
|
}
|
|
211
141
|
catch (error) {
|
|
212
|
-
const delay = this.
|
|
142
|
+
const delay = this._nextConnectionDelay(error);
|
|
213
143
|
console.warn(`⚠️ Connection attempt ${this.connectionAttempts} failed. Retrying in ${delay}ms...`);
|
|
214
144
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
215
145
|
}
|
|
216
146
|
}
|
|
217
147
|
}
|
|
218
148
|
/**
|
|
219
|
-
*
|
|
149
|
+
* Advance the attempt counter and compute the next retry delay.
|
|
150
|
+
* Throws immediately for non-retriable errors or when exhausted.
|
|
220
151
|
*
|
|
221
|
-
* @
|
|
222
|
-
*
|
|
223
|
-
|
|
152
|
+
* @param error - The error from the failed attempt
|
|
153
|
+
* @returns Delay in milliseconds to wait before the next attempt
|
|
154
|
+
*/
|
|
155
|
+
_nextConnectionDelay(error) {
|
|
156
|
+
if (error instanceof MCPSessionExpiredError)
|
|
157
|
+
throw error;
|
|
158
|
+
this.connectionAttempts++;
|
|
159
|
+
if (this.connectionAttempts >= this.maxConnectionAttempts) {
|
|
160
|
+
console.error('❌ Failed to connect to MCP server after', this.maxConnectionAttempts, 'attempts');
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
if (error instanceof MCPRateLimitError && error.retryAfterMs > 0) {
|
|
164
|
+
return error.retryAfterMs;
|
|
165
|
+
}
|
|
166
|
+
return this.connectionRetryDelay * Math.pow(2, this.connectionAttempts - 1);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Build a {@link GatewayContext} adapter for gateway.ts helpers.
|
|
170
|
+
*
|
|
171
|
+
* @returns Context adapter for gateway.ts helpers
|
|
224
172
|
*/
|
|
225
173
|
_gatewayContext() {
|
|
226
174
|
return {
|
|
@@ -237,72 +185,39 @@ export class MCPConnection {
|
|
|
237
185
|
},
|
|
238
186
|
};
|
|
239
187
|
}
|
|
240
|
-
/** Attempt a single connection via MCP Gateway (HTTP transport). */
|
|
241
|
-
async _attemptGatewayConnection() {
|
|
242
|
-
await attemptGatewayConnection(this._gatewayContext());
|
|
243
|
-
}
|
|
244
188
|
/**
|
|
245
|
-
*
|
|
189
|
+
* Build a {@link SpawnContext} adapter for process.ts spawn helpers.
|
|
190
|
+
*
|
|
191
|
+
* @returns Context adapter for stdio spawn helpers
|
|
246
192
|
*/
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
this.process = spawn(command, args, {
|
|
261
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
262
|
-
env: childEnv,
|
|
263
|
-
});
|
|
264
|
-
let buffer = '';
|
|
265
|
-
let startupError = null;
|
|
266
|
-
this.process.stdout?.on('data', (data) => {
|
|
267
|
-
buffer += data.toString();
|
|
268
|
-
const lines = buffer.split('\n');
|
|
269
|
-
buffer = lines.pop() ?? '';
|
|
270
|
-
for (const line of lines) {
|
|
271
|
-
if (line.trim()) {
|
|
272
|
-
this.handleMessage(line);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
this.process.stderr?.on('data', (data) => {
|
|
277
|
-
const message = data.toString().trim();
|
|
278
|
-
if (message) {
|
|
279
|
-
console.error(`MCP Server: ${message}`);
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
this.process.on('close', (code) => {
|
|
283
|
-
console.log(`MCP Server exited with code ${code}`);
|
|
284
|
-
this.connected = false;
|
|
193
|
+
_spawnContext() {
|
|
194
|
+
return {
|
|
195
|
+
serverPath: this.serverPath,
|
|
196
|
+
serverLabel: this.serverLabel,
|
|
197
|
+
requestTimeoutMs: REQUEST_TIMEOUT_MS,
|
|
198
|
+
setProcess: (p) => {
|
|
199
|
+
this.process = p;
|
|
200
|
+
},
|
|
201
|
+
setConnected: (v) => {
|
|
202
|
+
this.connected = v;
|
|
203
|
+
},
|
|
204
|
+
onMessage: (line) => this.handleMessage(line),
|
|
205
|
+
rejectAllPending: (msg) => {
|
|
285
206
|
for (const [id, { reject }] of this.pendingRequests.entries()) {
|
|
286
|
-
reject(new Error(
|
|
207
|
+
reject(new Error(msg));
|
|
287
208
|
this.pendingRequests.delete(id);
|
|
288
209
|
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
catch (error) {
|
|
302
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
303
|
-
console.error('❌ Failed to spawn MCP server:', message);
|
|
304
|
-
throw error;
|
|
305
|
-
}
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Attempt a single connection via stdio (spawns server binary).
|
|
215
|
+
* Delegates to {@link attemptStdioConnection} in process.ts.
|
|
216
|
+
*
|
|
217
|
+
* @returns Resolves when the spawn has completed (or rejects on spawn failure)
|
|
218
|
+
*/
|
|
219
|
+
async _attemptConnection() {
|
|
220
|
+
return attemptStdioConnection(this._spawnContext());
|
|
306
221
|
}
|
|
307
222
|
/**
|
|
308
223
|
* Disconnect from the MCP server
|
|
@@ -316,49 +231,13 @@ export class MCPConnection {
|
|
|
316
231
|
this.mcpSessionId = null;
|
|
317
232
|
}
|
|
318
233
|
/**
|
|
319
|
-
* Handle incoming messages from MCP server (stdio mode only)
|
|
234
|
+
* Handle incoming messages from MCP server (stdio mode only).
|
|
235
|
+
* Delegates to {@link handleIncomingMessage} in process.ts.
|
|
320
236
|
*
|
|
321
237
|
* @param line - JSON message line from server
|
|
322
238
|
*/
|
|
323
239
|
handleMessage(line) {
|
|
324
|
-
|
|
325
|
-
const message = JSON.parse(line);
|
|
326
|
-
if (message.id !== null && message.id !== undefined && this.pendingRequests.has(message.id)) {
|
|
327
|
-
const pending = this.pendingRequests.get(message.id);
|
|
328
|
-
if (pending) {
|
|
329
|
-
this.pendingRequests.delete(message.id);
|
|
330
|
-
if (message.error) {
|
|
331
|
-
pending.reject(new Error(message.error.message ?? 'MCP server error'));
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
pending.resolve(message.result);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
this.pendingRequests.delete(message.id);
|
|
339
|
-
console.error(`MCP pending request ${String(message.id)} vanished before handling`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
else if ((message.id === null || message.id === undefined) && message.method) {
|
|
343
|
-
console.log(`MCP Notification: ${message.method}`);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
catch (error) {
|
|
347
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
348
|
-
console.error('Error parsing MCP message:', errorMessage);
|
|
349
|
-
console.error('Problematic line:', line);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Send a request via MCP Gateway (HTTP transport). Delegates to the
|
|
354
|
-
* gateway helper module.
|
|
355
|
-
*
|
|
356
|
-
* @param method - RPC method name
|
|
357
|
-
* @param params - Method parameters
|
|
358
|
-
* @returns Server response result payload
|
|
359
|
-
*/
|
|
360
|
-
async _sendGatewayRequest(method, params = {}) {
|
|
361
|
-
return sendGatewayRequest(method, params, this._gatewayContext());
|
|
240
|
+
handleIncomingMessage(line, this.pendingRequests);
|
|
362
241
|
}
|
|
363
242
|
/**
|
|
364
243
|
* Send a request to the MCP server
|
|
@@ -372,7 +251,7 @@ export class MCPConnection {
|
|
|
372
251
|
throw new Error('Not connected to MCP server');
|
|
373
252
|
}
|
|
374
253
|
if (this.gatewayUrl) {
|
|
375
|
-
return (await
|
|
254
|
+
return (await sendGatewayRequest(method, params, this._gatewayContext()));
|
|
376
255
|
}
|
|
377
256
|
const id = ++this.requestId;
|
|
378
257
|
const request = {
|
|
@@ -418,68 +297,43 @@ export class MCPConnection {
|
|
|
418
297
|
return this.sendRequest('tools/call', { name, arguments: args });
|
|
419
298
|
}
|
|
420
299
|
/**
|
|
421
|
-
*
|
|
422
|
-
* Concurrent callers await the same in-flight reconnect instead of no-oping,
|
|
423
|
-
* ensuring the connection is re-established before all waiting callers continue.
|
|
424
|
-
*
|
|
425
|
-
* @returns Promise that resolves when reconnection succeeds or all attempts are exhausted
|
|
426
|
-
*/
|
|
427
|
-
async reconnect() {
|
|
428
|
-
if (this.reconnectingPromise !== null) {
|
|
429
|
-
return this.reconnectingPromise;
|
|
430
|
-
}
|
|
431
|
-
this.reconnectCount++;
|
|
432
|
-
console.log(`🔄 Reconnecting to ${this.serverLabel} (attempt ${this.reconnectCount})...`);
|
|
433
|
-
this.reconnectingPromise = this._doReconnect();
|
|
434
|
-
try {
|
|
435
|
-
await this.reconnectingPromise;
|
|
436
|
-
}
|
|
437
|
-
finally {
|
|
438
|
-
this.reconnectingPromise = null;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* Internal reconnect helper.
|
|
443
|
-
*
|
|
444
|
-
* Waits for an exponential back-off delay derived from the current
|
|
445
|
-
* `reconnectCount`, then delegates to `connect()` which handles its own
|
|
446
|
-
* retry loop. This avoids composing N×N attempts.
|
|
300
|
+
* Build a {@link ReconnectOps} adapter for reconnect.ts helpers.
|
|
447
301
|
*
|
|
448
|
-
* @returns
|
|
302
|
+
* @returns Context adapter for reconnect/retry helpers
|
|
449
303
|
*/
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
this.connected
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
304
|
+
_reconnectOps() {
|
|
305
|
+
return {
|
|
306
|
+
maxConnectionAttempts: this.maxConnectionAttempts,
|
|
307
|
+
connectionRetryDelay: this.connectionRetryDelay,
|
|
308
|
+
serverLabel: this.serverLabel,
|
|
309
|
+
callTool: (n, a) => this.callTool(n, a),
|
|
310
|
+
isConnected: () => this.connected,
|
|
311
|
+
connect: () => this.connect(),
|
|
312
|
+
setConnected: (v) => {
|
|
313
|
+
this.connected = v;
|
|
314
|
+
},
|
|
315
|
+
getReconnectCount: () => this.reconnectCount,
|
|
316
|
+
setReconnectCount: (n) => {
|
|
317
|
+
this.reconnectCount = n;
|
|
318
|
+
},
|
|
319
|
+
getReconnectingPromise: () => this.reconnectingPromise,
|
|
320
|
+
setReconnectingPromise: (p) => {
|
|
321
|
+
this.reconnectingPromise = p;
|
|
322
|
+
},
|
|
323
|
+
getTimeoutCount: () => this.timeoutCount,
|
|
324
|
+
setTimeoutCount: (n) => {
|
|
325
|
+
this.timeoutCount = n;
|
|
326
|
+
},
|
|
327
|
+
};
|
|
462
328
|
}
|
|
463
329
|
/**
|
|
464
|
-
*
|
|
330
|
+
* Attempt to reconnect to the MCP server.
|
|
331
|
+
* Deduplicates concurrent calls — only one reconnect runs at a time.
|
|
465
332
|
*
|
|
466
|
-
* @
|
|
467
|
-
* @param attempt - Zero-based current attempt index
|
|
468
|
-
* @param retries - Total retry count
|
|
469
|
-
* @returns Promise that resolves after logging, optional reconnect, and inter-retry delay
|
|
333
|
+
* @returns Resolves when reconnect succeeds or all attempts are exhausted
|
|
470
334
|
*/
|
|
471
|
-
async
|
|
472
|
-
|
|
473
|
-
this.timeoutCount++;
|
|
474
|
-
console.warn(`⏱️ Request timeout (total: ${this.timeoutCount}), retrying ${attempt + 1}/${retries}...`);
|
|
475
|
-
}
|
|
476
|
-
else {
|
|
477
|
-
console.warn(`⚠️ Request failed, retrying ${attempt + 1}/${retries}: ${lastError.message}`);
|
|
478
|
-
}
|
|
479
|
-
if (!this.connected) {
|
|
480
|
-
await this.reconnect();
|
|
481
|
-
}
|
|
482
|
-
await new Promise((r) => setTimeout(r, this.connectionRetryDelay * (attempt + 1)));
|
|
335
|
+
async reconnect() {
|
|
336
|
+
return performReconnect(this._reconnectOps());
|
|
483
337
|
}
|
|
484
338
|
/**
|
|
485
339
|
* Call an MCP tool with automatic retry on timeout or connection loss.
|
|
@@ -499,21 +353,7 @@ export class MCPConnection {
|
|
|
499
353
|
if (retries < 0) {
|
|
500
354
|
throw new RangeError(`maxRetries must be >= 0, received ${retries}`);
|
|
501
355
|
}
|
|
502
|
-
|
|
503
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
504
|
-
try {
|
|
505
|
-
return await this.callTool(name, args);
|
|
506
|
-
}
|
|
507
|
-
catch (error) {
|
|
508
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
509
|
-
if (!isRetriableError(lastError))
|
|
510
|
-
throw lastError;
|
|
511
|
-
if (attempt === retries)
|
|
512
|
-
break;
|
|
513
|
-
await this._handleRetryAttempt(lastError, attempt, retries);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
throw lastError;
|
|
356
|
+
return runWithRetry(name, args, retries, this._reconnectOps());
|
|
517
357
|
}
|
|
518
358
|
}
|
|
519
359
|
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module MCP/transport/process
|
|
3
|
+
* @description Stdio process spawn / teardown helpers and JSON-RPC message routing
|
|
4
|
+
* for the MCPConnection stdio transport.
|
|
5
|
+
*
|
|
6
|
+
* Extracted from `connection.ts` to keep individual file sizes under 400 LOC.
|
|
7
|
+
* Operates on an explicit {@link SpawnContext} adapter rather than `this`.
|
|
8
|
+
*/
|
|
9
|
+
import { type ChildProcess } from 'child_process';
|
|
10
|
+
import type { PendingRequest } from '../../types/index.js';
|
|
11
|
+
/** npm binary name for the European Parliament MCP server */
|
|
12
|
+
export declare const BINARY_NAME = "european-parliament-mcp-server";
|
|
13
|
+
/** Platform-specific binary filename (Windows uses .cmd shim) */
|
|
14
|
+
export declare const BINARY_FILE: string;
|
|
15
|
+
/** Default binary resolved from node_modules/.bin relative to this file's compiled location */
|
|
16
|
+
export declare const DEFAULT_SERVER_BINARY: string;
|
|
17
|
+
/** Default request timeout in milliseconds — EU Parliament API responses commonly take 30-120+ seconds for large datasets */
|
|
18
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 180000;
|
|
19
|
+
/**
|
|
20
|
+
* Effective request timeout, configurable via `EP_REQUEST_TIMEOUT_MS` env var.
|
|
21
|
+
* This keeps the client-side timeout aligned with the MCP server timeout set
|
|
22
|
+
* in workflow configs and copilot-mcp.json.
|
|
23
|
+
*/
|
|
24
|
+
export declare const REQUEST_TIMEOUT_MS: number;
|
|
25
|
+
/** Connection startup delay in milliseconds */
|
|
26
|
+
export declare const CONNECTION_STARTUP_DELAY_MS = 500;
|
|
27
|
+
/**
|
|
28
|
+
* Adapter passed by {@link MCPConnection} to stdio spawn helpers.
|
|
29
|
+
* Lets the helpers read the few connection-level fields they need and
|
|
30
|
+
* mutate process state without pulling in the whole connection class.
|
|
31
|
+
*/
|
|
32
|
+
export interface SpawnContext {
|
|
33
|
+
/** Resolved path to the server binary or JS entry-point */
|
|
34
|
+
readonly serverPath: string;
|
|
35
|
+
/** Human-readable label for log messages */
|
|
36
|
+
readonly serverLabel: string;
|
|
37
|
+
/** Per-request timeout (milliseconds) */
|
|
38
|
+
readonly requestTimeoutMs: number;
|
|
39
|
+
/** Store or clear the spawned child process */
|
|
40
|
+
readonly setProcess: (p: ChildProcess | null) => void;
|
|
41
|
+
/** Mark the connection as (dis)connected */
|
|
42
|
+
readonly setConnected: (v: boolean) => void;
|
|
43
|
+
/** Route an incoming newline-delimited JSON-RPC message line */
|
|
44
|
+
readonly onMessage: (line: string) => void;
|
|
45
|
+
/** Reject and clear all in-flight pending requests with an error */
|
|
46
|
+
readonly rejectAllPending: (message: string) => void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Attempt a single connection via stdio (spawns server binary).
|
|
50
|
+
*
|
|
51
|
+
* @param ctx - Spawn context adapter from MCPConnection
|
|
52
|
+
*/
|
|
53
|
+
export declare function attemptStdioConnection(ctx: SpawnContext): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Handle an incoming newline-delimited JSON-RPC message from the server.
|
|
56
|
+
* Routes responses to matching in-flight pending requests; logs notifications.
|
|
57
|
+
*
|
|
58
|
+
* @param line - Single JSON-RPC message line (no trailing newline)
|
|
59
|
+
* @param pending - In-flight request map maintained by {@link MCPConnection}
|
|
60
|
+
*/
|
|
61
|
+
export declare function handleIncomingMessage(line: string, pending: Map<number, PendingRequest>): void;
|
|
62
|
+
//# sourceMappingURL=process.d.ts.map
|