eur-lex-mcp-server

v0.9.8 pre-1.0

Search EU legislation, CJEU case law, and treaties; traverse the CELLAR relationship graph; resolve EuroVoc concepts via MCP. STDIO or Streamable HTTP.

eur-lex.caseyjhand.com/mcp
claude mcp add --transport http eur-lex-mcp-server https://eur-lex.caseyjhand.com/mcp
codex mcp add eur-lex-mcp-server --url https://eur-lex.caseyjhand.com/mcp
{
  "mcpServers": {
    "eur-lex-mcp-server": {
      "url": "https://eur-lex.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http eur-lex-mcp-server https://eur-lex.caseyjhand.com/mcp
{
  "mcpServers": {
    "eur-lex-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "https://eur-lex.caseyjhand.com/mcp"
      ]
    }
  }
}
{
  "mcpServers": {
    "eur-lex-mcp-server": {
      "type": "http",
      "url": "https://eur-lex.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://eur-lex.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

7

eurlex_lookup_celex

open-world

Resolve an EU legal citation — a CELEX number or ELI URI — to its canonical CELLAR work, confirming it exists before you fetch or traverse it. Returns the work URI, confirmed CELEX number, document type, and date.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "eurlex_lookup_celex",
    "arguments": {
      "identifier": "<identifier>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "identifier": {
      "type": "string",
      "minLength": 1,
      "description": "The EU legal citation to resolve: a CELEX number (e.g. 32016R0679) or a work-level ELI URI (e.g. http://data.europa.eu/eli/reg/2016/679, with or without the /oj suffix)."
    },
    "identifier_type": {
      "default": "auto",
      "description": "Format of the identifier. \"auto\" detects it automatically; supply \"celex\" or \"eli\" explicitly if detection fails.",
      "type": "string",
      "enum": [
        "celex",
        "eli",
        "auto"
      ]
    }
  },
  "required": [
    "identifier",
    "identifier_type"
  ],
  "additionalProperties": false
}
view source ↗

eurlex_browse_subjects

open-world

Search the EuroVoc thesaurus, resolving a keyword into concept URIs usable in the eurovoc_concept subject filter of eurlex_search_documents. Returns each concept URI, its preferred label in the requested language, code, and broader (parent) label, ordered alphabetically by label.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "eurlex_browse_subjects",
    "arguments": {
      "keyword": "<keyword>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "keyword": {
      "type": "string",
      "minLength": 1,
      "description": "Search term to match against EuroVoc concept labels (e.g. \"privacy\", \"agriculture\", \"trade\")."
    },
    "language": {
      "default": "en",
      "description": "Language code for concept labels (e.g. \"en\", \"fr\", \"de\"). Case-insensitive — \"EN\" and \"en\" behave identically. Defaults to English.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}$"
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset — number of concepts to skip. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 20,
      "description": "Maximum number of EuroVoc concepts to return (1–50). Defaults to 20.",
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    }
  },
  "required": [
    "keyword",
    "language",
    "offset",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

eurlex_search_documents

open-world

Search EU legislation, treaties, and preparatory acts across the CELLAR corpus by document type, date range, EuroVoc subject, author institution, and in-force status. Keyword matches English titles and CELEX strings only — there is no full-text body search. Returns a page of CELEX numbers, work URIs, type labels, dates, and titles, newest first, each flagged with is_consolidated. At least one filter is required.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "eurlex_search_documents",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "keyword": {
      "description": "Keyword matched against English document titles via the full-text index (multi-word input is treated as a phrase), or against CELEX substrings.",
      "type": "string"
    },
    "document_type": {
      "description": "Document type: REG=Regulation, DIR=Directive, DEC=Decision, TREATY=Treaty, JUDG=Judgment, OPIN_AG=AG Opinion, PROP=Proposal, REC=Recommendation. Omit to search all types. A type filter excludes consolidated texts (CONS_TEXT) — set include_consolidated to fold them back in.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "enum": [
            "REG",
            "DIR",
            "DEC",
            "TREATY",
            "JUDG",
            "OPIN_AG",
            "PROP",
            "REC"
          ],
          "description": "Document type: REG=Regulation, DIR=Directive, DEC=Decision, TREATY=Treaty, JUDG=Judgment, OPIN_AG=AG Opinion, PROP=Proposal, REC=Recommendation."
        }
      ]
    },
    "include_consolidated": {
      "default": false,
      "description": "When true and document_type is set, also match consolidated texts (CONS_TEXT) of that type — point-in-time versions that a plain type filter omits. No effect when document_type is omitted. Consolidated rows are always tagged is_consolidated: true.",
      "type": "boolean"
    },
    "date_from": {
      "description": "Start of date range (YYYY-MM-DD), matched against document date. Omit for no lower bound.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "Start date in ISO 8601 format (YYYY-MM-DD)."
        }
      ]
    },
    "date_to": {
      "description": "End of date range (YYYY-MM-DD), matched against document date. Omit for no upper bound.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "End date in ISO 8601 format (YYYY-MM-DD)."
        }
      ]
    },
    "eurovoc_concept": {
      "description": "EuroVoc concept URI to filter by subject (e.g. http://eurovoc.europa.eu/2828), obtained from eurlex_browse_subjects. Omit for no subject filter.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "description": "EuroVoc concept URI (e.g. http://eurovoc.europa.eu/2828)."
        }
      ]
    },
    "author_institution": {
      "description": "Author institution name (e.g. \"European Parliament\", \"Council\", \"European Commission\"), matched against the English names of EU corporate bodies.",
      "type": "string"
    },
    "in_force": {
      "description": "If true, restrict to acts currently in force. Omit to return all regardless of in-force status.",
      "type": "boolean"
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset — number of results to skip. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 20,
      "description": "Maximum number of results to return (1–100). Defaults to 20.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": [
    "include_consolidated",
    "offset",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

eurlex_get_cases

open-world

Search CJEU and General Court case law — judgments, orders, and Advocate General opinions — by case number, court, case type, keyword, and date range. By default only these primary records are returned; derivative judicial information notices, case abstracts, summaries, and corrigenda are excluded so distinct cases fill the page (set include_derivative to include them). Keyword matches English case titles (which carry party names) and CELEX strings; there is no full-text body search. Returns each case with its court, date, and type, plus — parsed from the title where present — the parties, subject matter, and case reference.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "eurlex_get_cases",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "case_number": {
      "description": "Case number in standard format: C-{num}/{year} for CJEU or T-{num}/{year} for General Court (e.g. C-131/12).",
      "type": "string"
    },
    "keyword": {
      "description": "Keyword to match against case titles and CELEX strings.",
      "type": "string"
    },
    "court": {
      "description": "Court filter: CJEU = Court of Justice of the EU, GC = General Court. Omit to search both.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "enum": [
            "CJEU",
            "GC"
          ],
          "description": "CJEU = Court of Justice of the EU, GC = General Court."
        }
      ]
    },
    "case_type": {
      "description": "Case type: judgment, order (procedural decision), or ag_opinion (Advocate General opinion). Omit to search all.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "enum": [
            "judgment",
            "order",
            "ag_opinion"
          ],
          "description": "judgment, order (procedural decision), or ag_opinion (Advocate General opinion)."
        }
      ]
    },
    "include_derivative": {
      "default": false,
      "description": "Include derivative sector-6 records — judicial information notices, case abstracts, case summaries, and corrigenda — alongside primary judgments, orders, and AG opinions. Default false: these are excluded so distinct primary cases fill the page. Ignored when case_type is set (that path already returns a single primary type).",
      "type": "boolean"
    },
    "date_from": {
      "description": "Start of date range in ISO 8601 format (YYYY-MM-DD). Leave blank or omit for no lower bound.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "Start date in ISO 8601 format (YYYY-MM-DD)."
        }
      ]
    },
    "date_to": {
      "description": "End of date range in ISO 8601 format (YYYY-MM-DD). Leave blank or omit for no upper bound.",
      "anyOf": [
        {
          "type": "string",
          "const": ""
        },
        {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "End date in ISO 8601 format (YYYY-MM-DD)."
        }
      ]
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset — number of results to skip. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 20,
      "description": "Maximum number of results to return (1–100). Defaults to 20.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": [
    "include_derivative",
    "offset",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

eurlex_get_document

open-world

Fetch the metadata and full text of an EU act by CELEX number, ELI URI, or work URI. Returns structured metadata (title, date, type, author institution, legal basis, EuroVoc subjects, in-force status) plus the act body as HTML, Markdown, or Formex4 XML, defaulting to English with automatic fallback. Large bodies are paged by default (offset/limit with has_more) or returned whole with content_mode "full"; use outline: true for a heading map and select to pull specific articles, chapters, recitals, or annexes.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "eurlex_get_document",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "celex_number": {
      "description": "CELEX number of the act to fetch (e.g. 32016R0679 for GDPR). Provide exactly one of celex_number, eli_uri, or work_uri.",
      "type": "string"
    },
    "eli_uri": {
      "description": "Work-level ELI URI of the act to fetch (e.g. http://data.europa.eu/eli/reg/2016/679, with or without the /oj suffix). Provide exactly one of celex_number, eli_uri, or work_uri.",
      "type": "string"
    },
    "work_uri": {
      "description": "CELLAR work resource URI to fetch (e.g. http://publications.europa.eu/resource/cellar/3e485e15-11bd-11e6-ba9a-01aa75ed71a1) — the form returned by eurlex_lookup_celex, eurlex_get_relations, and eurlex_search_documents. Provide exactly one of celex_number, eli_uri, or work_uri.",
      "type": "string"
    },
    "resolve": {
      "default": "as_requested",
      "description": "Which version to serve for a base act with newer consolidated versions. \"as_requested\" (default) returns the exact CELEX requested; \"current_consolidated\" serves the newest consolidated version instead (echoing the request in requested_celex), a no-op when none exists. Either way, is_superseded / current_consolidated_celex / consolidated_as_of flag a stale base act.",
      "type": "string",
      "enum": [
        "as_requested",
        "current_consolidated"
      ]
    },
    "language": {
      "default": "EN",
      "description": "Language code for document content (ISO 639-1 uppercase, e.g. EN, FR, DE). Defaults to EN, and falls back to EN if the requested language is unavailable.",
      "type": "string",
      "pattern": "^[A-Za-z]{2,3}$"
    },
    "format": {
      "default": "html",
      "description": "Content format: \"html\" for the act text as served by EUR-Lex (default), \"markdown\" for that HTML converted to clean Markdown server-side, or \"xml\" for Formex4 structured XML.",
      "type": "string",
      "enum": [
        "html",
        "xml",
        "markdown"
      ]
    },
    "content_mode": {
      "default": "paged",
      "description": "How much of the body to return. \"paged\" (default) returns a bounded character window (see offset/limit); \"full\" returns the entire body in one call (large acts can be hundreds of KB); \"metadata_only\" skips the content fetch. offset and limit apply only to \"paged\".",
      "type": "string",
      "enum": [
        "metadata_only",
        "paged",
        "full"
      ]
    },
    "offset": {
      "default": 0,
      "description": "Character offset into the body where the returned window starts (\"paged\" mode only). Page forward by setting offset = content_offset + content_chars_returned from the previous call. Offsets are format-specific: an offset is only valid against the same format it was measured in — keep format constant when paging.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 25000,
      "description": "Maximum characters to return in this window (\"paged\" mode only). Default 25000, max 100000. For the entire body in one response, use content_mode \"full\".",
      "type": "integer",
      "minimum": 1,
      "maximum": 100000
    },
    "outline": {
      "default": false,
      "description": "Return a structural outline of the act — chapters, sections, articles, annexes, and recitals as a heading list, each with its character offset — instead of body text. Read a section by paging with its offset, keeping the same format: outline offsets are measured in the requested format's body and land in the wrong place under any other format. Ignores offset/limit and select; no detectable structure returns an empty outline. Not applied in content_mode \"metadata_only\".",
      "type": "boolean"
    },
    "select": {
      "description": "Return only the text of specific sections by type and number, instead of a raw character window (Roman and Arabic numbers are equivalent). Sections are located in the body of the requested format, so pair select with the same format used for any outline. A section that cannot be located is reported in selection.missed with no wrong text returned. Ignored when outline is true or in content_mode \"metadata_only\".",
      "type": "object",
      "properties": {
        "articles": {
          "description": "Comma-separated article numbers, e.g. \"1,5,17\".",
          "type": "string"
        },
        "chapters": {
          "description": "Comma-separated chapter numbers, Roman or Arabic, e.g. \"I,IV\" or \"1,4\".",
          "type": "string"
        },
        "recitals": {
          "description": "Comma-separated recital numbers, e.g. \"1,10\".",
          "type": "string"
        },
        "annexes": {
          "description": "Comma-separated annex numbers or letters, e.g. \"I,II\".",
          "type": "string"
        }
      },
      "additionalProperties": false
    }
  },
  "required": [
    "resolve",
    "language",
    "format",
    "content_mode",
    "offset",
    "limit",
    "outline"
  ],
  "additionalProperties": false
}
view source ↗

eurlex_get_relations

open-world

Traverse the one-hop CDM relationship graph of an EU act: what it amends or is amended by, what it repeals or is repealed by (explicit and implicit), its consolidated versions, its legal basis, and works that cite it. Returns direct relations only, paginated per relation type and direction. Requires a CELEX number or CELLAR work URI.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "eurlex_get_relations",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "celex_number": {
      "description": "CELEX number of the work to traverse (e.g. 32016R0679). Provide exactly one of celex_number or work_uri.",
      "type": "string"
    },
    "work_uri": {
      "description": "CELLAR work resource URI to traverse (e.g. http://publications.europa.eu/resource/cellar/3e485e15-11bd-11e6-ba9a-01aa75ed71a1). Used directly without CELEX resolution. Provide exactly one of celex_number or work_uri.",
      "type": "string"
    },
    "relation_types": {
      "description": "Subset of relation types to return; omit for all. Types: cites, amends, amended_by, repeals, repealed_by, implicitly_repeals, implicitly_repealed_by, legal_basis (treaty/article this act rests on), consolidated_version (consolidated texts of this act).",
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "cites",
          "amends",
          "amended_by",
          "repeals",
          "repealed_by",
          "implicitly_repeals",
          "implicitly_repealed_by",
          "legal_basis",
          "consolidated_version"
        ]
      }
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset applied per relation type and direction — related works to skip (default 0). Page forward by adding limit; incoming edges are newest-first, so higher offsets reach older works.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 100,
      "description": "Maximum related works per relation type and direction (1–100, default 100). Incoming edges are ordered newest-first, so the cap keeps the newest — page with offset for older ones. When truncated is true, at least one direction filled its cap.",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": [
    "offset",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

eurlex_query_sparql

open-world

Run a raw, read-only SPARQL SELECT against the CELLAR Virtuoso endpoint — an escape hatch for CDM ontology traversals the curated tools do not cover. Only SELECT is accepted; update forms and ASK/CONSTRUCT/DESCRIBE are rejected before execution, and results are capped at 100. The cdm:, skos:, and xsd: prefixes are auto-injected.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "eurlex_query_sparql",
    "arguments": {
      "sparql_query": "<sparql_query>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "sparql_query": {
      "type": "string",
      "minLength": 10,
      "description": "A read-only SPARQL SELECT query. Leading comments and PREFIX/BASE declarations are allowed; the cdm:, skos:, and xsd: prefixes are auto-injected. LIMIT is injected at 100 if absent, or capped to 100. Key CDM predicates: cdm:resource_legal_id_celex (CELEX), cdm:work_date_document (date), cdm:work_has_resource-type (type), cdm:work_is_about_concept_eurovoc (EuroVoc subject), cdm:work_cites_work (citation). CELEX is an xsd:string literal — match it with FILTER(STR(?celex) = \"…\"). For text, use bif:contains with a single-quoted phrase."
    },
    "timeout_hint": {
      "description": "Optional client-side timeout in milliseconds (1000–55000). Defaults apply when omitted; the endpoint hard limit is 60 seconds.",
      "type": "integer",
      "minimum": 1000,
      "maximum": 55000
    }
  },
  "required": [
    "sparql_query"
  ],
  "additionalProperties": false
}
view source ↗

Resources

2

Metadata snapshot for a CELLAR work by CELEX number — human-readable document type and author institution labels, date, title, and in-force flag.

uri eurlex://document/{celexNumber} mime application/json

One-hop CDM relationship summary for a CELLAR work by CELEX number: amendment chain, consolidations, legal basis, and cited-by.

uri eurlex://document/{celexNumber}/relations mime application/json

Prompts

1

Frame a comparative legal analysis of a policy domain across EU and US law, driving eurlex_search_documents and eurlex_get_document for the EU side and courtlistener_search_opinions for the US counterpart.

  • domainrequired — Policy domain for the comparison (e.g. "data privacy", "antitrust", "AI regulation", "food safety").
  • focus — Optional sub-topic or specific aspect to focus on (e.g. "enforcement mechanisms", "data subject rights", "remedies").