@cyanheads/eur-lex-mcp-server 0.2.0 → 0.3.0

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.
Files changed (33) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/README.md +3 -2
  4. package/changelog/0.2.x/0.2.1.md +16 -0
  5. package/changelog/0.3.x/0.3.0.md +26 -0
  6. package/dist/config/server-config.d.ts.map +1 -1
  7. package/dist/config/server-config.js +4 -2
  8. package/dist/config/server-config.js.map +1 -1
  9. package/dist/mcp-server/tools/definitions/eurlex-get-cases.tool.d.ts +6 -6
  10. package/dist/mcp-server/tools/definitions/eurlex-get-cases.tool.d.ts.map +1 -1
  11. package/dist/mcp-server/tools/definitions/eurlex-get-cases.tool.js +54 -20
  12. package/dist/mcp-server/tools/definitions/eurlex-get-cases.tool.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/eurlex-get-document.tool.d.ts +12 -0
  14. package/dist/mcp-server/tools/definitions/eurlex-get-document.tool.d.ts.map +1 -1
  15. package/dist/mcp-server/tools/definitions/eurlex-get-document.tool.js +130 -23
  16. package/dist/mcp-server/tools/definitions/eurlex-get-document.tool.js.map +1 -1
  17. package/dist/mcp-server/tools/definitions/eurlex-lookup-celex.tool.d.ts +1 -1
  18. package/dist/mcp-server/tools/definitions/eurlex-search-documents.tool.d.ts +5 -5
  19. package/dist/mcp-server/tools/definitions/eurlex-search-documents.tool.d.ts.map +1 -1
  20. package/dist/mcp-server/tools/definitions/eurlex-search-documents.tool.js +63 -24
  21. package/dist/mcp-server/tools/definitions/eurlex-search-documents.tool.js.map +1 -1
  22. package/dist/mcp-server/tools/definitions/index.d.ts +24 -12
  23. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
  24. package/dist/services/cellar-sparql/cdm-labels.d.ts +9 -0
  25. package/dist/services/cellar-sparql/cdm-labels.d.ts.map +1 -1
  26. package/dist/services/cellar-sparql/cdm-labels.js +16 -0
  27. package/dist/services/cellar-sparql/cdm-labels.js.map +1 -1
  28. package/dist/services/eurlex-content/eurlex-content-service.d.ts +42 -9
  29. package/dist/services/eurlex-content/eurlex-content-service.d.ts.map +1 -1
  30. package/dist/services/eurlex-content/eurlex-content-service.js +135 -30
  31. package/dist/services/eurlex-content/eurlex-content-service.js.map +1 -1
  32. package/package.json +1 -1
  33. package/server.json +7 -7
@@ -1,11 +1,86 @@
1
1
  /**
2
- * @fileoverview EurLexContentService — HTTP client for the EUR-Lex REST content API.
3
- * Fetches full HTML or XML text of EU legal acts via the legal-content URL pattern.
4
- * Document content is NOT available via CELLAR work URI content negotiation (returns 400).
2
+ * @fileoverview EurLexContentService — HTTP client for EU act full-text content.
3
+ *
4
+ * Sources content from the EU Publications Office CELLAR content-negotiation
5
+ * resolver (`publications.europa.eu/resource/celex/{CELEX}`) — the same host the
6
+ * metadata SPARQL pipeline already queries — rather than the legacy
7
+ * `eur-lex.europa.eu` legal-content endpoint, which is now fronted by an AWS WAF
8
+ * that returns a JavaScript bot-challenge stub instead of the act text (issue #16).
9
+ *
10
+ * Content negotiation:
11
+ * - `Accept`: HTML acts vary by document family — OJ legislation exposes
12
+ * `application/xhtml+xml`, CJEU judgments expose `text/html`, so the HTML path
13
+ * tries both. The XML path requests Formex 4 (`application/xml;type=fmx4`),
14
+ * which CELLAR serves directly for single-part acts and returns HTTP 300
15
+ * (multiple manifestation streams) for multi-part OJ acts — treated as
16
+ * unavailable rather than reconstructed.
17
+ * - `Accept-Language`: CELLAR requires an ISO 639-2/T (three-letter) code and
18
+ * 400s on a missing one or on a bibliographic 639-2/B code (`ger`, `fre`);
19
+ * EUR-Lex two-letter codes are mapped before the request.
20
+ *
21
+ * Defense in depth: any response carrying an AWS WAF challenge signature is
22
+ * refused (never surfaced as content) and raised as a ServiceUnavailable error,
23
+ * so a challenge stub can never again be reported as `contentAvailable: true`.
5
24
  * @module services/eurlex-content/eurlex-content-service
6
25
  */
7
26
  import { serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
8
27
  import { withRetry } from '@cyanheads/mcp-ts-core/utils';
28
+ /**
29
+ * Map EUR-Lex two-letter language codes to the ISO 639-2/T (terminological,
30
+ * three-letter) codes CELLAR's content-negotiation resolver accepts in
31
+ * `Accept-Language`. CELLAR rejects bibliographic 639-2/B codes (`ger`, `fre`,
32
+ * `dut`, …), so the terminological forms (`deu`, `fra`, `nld`, …) are used.
33
+ */
34
+ const LANGUAGE_TO_ISO_639_2 = {
35
+ EN: 'eng',
36
+ FR: 'fra',
37
+ DE: 'deu',
38
+ ES: 'spa',
39
+ IT: 'ita',
40
+ PL: 'pol',
41
+ PT: 'por',
42
+ NL: 'nld',
43
+ CS: 'ces',
44
+ DA: 'dan',
45
+ EL: 'ell',
46
+ ET: 'est',
47
+ FI: 'fin',
48
+ HU: 'hun',
49
+ LT: 'lit',
50
+ LV: 'lav',
51
+ MT: 'mlt',
52
+ RO: 'ron',
53
+ SK: 'slk',
54
+ SL: 'slv',
55
+ SV: 'swe',
56
+ BG: 'bul',
57
+ HR: 'hrv',
58
+ GA: 'gle',
59
+ };
60
+ /**
61
+ * `Accept` values tried per format, in order. HTML resolves to `application/xhtml+xml`
62
+ * for OJ legislation and `text/html` for CJEU judgments; the first to return a body
63
+ * wins. XML requests Formex 4 only.
64
+ */
65
+ const ACCEPT_BY_FORMAT = {
66
+ html: ['application/xhtml+xml', 'text/html'],
67
+ xml: ['application/xml;type=fmx4'],
68
+ };
69
+ /**
70
+ * AWS WAF bot-challenge signatures. `awswaf` matches the challenge.js host
71
+ * (`token.awswaf.com`), the cookie-domain list, and the `AwsWafIntegration`
72
+ * calls; `gokuprops` matches the per-request challenge blob. Both are
73
+ * WAF-specific and never appear in legitimate EU legal text. Matched
74
+ * case-insensitively against the response head.
75
+ */
76
+ const CHALLENGE_MARKERS = ['awswaf', 'gokuprops'];
77
+ /** Bodies shorter than this (after trimming) are treated as empty/unavailable. */
78
+ const MIN_CONTENT_LENGTH = 100;
79
+ /** True when a response body carries an AWS WAF bot-challenge signature. */
80
+ function isChallengeResponse(body) {
81
+ const head = body.slice(0, 4096).toLowerCase();
82
+ return CHALLENGE_MARKERS.some((marker) => head.includes(marker));
83
+ }
9
84
  export class EurLexContentService {
10
85
  baseUrl;
11
86
  timeoutMs;
@@ -14,26 +89,28 @@ export class EurLexContentService {
14
89
  this.timeoutMs = serverConfig.sparqlQueryTimeoutMs;
15
90
  }
16
91
  /**
17
- * Build the EUR-Lex legal-content URL for a CELEX number.
18
- * Pattern: /legal-content/{LANG}/TXT/{FORMAT}/?uri=CELEX:{celex}
92
+ * Build the CELLAR content-negotiation URL for a CELEX number.
93
+ * Pattern: /resource/celex/{CELEX} (format + language come from request headers).
19
94
  */
20
- buildContentUrl(celexNumber, language, format) {
21
- const fmt = format === 'xml' ? 'XML' : 'HTML';
22
- return `${this.baseUrl}/legal-content/${language}/TXT/${fmt}/?uri=CELEX:${celexNumber}`;
95
+ buildContentUrl(celexNumber) {
96
+ return `${this.baseUrl}/resource/celex/${encodeURIComponent(celexNumber)}`;
23
97
  }
24
98
  /**
25
99
  * Fetch the full text content of an EU act by CELEX number.
26
100
  * If the requested language is unavailable, falls back to English.
27
101
  * Returns `contentAvailable: false` with an empty string if both attempts fail.
102
+ *
103
+ * Throws ServiceUnavailable if the content host returns an AWS WAF bot-challenge
104
+ * stub — a challenge is never reported as available content.
28
105
  */
29
106
  async fetchContent(celexNumber, language, format, ctx) {
30
- const primary = await this.tryFetch(celexNumber, language, format, ctx);
107
+ const primary = await this.fetchForLanguage(celexNumber, language, format, ctx);
31
108
  if (primary !== null) {
32
109
  return { content: primary, language, format, contentAvailable: true };
33
110
  }
34
- // Language fallback: try English if primary language failed
111
+ // Language fallback: try English if primary language failed.
35
112
  if (language !== 'EN') {
36
- const fallback = await this.tryFetch(celexNumber, 'EN', format, ctx);
113
+ const fallback = await this.fetchForLanguage(celexNumber, 'EN', format, ctx);
37
114
  if (fallback !== null) {
38
115
  return {
39
116
  content: fallback,
@@ -47,36 +124,64 @@ export class EurLexContentService {
47
124
  return { content: '', language, format, contentAvailable: false };
48
125
  }
49
126
  /**
50
- * Attempt to fetch content for a specific language. Returns null on non-200 or
51
- * empty/redirect response rather than throwing callers handle fallback logic.
127
+ * Resolve content for one language by trying each `Accept` variant for the
128
+ * format. Returns the first non-empty body, or null when none of the variants
129
+ * yield content (so the caller can fall back to English). Throws when a variant
130
+ * returns a bot-challenge stub.
52
131
  */
53
- async tryFetch(celexNumber, language, format, ctx) {
54
- const url = this.buildContentUrl(celexNumber, language, format);
132
+ async fetchForLanguage(celexNumber, language, format, ctx) {
133
+ const isoLanguage = LANGUAGE_TO_ISO_639_2[language];
134
+ if (!isoLanguage)
135
+ return null;
136
+ for (const accept of ACCEPT_BY_FORMAT[format]) {
137
+ const outcome = await this.fetchVariant(celexNumber, accept, isoLanguage, ctx);
138
+ if (outcome.kind === 'challenge') {
139
+ throw serviceUnavailable(`The EU content endpoint returned a bot-challenge interstitial instead of the act text for CELEX ${celexNumber}.`, {
140
+ celexNumber,
141
+ reason: 'content_challenge',
142
+ recovery: {
143
+ hint: 'The content host is behind a WAF/bot challenge. Retry shortly; metadata remains ' +
144
+ 'reachable via content_mode "metadata_only". A persistent challenge means ' +
145
+ 'EURLEX_CONTENT_BASE_URL points at a WAF-protected host rather than the EU ' +
146
+ 'Publications Office CELLAR resolver.',
147
+ },
148
+ });
149
+ }
150
+ if (outcome.kind === 'content')
151
+ return outcome.text;
152
+ }
153
+ return null;
154
+ }
155
+ /**
156
+ * Single content-negotiation request for one `Accept`/`Accept-Language` pair.
157
+ * Non-2xx responses (404 = no datastream of that type, 300 = multi-part Formex,
158
+ * 4xx/5xx) and network failures resolve to `none` so callers can try the next
159
+ * variant or language. A WAF challenge body resolves to `challenge`. The inner
160
+ * function only throws on a `fetch` rejection, so `withRetry` retries transient
161
+ * network errors but never a 404 or a challenge.
162
+ */
163
+ fetchVariant(celexNumber, accept, isoLanguage, ctx) {
164
+ const url = this.buildContentUrl(celexNumber);
55
165
  return withRetry(async () => {
56
166
  const response = await fetch(url, {
57
- headers: { Accept: format === 'xml' ? 'application/xml' : 'text/html' },
167
+ headers: { Accept: accept, 'Accept-Language': isoLanguage },
58
168
  signal: AbortSignal.timeout(this.timeoutMs),
59
169
  redirect: 'follow',
60
170
  });
61
- if (response.status === 404 || response.status === 302 || !response.ok) {
62
- return null;
63
- }
171
+ if (!response.ok)
172
+ return { kind: 'none' };
64
173
  const text = await response.text();
65
- // Detect HTML error pages masquerading as success (e.g. rate-limit pages)
66
- if (format === 'xml' && /^\s*<(!DOCTYPE\s+html|html[\s>])/i.test(text)) {
67
- throw serviceUnavailable('EUR-Lex returned HTML instead of XML content.');
68
- }
69
- // Empty content means unavailable
70
- if (text.trim().length < 100) {
71
- return null;
72
- }
73
- return text;
174
+ if (isChallengeResponse(text))
175
+ return { kind: 'challenge' };
176
+ if (text.trim().length < MIN_CONTENT_LENGTH)
177
+ return { kind: 'none' };
178
+ return { kind: 'content', text };
74
179
  }, {
75
- operation: 'EurLexContentService.tryFetch',
180
+ operation: 'EurLexContentService.fetchVariant',
76
181
  baseDelayMs: 1000,
77
182
  maxRetries: 2,
78
183
  signal: ctx.signal,
79
- }).catch(() => null); // Treat fetch failures as unavailable for language fallback
184
+ }).catch(() => ({ kind: 'none' }));
80
185
  }
81
186
  }
82
187
  // --- Init/accessor pattern ---
@@ -1 +1 @@
1
- {"version":3,"file":"eurlex-content-service.js","sourceRoot":"","sources":["../../../src/services/eurlex-content/eurlex-content-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAyCzD,MAAM,OAAO,oBAAoB;IACd,OAAO,CAAS;IAChB,SAAS,CAAS;IAEnC,YAAY,OAAkB,EAAE,QAAwB,EAAE,YAA0B;QAClF,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC,oBAAoB,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,eAAe,CAAC,WAAmB,EAAE,QAAwB,EAAE,MAAqB;QAClF,MAAM,GAAG,GAAG,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC9C,OAAO,GAAG,IAAI,CAAC,OAAO,kBAAkB,QAAQ,QAAQ,GAAG,eAAe,WAAW,EAAE,CAAC;IAC1F,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAChB,WAAmB,EACnB,QAAwB,EACxB,MAAqB,EACrB,GAAY;QAEZ,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QACxE,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC;QACxE,CAAC;QAED,4DAA4D;QAC5D,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YACrE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACtB,OAAO;oBACL,OAAO,EAAE,QAAQ;oBACjB,QAAQ,EAAE,IAAI;oBACd,MAAM;oBACN,gBAAgB,EAAE,IAAI;oBACtB,gBAAgB,EAAE,sBAAsB,QAAQ,yCAAyC;iBAC1F,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC;IACpE,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,QAAQ,CACpB,WAAmB,EACnB,QAAwB,EACxB,MAAqB,EACrB,GAAY;QAEZ,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEhE,OAAO,SAAS,CACd,KAAK,IAAI,EAAE;YACT,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,EAAE;gBACvE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;gBAC3C,QAAQ,EAAE,QAAQ;aACnB,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACvE,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEnC,0EAA0E;YAC1E,IAAI,MAAM,KAAK,KAAK,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvE,MAAM,kBAAkB,CAAC,+CAA+C,CAAC,CAAC;YAC5E,CAAC;YAED,kCAAkC;YAClC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBAC7B,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC,EACD;YACE,SAAS,EAAE,+BAA+B;YAC1C,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,CAAC;YACb,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CACF,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,4DAA4D;IACnF,CAAC;CACF;AAED,gCAAgC;AAEhC,IAAI,QAA0C,CAAC;AAE/C,MAAM,UAAU,wBAAwB,CACtC,MAAiB,EACjB,OAAuB,EACvB,YAA0B;IAE1B,QAAQ,GAAG,IAAI,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,mFAAmF,CACpF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"eurlex-content-service.js","sourceRoot":"","sources":["../../../src/services/eurlex-content/eurlex-content-service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/eur-lex-mcp-server",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "mcpName": "io.github.cyanheads/eur-lex-mcp-server",
5
5
  "description": "Search EU legislation, CJEU case law, and treaties; traverse the CELLAR relationship graph; resolve EuroVoc concepts via MCP. STDIO or Streamable HTTP.",
6
6
  "type": "module",
package/server.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/cyanheads/eur-lex-mcp-server",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.2.0",
9
+ "version": "0.3.0",
10
10
  "remotes": [
11
11
  {
12
12
  "type": "streamable-http",
@@ -19,7 +19,7 @@
19
19
  "registryBaseUrl": "https://registry.npmjs.org",
20
20
  "identifier": "@cyanheads/eur-lex-mcp-server",
21
21
  "runtimeHint": "bun",
22
- "version": "0.2.0",
22
+ "version": "0.3.0",
23
23
  "packageArguments": [
24
24
  {
25
25
  "type": "positional",
@@ -40,10 +40,10 @@
40
40
  },
41
41
  {
42
42
  "name": "EURLEX_CONTENT_BASE_URL",
43
- "description": "EUR-Lex content API base URL override.",
43
+ "description": "Base URL of the EU Publications Office CELLAR content-negotiation resolver that serves act full text; override e.g. for a local mirror.",
44
44
  "format": "string",
45
45
  "isRequired": false,
46
- "default": "https://eur-lex.europa.eu"
46
+ "default": "http://publications.europa.eu"
47
47
  },
48
48
  {
49
49
  "name": "SPARQL_QUERY_TIMEOUT_MS",
@@ -76,7 +76,7 @@
76
76
  "registryBaseUrl": "https://registry.npmjs.org",
77
77
  "identifier": "@cyanheads/eur-lex-mcp-server",
78
78
  "runtimeHint": "bun",
79
- "version": "0.2.0",
79
+ "version": "0.3.0",
80
80
  "packageArguments": [
81
81
  {
82
82
  "type": "positional",
@@ -138,10 +138,10 @@
138
138
  },
139
139
  {
140
140
  "name": "EURLEX_CONTENT_BASE_URL",
141
- "description": "EUR-Lex content API base URL override.",
141
+ "description": "Base URL of the EU Publications Office CELLAR content-negotiation resolver that serves act full text; override e.g. for a local mirror.",
142
142
  "format": "string",
143
143
  "isRequired": false,
144
- "default": "https://eur-lex.europa.eu"
144
+ "default": "http://publications.europa.eu"
145
145
  },
146
146
  {
147
147
  "name": "SPARQL_QUERY_TIMEOUT_MS",