# EULEX MCP — Public Tools (v4.0)

Production tool reference for the EULEX legal-research MCP server. Used as
the source artefact for connector.
The same names appear in `tools/list` returned by the server at
`https://mcp.eulex.ai/mcp` over Streamable HTTP.

> Status: **read-only** — no write tools, no irreversible operations,
> no file I/O. All tools call into the EULEX backend (CloudSQL, Neo4j,
> Pinecone, EUR-Lex Cellar SPARQL, Eurostat REST) and return structured
> JSON with citations.

---

## Tier overview

| Tier | Count | Names |
|------|-------|-------|
| Descriptors (Free) | 2 | `about`, `list_scopes` |
| Universal (Free) | 8 | `search`, `get_section`, `get_metadata`, `get_structure`, `find_by_date`, `get_timeline`, `get_related`, `verify` |
| Specialized (Plus) | 2 | `eu_transposition`, `eurostat_query` |
| **Total public** | **12** | |
| Hidden in v4.0 (still in source) | 3 | `eulex_compare_documents`, `eulex_compliance_snapshot`, `eulex_check_in_force_live` |

Free tier: 50 calls/day. Plus tier: 2,000 calls/day. Partner tokens
bypass the limiter and self-throttle.

---

## Public tools — full table

| # | Name | Purpose | Input (summary) | Output / provenance | Tier / auth scope | Latency hint | Read-only |
|---|------|---------|-----------------|---------------------|-------------------|--------------|-----------|
| 1 | `about` | System info, sources, freshness | `()` | `AboutResponseModel` (sources, counts, build, disclaimer); `eulex_citation` | Free / no scope | < 200 ms (cached) | ✅ |
| 2 | `list_scopes` | Filterable values for `search` | `(source?)` | `ScopeInfo` (collections, doc types, EuroVoc descriptors); `eulex_citation` | Free / no scope | < 200 ms (cached) | ✅ |
| 3 | `search` | Hybrid semantic + keyword search | `query`, `scope?`, `document_types?`, `date_from?`, `date_to?`, `in_force_only?`, `top_k=10` (1–50) | `SearchResponse` (ranked results: text, celex, title, source URL, score); per-result `eulex_citation` | Free / no scope | 0.8–2.5 s typical | ✅ |
| 4 | `get_section` | Full text of a specific section | `celex_id`, `section_ref` | `ArticleContent` (text, neighbors, source URL); `eulex_citation` | Free / no scope | 200–600 ms | ✅ |
| 5 | `get_metadata` | Identity / classification / abstract | `celex_id`, `include_toc=false` | `DocumentInfo` (title, type, dates, EuroVoc, status, optional GPT abstract); `eulex_citation` | Free / no scope | 200–500 ms | ✅ |
| 6 | `get_structure` | Table of contents (chapters / articles) | `celex_id`, `include_summaries=false` | `TableOfContents` (chapter / article hierarchy with refs); `eulex_citation` | Free / no scope | 300–800 ms | ✅ |
| 7 | `find_by_date` | Documents by entry-into-force / publication date | `year?`, `date_from?`, `date_to?`, `document_type?`, `collection?`, `in_force_only?`, `limit=20` (1–100) | `DatesResult` (`DatedDocument[]` with effect / document / end-of-validity dates); `eulex_citation` | Free / no scope | 400 ms – 1.5 s | ✅ |
| 8 | `get_timeline` | Chronological legal events | `celex_id`, `event_types?` (client-side filter) | `TimelineResponse` (publication, entry into force, application, amendments, repeal); `eulex_citation` | Free / no scope | 400 ms – 1.2 s | ✅ |
| 9 | `get_related` | Knowledge-graph relationships | `celex_id`, `relationship_types?`, `include_implementing_acts=false`, `scope?` | `RelationshipMap` (amendments, citations, legal basis, consolidations, implementing acts, suggested types); `eulex_citation` | Free / no scope | 300 ms – 1.5 s | ✅ |
| 10 | `verify` | In-force check; cached or live SPARQL | `celex_id`, `live=false` | `DocumentStatus` (status, dates, source URL); `live=true` adds `found`, `entry_into_force`, `end_of_validity`; `eulex_citation` | Free / no scope | Cached: 200 ms · live: **5–30 s** | ✅ |
| 11 | `eu_transposition` | EU directive → national-law transposition | `doc_id`, `member_state?`, `summary_only=false`, `nim_details_cap=5` | `TranspositionStatus` (per-MS counts, NIM details, deadlines); `eulex_citation` | **Plus / `mcp:plus`** | **5–30 s** (Cellar SPARQL) | ✅ |
| 12 | `eurostat_query` | Natural-language Eurostat query | `question` | `EurostatResult` (`dataset_code`, `dataset_title`, `markdown_table`, `source_url`, `match_confidence`, `alternative_sources`); `eulex_citation` | **Plus / `mcp:plus`** | **15–30 s** (NL → dataset, Eurostat REST) | ✅ |

**Latency hint** values are typical p50/p95 ranges from production
traffic. Slow paths are explicitly flagged: `verify(live=true)`,
`eu_transposition`, `eurostat_query` all hit upstream services that we
cannot speed up — set client-side timeouts ≥ 30 s, see
[`MCP_DATA_COVERAGE.md` → Rate limits and graceful degradation](MCP_DATA_COVERAGE.md#rate-limits-and-graceful-degradation).

---

## Hidden tools (kept in source, NOT in `tools/list`)

These are intentionally not registered as MCP tools in v4.0. The Python
implementations remain in the codebase so internal callers and a future
return are straightforward. Listed here for transparency:

| Hidden tool | Replacement |
|-------------|-------------|
| `eulex_compare_documents` | Compose with two `get_section` calls and reason over the results agent-side. |
| `eulex_compliance_snapshot` | Compose with `get_metadata` + `verify` + `get_related` (+ `eu_transposition` for directives). |
| `eulex_check_in_force_live` | Use `verify(celex_id, live=true)` — same SPARQL backend, exposed as a flag. |

Why hide them?

* `compare` and `compliance_snapshot` are **compositions of other
  tools**. They live more naturally in the agent stack: the agent
  decides which sub-calls are actually needed for the user's question.
* `check_in_force_live` and `verify` answered the same question with
  different cost / freshness trade-offs. Folding into one tool with a
  `live` flag reduces the API surface and makes the trade-off explicit.

---

## Agent composition recipes

Suggested recipes for agents (prompted to compose what would otherwise
have been a hidden tool). These are referenced in
`app/services/mcpagent/system_prompt.py`.

### Compare two sections / documents

```
get_section(celex_a, "<ref>")
get_section(celex_b, "<ref>")
# then reason over both texts and surface differences / overlaps
```

If the user wants a structural comparison (EuroVoc / amendments /
common citations rather than free-text), supplement with:

```
get_metadata(celex_a)
get_metadata(celex_b)
get_related(celex_a)
get_related(celex_b)
```

### Compliance snapshot

```
get_metadata(doc_id)        # identity, dates, EuroVoc, abstract
verify(doc_id)              # cached in-force status (fast)
get_related(doc_id)         # amendments, case law, implementing acts
# for a directive, also:
eu_transposition(doc_id)    # per-member-state status
```

### Live in-force check

```
verify(doc_id, live=true)   # EUR-Lex Cellar SPARQL, 5–30 s
```

Use only when freshness is critical (e.g., the cached value is older
than `about().sources['EUR-Lex Legislation'].last_sync` and the act
might have changed since).

---

## CSV-style summary (for Anthropic submission)

```
name,tier,read_only,latency_ms_p50,description
about,free,true,150,System info & data freshness
list_scopes,free,true,150,Filterable scope discovery
search,free,true,1500,Hybrid semantic + keyword search
get_section,free,true,400,Full text of a section (article / recital / annex)
get_metadata,free,true,350,Identity / classification / abstract
get_structure,free,true,500,Table of contents (chapters / articles)
find_by_date,free,true,800,Documents by entry-into-force / publication date
get_timeline,free,true,700,Chronological legal events
get_related,free,true,800,Knowledge-graph relationships
verify,free,true,250,In-force check (cached); 5–30 s when live=true
eu_transposition,plus,true,15000,Directive transposition status (live SPARQL)
eurostat_query,plus,true,20000,Natural-language Eurostat query
```
