euparliamentmonitor 0.8.43 → 0.8.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "euparliamentmonitor",
3
- "version": "0.8.43",
3
+ "version": "0.8.44",
4
4
  "type": "module",
5
5
  "description": "European Parliament Intelligence Platform - Monitor political activity with systematic transparency",
6
6
  "main": "scripts/index.js",
@@ -165,4 +165,13 @@ export declare function renderAnalysisIndex(included: readonly IncludedArtifact[
165
165
  * @returns {@link AggregatedRun} describing the rendered document
166
166
  */
167
167
  export declare function aggregateAnalysisRun(options: AggregateOptions): AggregatedRun;
168
+ /**
169
+ * Extract a `YYYY-MM-DD` date from a path like
170
+ * `analysis/daily/2026-01-15/run`. Falls back to the epoch date when no
171
+ * ISO date is embedded in the path.
172
+ *
173
+ * @param runDirRelPath - Repo-relative path of the run directory
174
+ * @returns ISO date string in `YYYY-MM-DD` form
175
+ */
176
+ export declare function guessDateFromRunDir(runDirRelPath: string): string;
168
177
  //# sourceMappingURL=analysis-aggregator.d.ts.map
@@ -119,6 +119,24 @@ export function discoverTradecraftFiles(repoRoot) {
119
119
  }
120
120
  return result.sort();
121
121
  }
122
+ /**
123
+ * Return `true` when a `.md` filename should be excluded from the run
124
+ * artifact set. Keeps the walk closure under the cognitive-complexity limit.
125
+ *
126
+ * Excluded names:
127
+ * - `article.md` and translated variants (`article.sv.md`, etc.) — these are
128
+ * outputs of the aggregator, not inputs.
129
+ * - `README.md` (case-insensitive) — required for the analysis gate but not
130
+ * relevant to the published article.
131
+ *
132
+ * @param name - Bare filename (no directory prefix)
133
+ * @returns `true` when the file should be skipped
134
+ */
135
+ function isExcludedArtifact(name) {
136
+ if (name.toLowerCase() === 'readme.md')
137
+ return true;
138
+ return name.startsWith('article.') && name.endsWith('.md');
139
+ }
122
140
  /**
123
141
  * Walk the run directory and return every `.md` file as a run-relative
124
142
  * POSIX path, excluding files under `data/` (raw MCP payloads, not meant
@@ -137,11 +155,13 @@ function collectRunArtifacts(runDir) {
137
155
  const full = path.join(dir, entry.name);
138
156
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
139
157
  if (entry.isDirectory()) {
140
- if (entry.name === 'data' || entry.name === 'runs')
158
+ // Skip raw payloads, legacy run snapshots, and Pass-1 work-in-progress
159
+ // snapshots so they are not rendered as supplementary artifacts.
160
+ if (entry.name === 'data' || entry.name === 'runs' || entry.name === 'pass1')
141
161
  continue;
142
162
  walk(full, rel);
143
163
  }
144
- else if (entry.isFile() && entry.name.endsWith('.md')) {
164
+ else if (entry.isFile() && entry.name.endsWith('.md') && !isExcludedArtifact(entry.name)) {
145
165
  result.push(rel);
146
166
  }
147
167
  }
@@ -471,7 +491,7 @@ export function aggregateAnalysisRun(options) {
471
491
  * @param runDirRelPath - Repo-relative path of the run directory
472
492
  * @returns ISO date string in `YYYY-MM-DD` form
473
493
  */
474
- function guessDateFromRunDir(runDirRelPath) {
494
+ export function guessDateFromRunDir(runDirRelPath) {
475
495
  const match = /(\d{4}-\d{2}-\d{2})/.exec(runDirRelPath);
476
496
  return match ? (match[1] ?? '1970-01-01') : '1970-01-01';
477
497
  }
@@ -38,6 +38,12 @@ export interface CliOptions {
38
38
  export interface GenerateResult {
39
39
  /** Repo-relative path of the English source Markdown that was written. */
40
40
  readonly sourceMarkdownRelPath: string;
41
+ /**
42
+ * Repo-relative path of the `article.md` written directly into the
43
+ * analysis run directory — canonical Markdown source that lives alongside
44
+ * the artifacts that produced it (riksdagsmonitor pattern).
45
+ */
46
+ readonly runArticleMdRelPath: string;
41
47
  /** Filenames written under `outDir`, relative to `outDir`. */
42
48
  readonly writtenFiles: readonly string[];
43
49
  /** Metadata from {@link aggregateAnalysisRun}. */
@@ -310,6 +310,7 @@ function writeLanguageVariant(lang, slug, aggregated, englishHtml, chromeOptions
310
310
  sourceMarkdownRelPath: chromeOptions.sourceMarkdownRelPath,
311
311
  toc: aggregated.sectionToc,
312
312
  articleCount: chromeOptions.articleCount,
313
+ isBasedOn: aggregated.includedArtifacts.map((a) => `https://github.com/Hack23/euparliamentmonitor/blob/main/${a.repoRelPath}`),
313
314
  });
314
315
  const filename = getArticleFilename(slug, lang);
315
316
  fs.writeFileSync(path.join(opts.outDir, filename), html, 'utf8');
@@ -396,18 +397,31 @@ export function generateArticle(opts, runSuffix, articleCountOverride) {
396
397
  const effectiveMetadata = opts.title || opts.description
397
398
  ? applyCliOverrides(resolvedMetadata, opts.title, opts.description)
398
399
  : resolvedMetadata;
399
- // Write source Markdown under <outDir>/<slug>.en.md for transparency.
400
+ // Write article.md INTO the analysis run directory — canonical Markdown
401
+ // source that lives alongside the artifacts that produced it.
402
+ // This mirrors the riksdagsmonitor pattern where `article.md` is committed
403
+ // inside `analysis/daily/<date>/<type>/` so every run has a browsable,
404
+ // version-controlled Markdown source in its own directory.
405
+ const runArticleMdAbs = path.join(opts.runDir, 'article.md');
406
+ fs.writeFileSync(runArticleMdAbs, aggregated.markdown, 'utf8');
407
+ const runArticleMdRelPath = path
408
+ .relative(opts.repoRoot, runArticleMdAbs)
409
+ .split(path.sep)
410
+ .join('/');
411
+ // Also write source Markdown under <outDir>/<slug>.en.md for search
412
+ // indexing and backwards compatibility with existing news-index scripts.
400
413
  ensureDir(opts.outDir);
401
414
  const sourceMdFilename = `${slug}.en.md`;
402
415
  const sourceMdAbs = path.join(opts.outDir, sourceMdFilename);
403
416
  fs.writeFileSync(sourceMdAbs, aggregated.markdown, 'utf8');
404
- const sourceMdRelPath = path.relative(opts.repoRoot, sourceMdAbs).split(path.sep).join('/');
405
417
  const written = [sourceMdFilename];
406
418
  if (!opts.markdownOnly) {
407
419
  const rendered = renderMarkdown(aggregated.markdown);
408
420
  const chromeOptions = {
409
421
  metadata: effectiveMetadata,
410
- sourceMarkdownRelPath: sourceMdRelPath,
422
+ // Point the "View source Markdown" link at the canonical run-directory
423
+ // article.md so readers can trace the HTML back to the analysis tree.
424
+ sourceMarkdownRelPath: runArticleMdRelPath,
411
425
  articleCount: articleCountOverride ?? countPublishedArticles(opts.repoRoot),
412
426
  };
413
427
  for (const lang of opts.langs) {
@@ -415,7 +429,12 @@ export function generateArticle(opts, runSuffix, articleCountOverride) {
415
429
  written.push(filename);
416
430
  }
417
431
  }
418
- return { sourceMarkdownRelPath: sourceMdRelPath, writtenFiles: written, aggregated };
432
+ return {
433
+ sourceMarkdownRelPath: runArticleMdRelPath,
434
+ runArticleMdRelPath,
435
+ writtenFiles: written,
436
+ aggregated,
437
+ };
419
438
  }
420
439
  /**
421
440
  * Walk `analysis/daily/` recursively and return every subdirectory that
@@ -44,6 +44,11 @@ export interface WrapArticleOptions {
44
44
  * the line is omitted.
45
45
  */
46
46
  readonly articleCount?: number;
47
+ /**
48
+ * Optional: URLs of source artifacts included in the aggregated article.
49
+ * Emitted as `isBasedOn` in the JSON-LD `NewsArticle` schema for provenance.
50
+ */
51
+ readonly isBasedOn?: readonly string[];
47
52
  }
48
53
  /**
49
54
  * Build the canonical filename for an article in a given language. English
@@ -134,6 +134,11 @@ export function wrapArticleHtml(options) {
134
134
  name: 'EU Parliament Monitor',
135
135
  url: BASE_URL,
136
136
  },
137
+ ...(options.isBasedOn && options.isBasedOn.length > 0
138
+ ? {
139
+ isBasedOn: options.isBasedOn.map((url) => ({ '@type': 'CreativeWork', url })),
140
+ }
141
+ : {}),
137
142
  };
138
143
  const jsonLdString = JSON.stringify(jsonLd).replace(/</g, '\\u003c');
139
144
  const pageTitle = `${options.title} — ${siteTitle}`;
@@ -35,6 +35,8 @@ export interface CleanArtifactResult {
35
35
  readonly strippedH1s: number;
36
36
  /** Banner/metadata lines removed. */
37
37
  readonly strippedBannerLines: number;
38
+ /** Operational metadata preamble lines removed (e.g. **Run:** / **Window:** blocks). */
39
+ readonly strippedMetaLines: number;
38
40
  /** Mermaid blocks deduplicated as a reference to a previous occurrence. */
39
41
  readonly dedupedMermaidBlocks: number;
40
42
  }
@@ -126,6 +128,28 @@ export declare function dedupMermaid(md: string, seen: Set<string>): {
126
128
  md: string;
127
129
  deduped: number;
128
130
  };
131
+ /**
132
+ * Strip the operational metadata preamble that agent pipelines prepend to
133
+ * artifacts. These are lines of the form `**Run:** …`, `**Window:** …`,
134
+ * `**Methodology:** …`, etc., followed optionally by a standalone `---`
135
+ * horizontal rule. They are agent-operational metadata that should not appear
136
+ * in the published article.
137
+ *
138
+ * Algorithm:
139
+ * 1. Skip leading blank lines (they don't count as metadata).
140
+ * 2. If the first non-blank line does NOT match the metadata pattern, return
141
+ * the document unchanged (`lines: 0`).
142
+ * 3. Otherwise consume all metadata lines and interspersed blank lines.
143
+ * 4. If the next non-blank line is a standalone `---`, consume that too.
144
+ * 5. Return the stripped Markdown and the count of lines removed.
145
+ *
146
+ * @param md - Markdown source (after banner/heading passes)
147
+ * @returns `{ md, lines }` — stripped Markdown and number of lines removed
148
+ */
149
+ export declare function stripArtifactMetadataPreamble(md: string): {
150
+ md: string;
151
+ lines: number;
152
+ };
129
153
  /**
130
154
  * Apply all cleanup passes and return the normalised Markdown plus
131
155
  * simple counters for telemetry/tests.
@@ -578,6 +578,63 @@ function hashString(input) {
578
578
  }
579
579
  return (h >>> 0).toString(16).padStart(8, '0');
580
580
  }
581
+ /**
582
+ * Pattern matching an operational metadata line at the start of an artifact.
583
+ * Examples: `**Run:** breaking-run-123`, `**Window:** 2026-04-24 00:00Z — 05:49Z`.
584
+ * The pattern requires the line to start with `**<Word>**` followed by a colon
585
+ * or whitespace so ordinary bold prose is not mistakenly treated as metadata.
586
+ */
587
+ const METADATA_LINE_PATTERN = /^\*\*[A-Za-z][^*\n]*\*\*[:\s]/;
588
+ /**
589
+ * Strip the operational metadata preamble that agent pipelines prepend to
590
+ * artifacts. These are lines of the form `**Run:** …`, `**Window:** …`,
591
+ * `**Methodology:** …`, etc., followed optionally by a standalone `---`
592
+ * horizontal rule. They are agent-operational metadata that should not appear
593
+ * in the published article.
594
+ *
595
+ * Algorithm:
596
+ * 1. Skip leading blank lines (they don't count as metadata).
597
+ * 2. If the first non-blank line does NOT match the metadata pattern, return
598
+ * the document unchanged (`lines: 0`).
599
+ * 3. Otherwise consume all metadata lines and interspersed blank lines.
600
+ * 4. If the next non-blank line is a standalone `---`, consume that too.
601
+ * 5. Return the stripped Markdown and the count of lines removed.
602
+ *
603
+ * @param md - Markdown source (after banner/heading passes)
604
+ * @returns `{ md, lines }` — stripped Markdown and number of lines removed
605
+ */
606
+ export function stripArtifactMetadataPreamble(md) {
607
+ const lines = md.split('\n');
608
+ let i = 0;
609
+ // Skip purely blank lines at the very head
610
+ while (i < lines.length && (lines[i] ?? '').trim() === '')
611
+ i++;
612
+ // If the first real line is not a metadata line, return unchanged
613
+ if (i >= lines.length || !METADATA_LINE_PATTERN.test(lines[i] ?? '')) {
614
+ return { md, lines: 0 };
615
+ }
616
+ // Consume the metadata block (metadata lines + interspersed blank lines)
617
+ let metaEnd = i;
618
+ while (metaEnd < lines.length) {
619
+ const line = lines[metaEnd] ?? '';
620
+ if (METADATA_LINE_PATTERN.test(line) || line.trim() === '') {
621
+ metaEnd++;
622
+ }
623
+ else {
624
+ break;
625
+ }
626
+ }
627
+ // If the next non-blank line is a standalone HR, absorb it
628
+ let scanAhead = metaEnd;
629
+ while (scanAhead < lines.length && (lines[scanAhead] ?? '').trim() === '')
630
+ scanAhead++;
631
+ if (scanAhead < lines.length && /^\s*---\s*$/.test(lines[scanAhead] ?? '')) {
632
+ metaEnd = scanAhead + 1;
633
+ }
634
+ const removed = metaEnd;
635
+ const stripped = lines.slice(removed).join('\n').replace(/^\n+/, '');
636
+ return { md: stripped, lines: removed };
637
+ }
581
638
  /**
582
639
  * Apply all cleanup passes and return the normalised Markdown plus
583
640
  * simple counters for telemetry/tests.
@@ -594,6 +651,8 @@ export function cleanArtifact(source, options) {
594
651
  md = mdAfterBanners;
595
652
  const { md: mdAfterHeadings, h1Count } = demoteHeadings(md);
596
653
  md = mdAfterHeadings;
654
+ const { md: mdAfterMeta, lines: strippedMetaLines } = stripArtifactMetadataPreamble(md);
655
+ md = mdAfterMeta;
597
656
  md = rewriteLinks(md, options.artifactRelPath);
598
657
  const { md: mdAfterMermaid, deduped } = dedupMermaid(md, seen);
599
658
  md = mdAfterMermaid;
@@ -603,6 +662,7 @@ export function cleanArtifact(source, options) {
603
662
  markdown: md,
604
663
  strippedH1s: h1Count,
605
664
  strippedBannerLines,
665
+ strippedMetaLines,
606
666
  dedupedMermaidBlocks: deduped,
607
667
  };
608
668
  }
@@ -9,21 +9,25 @@
9
9
  * because the upstream project is a Python git-URL package (not npm) and
10
10
  * could not be pinned to an integrity hash per the ISMS Secure Development
11
11
  * Policy §7. This module replaces the Python transport with a direct,
12
- * typed HTTP client the public API is preserved so callers
13
- * (`src/utils/imf-data.ts`, validator fingerprints, workflow probes) are
14
- * untouched.
12
+ * typed HTTP client. The public API (`IMFMCPClient`, five tool methods,
13
+ * `MCPToolResult`-shaped envelope) is stable across the migration. The
14
+ * earlier companion module `src/utils/imf-data.ts` (SDMX-JSON parser,
15
+ * indicator/country maps, HTML builders) was purged in the April-2026
16
+ * aggregator-pipeline migration — callers now consume the raw SDMX-JSON
17
+ * envelope returned by {@link IMFMCPClient.fetchData} directly.
15
18
  *
16
19
  * ## Public API (unchanged from the MCP-backed iteration)
17
20
  *
18
21
  * - {@link IMFMCPClient} — class with semantic wrappers for five "tools".
19
22
  * - {@link IMF_MCP_TOOLS} — stable virtual tool-name list used by the
20
- * content-validator fingerprint and the workflow probe.
23
+ * Stage-C editorial fingerprint and the workflow probe. Drift-guarded
24
+ * by `test/integration/mcp/imf-mcp.test.js`.
21
25
  * - {@link getIMFMCPClient} / {@link closeIMFMCPClient} — singleton lifecycle.
22
26
  *
23
27
  * The return envelope of every method is {@link MCPToolResult}
24
- * (`{ content: [{ type: "text", text: "<json>" }] }`) so downstream code
25
- * that already calls `parseSDMXJSON(response.content[0]?.text)` continues
26
- * to work unmodified.
28
+ * (`{ content: [{ type: "text", text: "<json>" }] }`). The `text` payload
29
+ * is the raw SDMX-JSON document returned by the IMF REST endpoint;
30
+ * downstream code parses it with any standard SDMX-JSON reader.
27
31
  *
28
32
  * ## Transport
29
33
  *
@@ -50,7 +54,11 @@ import type { MCPToolResult, MCPClientOptions } from '../types/index.js';
50
54
  * longer talks to an MCP server, but the tool-name list is preserved so
51
55
  * it continues to serve as:
52
56
  *
53
- * 1. The content-validator fingerprint source (`IMF_STRONG_FINGERPRINTS`).
57
+ * 1. The Stage-C editorial fingerprint source for "IMF is cited" (see
58
+ * `analysis/imf/indicator-catalog.md §6` — the earlier runtime
59
+ * fingerprint table `IMF_STRONG_FINGERPRINTS` in
60
+ * `src/utils/content-validator.ts` was purged in the April-2026
61
+ * aggregator-pipeline migration).
54
62
  * 2. The workflow probe's heartbeat identifiers.
55
63
  * 3. A drift guard against method additions: if a new helper method lands
56
64
  * here, `test/integration/mcp/imf-mcp.test.js` fails unless the list
@@ -169,8 +177,10 @@ export declare class IMFMCPClient {
169
177
  * Fetch a time-series slice from an IMF dataflow as SDMX-JSON.
170
178
  *
171
179
  * Virtual tool: `imf-fetch-data`. The response is already in SDMX-JSON
172
- * format, so {@link parseSDMXJSON} (`src/utils/imf-data.ts`) can
173
- * consume `response.content[0]?.text` directly without reshaping.
180
+ * format; callers read the series under `data.dataSets[0].series`
181
+ * using any standard SDMX-JSON reader. (The earlier helper
182
+ * `parseSDMXJSON` in `src/utils/imf-data.ts` was purged in the
183
+ * April-2026 aggregator-pipeline migration.)
174
184
  *
175
185
  * @param options - Fetch parameters.
176
186
  * @param options.databaseId - IMF dataflow ID (`"WEO"`, `"IFS"`, ...).
@@ -14,7 +14,11 @@ const IMF_FALLBACK = {
14
14
  * longer talks to an MCP server, but the tool-name list is preserved so
15
15
  * it continues to serve as:
16
16
  *
17
- * 1. The content-validator fingerprint source (`IMF_STRONG_FINGERPRINTS`).
17
+ * 1. The Stage-C editorial fingerprint source for "IMF is cited" (see
18
+ * `analysis/imf/indicator-catalog.md §6` — the earlier runtime
19
+ * fingerprint table `IMF_STRONG_FINGERPRINTS` in
20
+ * `src/utils/content-validator.ts` was purged in the April-2026
21
+ * aggregator-pipeline migration).
18
22
  * 2. The workflow probe's heartbeat identifiers.
19
23
  * 3. A drift guard against method additions: if a new helper method lands
20
24
  * here, `test/integration/mcp/imf-mcp.test.js` fails unless the list
@@ -375,8 +379,10 @@ export class IMFMCPClient {
375
379
  * Fetch a time-series slice from an IMF dataflow as SDMX-JSON.
376
380
  *
377
381
  * Virtual tool: `imf-fetch-data`. The response is already in SDMX-JSON
378
- * format, so {@link parseSDMXJSON} (`src/utils/imf-data.ts`) can
379
- * consume `response.content[0]?.text` directly without reshaping.
382
+ * format; callers read the series under `data.dataSets[0].series`
383
+ * using any standard SDMX-JSON reader. (The earlier helper
384
+ * `parseSDMXJSON` in `src/utils/imf-data.ts` was purged in the
385
+ * April-2026 aggregator-pipeline migration.)
380
386
  *
381
387
  * @param options - Fetch parameters.
382
388
  * @param options.databaseId - IMF dataflow ID (`"WEO"`, `"IFS"`, ...).
@@ -22,12 +22,15 @@
22
22
  * `c-cf/imf-data-mcp` MCP server. That transport was replaced with a
23
23
  * native TypeScript HTTP client so the stack remains npm-pure and
24
24
  * pinned per ISMS §7. The `IMF_MCP_TOOLS` identifier list is retained
25
- * as a stable "virtual tool" surface for the content-validator
26
- * fingerprint and workflow probes.
25
+ * as a stable "virtual tool" surface for the Stage-C editorial
26
+ * fingerprint (see `analysis/imf/indicator-catalog.md §6`) and the
27
+ * workflow probe. The earlier companion module `src/utils/imf-data.ts`
28
+ * (parsers, indicator/country maps, HTML builders) was purged in the
29
+ * April-2026 aggregator-pipeline migration.
27
30
  *
28
31
  * @see {@link https://dataservices.imf.org/REST/SDMX_3.0 | IMF SDMX 3.0 REST API}
29
- * @see `analysis/methodologies/imf-indicator-mapping.md` for the committee →
30
- * IMF indicator mapping enforced by the validator.
32
+ * @see {@link https://github.com/Hack23/euparliamentmonitor/blob/main/analysis/methodologies/imf-indicator-mapping.md | IMF Indicator Mapping methodology} for the committee →
33
+ * IMF indicator mapping enforced at Stage-C editorial review.
31
34
  */
32
35
  import type { MCPClientOptions } from './mcp.js';
33
36
  /**
@@ -144,11 +147,17 @@ export interface IMFForecastPoint {
144
147
  /**
145
148
  * Curated set of IMF indicator keys used across the EU Parliament Monitor.
146
149
  *
147
- * These map onto specific IMF database + SDMX dimension codes see
148
- * `IMF_POLICY_INDICATORS` in `src/utils/imf-data.ts` for the concrete
149
- * mapping. The key names are chosen to parallel the World Bank
150
- * `PolicyRelevantIndicators` keys where an equivalent series exists, so
151
- * article generators and templates can migrate incrementally.
150
+ * These map onto specific IMF database + SDMX dimension codes. The key
151
+ * names are chosen to parallel the World Bank `PolicyRelevantIndicators`
152
+ * keys where an equivalent series exists, so article generators and
153
+ * templates can migrate incrementally.
154
+ *
155
+ * The earlier runtime constant `IMF_POLICY_INDICATORS` in
156
+ * `src/utils/imf-data.ts` was purged in the April-2026
157
+ * aggregator-pipeline migration.
158
+ *
159
+ * @see {@link https://github.com/Hack23/euparliamentmonitor/blob/main/analysis/imf/indicator-catalog.md#2-policy-domain--imf-indicator-mapping | IMF indicator catalog §2}
160
+ * @see {@link https://github.com/Hack23/euparliamentmonitor/blob/main/analysis/methodologies/imf-indicator-mapping.md | IMF per-committee indicator mapping}
152
161
  */
153
162
  export type IMFMacroIndicatorKey = 'gdp' | 'gdpGrowth' | 'gdpPerCapita' | 'inflation' | 'unemployment' | 'population' | 'currentAccount' | 'exportsGdp' | 'govDebt' | 'primaryBalance' | 'structuralBalance' | 'fdiInflow' | 'realEffectiveExchangeRate' | 'policyRate';
154
163
  /**
@@ -212,10 +221,11 @@ export interface IMFEconomicContext {
212
221
  /**
213
222
  * Canonical IMF "virtual tool" names used by the native TypeScript
214
223
  * client. These identifiers are preserved from the earlier MCP-backed
215
- * iteration so the content-validator fingerprint list and workflow
216
- * probes (`scripts/imf-mcp-probe.sh`) remain stable across the
217
- * transport swap. Each name maps to a semantic method on
218
- * {@link IMFMCPClient} rather than to a remote MCP tool call.
224
+ * iteration so the Stage-C editorial fingerprint list (see
225
+ * `analysis/imf/indicator-catalog.md §6`) and the workflow probe
226
+ * (`scripts/imf-mcp-probe.sh`) remain stable across the transport
227
+ * swap. Each name maps to a semantic method on {@link IMFMCPClient}
228
+ * rather than to a remote MCP tool call.
219
229
  */
220
230
  export type IMFMCPToolName = 'imf-list-databases' | 'imf-search-databases' | 'imf-get-parameter-defs' | 'imf-get-parameter-codes' | 'imf-fetch-data';
221
231
  //# sourceMappingURL=imf.d.ts.map