euparliamentmonitor 0.8.30 → 0.8.32
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 +1 -1
- package/package.json +4 -4
- package/scripts/generators/motions-content.js +6 -2
- package/scripts/generators/news-enhanced.js +4 -3
- package/scripts/generators/pipeline/fetch-stage.js +9 -10
- package/scripts/generators/sitemap.js +4 -4
- package/scripts/generators/strategies/committee-reports-strategy.js +1 -1
- package/scripts/mcp/ep-mcp-client.d.ts +15 -19
- package/scripts/mcp/ep-mcp-client.js +29 -66
- package/scripts/mcp/mcp-connection.js +20 -7
- package/scripts/types/mcp.d.ts +63 -31
- package/scripts/utils/content-validator.js +436 -1
- package/scripts/utils/file-utils.js +11 -7
- package/scripts/utils/political-risk-assessment.js +3 -0
- package/scripts/utils/world-bank-data.js +3 -3
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ import {
|
|
|
124
124
|
|
|
125
125
|
**MCP Server Integration**: The project uses the
|
|
126
126
|
[European-Parliament-MCP-Server](https://github.com/Hack23/European-Parliament-MCP-Server)
|
|
127
|
-
v1.2.
|
|
127
|
+
v1.2.8 for accessing real EU Parliament data via the Model Context Protocol.
|
|
128
128
|
|
|
129
129
|
- **MCP Server Status**: ✅ Fully operational — 60+ EP data tools available
|
|
130
130
|
(feeds, direct lookups, analytical tools, intelligence correlation)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "euparliamentmonitor",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "European Parliament Intelligence Platform - Monitor political activity with systematic transparency",
|
|
6
6
|
"main": "scripts/index.js",
|
|
@@ -152,14 +152,14 @@
|
|
|
152
152
|
"eslint-config-prettier": "10.1.8",
|
|
153
153
|
"eslint-plugin-jsdoc": "62.9.0",
|
|
154
154
|
"eslint-plugin-security": "4.0.0",
|
|
155
|
-
"eslint-plugin-sonarjs": "4.0.
|
|
155
|
+
"eslint-plugin-sonarjs": "4.0.3",
|
|
156
156
|
"happy-dom": "20.9.0",
|
|
157
157
|
"htmlhint": "1.9.2",
|
|
158
158
|
"husky": "9.1.7",
|
|
159
159
|
"jscpd": "4.0.9",
|
|
160
160
|
"lint-staged": "16.4.0",
|
|
161
161
|
"papaparse": "5.5.3",
|
|
162
|
-
"prettier": "3.8.
|
|
162
|
+
"prettier": "3.8.3",
|
|
163
163
|
"ts-api-utils": "2.5.0",
|
|
164
164
|
"tsx": "4.21.0",
|
|
165
165
|
"typedoc": "0.28.19",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"node": ">=25"
|
|
171
171
|
},
|
|
172
172
|
"dependencies": {
|
|
173
|
-
"european-parliament-mcp-server": "1.2.
|
|
173
|
+
"european-parliament-mcp-server": "1.2.8"
|
|
174
174
|
},
|
|
175
175
|
"optionalDependencies": {
|
|
176
176
|
"worldbank-mcp": "1.0.1"
|
|
@@ -349,9 +349,13 @@ export function buildAdoptedTextsSection(adoptedTexts, language) {
|
|
|
349
349
|
if (adoptedTexts.length === 0)
|
|
350
350
|
return '';
|
|
351
351
|
const heading = ADOPTED_TEXTS_HEADINGS[language] ?? ADOPTED_TEXTS_HEADINGS['en'] ?? 'Recently Adopted Texts';
|
|
352
|
-
const countFn = ADOPTED_TEXTS_COUNT_STRINGS[language] ??
|
|
352
|
+
const countFn = ADOPTED_TEXTS_COUNT_STRINGS[language] ??
|
|
353
|
+
ADOPTED_TEXTS_COUNT_STRINGS['en'] ??
|
|
354
|
+
((n) => `${n} adopted texts`);
|
|
353
355
|
const countText = countFn(adoptedTexts.length);
|
|
354
|
-
const unknownDate = ADOPTED_TEXTS_DATE_UNKNOWN_STRINGS[language] ??
|
|
356
|
+
const unknownDate = ADOPTED_TEXTS_DATE_UNKNOWN_STRINGS[language] ??
|
|
357
|
+
ADOPTED_TEXTS_DATE_UNKNOWN_STRINGS['en'] ??
|
|
358
|
+
'Unknown date';
|
|
355
359
|
// Group by date, sort most recent first
|
|
356
360
|
const byDate = new Map();
|
|
357
361
|
for (const item of adoptedTexts) {
|
|
@@ -118,8 +118,9 @@ let languagesInput = languagesArg
|
|
|
118
118
|
? (languagesArg.split(ARG_SEPARATOR)[1] ?? '').trim().toLowerCase()
|
|
119
119
|
: 'en';
|
|
120
120
|
// Expand presets
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
const presetLanguages = LANGUAGE_PRESETS[languagesInput];
|
|
122
|
+
if (presetLanguages) {
|
|
123
|
+
languagesInput = presetLanguages.join(',');
|
|
123
124
|
}
|
|
124
125
|
const languages = languagesInput
|
|
125
126
|
.split(',')
|
|
@@ -402,7 +403,7 @@ function wireAIMetadata() {
|
|
|
402
403
|
*/
|
|
403
404
|
export function computeDedupSuffix(articleTypes, analysisDir) {
|
|
404
405
|
const baseSlugNoRun = deriveArticleTypeSlug(articleTypes.filter((t) => VALID_ARTICLE_CATEGORIES.includes(t)));
|
|
405
|
-
const rawSuffix = analysisDir
|
|
406
|
+
const rawSuffix = analysisDir?.startsWith(baseSlugNoRun)
|
|
406
407
|
? analysisDir.slice(baseSlugNoRun.length)
|
|
407
408
|
: '';
|
|
408
409
|
// Suffix validation patterns for dedup suffix extraction.
|
|
@@ -427,17 +427,17 @@ export async function fetchWeekAheadData(client, dateRange) {
|
|
|
427
427
|
const wasHalfOpen = mcpCircuitBreaker.getState() === 'HALF_OPEN';
|
|
428
428
|
console.log(`${MCP_FETCH_PREFIX} Fetching week-ahead data from MCP (parallel)...`);
|
|
429
429
|
const [plenarySessions, committeeInfo, documents, pipeline, questions, epEvents] = await Promise.allSettled([
|
|
430
|
-
client.getPlenarySessions({
|
|
431
|
-
client.getCommitteeInfo({
|
|
432
|
-
client.searchDocuments({
|
|
430
|
+
client.getPlenarySessions({ dateFrom: dateRange.start, dateTo: dateRange.end, limit: 50 }),
|
|
431
|
+
client.getCommitteeInfo({ showCurrent: true }),
|
|
432
|
+
client.searchDocuments({ keyword: 'parliament', limit: 20 }),
|
|
433
433
|
client.monitorLegislativePipeline({
|
|
434
434
|
dateFrom: dateRange.start,
|
|
435
435
|
dateTo: dateRange.end,
|
|
436
436
|
status: 'ACTIVE',
|
|
437
437
|
limit: 20,
|
|
438
438
|
}),
|
|
439
|
-
client.getParliamentaryQuestions({
|
|
440
|
-
client.getEvents({
|
|
439
|
+
client.getParliamentaryQuestions({ dateFrom: dateRange.start, limit: 20 }),
|
|
440
|
+
client.getEvents({ limit: 20 }),
|
|
441
441
|
]);
|
|
442
442
|
const allFailed = [
|
|
443
443
|
plenarySessions,
|
|
@@ -459,7 +459,7 @@ export async function fetchWeekAheadData(client, dateRange) {
|
|
|
459
459
|
const additionalEvents = parseEPEvents(epEvents, dateRange.start);
|
|
460
460
|
const events = [...plenaryEvents, ...additionalEvents];
|
|
461
461
|
return {
|
|
462
|
-
events: events.length > 0 ? events :
|
|
462
|
+
events: events.length > 0 ? events : PLACEHOLDER_EVENTS.map((e) => ({ ...e, date: dateRange.start })),
|
|
463
463
|
committees: parseCommitteeMeetings(committeeInfo, dateRange.start),
|
|
464
464
|
documents: parseLegislativeDocuments(documents),
|
|
465
465
|
pipeline: parseLegislativePipeline(pipeline),
|
|
@@ -728,7 +728,7 @@ export async function fetchCommitteeData(client, abbreviation) {
|
|
|
728
728
|
return defaultResult;
|
|
729
729
|
try {
|
|
730
730
|
console.log(`${MCP_FETCH_PREFIX} Fetching committee info for ${abbreviation}...`);
|
|
731
|
-
const committeeResult = await callMCP(() => client.getCommitteeInfo({
|
|
731
|
+
const committeeResult = await callMCP(() => client.getCommitteeInfo({ abbreviation }), null, `getCommitteeInfo(${abbreviation})`);
|
|
732
732
|
if (committeeResult)
|
|
733
733
|
applyCommitteeInfo(committeeResult, defaultResult, abbreviation);
|
|
734
734
|
}
|
|
@@ -738,7 +738,7 @@ export async function fetchCommitteeData(client, abbreviation) {
|
|
|
738
738
|
}
|
|
739
739
|
try {
|
|
740
740
|
console.log(`${MCP_FETCH_PREFIX} Fetching documents for ${abbreviation}...`);
|
|
741
|
-
const docsResult = await callMCP(() => client.searchDocuments({
|
|
741
|
+
const docsResult = await callMCP(() => client.searchDocuments({ keyword: abbreviation, limit: 5 }), null, `searchDocuments(${abbreviation})`);
|
|
742
742
|
if (docsResult)
|
|
743
743
|
applyDocuments(docsResult, defaultResult);
|
|
744
744
|
}
|
|
@@ -1062,8 +1062,7 @@ const TIMEFRAME_FALLBACK_CHAIN = new Map([
|
|
|
1062
1062
|
['one-day', 'one-week'],
|
|
1063
1063
|
['one-week', 'one-month'],
|
|
1064
1064
|
['one-month', undefined],
|
|
1065
|
-
['
|
|
1066
|
-
['one-year', undefined],
|
|
1065
|
+
['custom', undefined],
|
|
1067
1066
|
]);
|
|
1068
1067
|
/**
|
|
1069
1068
|
* Get the next wider timeframe for fallback, or `undefined` if no fallback exists.
|
|
@@ -49,7 +49,7 @@ export function collectDocsHtmlFiles(dir, rootDir = PROJECT_ROOT) {
|
|
|
49
49
|
*/
|
|
50
50
|
export function generateSitemap(articles, docsFiles = []) {
|
|
51
51
|
const urls = [];
|
|
52
|
-
const today = new Date().toISOString().
|
|
52
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
53
53
|
// Add home pages for each language
|
|
54
54
|
for (const lang of ALL_LANGUAGES) {
|
|
55
55
|
const filename = lang === 'en' ? 'index.html' : `index-${lang}.html`;
|
|
@@ -301,8 +301,8 @@ export function generateSitemapHTML(lang, articleInfos, hasDocsDir = false) {
|
|
|
301
301
|
const skipLinkText = getLocalizedString(SKIP_LINK_TEXTS, lang);
|
|
302
302
|
const dir = getTextDirection(lang);
|
|
303
303
|
const year = new Date().getFullYear();
|
|
304
|
-
const sections = SITEMAP_SECTIONS[lang] ?? SITEMAP_SECTIONS['en'];
|
|
305
|
-
const docsLabels = DOCS_LABELS[lang] ?? DOCS_LABELS['en'];
|
|
304
|
+
const sections = (SITEMAP_SECTIONS[lang] ?? SITEMAP_SECTIONS['en']);
|
|
305
|
+
const docsLabels = (DOCS_LABELS[lang] ?? DOCS_LABELS['en']);
|
|
306
306
|
const heroTitle = getLocalizedString(PAGE_TITLES, lang).split(' - ')[0] ?? '';
|
|
307
307
|
const headerSubtitle = escapeHTML(getLocalizedString(HEADER_SUBTITLE_LABELS, lang));
|
|
308
308
|
const themeToggleLabel = escapeHTML(getLocalizedString(THEME_TOGGLE_LABELS, lang));
|
|
@@ -539,7 +539,7 @@ function main() {
|
|
|
539
539
|
// Generate sitemap HTML for each language
|
|
540
540
|
let htmlGenerated = 0;
|
|
541
541
|
for (const lang of ALL_LANGUAGES) {
|
|
542
|
-
const langArticles = articlesByLang.get(lang)
|
|
542
|
+
const langArticles = articlesByLang.get(lang) ?? [];
|
|
543
543
|
// Sort newest first
|
|
544
544
|
langArticles.sort((a, b) => b.date.localeCompare(a.date));
|
|
545
545
|
const html = generateSitemapHTML(lang, langArticles, hasDocsDir);
|
|
@@ -249,7 +249,7 @@ function buildAdoptedTextsSection(feedData, lang) {
|
|
|
249
249
|
const sections = displayOrder
|
|
250
250
|
.filter((cat) => grouped[cat]?.length)
|
|
251
251
|
.map((cat) => {
|
|
252
|
-
const items = grouped[cat];
|
|
252
|
+
const items = grouped[cat] ?? [];
|
|
253
253
|
const listItems = items
|
|
254
254
|
.map((item) => `<li class="adopted-text-item"><strong>${escapeHTML(item.title)}</strong> <span class="document-date">(${escapeHTML(item.date)})</span></li>`)
|
|
255
255
|
.join('\n ');
|
|
@@ -70,32 +70,28 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
70
70
|
/**
|
|
71
71
|
* Get plenary sessions
|
|
72
72
|
*
|
|
73
|
-
* @param options - Filter options
|
|
74
|
-
* per the tool schema when the canonical fields are absent.
|
|
73
|
+
* @param options - Filter options including dateFrom, dateTo, eventId, year, location
|
|
75
74
|
* @returns Plenary sessions data
|
|
76
75
|
*/
|
|
77
76
|
getPlenarySessions(options?: GetPlenarySessionsOptions): Promise<MCPToolResult>;
|
|
78
77
|
/**
|
|
79
78
|
* Search legislative documents
|
|
80
79
|
*
|
|
81
|
-
* @param options - Search options
|
|
82
|
-
* since the MCP tool schema requires the `keyword` parameter)
|
|
80
|
+
* @param options - Search options using v1.2.8 parameters: keyword, documentType, docId, etc.
|
|
83
81
|
* @returns Search results
|
|
84
82
|
*/
|
|
85
83
|
searchDocuments(options?: SearchDocumentsOptions): Promise<MCPToolResult>;
|
|
86
84
|
/**
|
|
87
85
|
* Get parliamentary questions
|
|
88
86
|
*
|
|
89
|
-
* @param options - Filter options
|
|
90
|
-
* `dateTo` is intentionally ignored because the `get_parliamentary_questions` tool schema
|
|
91
|
-
* only supports `startDate` as a date filter; passing `dateTo` would have no effect.
|
|
87
|
+
* @param options - Filter options including docId, type, author, topic, status, dateFrom, dateTo
|
|
92
88
|
* @returns Parliamentary questions data
|
|
93
89
|
*/
|
|
94
90
|
getParliamentaryQuestions(options?: GetParliamentaryQuestionsOptions): Promise<MCPToolResult>;
|
|
95
91
|
/**
|
|
96
92
|
* Get committee information
|
|
97
93
|
*
|
|
98
|
-
* @param options - Filter options
|
|
94
|
+
* @param options - Filter options: id, abbreviation, showCurrent
|
|
99
95
|
* @returns Committee info data
|
|
100
96
|
*/
|
|
101
97
|
getCommitteeInfo(options?: GetCommitteeInfoOptions): Promise<MCPToolResult>;
|
|
@@ -123,21 +119,21 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
123
119
|
/**
|
|
124
120
|
* Analyze coalition dynamics and cohesion
|
|
125
121
|
*
|
|
126
|
-
* @param options - Options including optional
|
|
122
|
+
* @param options - Options including optional groupIds and date range
|
|
127
123
|
* @returns Coalition cohesion and stress analysis
|
|
128
124
|
*/
|
|
129
125
|
analyzeCoalitionDynamics(options?: AnalyzeCoalitionDynamicsOptions): Promise<MCPToolResult>;
|
|
130
126
|
/**
|
|
131
127
|
* Detect voting anomalies and party defections
|
|
132
128
|
*
|
|
133
|
-
* @param options - Options including optional MEP id,
|
|
129
|
+
* @param options - Options including optional MEP id, groupId, and date range
|
|
134
130
|
* @returns Anomaly detection results
|
|
135
131
|
*/
|
|
136
132
|
detectVotingAnomalies(options?: DetectVotingAnomaliesOptions): Promise<MCPToolResult>;
|
|
137
133
|
/**
|
|
138
134
|
* Compare political groups across dimensions
|
|
139
135
|
*
|
|
140
|
-
* @param options - Options including required
|
|
136
|
+
* @param options - Options including required groupIds and optional dimensions and date range
|
|
141
137
|
* @returns Cross-group comparative analysis
|
|
142
138
|
*/
|
|
143
139
|
comparePoliticalGroups(options: ComparePoliticalGroupsOptions): Promise<MCPToolResult>;
|
|
@@ -151,7 +147,7 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
151
147
|
/**
|
|
152
148
|
* Retrieve voting records with optional filters
|
|
153
149
|
*
|
|
154
|
-
* @param options - Filter options (mepId,
|
|
150
|
+
* @param options - Filter options (sessionId, mepId, topic, dateFrom, dateTo, limit, offset)
|
|
155
151
|
* @returns Voting records data
|
|
156
152
|
*/
|
|
157
153
|
getVotingRecords(options?: VotingRecordsOptions): Promise<MCPToolResult>;
|
|
@@ -214,14 +210,14 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
214
210
|
/**
|
|
215
211
|
* Get plenary speeches and debate contributions
|
|
216
212
|
*
|
|
217
|
-
* @param options - Filter options including optional speechId
|
|
213
|
+
* @param options - Filter options including optional speechId, dateFrom/dateTo (v1.2.8: year removed)
|
|
218
214
|
* @returns Speeches data
|
|
219
215
|
*/
|
|
220
216
|
getSpeeches(options?: GetSpeechesOptions): Promise<MCPToolResult>;
|
|
221
217
|
/**
|
|
222
218
|
* Get legislative procedures
|
|
223
219
|
*
|
|
224
|
-
* @param options - Filter options including optional processId
|
|
220
|
+
* @param options - Filter options including optional processId (v1.2.8: year removed)
|
|
225
221
|
* @returns Procedures data
|
|
226
222
|
*/
|
|
227
223
|
getProcedures(options?: GetProceduresOptions): Promise<MCPToolResult>;
|
|
@@ -235,7 +231,7 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
235
231
|
/**
|
|
236
232
|
* Get European Parliament events (hearings, conferences, seminars)
|
|
237
233
|
*
|
|
238
|
-
* @param options - Filter options including optional eventId
|
|
234
|
+
* @param options - Filter options including optional eventId, pagination only (v1.2.8: year/dateFrom/dateTo removed — EP API /events has no date filtering)
|
|
239
235
|
* @returns Events data
|
|
240
236
|
*/
|
|
241
237
|
getEvents(options?: GetEventsOptions): Promise<MCPToolResult>;
|
|
@@ -291,7 +287,7 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
291
287
|
/**
|
|
292
288
|
* Get committee documents
|
|
293
289
|
*
|
|
294
|
-
* @param options - Filter options including optional docId
|
|
290
|
+
* @param options - Filter options including optional docId (v1.2.8: year removed)
|
|
295
291
|
* @returns Committee documents data
|
|
296
292
|
*/
|
|
297
293
|
getCommitteeDocuments(options?: GetCommitteeDocumentsOptions): Promise<MCPToolResult>;
|
|
@@ -319,7 +315,7 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
319
315
|
/**
|
|
320
316
|
* Get external documents (non-EP documents such as Council positions)
|
|
321
317
|
*
|
|
322
|
-
* @param options - Filter options including optional docId
|
|
318
|
+
* @param options - Filter options including optional docId (v1.2.8: year removed)
|
|
323
319
|
* @returns External documents data
|
|
324
320
|
*/
|
|
325
321
|
getExternalDocuments(options?: GetExternalDocumentsOptions): Promise<MCPToolResult>;
|
|
@@ -382,10 +378,10 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
382
378
|
/**
|
|
383
379
|
* Cross-tool OSINT intelligence correlation engine
|
|
384
380
|
*
|
|
385
|
-
* @param options - Options including optional
|
|
381
|
+
* @param options - Options including required mepIds, optional groups, sensitivityLevel, includeNetworkAnalysis
|
|
386
382
|
* @returns Correlated intelligence alerts and insights
|
|
387
383
|
*/
|
|
388
|
-
correlateIntelligence(options
|
|
384
|
+
correlateIntelligence(options: CorrelateIntelligenceOptions): Promise<MCPToolResult>;
|
|
389
385
|
/**
|
|
390
386
|
* Retrieve precomputed European Parliament activity statistics (EP6–EP10, 2004–2025).
|
|
391
387
|
* Includes yearly stats, category rankings, political landscape history, and
|
|
@@ -29,7 +29,7 @@ const SERVER_HEALTH_FALLBACK = '{"server": null, "feeds": []}';
|
|
|
29
29
|
/**
|
|
30
30
|
* Classify an error message into a diagnostic error category.
|
|
31
31
|
*
|
|
32
|
-
* Maps EP MCP Server v1.2.
|
|
32
|
+
* Maps EP MCP Server v1.2.8 structured error codes and generic HTTP/network
|
|
33
33
|
* errors into one of six broad categories used for logging and retry decisions:
|
|
34
34
|
*
|
|
35
35
|
* Returned categories (priority order):
|
|
@@ -45,7 +45,7 @@ const SERVER_HEALTH_FALLBACK = '{"server": null, "feeds": []}';
|
|
|
45
45
|
*/
|
|
46
46
|
function classifyToolError(message) {
|
|
47
47
|
const lowerMsg = message.toLowerCase();
|
|
48
|
-
// EP MCP Server v1.2.
|
|
48
|
+
// EP MCP Server v1.2.8 structured error codes (matched case-insensitively)
|
|
49
49
|
if (lowerMsg.includes('internal_error')) {
|
|
50
50
|
return 'INTERNAL_ERROR';
|
|
51
51
|
}
|
|
@@ -205,78 +205,38 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
205
205
|
/**
|
|
206
206
|
* Get plenary sessions
|
|
207
207
|
*
|
|
208
|
-
* @param options - Filter options
|
|
209
|
-
* per the tool schema when the canonical fields are absent.
|
|
208
|
+
* @param options - Filter options including dateFrom, dateTo, eventId, year, location
|
|
210
209
|
* @returns Plenary sessions data
|
|
211
210
|
*/
|
|
212
211
|
async getPlenarySessions(options = {}) {
|
|
213
|
-
return this.safeCallTool('get_plenary_sessions',
|
|
214
|
-
const { dateFrom, dateTo, ...rest } = options;
|
|
215
|
-
const normalizedOptions = { ...rest };
|
|
216
|
-
if (normalizedOptions['startDate'] === undefined && dateFrom !== undefined) {
|
|
217
|
-
normalizedOptions['startDate'] = dateFrom;
|
|
218
|
-
}
|
|
219
|
-
if (normalizedOptions['endDate'] === undefined && dateTo !== undefined) {
|
|
220
|
-
normalizedOptions['endDate'] = dateTo;
|
|
221
|
-
}
|
|
222
|
-
return normalizedOptions;
|
|
223
|
-
}, '{"sessions": []}');
|
|
212
|
+
return this.safeCallTool('get_plenary_sessions', options, '{"sessions": []}');
|
|
224
213
|
}
|
|
225
214
|
/**
|
|
226
215
|
* Search legislative documents
|
|
227
216
|
*
|
|
228
|
-
* @param options - Search options
|
|
229
|
-
* since the MCP tool schema requires the `keyword` parameter)
|
|
217
|
+
* @param options - Search options using v1.2.8 parameters: keyword, documentType, docId, etc.
|
|
230
218
|
* @returns Search results
|
|
231
219
|
*/
|
|
232
220
|
async searchDocuments(options = {}) {
|
|
233
|
-
return this.safeCallTool('search_documents',
|
|
234
|
-
const { query, ...rest } = options;
|
|
235
|
-
const normalizedOptions = { ...rest };
|
|
236
|
-
// MCP tool schema expects 'keyword', not 'query'
|
|
237
|
-
if (normalizedOptions['keyword'] === undefined && query !== undefined) {
|
|
238
|
-
const trimmed = String(query).trim();
|
|
239
|
-
if (trimmed.length > 0) {
|
|
240
|
-
normalizedOptions['keyword'] = trimmed;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return normalizedOptions;
|
|
244
|
-
}, DOCUMENTS_FALLBACK);
|
|
221
|
+
return this.safeCallTool('search_documents', options, DOCUMENTS_FALLBACK);
|
|
245
222
|
}
|
|
246
223
|
/**
|
|
247
224
|
* Get parliamentary questions
|
|
248
225
|
*
|
|
249
|
-
* @param options - Filter options
|
|
250
|
-
* `dateTo` is intentionally ignored because the `get_parliamentary_questions` tool schema
|
|
251
|
-
* only supports `startDate` as a date filter; passing `dateTo` would have no effect.
|
|
226
|
+
* @param options - Filter options including docId, type, author, topic, status, dateFrom, dateTo
|
|
252
227
|
* @returns Parliamentary questions data
|
|
253
228
|
*/
|
|
254
229
|
async getParliamentaryQuestions(options = {}) {
|
|
255
|
-
return this.safeCallTool('get_parliamentary_questions',
|
|
256
|
-
const { dateFrom, dateTo: _dateTo, ...rest } = options;
|
|
257
|
-
const toolOptions = { ...rest };
|
|
258
|
-
if (toolOptions['startDate'] === undefined && dateFrom !== undefined) {
|
|
259
|
-
toolOptions['startDate'] = dateFrom;
|
|
260
|
-
}
|
|
261
|
-
return toolOptions;
|
|
262
|
-
}, '{"questions": []}');
|
|
230
|
+
return this.safeCallTool('get_parliamentary_questions', options, '{"questions": []}');
|
|
263
231
|
}
|
|
264
232
|
/**
|
|
265
233
|
* Get committee information
|
|
266
234
|
*
|
|
267
|
-
* @param options - Filter options
|
|
235
|
+
* @param options - Filter options: id, abbreviation, showCurrent
|
|
268
236
|
* @returns Committee info data
|
|
269
237
|
*/
|
|
270
238
|
async getCommitteeInfo(options = {}) {
|
|
271
|
-
return this.safeCallTool('get_committee_info',
|
|
272
|
-
const { committeeId, ...rest } = options;
|
|
273
|
-
const toolOptions = { ...rest };
|
|
274
|
-
// MCP tool schema expects 'abbreviation', not 'committeeId'
|
|
275
|
-
if (toolOptions['abbreviation'] === undefined && committeeId !== undefined) {
|
|
276
|
-
toolOptions['abbreviation'] = committeeId;
|
|
277
|
-
}
|
|
278
|
-
return toolOptions;
|
|
279
|
-
}, '{"committees": []}');
|
|
239
|
+
return this.safeCallTool('get_committee_info', options, '{"committees": []}');
|
|
280
240
|
}
|
|
281
241
|
/**
|
|
282
242
|
* Monitor legislative pipeline
|
|
@@ -319,7 +279,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
319
279
|
/**
|
|
320
280
|
* Analyze coalition dynamics and cohesion
|
|
321
281
|
*
|
|
322
|
-
* @param options - Options including optional
|
|
282
|
+
* @param options - Options including optional groupIds and date range
|
|
323
283
|
* @returns Coalition cohesion and stress analysis
|
|
324
284
|
*/
|
|
325
285
|
async analyzeCoalitionDynamics(options = {}) {
|
|
@@ -328,7 +288,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
328
288
|
/**
|
|
329
289
|
* Detect voting anomalies and party defections
|
|
330
290
|
*
|
|
331
|
-
* @param options - Options including optional MEP id,
|
|
291
|
+
* @param options - Options including optional MEP id, groupId, and date range
|
|
332
292
|
* @returns Anomaly detection results
|
|
333
293
|
*/
|
|
334
294
|
async detectVotingAnomalies(options = {}) {
|
|
@@ -337,19 +297,18 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
337
297
|
/**
|
|
338
298
|
* Compare political groups across dimensions
|
|
339
299
|
*
|
|
340
|
-
* @param options - Options including required
|
|
300
|
+
* @param options - Options including required groupIds and optional dimensions and date range
|
|
341
301
|
* @returns Cross-group comparative analysis
|
|
342
302
|
*/
|
|
343
303
|
async comparePoliticalGroups(options) {
|
|
344
|
-
const
|
|
345
|
-
const groups = rawGroups
|
|
304
|
+
const groupIds = (Array.isArray(options.groupIds) ? options.groupIds : [])
|
|
346
305
|
.map((g) => (typeof g === 'string' ? g.trim() : ''))
|
|
347
306
|
.filter((g) => g.length > 0);
|
|
348
|
-
if (
|
|
349
|
-
console.warn('compare_political_groups called without valid
|
|
307
|
+
if (groupIds.length === 0) {
|
|
308
|
+
console.warn('compare_political_groups called without valid groupIds (non-empty string array required)');
|
|
350
309
|
return { content: [{ type: 'text', text: '{"comparison": {}}' }] };
|
|
351
310
|
}
|
|
352
|
-
return this.safeCallTool('compare_political_groups', { ...options,
|
|
311
|
+
return this.safeCallTool('compare_political_groups', { ...options, groupIds }, '{"comparison": {}}');
|
|
353
312
|
}
|
|
354
313
|
/**
|
|
355
314
|
* Get detailed information about a specific MEP
|
|
@@ -367,7 +326,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
367
326
|
/**
|
|
368
327
|
* Retrieve voting records with optional filters
|
|
369
328
|
*
|
|
370
|
-
* @param options - Filter options (mepId,
|
|
329
|
+
* @param options - Filter options (sessionId, mepId, topic, dateFrom, dateTo, limit, offset)
|
|
371
330
|
* @returns Voting records data
|
|
372
331
|
*/
|
|
373
332
|
async getVotingRecords(options = {}) {
|
|
@@ -464,7 +423,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
464
423
|
/**
|
|
465
424
|
* Get plenary speeches and debate contributions
|
|
466
425
|
*
|
|
467
|
-
* @param options - Filter options including optional speechId
|
|
426
|
+
* @param options - Filter options including optional speechId, dateFrom/dateTo (v1.2.8: year removed)
|
|
468
427
|
* @returns Speeches data
|
|
469
428
|
*/
|
|
470
429
|
async getSpeeches(options = {}) {
|
|
@@ -473,7 +432,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
473
432
|
/**
|
|
474
433
|
* Get legislative procedures
|
|
475
434
|
*
|
|
476
|
-
* @param options - Filter options including optional processId
|
|
435
|
+
* @param options - Filter options including optional processId (v1.2.8: year removed)
|
|
477
436
|
* @returns Procedures data
|
|
478
437
|
*/
|
|
479
438
|
async getProcedures(options = {}) {
|
|
@@ -491,7 +450,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
491
450
|
/**
|
|
492
451
|
* Get European Parliament events (hearings, conferences, seminars)
|
|
493
452
|
*
|
|
494
|
-
* @param options - Filter options including optional eventId
|
|
453
|
+
* @param options - Filter options including optional eventId, pagination only (v1.2.8: year/dateFrom/dateTo removed — EP API /events has no date filtering)
|
|
495
454
|
* @returns Events data
|
|
496
455
|
*/
|
|
497
456
|
async getEvents(options = {}) {
|
|
@@ -571,7 +530,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
571
530
|
/**
|
|
572
531
|
* Get committee documents
|
|
573
532
|
*
|
|
574
|
-
* @param options - Filter options including optional docId
|
|
533
|
+
* @param options - Filter options including optional docId (v1.2.8: year removed)
|
|
575
534
|
* @returns Committee documents data
|
|
576
535
|
*/
|
|
577
536
|
async getCommitteeDocuments(options = {}) {
|
|
@@ -607,7 +566,7 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
607
566
|
/**
|
|
608
567
|
* Get external documents (non-EP documents such as Council positions)
|
|
609
568
|
*
|
|
610
|
-
* @param options - Filter options including optional docId
|
|
569
|
+
* @param options - Filter options including optional docId (v1.2.8: year removed)
|
|
611
570
|
* @returns External documents data
|
|
612
571
|
*/
|
|
613
572
|
async getExternalDocuments(options = {}) {
|
|
@@ -708,10 +667,14 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
708
667
|
/**
|
|
709
668
|
* Cross-tool OSINT intelligence correlation engine
|
|
710
669
|
*
|
|
711
|
-
* @param options - Options including optional
|
|
670
|
+
* @param options - Options including required mepIds, optional groups, sensitivityLevel, includeNetworkAnalysis
|
|
712
671
|
* @returns Correlated intelligence alerts and insights
|
|
713
672
|
*/
|
|
714
|
-
async correlateIntelligence(options
|
|
673
|
+
async correlateIntelligence(options) {
|
|
674
|
+
if (!Array.isArray(options.mepIds) || options.mepIds.length === 0) {
|
|
675
|
+
console.warn('correlate_intelligence called without valid mepIds (non-empty string array required)');
|
|
676
|
+
return { content: [{ type: 'text', text: INTELLIGENCE_FALLBACK }] };
|
|
677
|
+
}
|
|
715
678
|
return this.safeCallTool('correlate_intelligence', options, INTELLIGENCE_FALLBACK);
|
|
716
679
|
}
|
|
717
680
|
/**
|
|
@@ -419,7 +419,7 @@ export class MCPConnection {
|
|
|
419
419
|
return trimmedKey;
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
|
-
const rawScheme = typeof process !== 'undefined' && process.env
|
|
422
|
+
const rawScheme = typeof process !== 'undefined' && process.env?.['EP_MCP_GATEWAY_AUTH_SCHEME'];
|
|
423
423
|
const scheme = typeof rawScheme === 'string' ? rawScheme.trim() : '';
|
|
424
424
|
if (scheme && tokenRegex.test(scheme)) {
|
|
425
425
|
return `${scheme} ${trimmedKey}`;
|
|
@@ -430,6 +430,9 @@ export class MCPConnection {
|
|
|
430
430
|
* Attempt a single connection via MCP Gateway (HTTP transport)
|
|
431
431
|
*/
|
|
432
432
|
async _attemptGatewayConnection() {
|
|
433
|
+
if (!this.gatewayUrl) {
|
|
434
|
+
throw new Error('Gateway URL not configured. Set the EP_MCP_GATEWAY_URL environment variable or provide the gatewayUrl constructor option.');
|
|
435
|
+
}
|
|
433
436
|
try {
|
|
434
437
|
const headers = {
|
|
435
438
|
'Content-Type': 'application/json',
|
|
@@ -562,17 +565,24 @@ export class MCPConnection {
|
|
|
562
565
|
handleMessage(line) {
|
|
563
566
|
try {
|
|
564
567
|
const message = JSON.parse(line);
|
|
565
|
-
if (message.id && this.pendingRequests.has(message.id)) {
|
|
568
|
+
if (message.id !== null && message.id !== undefined && this.pendingRequests.has(message.id)) {
|
|
566
569
|
const pending = this.pendingRequests.get(message.id);
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
+
if (pending) {
|
|
571
|
+
this.pendingRequests.delete(message.id);
|
|
572
|
+
if (message.error) {
|
|
573
|
+
pending.reject(new Error(message.error.message ?? 'MCP server error'));
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
pending.resolve(message.result);
|
|
577
|
+
}
|
|
570
578
|
}
|
|
571
579
|
else {
|
|
572
|
-
|
|
580
|
+
// has() returned true but get() returned undefined — unexpected
|
|
581
|
+
this.pendingRequests.delete(message.id);
|
|
582
|
+
console.error(`MCP pending request ${String(message.id)} vanished before handling`);
|
|
573
583
|
}
|
|
574
584
|
}
|
|
575
|
-
else if (
|
|
585
|
+
else if ((message.id === null || message.id === undefined) && message.method) {
|
|
576
586
|
console.log(`MCP Notification: ${message.method}`);
|
|
577
587
|
}
|
|
578
588
|
}
|
|
@@ -619,6 +629,9 @@ export class MCPConnection {
|
|
|
619
629
|
* @returns Server response
|
|
620
630
|
*/
|
|
621
631
|
async _sendGatewayRequest(method, params = {}) {
|
|
632
|
+
if (!this.gatewayUrl) {
|
|
633
|
+
throw new Error('Gateway URL not configured. Set EP_MCP_GATEWAY_URL or provide gatewayUrl in MCP client options.');
|
|
634
|
+
}
|
|
622
635
|
const id = ++this.requestId;
|
|
623
636
|
const request = {
|
|
624
637
|
jsonrpc: '2.0',
|