euparliamentmonitor 0.8.19 → 0.8.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/package.json +7 -7
- package/scripts/constants/language-articles.d.ts +4 -0
- package/scripts/constants/language-articles.js +20 -0
- package/scripts/constants/language-ui.d.ts +8 -8
- package/scripts/constants/language-ui.js +64 -64
- package/scripts/constants/languages.d.ts +2 -2
- package/scripts/constants/languages.js +2 -2
- package/scripts/generators/news-enhanced.js +13 -3
- package/scripts/generators/pipeline/analysis-classification.d.ts +49 -0
- package/scripts/generators/pipeline/analysis-classification.js +333 -0
- package/scripts/generators/pipeline/analysis-existing.d.ts +67 -0
- package/scripts/generators/pipeline/analysis-existing.js +547 -0
- package/scripts/generators/pipeline/analysis-helpers.d.ts +140 -0
- package/scripts/generators/pipeline/analysis-helpers.js +266 -0
- package/scripts/generators/pipeline/analysis-risk.d.ts +49 -0
- package/scripts/generators/pipeline/analysis-risk.js +417 -0
- package/scripts/generators/pipeline/analysis-stage.d.ts +19 -39
- package/scripts/generators/pipeline/analysis-stage.js +219 -1704
- package/scripts/generators/pipeline/analysis-threats.d.ts +41 -0
- package/scripts/generators/pipeline/analysis-threats.js +142 -0
- package/scripts/generators/pipeline/fetch-stage.d.ts +25 -15
- package/scripts/generators/pipeline/fetch-stage.js +293 -117
- package/scripts/generators/strategies/article-strategy.d.ts +126 -7
- package/scripts/generators/strategies/article-strategy.js +491 -1
- package/scripts/generators/strategies/breaking-news-strategy.js +98 -8
- package/scripts/generators/strategies/committee-reports-strategy.js +23 -2
- package/scripts/generators/strategies/month-ahead-strategy.js +23 -2
- package/scripts/generators/strategies/monthly-review-strategy.js +13 -1
- package/scripts/generators/strategies/motions-strategy.js +15 -1
- package/scripts/generators/strategies/propositions-strategy.js +15 -1
- package/scripts/generators/strategies/week-ahead-strategy.js +19 -1
- package/scripts/generators/strategies/weekly-review-strategy.js +17 -1
- package/scripts/generators/synthesis-summary.d.ts +93 -0
- package/scripts/generators/synthesis-summary.js +364 -0
- package/scripts/index.d.ts +5 -2
- package/scripts/index.js +6 -1
- package/scripts/mcp/ep-mcp-client.d.ts +34 -1
- package/scripts/mcp/ep-mcp-client.js +110 -2
- package/scripts/mcp/mcp-connection.d.ts +3 -1
- package/scripts/mcp/mcp-connection.js +35 -4
- package/scripts/templates/article-template.js +24 -22
- package/scripts/templates/section-builders.js +2 -5
- package/scripts/types/index.d.ts +2 -1
- package/scripts/types/mcp.d.ts +7 -0
- package/scripts/types/political-classification.d.ts +1 -1
- package/scripts/types/quality.d.ts +9 -6
- package/scripts/types/significance.d.ts +130 -0
- package/scripts/types/significance.js +4 -0
- package/scripts/utils/article-quality-scorer.d.ts +13 -11
- package/scripts/utils/article-quality-scorer.js +36 -23
- package/scripts/utils/file-utils.d.ts +2 -2
- package/scripts/utils/file-utils.js +2 -2
- package/scripts/utils/html-sanitize.d.ts +10 -0
- package/scripts/utils/html-sanitize.js +32 -0
- package/scripts/utils/political-classification.d.ts +8 -7
- package/scripts/utils/political-classification.js +8 -7
- package/scripts/utils/political-risk-assessment.d.ts +1 -1
- package/scripts/utils/political-risk-assessment.js +1 -1
- package/scripts/utils/significance-scoring.d.ts +97 -0
- package/scripts/utils/significance-scoring.js +190 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* @module Generators/SynthesisSummary
|
|
5
|
+
* @description Aggregation engine that reads per-file analysis outputs and
|
|
6
|
+
* produces a synthesis summary — a single intelligence briefing consumed by
|
|
7
|
+
* article generators to determine narrative direction, headline selection,
|
|
8
|
+
* and publication priority.
|
|
9
|
+
*
|
|
10
|
+
* The synthesiser:
|
|
11
|
+
* 1. Scans the analysis date directory for markdown files
|
|
12
|
+
* 2. Extracts YAML frontmatter (method, confidence) from each
|
|
13
|
+
* 3. Counts SWOT mentions and risk-level keywords
|
|
14
|
+
* 4. Ranks findings by confidence and produces editorial recommendations
|
|
15
|
+
*
|
|
16
|
+
* @see analysis/templates/synthesis-summary.md
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'fs';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { randomUUID } from 'crypto';
|
|
21
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
22
|
+
/** Case-insensitive patterns for detecting SWOT mentions in analysis text */
|
|
23
|
+
const SWOT_PATTERNS = {
|
|
24
|
+
strengths: /\bstrength/giu,
|
|
25
|
+
weaknesses: /\bweakness/giu,
|
|
26
|
+
opportunities: /\bopportunit/giu,
|
|
27
|
+
threats: /\bthreat/giu,
|
|
28
|
+
};
|
|
29
|
+
/** Case-insensitive patterns for detecting risk-level mentions */
|
|
30
|
+
const RISK_PATTERNS = {
|
|
31
|
+
critical: /\bcritical\b/giu,
|
|
32
|
+
high: /\bhigh[- ]risk\b/giu,
|
|
33
|
+
medium: /\bmedium[- ]risk\b/giu,
|
|
34
|
+
low: /\blow[- ]risk\b/giu,
|
|
35
|
+
};
|
|
36
|
+
/** Confidence value ordering (higher = better) */
|
|
37
|
+
const CONFIDENCE_RANK = {
|
|
38
|
+
high: 3,
|
|
39
|
+
medium: 2,
|
|
40
|
+
low: 1,
|
|
41
|
+
};
|
|
42
|
+
/** Filename of the synthesis output itself — excluded from scanning to prevent self-contamination */
|
|
43
|
+
const SYNTHESIS_OUTPUT_FILENAME = 'synthesis-summary.md';
|
|
44
|
+
/** Subdirectory containing per-document analysis — excluded to prevent I/O bloat and skewed aggregation */
|
|
45
|
+
const DOCUMENTS_SUBDIR = 'documents';
|
|
46
|
+
// ─── Markdown sanitization ────────────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Sanitize untrusted text for safe use in a Markdown table cell.
|
|
49
|
+
*
|
|
50
|
+
* Escapes pipe characters, backslashes, and HTML entities, then normalizes
|
|
51
|
+
* whitespace to prevent table layout corruption.
|
|
52
|
+
*
|
|
53
|
+
* @param input - Untrusted cell text
|
|
54
|
+
* @returns Sanitized text safe for Markdown table cells
|
|
55
|
+
*/
|
|
56
|
+
function sanitizeMdCell(input) {
|
|
57
|
+
return input
|
|
58
|
+
.replace(/\\/gu, '\\\\')
|
|
59
|
+
.replace(/\|/gu, '\\|')
|
|
60
|
+
.replace(/&/gu, '&')
|
|
61
|
+
.replace(/</gu, '<')
|
|
62
|
+
.replace(/>/gu, '>')
|
|
63
|
+
.replace(/[\r\n]+/gu, ' ')
|
|
64
|
+
.trim();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse YAML frontmatter from a markdown file's content.
|
|
68
|
+
*
|
|
69
|
+
* Extracts `method`, `confidence`, and `date` fields from the `---` delimited
|
|
70
|
+
* YAML block at the start of the file. Returns null when no valid frontmatter
|
|
71
|
+
* is found.
|
|
72
|
+
*
|
|
73
|
+
* @param content - Raw markdown content
|
|
74
|
+
* @returns Parsed frontmatter or null
|
|
75
|
+
*/
|
|
76
|
+
export function parseFrontmatter(content) {
|
|
77
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/u.exec(content);
|
|
78
|
+
if (!match)
|
|
79
|
+
return null;
|
|
80
|
+
const yaml = match[1] ?? '';
|
|
81
|
+
const methodMatch = /^method:\s+(\S.*)$/mu.exec(yaml);
|
|
82
|
+
const confidenceMatch = /^confidence:\s+(\S.*)$/mu.exec(yaml);
|
|
83
|
+
const dateMatch = /^date:\s+(\S.*)$/mu.exec(yaml);
|
|
84
|
+
const method = methodMatch?.[1]?.trim() ?? 'unknown';
|
|
85
|
+
const rawConf = confidenceMatch?.[1]?.trim().toLowerCase() ?? 'low';
|
|
86
|
+
const confidence = rawConf === 'high' || rawConf === 'medium' ? rawConf : 'low';
|
|
87
|
+
const date = dateMatch?.[1]?.trim() ?? '';
|
|
88
|
+
return { method, confidence, date };
|
|
89
|
+
}
|
|
90
|
+
// ─── Text analysis ────────────────────────────────────────────────────────────
|
|
91
|
+
/**
|
|
92
|
+
* Count regex pattern matches in a body of text.
|
|
93
|
+
*
|
|
94
|
+
* @param text - Source text to scan
|
|
95
|
+
* @param pattern - RegExp with global flag
|
|
96
|
+
* @returns Number of matches
|
|
97
|
+
*/
|
|
98
|
+
function countMatches(text, pattern) {
|
|
99
|
+
// Reset lastIndex for global regexps to avoid stale state
|
|
100
|
+
pattern.lastIndex = 0;
|
|
101
|
+
const matches = text.match(pattern);
|
|
102
|
+
return matches ? matches.length : 0;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Aggregate SWOT mention counts from a body of text.
|
|
106
|
+
*
|
|
107
|
+
* @param text - Combined analysis text
|
|
108
|
+
* @returns SWOT counts
|
|
109
|
+
*/
|
|
110
|
+
export function aggregateSWOT(text) {
|
|
111
|
+
return {
|
|
112
|
+
strengths: countMatches(text, SWOT_PATTERNS.strengths),
|
|
113
|
+
weaknesses: countMatches(text, SWOT_PATTERNS.weaknesses),
|
|
114
|
+
opportunities: countMatches(text, SWOT_PATTERNS.opportunities),
|
|
115
|
+
threats: countMatches(text, SWOT_PATTERNS.threats),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Aggregate risk-level mention counts from a body of text.
|
|
120
|
+
*
|
|
121
|
+
* @param text - Combined analysis text
|
|
122
|
+
* @returns Risk level counts
|
|
123
|
+
*/
|
|
124
|
+
export function aggregateRisks(text) {
|
|
125
|
+
return {
|
|
126
|
+
critical: countMatches(text, RISK_PATTERNS.critical),
|
|
127
|
+
high: countMatches(text, RISK_PATTERNS.high),
|
|
128
|
+
medium: countMatches(text, RISK_PATTERNS.medium),
|
|
129
|
+
low: countMatches(text, RISK_PATTERNS.low),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Extract the first non-empty non-frontmatter heading or paragraph as a
|
|
134
|
+
* one-line summary from a markdown file.
|
|
135
|
+
*
|
|
136
|
+
* @param content - Raw markdown content
|
|
137
|
+
* @returns One-line summary string
|
|
138
|
+
*/
|
|
139
|
+
export function extractSummaryLine(content) {
|
|
140
|
+
// Strip frontmatter
|
|
141
|
+
const body = content.replace(/^---[\s\S]*?---\s*/u, '');
|
|
142
|
+
// Try first heading (# followed by at least one space and then non-space content)
|
|
143
|
+
const headingMatch = /^#+\s(\S.*)$/mu.exec(body);
|
|
144
|
+
if (headingMatch?.[1])
|
|
145
|
+
return headingMatch[1].trim();
|
|
146
|
+
// Fall back to first non-empty line
|
|
147
|
+
const lines = body.split('\n');
|
|
148
|
+
for (const line of lines) {
|
|
149
|
+
const trimmed = line.trim();
|
|
150
|
+
if (trimmed.length > 0 && !trimmed.startsWith('|') && !trimmed.startsWith('```')) {
|
|
151
|
+
return trimmed.slice(0, 200);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return 'No summary available';
|
|
155
|
+
}
|
|
156
|
+
// ─── Confidence aggregation ──────────────────────────────────────────────────
|
|
157
|
+
/**
|
|
158
|
+
* Determine the overall confidence level from a set of findings.
|
|
159
|
+
*
|
|
160
|
+
* Uses majority vote: whichever confidence level appears most often wins.
|
|
161
|
+
*
|
|
162
|
+
* @param findings - Findings with individual confidence levels
|
|
163
|
+
* @returns Aggregated confidence level
|
|
164
|
+
*/
|
|
165
|
+
export function aggregateConfidence(findings) {
|
|
166
|
+
if (findings.length === 0)
|
|
167
|
+
return 'low';
|
|
168
|
+
const counts = { high: 0, medium: 0, low: 0 };
|
|
169
|
+
for (const f of findings) {
|
|
170
|
+
counts[f.confidence]++;
|
|
171
|
+
}
|
|
172
|
+
if (counts.high >= counts.medium && counts.high >= counts.low)
|
|
173
|
+
return 'high';
|
|
174
|
+
if (counts.medium >= counts.low)
|
|
175
|
+
return 'medium';
|
|
176
|
+
return 'low';
|
|
177
|
+
}
|
|
178
|
+
// ─── Directory scanning ──────────────────────────────────────────────────────
|
|
179
|
+
/**
|
|
180
|
+
* Recursively find all `.md` analysis files under a directory.
|
|
181
|
+
*
|
|
182
|
+
* Excludes:
|
|
183
|
+
* - The synthesis output file itself (prevents self-contamination on re-runs)
|
|
184
|
+
* - The `documents/` subdirectory (per-document analysis can bloat I/O and skew aggregation)
|
|
185
|
+
*
|
|
186
|
+
* @param dir - Absolute directory path
|
|
187
|
+
* @returns Array of absolute file paths
|
|
188
|
+
*/
|
|
189
|
+
export function findMarkdownFiles(dir) {
|
|
190
|
+
const results = [];
|
|
191
|
+
if (!fs.existsSync(dir))
|
|
192
|
+
return results;
|
|
193
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
194
|
+
for (const entry of entries) {
|
|
195
|
+
const fullPath = path.join(dir, entry.name);
|
|
196
|
+
if (entry.isDirectory()) {
|
|
197
|
+
// Skip the documents/ subdirectory to avoid per-document analysis bloat
|
|
198
|
+
if (entry.name === DOCUMENTS_SUBDIR)
|
|
199
|
+
continue;
|
|
200
|
+
results.push(...findMarkdownFiles(fullPath));
|
|
201
|
+
}
|
|
202
|
+
else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
203
|
+
// Skip the synthesis output itself to prevent self-contamination
|
|
204
|
+
if (entry.name === SYNTHESIS_OUTPUT_FILENAME)
|
|
205
|
+
continue;
|
|
206
|
+
results.push(fullPath);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return results;
|
|
210
|
+
}
|
|
211
|
+
// ─── Editorial recommendations ───────────────────────────────────────────────
|
|
212
|
+
/**
|
|
213
|
+
* Generate editorial recommendations based on aggregated analysis data.
|
|
214
|
+
*
|
|
215
|
+
* @param findings - Ranked findings
|
|
216
|
+
* @param swot - Aggregated SWOT counts
|
|
217
|
+
* @param risks - Risk level distribution
|
|
218
|
+
* @returns Array of recommendation strings
|
|
219
|
+
*/
|
|
220
|
+
export function generateEditorialRecommendations(findings, swot, risks) {
|
|
221
|
+
const recommendations = [];
|
|
222
|
+
if (findings.length === 0) {
|
|
223
|
+
recommendations.push('No analysis files found — verify pipeline execution.');
|
|
224
|
+
return recommendations;
|
|
225
|
+
}
|
|
226
|
+
// High-confidence findings drive lead stories
|
|
227
|
+
const highConfCount = findings.filter((f) => f.confidence === 'high').length;
|
|
228
|
+
if (highConfCount > 0) {
|
|
229
|
+
recommendations.push(`${highConfCount} high-confidence finding(s) available for lead story selection.`);
|
|
230
|
+
}
|
|
231
|
+
// Risk-driven recommendations
|
|
232
|
+
if (risks.critical > 0) {
|
|
233
|
+
recommendations.push(`${risks.critical} critical-risk mention(s) detected — consider priority coverage.`);
|
|
234
|
+
}
|
|
235
|
+
// SWOT balance indicator
|
|
236
|
+
const totalSwot = swot.strengths + swot.weaknesses + swot.opportunities + swot.threats;
|
|
237
|
+
if (totalSwot > 0) {
|
|
238
|
+
const threatRatio = swot.threats / totalSwot;
|
|
239
|
+
if (threatRatio > 0.4) {
|
|
240
|
+
recommendations.push('Threat-heavy SWOT balance — narrative may benefit from opportunity framing.');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Volume recommendation
|
|
244
|
+
if (findings.length >= 10) {
|
|
245
|
+
recommendations.push(`${findings.length} analysis files processed — consider multi-article output.`);
|
|
246
|
+
}
|
|
247
|
+
else if (findings.length <= 2) {
|
|
248
|
+
recommendations.push('Limited analysis coverage — consider consolidating into a single digest article.');
|
|
249
|
+
}
|
|
250
|
+
return recommendations;
|
|
251
|
+
}
|
|
252
|
+
// ─── Main synthesis ──────────────────────────────────────────────────────────
|
|
253
|
+
/**
|
|
254
|
+
* Build a synthesis summary from all analysis files in a date directory.
|
|
255
|
+
*
|
|
256
|
+
* Scans the directory recursively for `.md` analysis files, parses their
|
|
257
|
+
* frontmatter, extracts findings, aggregates SWOT and risk mentions, and
|
|
258
|
+
* produces a {@link SynthesisSummary} object.
|
|
259
|
+
*
|
|
260
|
+
* @param dateOutputDir - Absolute path to the date-scoped analysis directory
|
|
261
|
+
* @param date - ISO date string (YYYY-MM-DD)
|
|
262
|
+
* @returns Synthesis summary object
|
|
263
|
+
*/
|
|
264
|
+
export function buildSynthesisSummary(dateOutputDir, date) {
|
|
265
|
+
const files = findMarkdownFiles(dateOutputDir);
|
|
266
|
+
const findings = [];
|
|
267
|
+
let combinedText = '';
|
|
268
|
+
for (const filePath of files) {
|
|
269
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
270
|
+
const frontmatter = parseFrontmatter(content);
|
|
271
|
+
combinedText += content + '\n';
|
|
272
|
+
findings.push({
|
|
273
|
+
method: frontmatter?.method ?? 'unknown',
|
|
274
|
+
file: path.basename(filePath),
|
|
275
|
+
confidence: frontmatter?.confidence ?? 'low',
|
|
276
|
+
summary: extractSummaryLine(content),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
// Sort findings: high confidence first, then medium, then low.
|
|
280
|
+
// The heading in the output says "Top Findings by Confidence" to match.
|
|
281
|
+
findings.sort((a, b) => (CONFIDENCE_RANK[b.confidence] ?? 0) - (CONFIDENCE_RANK[a.confidence] ?? 0));
|
|
282
|
+
const swot = aggregateSWOT(combinedText);
|
|
283
|
+
const riskOverview = aggregateRisks(combinedText);
|
|
284
|
+
const overallConfidence = aggregateConfidence(findings);
|
|
285
|
+
const editorialRecommendations = generateEditorialRecommendations(findings, swot, riskOverview);
|
|
286
|
+
return {
|
|
287
|
+
synthesisId: `SYN-${date}-${randomUUID().slice(0, 8).toUpperCase()}`,
|
|
288
|
+
date,
|
|
289
|
+
documentsAnalyzed: files.length,
|
|
290
|
+
overallConfidence,
|
|
291
|
+
topFindings: findings.slice(0, 5),
|
|
292
|
+
swot,
|
|
293
|
+
riskOverview,
|
|
294
|
+
editorialRecommendations,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Generate a markdown report from a synthesis summary.
|
|
299
|
+
*
|
|
300
|
+
* Follows the template format defined in `analysis/templates/synthesis-summary.md`.
|
|
301
|
+
*
|
|
302
|
+
* @param summary - Computed synthesis summary
|
|
303
|
+
* @returns Markdown string
|
|
304
|
+
*/
|
|
305
|
+
export function formatSynthesisMarkdown(summary) {
|
|
306
|
+
const findingsRows = summary.topFindings
|
|
307
|
+
.map((f, i) => `| ${i + 1} | ${sanitizeMdCell(f.file)} | ${sanitizeMdCell(f.method)} | ${f.confidence} | ${sanitizeMdCell(f.summary.slice(0, 80))} |`)
|
|
308
|
+
.join('\n');
|
|
309
|
+
return `---
|
|
310
|
+
method: synthesis-summary
|
|
311
|
+
date: ${summary.date}
|
|
312
|
+
confidence: ${summary.overallConfidence}
|
|
313
|
+
generated: ${new Date().toISOString()}
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
# 🧩 Synthesis Summary — ${summary.date}
|
|
317
|
+
|
|
318
|
+
## 📋 Synthesis Context
|
|
319
|
+
|
|
320
|
+
| Field | Value |
|
|
321
|
+
|-------|-------|
|
|
322
|
+
| **Synthesis ID** | \`${summary.synthesisId}\` |
|
|
323
|
+
| **Analysis Date** | \`${summary.date}\` |
|
|
324
|
+
| **Documents Analyzed** | ${summary.documentsAnalyzed} |
|
|
325
|
+
| **Overall Confidence** | ${summary.overallConfidence.toUpperCase()} |
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 🏆 Top Findings by Confidence
|
|
330
|
+
|
|
331
|
+
| Rank | File | Method | Confidence | Summary |
|
|
332
|
+
|:----:|------|--------|:----------:|---------|
|
|
333
|
+
${findingsRows || '| — | — | — | — | — |'}
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 💪 Aggregated SWOT Summary
|
|
338
|
+
|
|
339
|
+
| Dimension | Count |
|
|
340
|
+
|-----------|:-----:|
|
|
341
|
+
| ✅ Strengths | ${summary.swot.strengths} |
|
|
342
|
+
| ⚠️ Weaknesses | ${summary.swot.weaknesses} |
|
|
343
|
+
| 🚀 Opportunities | ${summary.swot.opportunities} |
|
|
344
|
+
| 🔴 Threats | ${summary.swot.threats} |
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## ⚖️ Risk Landscape Summary
|
|
349
|
+
|
|
350
|
+
| Level | Mentions |
|
|
351
|
+
|-------|:--------:|
|
|
352
|
+
| 🔴 Critical | ${summary.riskOverview.critical} |
|
|
353
|
+
| 🟠 High | ${summary.riskOverview.high} |
|
|
354
|
+
| 🟡 Medium | ${summary.riskOverview.medium} |
|
|
355
|
+
| 🟢 Low | ${summary.riskOverview.low} |
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 🎯 Editorial Recommendations
|
|
360
|
+
|
|
361
|
+
${summary.editorialRecommendations.map((r) => `- ${r}`).join('\n')}
|
|
362
|
+
`;
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=synthesis-summary.js.map
|
package/scripts/index.d.ts
CHANGED
|
@@ -34,12 +34,14 @@ export { type ToolHealthEntry, type HealthSnapshot, MCPHealthMonitor } from './m
|
|
|
34
34
|
export { scoreVotingAnomaly, analyzeCoalitionCohesion, scoreMEPInfluence, calculateLegislativeVelocity, rankBySignificance, buildIntelligenceSection, buildDefaultStakeholderPerspectives, scoreStakeholderInfluence, buildStakeholderOutcomeMatrix, rankStakeholdersByInfluence, computeVotingIntensity, detectCoalitionShifts, computePolarizationIndex, detectVotingTrends, computeCrossSessionCoalitionStability, rankMEPInfluenceByTopic, buildLegislativeVelocityReport, } from './utils/intelligence-analysis.js';
|
|
35
35
|
export { createEmptyIndex, addArticleToIndex, buildIndexFromEntries, findRelatedArticles, generateCrossReferences, detectTrends, findOrCreateSeries, buildRelatedArticlesHTML, } from './utils/intelligence-index.js';
|
|
36
36
|
export { assessAnalysisDepth, assessStakeholderCoverage, assessVisualizationQuality, calculateOverallScore, generateRecommendations, scoreArticleQuality, } from './utils/article-quality-scorer.js';
|
|
37
|
+
export { scoreSignificance, scoreBatch, clampScore, deriveDecision, formatScoreMarkdown, formatBatchMarkdown, WEIGHT_PARLIAMENTARY, WEIGHT_POLICY, WEIGHT_PUBLIC_INTEREST, WEIGHT_URGENCY, WEIGHT_INSTITUTIONAL, THRESHOLD_PUBLISH, THRESHOLD_HOLD, } from './utils/significance-scoring.js';
|
|
38
|
+
export { parseFrontmatter, aggregateSWOT, aggregateRisks, extractSummaryLine, aggregateConfidence, findMarkdownFiles, generateEditorialRecommendations, buildSynthesisSummary, formatSynthesisMarkdown, } from './generators/synthesis-summary.js';
|
|
37
39
|
export { validateArticleContent, validateTranslationCompleteness, } from './utils/content-validator.js';
|
|
38
40
|
export { enrichMetadataFromContent } from './utils/content-metadata.js';
|
|
39
41
|
export { buildMetadataDatabase, writeMetadataDatabase, readMetadataDatabase, updateMetadataDatabase, updateIntelligenceIndex, } from './utils/news-metadata.js';
|
|
40
42
|
export { pl, pl as pluralizeCount } from './utils/metadata-utils.js';
|
|
41
43
|
export { assessPoliticalThreats, buildActorThreatProfiles, buildConsequenceTree, analyzeLegislativeDisruption, generateThreatAssessmentMarkdown, ALL_THREAT_LANDSCAPE_DIMENSIONS, } from './utils/political-threat-assessment.js';
|
|
42
|
-
export { stripScriptBlocks } from './utils/html-sanitize.js';
|
|
44
|
+
export { stripHtmlTags, stripScriptBlocks } from './utils/html-sanitize.js';
|
|
43
45
|
export { parseArticleFilename, formatSlug, calculateReadTime, escapeHTML, isSafeURL, validateArticleHTML, type ArticleValidationResult, } from './utils/file-utils.js';
|
|
44
46
|
export { detectCategory } from './utils/article-category.js';
|
|
45
47
|
export { EU_COUNTRY_CODES, EU_AGGREGATE_CODE, POLICY_INDICATORS, parseWorldBankCSV, formatIndicatorValue, getMostRecentValue, buildEconomicContext, getWorldBankCountryCode, isEUMemberState, buildEconomicContextHTML, } from './utils/world-bank-data.js';
|
|
@@ -58,7 +60,8 @@ export { mcpCircuitBreaker, computeRollingDateRange, initializeMCPClient, loadFe
|
|
|
58
60
|
export { type ValidationResult, validateMCPResponse, normalizeISO8601Date, sanitizeText, isValidCountryCode, isValidLanguageCode, } from './generators/pipeline/transform-stage.js';
|
|
59
61
|
export { type StrategyRegistry, createStrategyRegistry, generateArticleForStrategy, } from './generators/pipeline/generate-stage.js';
|
|
60
62
|
export { type OutputOptions, writeArticleFile, writeSingleArticle, writeGenerationMetadata, } from './generators/pipeline/output-stage.js';
|
|
61
|
-
export type { ArticleData, ArticleMetadata, ArticleStrategyBase, ArticleStrategy, } from './generators/strategies/article-strategy.js';
|
|
63
|
+
export type { ArticleData, ArticleMetadata, ArticleStrategyBase, ArticleStrategy, LoadedAnalysisContext, AnalysisFileContent, } from './generators/strategies/article-strategy.js';
|
|
64
|
+
export { loadAnalysisContext, extractAnalysisSummary, buildAnalysisInsightsSection, } from './generators/strategies/article-strategy.js';
|
|
62
65
|
export { type BreakingNewsArticleData, BreakingNewsStrategy, breakingNewsStrategy, } from './generators/strategies/breaking-news-strategy.js';
|
|
63
66
|
export { type CommitteeReportsArticleData, type CommitteeTheme, AFET_KEYWORDS, LIBE_KEYWORDS, AGRI_KEYWORDS, ENVI_KEYWORDS, ECON_KEYWORDS, categorizeAdoptedText, CommitteeReportsStrategy, committeeReportsStrategy, } from './generators/strategies/committee-reports-strategy.js';
|
|
64
67
|
export { type MonthAheadArticleData, MonthAheadStrategy, monthAheadStrategy, } from './generators/strategies/month-ahead-strategy.js';
|
package/scripts/index.js
CHANGED
|
@@ -41,6 +41,10 @@ export { scoreVotingAnomaly, analyzeCoalitionCohesion, scoreMEPInfluence, calcul
|
|
|
41
41
|
export { createEmptyIndex, addArticleToIndex, buildIndexFromEntries, findRelatedArticles, generateCrossReferences, detectTrends, findOrCreateSeries, buildRelatedArticlesHTML, } from './utils/intelligence-index.js';
|
|
42
42
|
// ─── Article Quality ─────────────────────────────────────────────────────────
|
|
43
43
|
export { assessAnalysisDepth, assessStakeholderCoverage, assessVisualizationQuality, calculateOverallScore, generateRecommendations, scoreArticleQuality, } from './utils/article-quality-scorer.js';
|
|
44
|
+
// ─── Significance Scoring ────────────────────────────────────────────────────
|
|
45
|
+
export { scoreSignificance, scoreBatch, clampScore, deriveDecision, formatScoreMarkdown, formatBatchMarkdown, WEIGHT_PARLIAMENTARY, WEIGHT_POLICY, WEIGHT_PUBLIC_INTEREST, WEIGHT_URGENCY, WEIGHT_INSTITUTIONAL, THRESHOLD_PUBLISH, THRESHOLD_HOLD, } from './utils/significance-scoring.js';
|
|
46
|
+
// ─── Synthesis Summary ───────────────────────────────────────────────────────
|
|
47
|
+
export { parseFrontmatter, aggregateSWOT, aggregateRisks, extractSummaryLine, aggregateConfidence, findMarkdownFiles, generateEditorialRecommendations, buildSynthesisSummary, formatSynthesisMarkdown, } from './generators/synthesis-summary.js';
|
|
44
48
|
// ─── Content Validation ──────────────────────────────────────────────────────
|
|
45
49
|
export { validateArticleContent, validateTranslationCompleteness, } from './utils/content-validator.js';
|
|
46
50
|
// ─── Content Metadata ────────────────────────────────────────────────────────
|
|
@@ -52,7 +56,7 @@ export { pl, pl as pluralizeCount } from './utils/metadata-utils.js';
|
|
|
52
56
|
// ─── Political Threat Assessment ─────────────────────────────────────────────
|
|
53
57
|
export { assessPoliticalThreats, buildActorThreatProfiles, buildConsequenceTree, analyzeLegislativeDisruption, generateThreatAssessmentMarkdown, ALL_THREAT_LANDSCAPE_DIMENSIONS, } from './utils/political-threat-assessment.js';
|
|
54
58
|
// ─── HTML Utilities ──────────────────────────────────────────────────────────
|
|
55
|
-
export { stripScriptBlocks } from './utils/html-sanitize.js';
|
|
59
|
+
export { stripHtmlTags, stripScriptBlocks } from './utils/html-sanitize.js';
|
|
56
60
|
export { parseArticleFilename, formatSlug, calculateReadTime, escapeHTML, isSafeURL, validateArticleHTML, } from './utils/file-utils.js';
|
|
57
61
|
// ─── Article Category Detection ──────────────────────────────────────────────
|
|
58
62
|
export { detectCategory } from './utils/article-category.js';
|
|
@@ -80,6 +84,7 @@ export { mcpCircuitBreaker, computeRollingDateRange, initializeMCPClient, loadFe
|
|
|
80
84
|
export { validateMCPResponse, normalizeISO8601Date, sanitizeText, isValidCountryCode, isValidLanguageCode, } from './generators/pipeline/transform-stage.js';
|
|
81
85
|
export { createStrategyRegistry, generateArticleForStrategy, } from './generators/pipeline/generate-stage.js';
|
|
82
86
|
export { writeArticleFile, writeSingleArticle, writeGenerationMetadata, } from './generators/pipeline/output-stage.js';
|
|
87
|
+
export { loadAnalysisContext, extractAnalysisSummary, buildAnalysisInsightsSection, } from './generators/strategies/article-strategy.js';
|
|
83
88
|
export { BreakingNewsStrategy, breakingNewsStrategy, } from './generators/strategies/breaking-news-strategy.js';
|
|
84
89
|
export { AFET_KEYWORDS, LIBE_KEYWORDS, AGRI_KEYWORDS, ENVI_KEYWORDS, ECON_KEYWORDS, categorizeAdoptedText, CommitteeReportsStrategy, committeeReportsStrategy, } from './generators/strategies/committee-reports-strategy.js';
|
|
85
90
|
export { MonthAheadStrategy, monthAheadStrategy, } from './generators/strategies/month-ahead-strategy.js';
|
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
* built on top of the generic {@link MCPConnection} transport.
|
|
5
5
|
*/
|
|
6
6
|
import { MCPConnection } from './mcp-connection.js';
|
|
7
|
-
import type { MCPClientOptions, MCPToolResult, GetMEPsOptions, GetPlenarySessionsOptions, SearchDocumentsOptions, GetParliamentaryQuestionsOptions, GetCommitteeInfoOptions, MonitorLegislativePipelineOptions, AssessMEPInfluenceOptions, AnalyzeCoalitionDynamicsOptions, DetectVotingAnomaliesOptions, ComparePoliticalGroupsOptions, VotingRecordsOptions, VotingPatternsOptions, GenerateReportOptions, AnalyzeLegislativeEffectivenessOptions, AnalyzeCommitteeActivityOptions, TrackMEPAttendanceOptions, AnalyzeCountryDelegationOptions, GeneratePoliticalLandscapeOptions, GetCurrentMEPsOptions, GetSpeechesOptions, GetProceduresOptions, GetAdoptedTextsOptions, GetEventsOptions, GetMeetingActivitiesOptions, GetMeetingDecisionsOptions, GetMEPDeclarationsOptions, GetIncomingMEPsOptions, GetOutgoingMEPsOptions, GetHomonymMEPsOptions, GetPlenaryDocumentsOptions, GetCommitteeDocumentsOptions, GetPlenarySessionDocumentsOptions, GetPlenarySessionDocumentItemsOptions, GetControlledVocabulariesOptions, GetExternalDocumentsOptions, GetMeetingForeseenActivitiesOptions, GetProcedureEventsOptions, GetMeetingPlenarySessionDocumentsOptions, GetMeetingPlenarySessionDocumentItemsOptions, NetworkAnalysisOptions, SentimentTrackerOptions, EarlyWarningSystemOptions, ComparativeIntelligenceOptions, CorrelateIntelligenceOptions, GetAllGeneratedStatsOptions, GetMEPsFeedOptions, GetEventsFeedOptions, GetProceduresFeedOptions, GetAdoptedTextsFeedOptions, GetMEPDeclarationsFeedOptions, GetDocumentsFeedOptions, GetPlenaryDocumentsFeedOptions, GetCommitteeDocumentsFeedOptions, GetPlenarySessionDocumentsFeedOptions, GetExternalDocumentsFeedOptions, GetParliamentaryQuestionsFeedOptions, GetCorporateBodiesFeedOptions, GetControlledVocabulariesFeedOptions } from '../types/index.js';
|
|
7
|
+
import type { MCPClientOptions, MCPToolResult, GetMEPsOptions, GetPlenarySessionsOptions, SearchDocumentsOptions, GetParliamentaryQuestionsOptions, GetCommitteeInfoOptions, MonitorLegislativePipelineOptions, AssessMEPInfluenceOptions, AnalyzeCoalitionDynamicsOptions, DetectVotingAnomaliesOptions, ComparePoliticalGroupsOptions, VotingRecordsOptions, VotingPatternsOptions, GenerateReportOptions, AnalyzeLegislativeEffectivenessOptions, AnalyzeCommitteeActivityOptions, TrackMEPAttendanceOptions, AnalyzeCountryDelegationOptions, GeneratePoliticalLandscapeOptions, GetCurrentMEPsOptions, GetSpeechesOptions, GetProceduresOptions, GetAdoptedTextsOptions, GetEventsOptions, GetMeetingActivitiesOptions, GetMeetingDecisionsOptions, GetMEPDeclarationsOptions, GetIncomingMEPsOptions, GetOutgoingMEPsOptions, GetHomonymMEPsOptions, GetPlenaryDocumentsOptions, GetCommitteeDocumentsOptions, GetPlenarySessionDocumentsOptions, GetPlenarySessionDocumentItemsOptions, GetControlledVocabulariesOptions, GetExternalDocumentsOptions, GetMeetingForeseenActivitiesOptions, GetProcedureEventsOptions, GetMeetingPlenarySessionDocumentsOptions, GetMeetingPlenarySessionDocumentItemsOptions, NetworkAnalysisOptions, SentimentTrackerOptions, EarlyWarningSystemOptions, ComparativeIntelligenceOptions, CorrelateIntelligenceOptions, GetAllGeneratedStatsOptions, GetMEPsFeedOptions, GetEventsFeedOptions, GetProceduresFeedOptions, GetAdoptedTextsFeedOptions, GetMEPDeclarationsFeedOptions, GetDocumentsFeedOptions, GetPlenaryDocumentsFeedOptions, GetCommitteeDocumentsFeedOptions, GetPlenarySessionDocumentsFeedOptions, GetExternalDocumentsFeedOptions, GetParliamentaryQuestionsFeedOptions, GetCorporateBodiesFeedOptions, GetControlledVocabulariesFeedOptions, GetProcedureEventByIdOptions } from '../types/index.js';
|
|
8
8
|
/**
|
|
9
9
|
* MCP Client for European Parliament data access.
|
|
10
10
|
* Extends {@link MCPConnection} with EP-specific tool wrapper methods.
|
|
11
11
|
*/
|
|
12
12
|
export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
13
|
+
/** Tracks tools that returned fallback data in the current session */
|
|
14
|
+
private readonly _failedTools;
|
|
15
|
+
/** Tracks tools that have been called (attempted) in the current session */
|
|
16
|
+
private readonly _calledTools;
|
|
13
17
|
/**
|
|
14
18
|
* Generic error-safe wrapper around {@link callToolWithRetry}.
|
|
15
19
|
* Retries transient failures (timeouts, connection drops) with a bounded
|
|
@@ -28,6 +32,19 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
28
32
|
* @returns Tool result or fallback
|
|
29
33
|
*/
|
|
30
34
|
private safeCallTool;
|
|
35
|
+
/**
|
|
36
|
+
* Get a summary of tools that returned fallback data in the current session.
|
|
37
|
+
* Useful for diagnosing feed availability and data quality issues.
|
|
38
|
+
*
|
|
39
|
+
* @returns Map of tool name to error description
|
|
40
|
+
*/
|
|
41
|
+
getFailedTools(): ReadonlyMap<string, string>;
|
|
42
|
+
/**
|
|
43
|
+
* Get a human-readable feed health summary for diagnostics.
|
|
44
|
+
*
|
|
45
|
+
* @returns Formatted summary of feed availability
|
|
46
|
+
*/
|
|
47
|
+
getFeedHealthSummary(): string;
|
|
31
48
|
/**
|
|
32
49
|
* Get Members of European Parliament
|
|
33
50
|
*
|
|
@@ -456,6 +473,22 @@ export declare class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
456
473
|
* @returns Controlled vocabularies feed data
|
|
457
474
|
*/
|
|
458
475
|
getControlledVocabulariesFeed(options?: GetControlledVocabulariesFeedOptions): Promise<MCPToolResult>;
|
|
476
|
+
/**
|
|
477
|
+
* Get a specific event linked to a legislative procedure.
|
|
478
|
+
* Returns a single event for the specified procedure and event identifiers.
|
|
479
|
+
*
|
|
480
|
+
* @param options - Options including required processId and eventId
|
|
481
|
+
* @returns Procedure event data
|
|
482
|
+
*/
|
|
483
|
+
getProcedureEventById(options: GetProcedureEventByIdOptions): Promise<MCPToolResult>;
|
|
484
|
+
/**
|
|
485
|
+
* Check server health and feed availability status.
|
|
486
|
+
* Returns server version, uptime, per-feed health status, and overall availability.
|
|
487
|
+
* Does not make upstream API calls — reports cached status from recent tool invocations.
|
|
488
|
+
*
|
|
489
|
+
* @returns Server health and feed availability data
|
|
490
|
+
*/
|
|
491
|
+
getServerHealth(): Promise<MCPToolResult>;
|
|
459
492
|
}
|
|
460
493
|
/**
|
|
461
494
|
* Get or create singleton MCP client instance
|
|
@@ -22,11 +22,19 @@ const ITEMS_FALLBACK = '{"items": []}';
|
|
|
22
22
|
const INTELLIGENCE_FALLBACK = '{"analysis": null}';
|
|
23
23
|
/** Fallback payload for precomputed statistics */
|
|
24
24
|
const STATS_FALLBACK = '{"stats": null}';
|
|
25
|
+
/** Fallback payload for single procedure event lookup */
|
|
26
|
+
const PROCEDURE_EVENT_FALLBACK = '{"event": null}';
|
|
27
|
+
/** Fallback payload for server health status */
|
|
28
|
+
const SERVER_HEALTH_FALLBACK = '{"server": null, "feeds": []}';
|
|
25
29
|
/**
|
|
26
30
|
* MCP Client for European Parliament data access.
|
|
27
31
|
* Extends {@link MCPConnection} with EP-specific tool wrapper methods.
|
|
28
32
|
*/
|
|
29
33
|
export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
34
|
+
/** Tracks tools that returned fallback data in the current session */
|
|
35
|
+
_failedTools = new Map();
|
|
36
|
+
/** Tracks tools that have been called (attempted) in the current session */
|
|
37
|
+
_calledTools = new Set();
|
|
30
38
|
/**
|
|
31
39
|
* Generic error-safe wrapper around {@link callToolWithRetry}.
|
|
32
40
|
* Retries transient failures (timeouts, connection drops) with a bounded
|
|
@@ -45,16 +53,88 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
45
53
|
* @returns Tool result or fallback
|
|
46
54
|
*/
|
|
47
55
|
async safeCallTool(toolName, args, fallbackText) {
|
|
56
|
+
this._calledTools.add(toolName);
|
|
48
57
|
try {
|
|
49
58
|
const resolvedArgs = typeof args === 'function' ? args() : args;
|
|
50
|
-
|
|
59
|
+
const result = await this.callToolWithRetry(toolName, resolvedArgs);
|
|
60
|
+
// Clear from failed tools on success
|
|
61
|
+
this._failedTools.delete(toolName);
|
|
62
|
+
return result;
|
|
51
63
|
}
|
|
52
64
|
catch (error) {
|
|
53
65
|
const message = error instanceof Error ? error.message : String(error);
|
|
54
|
-
|
|
66
|
+
const lowerMsg = message.toLowerCase();
|
|
67
|
+
// Classify the error for better diagnostics.
|
|
68
|
+
// Check gateway 5xx first — a "504 Gateway Timeout" should be SERVER_ERROR,
|
|
69
|
+
// not TIMEOUT (which is reserved for client-side request timeouts).
|
|
70
|
+
const isGatewayServerError = lowerMsg.includes('gateway timeout') ||
|
|
71
|
+
lowerMsg.includes('gateway error 500') ||
|
|
72
|
+
lowerMsg.includes('gateway error 502') ||
|
|
73
|
+
lowerMsg.includes('gateway error 503') ||
|
|
74
|
+
lowerMsg.includes('gateway error 504');
|
|
75
|
+
const errorType = isGatewayServerError
|
|
76
|
+
? 'SERVER_ERROR'
|
|
77
|
+
: lowerMsg.includes('404')
|
|
78
|
+
? 'NOT_FOUND'
|
|
79
|
+
: lowerMsg.includes('timeout')
|
|
80
|
+
? 'TIMEOUT'
|
|
81
|
+
: 'UNKNOWN';
|
|
82
|
+
this._failedTools.set(toolName, `${errorType}: ${message}`);
|
|
83
|
+
console.warn(`⚠️ ${toolName} failed [${errorType}]:`, message);
|
|
55
84
|
return { content: [{ type: 'text', text: fallbackText }] };
|
|
56
85
|
}
|
|
57
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Get a summary of tools that returned fallback data in the current session.
|
|
89
|
+
* Useful for diagnosing feed availability and data quality issues.
|
|
90
|
+
*
|
|
91
|
+
* @returns Map of tool name to error description
|
|
92
|
+
*/
|
|
93
|
+
getFailedTools() {
|
|
94
|
+
return new Map(this._failedTools);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get a human-readable feed health summary for diagnostics.
|
|
98
|
+
*
|
|
99
|
+
* @returns Formatted summary of feed availability
|
|
100
|
+
*/
|
|
101
|
+
getFeedHealthSummary() {
|
|
102
|
+
const feedTools = [
|
|
103
|
+
'get_meps_feed',
|
|
104
|
+
'get_events_feed',
|
|
105
|
+
'get_procedures_feed',
|
|
106
|
+
'get_adopted_texts_feed',
|
|
107
|
+
'get_mep_declarations_feed',
|
|
108
|
+
'get_documents_feed',
|
|
109
|
+
'get_plenary_documents_feed',
|
|
110
|
+
'get_committee_documents_feed',
|
|
111
|
+
'get_plenary_session_documents_feed',
|
|
112
|
+
'get_external_documents_feed',
|
|
113
|
+
'get_parliamentary_questions_feed',
|
|
114
|
+
'get_corporate_bodies_feed',
|
|
115
|
+
'get_controlled_vocabularies_feed',
|
|
116
|
+
];
|
|
117
|
+
const lines = ['EP MCP Feed Health:'];
|
|
118
|
+
let operational = 0;
|
|
119
|
+
let unchecked = 0;
|
|
120
|
+
for (const tool of feedTools) {
|
|
121
|
+
const error = this._failedTools.get(tool);
|
|
122
|
+
if (error) {
|
|
123
|
+
lines.push(` ❌ ${tool}: ${error}`);
|
|
124
|
+
}
|
|
125
|
+
else if (this._calledTools.has(tool)) {
|
|
126
|
+
lines.push(` ✅ ${tool}`);
|
|
127
|
+
operational++;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
lines.push(` ⚪ ${tool} (not checked)`);
|
|
131
|
+
unchecked++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const checked = feedTools.length - unchecked;
|
|
135
|
+
lines.push(` Summary: ${operational}/${checked} checked feeds operational${unchecked > 0 ? `, ${unchecked} unchecked` : ''}`);
|
|
136
|
+
return lines.join('\n');
|
|
137
|
+
}
|
|
58
138
|
/**
|
|
59
139
|
* Get Members of European Parliament
|
|
60
140
|
*
|
|
@@ -707,6 +787,34 @@ export class EuropeanParliamentMCPClient extends MCPConnection {
|
|
|
707
787
|
async getControlledVocabulariesFeed(options = {}) {
|
|
708
788
|
return this.safeCallTool('get_controlled_vocabularies_feed', options, EuropeanParliamentMCPClient.FEED_FALLBACK);
|
|
709
789
|
}
|
|
790
|
+
/**
|
|
791
|
+
* Get a specific event linked to a legislative procedure.
|
|
792
|
+
* Returns a single event for the specified procedure and event identifiers.
|
|
793
|
+
*
|
|
794
|
+
* @param options - Options including required processId and eventId
|
|
795
|
+
* @returns Procedure event data
|
|
796
|
+
*/
|
|
797
|
+
async getProcedureEventById(options) {
|
|
798
|
+
if (typeof options.processId !== 'string' || options.processId.trim().length === 0) {
|
|
799
|
+
console.warn('get_procedure_event_by_id called without valid processId (non-empty string required)');
|
|
800
|
+
return { content: [{ type: 'text', text: PROCEDURE_EVENT_FALLBACK }] };
|
|
801
|
+
}
|
|
802
|
+
if (typeof options.eventId !== 'string' || options.eventId.trim().length === 0) {
|
|
803
|
+
console.warn('get_procedure_event_by_id called without valid eventId (non-empty string required)');
|
|
804
|
+
return { content: [{ type: 'text', text: PROCEDURE_EVENT_FALLBACK }] };
|
|
805
|
+
}
|
|
806
|
+
return this.safeCallTool('get_procedure_event_by_id', { processId: options.processId.trim(), eventId: options.eventId.trim() }, PROCEDURE_EVENT_FALLBACK);
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Check server health and feed availability status.
|
|
810
|
+
* Returns server version, uptime, per-feed health status, and overall availability.
|
|
811
|
+
* Does not make upstream API calls — reports cached status from recent tool invocations.
|
|
812
|
+
*
|
|
813
|
+
* @returns Server health and feed availability data
|
|
814
|
+
*/
|
|
815
|
+
async getServerHealth() {
|
|
816
|
+
return this.safeCallTool('get_server_health', {}, SERVER_HEALTH_FALLBACK);
|
|
817
|
+
}
|
|
710
818
|
}
|
|
711
819
|
let clientInstance = null;
|
|
712
820
|
/**
|
|
@@ -17,7 +17,8 @@ export declare class MCPRateLimitError extends Error {
|
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Returns true only for transient, retriable failures: request timeouts,
|
|
20
|
-
* network-level connection-closed/reset errors,
|
|
20
|
+
* network-level connection-closed/reset errors, "not connected" states,
|
|
21
|
+
* and transient HTTP gateway errors (502, 503, 504).
|
|
21
22
|
*
|
|
22
23
|
* Uses an allow-list of known transient error patterns so that unknown or
|
|
23
24
|
* server-level errors (e.g., tool runtime failures) are NOT retried:
|
|
@@ -25,6 +26,7 @@ export declare class MCPRateLimitError extends Error {
|
|
|
25
26
|
* - connection closed / reset / refused — network-level transport failures
|
|
26
27
|
* - not connected — local "not yet connected" guard error
|
|
27
28
|
* - socket hang up — Node.js HTTP socket-level disconnection
|
|
29
|
+
* - gateway error 502/503/504 — transient upstream server errors
|
|
28
30
|
*
|
|
29
31
|
* Everything else (MCPSessionExpiredError, TypeError, rate-limit errors,
|
|
30
32
|
* unknown errors) returns false so `callToolWithRetry` surfaces them immediately.
|