@cyanheads/eur-lex-mcp-server 0.2.1 → 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.
package/AGENTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** eur-lex-mcp-server
4
- **Version:** 0.2.1
4
+ **Version:** 0.3.0
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.10.9`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
package/CLAUDE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** eur-lex-mcp-server
4
- **Version:** 0.2.1
4
+ **Version:** 0.3.0
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.10.9`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.2.1-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/eur-lex-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/eur-lex-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/eur-lex-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.11-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.3.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/eur-lex-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/eur-lex-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/eur-lex-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.11-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -62,6 +62,7 @@ Fetch the notice and full text of an EU legal act.
62
62
  - Accepts CELEX numbers (e.g., `32016R0679`) or ELI URIs
63
63
  - Returns structured metadata: title, date, document type, author institution, legal basis, EuroVoc subjects, in-force flag
64
64
  - Full text in HTML (default) or Formex4 XML
65
+ - Content shaping for large acts: `content_mode` `"paged"` (default) returns a bounded character window (`offset` + `limit`) with `content_chars_total` and `has_more` so you can page to the end; `"full"` returns the whole body in one call; `"metadata_only"` skips the body
65
66
  - Supports all 24 official EU languages; defaults to English with automatic fallback when a translation is unavailable
66
67
  - Older acts and some CJEU judgments may lack English translations
67
68
 
@@ -275,7 +276,7 @@ All configuration is validated at startup via Zod schemas in `src/config/server-
275
276
  | Variable | Description | Default |
276
277
  |:---------|:------------|:--------|
277
278
  | `CELLAR_SPARQL_ENDPOINT` | CELLAR SPARQL endpoint URL override (e.g., for a local Virtuoso mirror). | `http://publications.europa.eu/webapi/rdf/sparql` |
278
- | `EURLEX_CONTENT_BASE_URL` | EUR-Lex content API base URL override. | `https://eur-lex.europa.eu` |
279
+ | `EURLEX_CONTENT_BASE_URL` | EU Publications Office CELLAR content resolver base URL override. | `http://publications.europa.eu` |
279
280
  | `SPARQL_QUERY_TIMEOUT_MS` | Client-side timeout for SPARQL requests in milliseconds. | `55000` |
280
281
  | `MAX_SPARQL_RESULTS` | Enforced ceiling on LIMIT in all generated SPARQL queries. | `100` |
281
282
  | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
@@ -0,0 +1,26 @@
1
+ ---
2
+ summary: "eurlex_get_document re-sources act text from the EU Publications Office CELLAR resolver and refuses AWS WAF bot-challenge stubs (previously surfaced as content); adds content_mode/offset/limit body pagination with content_* navigation fields and removes the 8,000-char text cut"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.3.0 — 2026-06-30
8
+
9
+ ## Added
10
+
11
+ - **`content_mode`, `offset`, `limit` inputs on `eurlex_get_document`** ([#12](https://github.com/cyanheads/eur-lex-mcp-server/issues/12)) — `content_mode` selects how much body to return: `"paged"` (default) yields a bounded `[offset, offset+limit)` window, `"full"` the entire body in one call, `"metadata_only"` skips the content fetch. `limit` defaults to 25,000 characters, capped at 100,000.
12
+ - **`content_*` navigation output fields** ([#12](https://github.com/cyanheads/eur-lex-mcp-server/issues/12)) — `content_mode`, `content_offset`, `content_chars_returned`, `content_chars_total`, and `has_more` let a client page contiguous windows to the end of an act and reconstruct the full body. Paging past the end returns an empty window with `has_more: false`, not an error.
13
+
14
+ ## Changed
15
+
16
+ - **Act full text re-sourced from CELLAR content negotiation** ([#16](https://github.com/cyanheads/eur-lex-mcp-server/issues/16)) — `EurLexContentService` fetches `publications.europa.eu/resource/celex/{CELEX}` (`application/xhtml+xml` → `text/html` for HTML, Formex 4 for XML) instead of the WAF-fronted `eur-lex.europa.eu` legal-content endpoint. `Accept-Language` maps EUR-Lex two-letter codes to the ISO 639-2/T forms CELLAR requires; a multi-part Formex `300` response is treated as unavailable rather than reconstructed.
17
+ - **`EURLEX_CONTENT_BASE_URL` default is now `http://publications.europa.eu`** ([#16](https://github.com/cyanheads/eur-lex-mcp-server/issues/16)) — was `https://eur-lex.europa.eu`. The env var name is unchanged; `server.json`, `README.md`, and `.env.example` updated to match the CELLAR content-negotiation source.
18
+ - **`content_available` distinguishes "not requested" from "unavailable"** ([#12](https://github.com/cyanheads/eur-lex-mcp-server/issues/12)) — now `false` in `"metadata_only"` mode (no fetch attempted); pair it with `content_mode` to tell the two apart.
19
+
20
+ ## Removed
21
+
22
+ - **The 8,000-character `format()` content cut** ([#12](https://github.com/cyanheads/eur-lex-mcp-server/issues/12)) — the text view and `structuredContent.content` honor the same `content_mode` window, so there is no separate downstream truncation.
23
+
24
+ ## Fixed
25
+
26
+ - **AWS WAF bot-challenge stub surfaced as act content** ([#16](https://github.com/cyanheads/eur-lex-mcp-server/issues/16)) — `eurlex_get_document` returned the JavaScript bot-challenge interstitial (the `awswaf`/`gokuProps` stub) as `content` with `content_available: true` for every CELEX. A response carrying a WAF challenge signature is now detected and raised as a `content_challenge` / `ServiceUnavailable` error rather than reported as available content.
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;iBAwBtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAQ9C"}
1
+ {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;iBA4BtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAQ9C"}
@@ -14,8 +14,10 @@ const ServerConfigSchema = z.object({
14
14
  eurLexContentBaseUrl: z
15
15
  .string()
16
16
  .url()
17
- .default('https://eur-lex.europa.eu')
18
- .describe('EUR-Lex content API base URL'),
17
+ .default('http://publications.europa.eu')
18
+ .describe('Base URL of the EU Publications Office CELLAR content-negotiation resolver, which serves ' +
19
+ 'act text via /resource/celex/{CELEX}. Replaces the WAF-protected eur-lex.europa.eu ' +
20
+ 'legal-content endpoint, which now returns an AWS WAF bot-challenge stub (issue #16).'),
19
21
  sparqlQueryTimeoutMs: z.coerce
20
22
  .number()
21
23
  .int()
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,oBAAoB,EAAE,CAAC;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,iDAAiD,CAAC;SAC1D,QAAQ,CAAC,uCAAuC,CAAC;IACpD,oBAAoB,EAAE,CAAC;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,2BAA2B,CAAC;SACpC,QAAQ,CAAC,8BAA8B,CAAC;IAC3C,oBAAoB,EAAE,CAAC,CAAC,MAAM;SAC3B,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,yDAAyD,CAAC;IACtE,gBAAgB,EAAE,CAAC,CAAC,MAAM;SACvB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,GAAG,CAAC,GAAG,CAAC;SACR,OAAO,CAAC,GAAG,CAAC;SACZ,QAAQ,CAAC,2DAA2D,CAAC;CACzE,CAAC,CAAC;AAIH,IAAI,OAAiC,CAAC;AAEtC,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,cAAc,CAAC,kBAAkB,EAAE;QAC7C,oBAAoB,EAAE,wBAAwB;QAC9C,oBAAoB,EAAE,yBAAyB;QAC/C,oBAAoB,EAAE,yBAAyB;QAC/C,gBAAgB,EAAE,oBAAoB;KACvC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,oBAAoB,EAAE,CAAC;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,iDAAiD,CAAC;SAC1D,QAAQ,CAAC,uCAAuC,CAAC;IACpD,oBAAoB,EAAE,CAAC;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,+BAA+B,CAAC;SACxC,QAAQ,CACP,2FAA2F;QACzF,qFAAqF;QACrF,sFAAsF,CACzF;IACH,oBAAoB,EAAE,CAAC,CAAC,MAAM;SAC3B,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,yDAAyD,CAAC;IACtE,gBAAgB,EAAE,CAAC,CAAC,MAAM;SACvB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,GAAG,CAAC,GAAG,CAAC;SACR,OAAO,CAAC,GAAG,CAAC;SACZ,QAAQ,CAAC,2DAA2D,CAAC;CACzE,CAAC,CAAC;AAIH,IAAI,OAAiC,CAAC;AAEtC,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,cAAc,CAAC,kBAAkB,EAAE;QAC7C,oBAAoB,EAAE,wBAAwB;QAC9C,oBAAoB,EAAE,yBAAyB;QAC/C,oBAAoB,EAAE,yBAAyB;QAC/C,gBAAgB,EAAE,oBAAoB;KACvC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -12,6 +12,13 @@ export declare const eurlex_get_document: import("@cyanheads/mcp-ts-core").ToolD
12
12
  html: "html";
13
13
  xml: "xml";
14
14
  }>>;
15
+ content_mode: z.ZodDefault<z.ZodEnum<{
16
+ full: "full";
17
+ metadata_only: "metadata_only";
18
+ paged: "paged";
19
+ }>>;
20
+ offset: z.ZodDefault<z.ZodNumber>;
21
+ limit: z.ZodDefault<z.ZodNumber>;
15
22
  }, z.core.$strip>, z.ZodObject<{
16
23
  celex_number: z.ZodString;
17
24
  work_uri: z.ZodOptional<z.ZodString>;
@@ -23,7 +30,12 @@ export declare const eurlex_get_document: import("@cyanheads/mcp-ts-core").ToolD
23
30
  eurovoc_subjects: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
31
  in_force: z.ZodOptional<z.ZodBoolean>;
25
32
  content: z.ZodOptional<z.ZodString>;
33
+ content_mode: z.ZodString;
26
34
  content_available: z.ZodBoolean;
35
+ content_offset: z.ZodOptional<z.ZodNumber>;
36
+ content_chars_returned: z.ZodOptional<z.ZodNumber>;
37
+ content_chars_total: z.ZodOptional<z.ZodNumber>;
38
+ has_more: z.ZodBoolean;
27
39
  language: z.ZodString;
28
40
  language_fallback: z.ZodOptional<z.ZodString>;
29
41
  content_format: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"eurlex-get-document.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurlex-get-document.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAiBjE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgS9B,CAAC"}
1
+ {"version":3,"file":"eurlex-get-document.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurlex-get-document.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA2BjE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA6Z9B,CAAC"}
@@ -8,6 +8,14 @@ import { ENG_LANGUAGE_URI, resolveCorporateBodyLabel, resolveResourceTypeLabel,
8
8
  import { CellarSparqlService, getCellarSparqlService, } from '../../../services/cellar-sparql/cellar-sparql-service.js';
9
9
  import { escapeSparqlLiteral, resolveEliToWork } from '../../../services/cellar-sparql/eli-resolution.js';
10
10
  import { getEurLexContentService, } from '../../../services/eurlex-content/eurlex-content-service.js';
11
+ /**
12
+ * Default character window returned for body content in "paged" mode — bounds a
13
+ * single call while keeping small acts whole. The tail of a larger act is never
14
+ * lost: page forward with `offset`, or request `content_mode: "full"`.
15
+ */
16
+ const DEFAULT_CONTENT_LIMIT = 25_000;
17
+ /** Hard ceiling on one paged window. Use `content_mode: "full"` for the whole body in a single call. */
18
+ const MAX_CONTENT_LIMIT = 100_000;
11
19
  export const eurlex_get_document = tool('eurlex_get_document', {
12
20
  title: 'Get EU Document',
13
21
  description: 'Fetch the notice (metadata) and full text of an EU act by CELEX number or ELI URI. ' +
@@ -18,7 +26,10 @@ export const eurlex_get_document = tool('eurlex_get_document', {
18
26
  'If the requested language is unavailable, the server automatically falls back to English and notes the fallback. ' +
19
27
  'CELEX format: {sector}{year}{type}{number} e.g. 32016R0679 for GDPR. ' +
20
28
  'Use eurlex_lookup_celex to validate an identifier before calling this tool. ' +
21
- 'HTML format returns the full act text suitable for reading; XML returns Formex4 for structured processing.',
29
+ 'HTML format returns the full act text suitable for reading; XML returns Formex4 for structured processing. ' +
30
+ 'Large bodies are bounded per call but never lost: content_mode "paged" (default) returns a character window ' +
31
+ '(offset + limit) alongside content_chars_total and has_more, so you can page to the end and reconstruct the whole act; ' +
32
+ 'content_mode "full" returns the entire body in one call; content_mode "metadata_only" returns metadata with no body and skips the content fetch.',
22
33
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
23
34
  input: z.object({
24
35
  celex_number: z
@@ -42,6 +53,27 @@ export const eurlex_get_document = tool('eurlex_get_document', {
42
53
  .enum(['html', 'xml'])
43
54
  .default('html')
44
55
  .describe('Content format: "html" for readable HTML text (default), "xml" for Formex4 XML structured format.'),
56
+ content_mode: z
57
+ .enum(['metadata_only', 'paged', 'full'])
58
+ .default('paged')
59
+ .describe('How much of the document body to return. "paged" (default) returns a bounded character window — see offset/limit; ' +
60
+ '"full" returns the entire body in one call (large acts can be hundreds of KB); ' +
61
+ '"metadata_only" returns metadata with no body and skips the content fetch. offset and limit apply only to "paged".'),
62
+ offset: z
63
+ .number()
64
+ .int()
65
+ .min(0)
66
+ .default(0)
67
+ .describe('Character offset into the full document body where the returned window starts ("paged" mode only). ' +
68
+ 'Page forward by setting offset = content_offset + content_chars_returned from the previous call.'),
69
+ limit: z
70
+ .number()
71
+ .int()
72
+ .min(1)
73
+ .max(MAX_CONTENT_LIMIT)
74
+ .default(DEFAULT_CONTENT_LIMIT)
75
+ .describe(`Maximum characters of body content to return in this window ("paged" mode only). Default ${DEFAULT_CONTENT_LIMIT}, max ${MAX_CONTENT_LIMIT}. ` +
76
+ 'For the entire body in one response, use content_mode "full" instead of a large limit.'),
45
77
  }),
46
78
  output: z.object({
47
79
  celex_number: z.string().describe('Confirmed CELEX number for the retrieved work.'),
@@ -71,8 +103,36 @@ export const eurlex_get_document = tool('eurlex_get_document', {
71
103
  content: z
72
104
  .string()
73
105
  .optional()
74
- .describe('Full text content of the act in the requested format and language.'),
75
- content_available: z.boolean().describe('Whether document content was successfully retrieved.'),
106
+ .describe('Body content of the act in the requested format and language. In "paged" mode this is a character window ' +
107
+ '(see content_offset / content_chars_returned / has_more); in "full" mode the entire body; ' +
108
+ 'omitted in "metadata_only" mode, when the window is empty (offset past the end), or when content is unavailable.'),
109
+ content_mode: z
110
+ .string()
111
+ .describe('Content mode applied to this response: "metadata_only", "paged", or "full".'),
112
+ content_available: z
113
+ .boolean()
114
+ .describe('Whether body content was fetched from EUR-Lex. False in "metadata_only" mode (no fetch attempted) — ' +
115
+ 'use content_mode to distinguish "not requested" from "unavailable upstream".'),
116
+ content_offset: z
117
+ .number()
118
+ .int()
119
+ .optional()
120
+ .describe('Character offset where the returned content window begins. Present when a body was fetched and available.'),
121
+ content_chars_returned: z
122
+ .number()
123
+ .int()
124
+ .optional()
125
+ .describe('Number of body characters returned in this response (equals content length). Present when a body was fetched and available.'),
126
+ content_chars_total: z
127
+ .number()
128
+ .int()
129
+ .optional()
130
+ .describe('Total character length of the full document body. Present when content was fetched and available; ' +
131
+ 'use with content_offset to page through the entire act.'),
132
+ has_more: z
133
+ .boolean()
134
+ .describe('True when body content exists beyond the returned window. Page forward with offset = content_offset + content_chars_returned, ' +
135
+ 'or request content_mode "full" for the entire act in one call. Always false in "metadata_only" mode.'),
76
136
  language: z.string().describe('Language code of the returned content.'),
77
137
  language_fallback: z
78
138
  .string()
@@ -183,12 +243,18 @@ SELECT ?work ?celexNumber ?type ?date ?title ?inForce ?author ?legalBasis ?eurov
183
243
  const inForceStr = CellarSparqlService.bindingValue(first, 'inForce');
184
244
  const inForce = inForceStr !== undefined ? inForceStr === 'true' : undefined;
185
245
  const authorUri = CellarSparqlService.bindingValue(first, 'author');
186
- // Step 2: Fetch document content via EUR-Lex content API
187
- const contentResult = await contentSvc.fetchContent(celexNumber, language, format, ctx);
246
+ // Step 2: assemble metadata, then shape the body per content_mode. The body
247
+ // is one navigable mechanism "metadata_only" skips the fetch entirely,
248
+ // "full" returns the whole body, and "paged" returns a bounded
249
+ // [offset, offset+limit) window with content_chars_total + has_more so the
250
+ // tail is always reachable. The same shaped `content` feeds both
251
+ // structuredContent and format(); there is no separate truncation downstream.
188
252
  const result = {
189
253
  celex_number: confirmedCelex,
190
- content_available: contentResult.contentAvailable,
191
- language: contentResult.language,
254
+ content_mode: input.content_mode,
255
+ content_available: false,
256
+ has_more: false,
257
+ language,
192
258
  content_format: format,
193
259
  };
194
260
  if (workUri)
@@ -207,11 +273,36 @@ SELECT ?work ?celexNumber ?type ?date ?title ?inForce ?author ?legalBasis ?eurov
207
273
  result.eurovoc_subjects = [...eurovocConcepts];
208
274
  if (typeof inForce === 'boolean')
209
275
  result.in_force = inForce;
210
- if (contentResult.contentAvailable && contentResult.content) {
211
- result.content = contentResult.content;
212
- }
213
- if (contentResult.languageFallback) {
214
- result.language_fallback = contentResult.languageFallback;
276
+ if (input.content_mode !== 'metadata_only') {
277
+ const contentResult = await contentSvc.fetchContent(celexNumber, language, format, ctx);
278
+ result.content_available = contentResult.contentAvailable;
279
+ result.language = contentResult.language;
280
+ if (contentResult.languageFallback) {
281
+ result.language_fallback = contentResult.languageFallback;
282
+ }
283
+ if (contentResult.contentAvailable && contentResult.content) {
284
+ const full = contentResult.content;
285
+ const total = full.length;
286
+ result.content_chars_total = total;
287
+ if (input.content_mode === 'full') {
288
+ result.content = full;
289
+ result.content_offset = 0;
290
+ result.content_chars_returned = total;
291
+ result.has_more = false;
292
+ }
293
+ else {
294
+ // Bounded [offset, offset+limit) window over the full body. offset is
295
+ // clamped to the body length so over-paging returns an empty window
296
+ // (has_more false) rather than erroring.
297
+ const offset = Math.min(input.offset, total);
298
+ const windowText = full.slice(offset, offset + input.limit);
299
+ result.content_offset = offset;
300
+ result.content_chars_returned = windowText.length;
301
+ result.has_more = offset + windowText.length < total;
302
+ if (windowText.length > 0)
303
+ result.content = windowText;
304
+ }
305
+ }
215
306
  }
216
307
  return result;
217
308
  },
@@ -238,22 +329,38 @@ SELECT ?work ?celexNumber ?type ?date ?title ?inForce ?author ?legalBasis ?eurov
238
329
  lines.push(`**Language:** ${result.language} | **Format:** ${result.content_format}`);
239
330
  if (result.language_fallback)
240
331
  lines.push(`*Note: ${result.language_fallback}*`);
241
- lines.push(`**Content Available:** ${result.content_available}`);
242
- if (result.content_available && result.content) {
332
+ // Body rendering honors the same window as structuredContent.content the
333
+ // shaped content is emitted verbatim with a navigation line; no second cut.
334
+ if (result.content_mode === 'metadata_only') {
243
335
  lines.push('');
244
- lines.push('---');
245
- lines.push('');
246
- // Truncate very large content for format() output
247
- const maxLen = 8000;
248
- if (result.content.length > maxLen) {
249
- lines.push(result.content.slice(0, maxLen));
250
- lines.push(`\n*[Content truncated ${result.content.length} chars total. Use the CELEX number to fetch directly.]*`);
336
+ lines.push('*Body omitted (content_mode "metadata_only"). Request content_mode "paged" or "full" to retrieve the text.*');
337
+ }
338
+ else if (result.content_available) {
339
+ const total = result.content_chars_total ?? result.content?.length ?? 0;
340
+ if (result.content) {
341
+ const start = result.content_offset ?? 0;
342
+ const returned = result.content_chars_returned ?? result.content.length;
343
+ const end = start + returned;
344
+ if (result.content_mode === 'full') {
345
+ lines.push(`**Content** (full): full body — ${returned} of ${total} characters.`);
346
+ }
347
+ else {
348
+ lines.push(`**Content** (${result.content_mode}): characters ${start}–${end} of ${total} (${returned} returned).` +
349
+ (result.has_more
350
+ ? ` More available — page forward with offset=${end}, or content_mode="full" for the entire act.`
351
+ : ' End of document.'));
352
+ }
353
+ lines.push('');
354
+ lines.push('---');
355
+ lines.push('');
356
+ lines.push(result.content);
251
357
  }
252
358
  else {
253
- lines.push(result.content);
359
+ lines.push('');
360
+ lines.push(`*No content at offset ${result.content_offset ?? 0} — past the end of the ${total}-character body. Lower offset to read.*`);
254
361
  }
255
362
  }
256
- else if (!result.content_available) {
363
+ else {
257
364
  lines.push('');
258
365
  lines.push('*Document content is not available for this work in the requested language.*');
259
366
  }
@@ -1 +1 @@
1
- {"version":3,"file":"eurlex-get-document.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurlex-get-document.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"eurlex-get-document.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurlex-get-document.tool.ts"],"names":[],"mappings":"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"}
@@ -72,6 +72,13 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
72
72
  html: "html";
73
73
  xml: "xml";
74
74
  }>>;
75
+ content_mode: import("zod").ZodDefault<import("zod").ZodEnum<{
76
+ full: "full";
77
+ metadata_only: "metadata_only";
78
+ paged: "paged";
79
+ }>>;
80
+ offset: import("zod").ZodDefault<import("zod").ZodNumber>;
81
+ limit: import("zod").ZodDefault<import("zod").ZodNumber>;
75
82
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
76
83
  celex_number: import("zod").ZodString;
77
84
  work_uri: import("zod").ZodOptional<import("zod").ZodString>;
@@ -83,7 +90,12 @@ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolD
83
90
  eurovoc_subjects: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
84
91
  in_force: import("zod").ZodOptional<import("zod").ZodBoolean>;
85
92
  content: import("zod").ZodOptional<import("zod").ZodString>;
93
+ content_mode: import("zod").ZodString;
86
94
  content_available: import("zod").ZodBoolean;
95
+ content_offset: import("zod").ZodOptional<import("zod").ZodNumber>;
96
+ content_chars_returned: import("zod").ZodOptional<import("zod").ZodNumber>;
97
+ content_chars_total: import("zod").ZodOptional<import("zod").ZodNumber>;
98
+ has_more: import("zod").ZodBoolean;
87
99
  language: import("zod").ZodString;
88
100
  language_fallback: import("zod").ZodOptional<import("zod").ZodString>;
89
101
  content_format: import("zod").ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQ9B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQ9B,CAAC"}
@@ -1,7 +1,26 @@
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 type { Context } from '@cyanheads/mcp-ts-core';
@@ -24,21 +43,35 @@ export declare class EurLexContentService {
24
43
  private readonly timeoutMs;
25
44
  constructor(_config: AppConfig, _storage: StorageService, serverConfig: ServerConfig);
26
45
  /**
27
- * Build the EUR-Lex legal-content URL for a CELEX number.
28
- * Pattern: /legal-content/{LANG}/TXT/{FORMAT}/?uri=CELEX:{celex}
46
+ * Build the CELLAR content-negotiation URL for a CELEX number.
47
+ * Pattern: /resource/celex/{CELEX} (format + language come from request headers).
29
48
  */
30
- buildContentUrl(celexNumber: string, language: EurLexLanguage, format: ContentFormat): string;
49
+ buildContentUrl(celexNumber: string): string;
31
50
  /**
32
51
  * Fetch the full text content of an EU act by CELEX number.
33
52
  * If the requested language is unavailable, falls back to English.
34
53
  * Returns `contentAvailable: false` with an empty string if both attempts fail.
54
+ *
55
+ * Throws ServiceUnavailable if the content host returns an AWS WAF bot-challenge
56
+ * stub — a challenge is never reported as available content.
35
57
  */
36
58
  fetchContent(celexNumber: string, language: EurLexLanguage, format: ContentFormat, ctx: Context): Promise<FetchContentResult>;
37
59
  /**
38
- * Attempt to fetch content for a specific language. Returns null on non-200 or
39
- * empty/redirect response rather than throwing callers handle fallback logic.
60
+ * Resolve content for one language by trying each `Accept` variant for the
61
+ * format. Returns the first non-empty body, or null when none of the variants
62
+ * yield content (so the caller can fall back to English). Throws when a variant
63
+ * returns a bot-challenge stub.
40
64
  */
41
- private tryFetch;
65
+ private fetchForLanguage;
66
+ /**
67
+ * Single content-negotiation request for one `Accept`/`Accept-Language` pair.
68
+ * Non-2xx responses (404 = no datastream of that type, 300 = multi-part Formex,
69
+ * 4xx/5xx) and network failures resolve to `none` so callers can try the next
70
+ * variant or language. A WAF challenge body resolves to `challenge`. The inner
71
+ * function only throws on a `fetch` rejection, so `withRetry` retries transient
72
+ * network errors but never a 404 or a challenge.
73
+ */
74
+ private fetchVariant;
42
75
  }
43
76
  export declare function initEurLexContentService(config: AppConfig, storage: StorageService, serverConfig: ServerConfig): void;
44
77
  export declare function getEurLexContentService(): EurLexContentService;
@@ -1 +1 @@
1
- {"version":3,"file":"eurlex-content-service.d.ts","sourceRoot":"","sources":["../../../src/services/eurlex-content/eurlex-content-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,sEAAsE;AACtE,MAAM,MAAM,cAAc,GACtB,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,CAAC;AAET,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,cAAc,CAAC;IACzB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY;IAKpF;;;OAGG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM;IAK7F;;;;OAIG;IACG,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,kBAAkB,CAAC;IAuB9B;;;OAGG;YACW,QAAQ;CA0CvB;AAMD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,YAAY,GACzB,IAAI,CAEN;AAED,wBAAgB,uBAAuB,IAAI,oBAAoB,CAO9D"}
1
+ {"version":3,"file":"eurlex-content-service.d.ts","sourceRoot":"","sources":["../../../src/services/eurlex-content/eurlex-content-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,CAAC;AAE3C,sEAAsE;AACtE,MAAM,MAAM,cAAc,GACtB,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,CAAC;AAkET,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,cAAc,CAAC;IACzB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY;IAKpF;;;OAGG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAI5C;;;;;;;OAOG;IACG,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,kBAAkB,CAAC;IAuB9B;;;;;OAKG;YACW,gBAAgB;IAgC9B;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;CA+BrB;AAMD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,YAAY,GACzB,IAAI,CAEN;AAED,wBAAgB,uBAAuB,IAAI,oBAAoB,CAO9D"}
@@ -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.1",
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.1",
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.1",
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.1",
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",