Skip to main content
Glama
librejustice

LibreJustice

Official

LibreJustice

librejustice.fr: a free search engine for French-language and European law, plus current public notices. It links case law from France's administrative and judicial courts to consolidated legal texts and makes them searchable in plain language on the web and through MCP.

Free and open source. ~3.8 million decisions and ~3.9 million legal articles in one database, linked article by article, resynchronised daily. That is the whole point: texts and case law live together, so there is no second server to plug in for the codes and no third one for the citation graph.

  • Case law: Conseil d'État, Cour de cassation, cours d'appel, administrative and judicial first-instance courts, Conseil constitutionnel, CNIL, CNDA (publicly released decisions only), Tribunal des conflits, ECHR, CJEU. Filters by court, date, outcome and cited articles.

  • Legal texts: codes and statutes as they stood on any date (consolidated versions with per-article history), the full Journal officiel, EU law, treaties and bilateral accords, collective agreements, BOFiP, circulaires, and the codes of 59 foreign countries.

  • Public notices: open public procurement opportunities from BOAMP and buyer platforms, deduplicated with submission links and deadlines.

  • Directory: lawyers, companies and courts, linked to their litigation.

Corpus refreshed daily from open sources (Judilibre, DILA/Légifrance, EUR-Lex...).

Related MCP server: mpc-legifrance

What the official sources do not offer

Public databases are silos, each with a search engine bounded by its own fonds (Judilibre covers the judicial courts only; the administrative open data searches CE/CAA/TA by keyword, court and date; Légifrance holds the texts). LibreJustice consolidates them and adds the missing layer:

  • plain-language search (hybrid lexical + semantic) with AI reranking, dynamic facets and similar decisions;

  • the graph: citations extracted and resolved, from a decision to the exact articles it cites at the right version, and from an article back to the decisions that cite it;

  • cross-source deduplication (one decision, one page) and recovery of decisions missing from the official channels;

  • AI summaries per decision, always kept distinct from verbatim excerpts;

  • a remote MCP endpoint for AI assistants.

Using LibreJustice from an AI assistant

The public MCP server is https://librejustice.fr/mcp (OAuth 2.1 with dynamic client registration: no API key to configure). Four tools: search_decisions, get_decision, search_norms, get_norm.

With Claude Code, the plugin installs the connector and the usage skills:

/plugin marketplace add librejustice/librejustice
/plugin install librejustice@librejustice

The skills alone (without the MCP connector) install into any compatible agent through skills:

npx skills add librejustice/librejustice

On claude.ai, LibreJustice is listed in the connector directory. One click, then the OAuth authorisation. With ChatGPT, Le Chat or Perplexity, add a custom connector pointing at https://librejustice.fr/mcp.

Using LibreJustice from your own code

The same corpus in REST, described by openapi.json (OpenAPI 3.1). The key is free: create an account, then a key from your profile. Guide at librejustice.fr/api-guide.

curl -H 'Authorization: Bearer ljk_your_key' \
  'https://librejustice.fr/api/search?q=trouble+anormal+de+voisinage&limit=3'

Five routes: /api/search, /api/search-textes, /api/decision/{id}, /api/texte/{code}/{article} and the same with a trailing /{date} for the version in force that day.

Stack

A single-server node, Postgres + ParadeDB + VectorChord (hybrid BM25 + vector search), API and front end in Rust.

A pure Rust Cargo workspace, split in two roots:

  • packages/ libraries:

    • lj-core pure core: the decision model and its normalisation.

    • lj-parse source parsers into the decision model.

    • lj-norm referential of the legal texts: article keys, aliases, applicability.

    • lj-extract field and citation extraction from decisions.

    • lj-sources source I/O (Judilibre JSON, opendata ZIP/XML).

    • lj-store Postgres access (tokio-postgres + deadpool) and migrations.

    • lj-llm embedding backends, cache, quantisation, Mistral client (chat/OCR).

    • lj-dtos api to web contracts (serde).

    • lj-telemetry tracing and OTLP export.

    • lj-api API layer (Axum + MCP rmcp + OAuth).

    • lj-web Leptos front end (SSR + WASM hydration), Tailwind.

  • apps/ shippable binaries:

    • lj-server single deployment: API + MCP/OAuth + SSR.

    • lj-ingest ingest CLI and cron runner.

Build and dev

The Rust toolchain is pinned by rust-toolchain.toml. Tasks run through mise:

mise run test      # rustfmt --check + clippy -D warnings + cargo test --workspace
mise run dev       # merged lj-server (cargo leptos watch, :3000)

For a local database: mise run dbs. The task prepares the Postgres state under LIBREJUSTICE_STATE_DIR, owning the data directory and copying the configuration mounted by the container, before starting it.

Deployment

Single server through podman compose (infra/docker-compose.yml): Postgres (ParadeDB + VectorChord), the merged lj-server binary, a caddy front end and the ingest cron. Fill in .env (see .env.example), then:

mise run ensure-pg                                                          # Postgres state and conf
podman compose -f infra/docker-compose.yml --profile prod up -d --build
podman compose -f infra/docker-compose.yml exec -T cron lj-ingest migrate   # migrations

lj-server serves the API, MCP/OAuth, SSR and the assets. It listens in clear; caddy holds :443 and terminates TLS, so the socket stays bound when the application container is recreated.

The shipped Postgres configuration (infra/pg-conf/) carries what the application requires (extensions, ParadeDB settings, compression) sized for a development machine. A production instance raises shared_buffers and effective_cache_size to match its hardware.

Licence

Apache-2.0.

Available Tools

4 tools
get_decisionGet DecisionA
Read-onlyIdempotent
Inspect

Fetch the full text and metadata of a decision by its url. The text carries inline markdown links to cited articles (/norm/, open with get_norm) and cited decisions (/decision/, open with get_decision); a citation spanning several articles (« articles 3 à 6 », « et suivants ») links its first article and appends the others as labelled links right after the span. appellateFate states in one line what became of THIS decision on review (INFIRMATION = reversed, it no longer stands; CONFIRMATION = upheld): read it before citing the decision as authority. caseChronology lists the prior AND subsequent decisions of the same case (appeal, pourvoi, renvoi). An absent fate or chronology never proves no recourse exists, only that none is linked in the corpus. commentaires links out to the rapporteur public's conclusions and the court's related documents: context, never the ruling, so quote the decision text and not a commentaire.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesA librejustice.fr decision URL, from a search_decisions hit or an inline citation link; search_decisions first if you have neither.

Output Schema

ParametersJSON Schema
NameRequiredDescription
urlYes
seatNo
textYes
titleYes
officeNo
solutionNo
procedureNo
dateLectureNo
legalDomainNo
officialUrlNo
publicationNo
commentairesNo
appellateFateNo
docketNumbersNo
caseChronologyNo
jurisdictionCodeNo
jurisdictionTypeYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes far beyond the readOnly/idempotent annotations, explaining inline markdown citation structure, the meaning of appellateFate, the scope of caseChronology, the caveat that absent data proves nothing, and the role of commentaires. This provides essential non-obvious context for correct use and interpretation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but every sentence earns its place: the core operation is front-loaded, and subsequent sentences cover citation links, legal fate semantics, chronology, absence caveats, and proper quoting behavior. There is no fluff or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a single parameter, a full input schema, an output schema, and strong annotations, the description is complete. It covers not only what the tool returns but how to interpret its key fields, how to handle missing fields, and how to relate results to sibling tools without leaving important gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is only one parameter, and the input schema already describes it fully with 100% coverage, including the URL source and fallback to search_decisions. The tool description itself adds little about the parameter beyond saying 'by its url', so the schema carries the burden and the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Fetch the full text and metadata of a decision by its url.' This clearly identifies the tool's purpose and distinguishes it from sibling tools like get_norm and search_decisions by focusing on decisions rather than norms or search results.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells the agent when to use get_decision versus alternatives: cited decisions open with get_decision, cited articles open with get_norm, and if you lack a URL, use search_decisions first. It also gives behavioral guidance about reading appellateFate before citing and not quoting commentaires.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_normGet NormA
Read-onlyIdempotent
Inspect

Fetch a legal text, or one of its articles, as it stood on a given date. Returns the version in force at date (omit for today): full text, status, validity dates, and the timeline of all versions: say which version you quote. The text carries inline markdown links to cross-referenced articles (/norm/, open with get_norm; when served at a date, the links point to the same date). commentaires links out to commentary anchored on the article. num absent means you hold the whole text; the url of an article's section, or of a section entry of articles, returns all its articles at once.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoA librejustice.fr article URL `/norm/{juridiction}/{code}/{article}`: take it from a search_norms hit or an inline citation link, or compose it: {juridiction} is the enacting state as ISO 3166-1 alpha-2 lowercase ("fr", "be"), "eu" for the Union, its treaties included, "intl" for a treaty signed outside it (the ECHR, a bilateral accord), {code} is the code slug ("code-civil", as in facets.legal_instrument), {article} the lowercase article key ("l761-1" for L. 761-1, "1240" for 1240). `/norm/{juridiction}/{code}` alone addresses the text itself, which is how a text with no articles (circulaire, publication decree) is read.
codeNoCode slug, when you hold the code and the article apart rather than as a url ("code-civil"). Alone, addresses the text itself.
dateNoConsultation date (YYYY-MM-DD): returns the version in force at that date. Omit for the version currently in force.
textIdNoLégifrance id: an article (LEGIARTI, JORFARTI, KALIARTI), a text (LEGITEXT, JORFTEXT) or a section (LEGISCTA).
articleNoLowercase article key, alongside `code` ("l761-1" for L. 761-1, "1240" for 1240).

Output Schema

ParametersJSON Schema
NameRequiredDescription
numNo
urlYes
codeYes
etatNo
notaNo
textNo
titleYes
dateFinNo
omittedNo
sectionNo
articlesNo
versionsYes
dateDebutNo
sourceUrlNo
modifiedByNo
articleCountNo
commentairesNo
travauxParlementairesNo

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, so the safety profile is covered. The description adds valuable behavioral context: the returned version is the one in force at `date`, the timeline of all versions is included, inline markdown links point to the same date when served at a date, and `commentaires` links out to commentary. It also discloses that `num` absent means the whole text and that an article's `section` URL returns all articles at once. This goes beyond the annotations and gives the agent a clear model of the tool's behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense and information-rich, with the core purpose front-loaded in the first sentence. It packs a lot of detail into a compact paragraph, and every sentence adds useful information. It is slightly long and could be seen as overwhelming, but the structure is logical: purpose, return contents, link behavior, and addressing modes. The density is justified by the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (5 parameters, multiple addressing modes, date-based versioning, inline links, section aggregation), the description is remarkably complete. It explains return contents (full text, status, validity dates, timeline), how to address texts vs articles, how to handle dates, and how cross-references behave. The output schema exists, so return values don't need further explanation. An agent has everything needed to call this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all five parameters. The description adds meaning by explaining how to compose a URL from juridiction/code/article, how `code` and `article` work together, and what `date` does (version in force at that date). It also clarifies the relationship between `url` and `code`/`article` as alternative ways to address the same resource. This is meaningful added value beyond the schema, though the schema already carries the basic semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb and resource: 'Fetch a legal text, or one of its articles, as it stood on a given date.' It clearly distinguishes the tool from siblings like search_norms and get_decision by focusing on retrieving a norm/article version at a date, with inline links and version timeline. The scope is unambiguous and the tool's identity is fully established.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells when to use this tool: to fetch a legal text or article at a date, and to open inline cross-reference links. It also explains how to construct URLs and when to use `code` vs `url`, and notes that `num` absent means the whole text. It doesn't explicitly name sibling alternatives, but the context signals show siblings are search/decision tools, and the description's focus on fetching a norm by URL or code makes the usage context clear. The guidance is rich enough to route an agent correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_decisionsSearch DecisionsA
Read-onlyIdempotent
Inspect

Search decisions by meaning and keywords combined. Returns a shortlist: title, url, an overview (aiSummary: AI-written summary of what the decision is about, a machine paraphrase never quotable as the court's words; or snippet: the verbatim passage where your keywords matched) and metadata; get_decision reads the full text. Put constraints in the structured filters (jurisdiction, dates, articles, codes), keep the query for the legal issue. Values within one filter are OR'd; different filters are AND'd. The response carries a facets block: per filter name, a map of filter value to decision count under the current query. Reuse those keys verbatim to refine. Hit metadata fields carry the same tokens under the same names: a hit's seat, legalDomain or solution passes back verbatim into the matching filter.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNo'auto' is the right choice in almost all cases, it picks the regime from the query shape. auto: combines meaning and keyword matching on plain queries; switches to keyword-only when the query contains ET / OU / SAUF or quoted phrases. lexical: force keyword-only matching. Every term becomes required and nothing recovers a miss, so it fits a string you expect word for word (a consecrated formula, the direction of a holding, a party named in the text) and an empty answer is then an answer. reference: force the lookup by identity, on the docket number, the case name, or the court and the date. semantic: force the meaning+keyword hybrid even on quoted or operator queries (where auto would fall back to keyword-only).auto
seatNoRestrict to one or more seats, as a dotted path from the court type down to the room: "CA" (a whole type), "CA.PARIS" and "TJ.BORDEAUX" (one court), "CC.C1_CIVILE", "CA.PARIS.P5.C3", "TJ.LYON.SOCIALE", "TJ.PARIS.REFERES", "TA.MARSEILLE.C5" (one room). A path matches itself and everything below it, so start at the type and go deeper. Types: TJ (tribunal judiciaire), CA (cour d'appel), CC (Cour de cassation), TCOM (tribunal des activités économiques), TA (tribunal administratif), CAA (cour administrative d'appel), CE (Conseil d'État), CNDA (asylum), CONSTIT (Conseil constitutionnel), TC (Tribunal des conflits), CNIL (sanctions), CEDH and CJUE (European courts). Take the paths from facets.seat.
sortNoResult ordering. Use 'relevance' (default) unless the user wants chronological order.relevance
limitNoMaximum number of results (default 10).
queryYesFrench query, the primary input. Two regimes, pick the right tool for the job: (a) Natural language or descriptive keywords for legal-issue searches; the engine handles synonyms and reformulations. Examples: « responsabilité hôpital infection nosocomiale » ; « étranger malade soins inaccessibles dans son pays d'origine » ; « licenciement discrimination syndicale charge de la preuve ». (b) Quoted exact phrases and ET / OU / SAUF operators for a named entity (company, municipality, person), a precise legal formula, or the direction of a holding: semantic matching ignores negations (« n'est pas X » ranks like « est X »), and only an exact phrase targets which way the court ruled. Examples: « "Société Générale" » ; « "commune de Saint-Denis" SAUF Réunion » ; « "force majeure" ET épidémie ». Using operators or quotes switches the engine to keyword-only matching for the whole query (no synonyms), so do not mix the two regimes.
officeNoFilter by specialised judge/office (JLD, JAF, JCP, JEX, juge des enfants, premier président, magistrat désigné). Absent value = ordinary bench.
date_toNoLatest decision date, inclusive (YYYY-MM-DD).
solutionNoFilter by the ruling of the operative part (référentiel solution). REJET / IRRECEVABILITE / DESISTEMENT / NON_LIEU_A_STATUER are procedural or negative endings; CONFIRMATION / INFIRMATION* / REFORMATION are appeal outcomes; CASSATION* is cassation-specific; ANNULATION covers administrative annulment; SATISFACTION_TOTALE / SATISFACTION_PARTIELLE cover first-instance civil rulings granting the claim.
ai_rerankNoWhen enabled (default), reorders results by actual relevance to the query using an LLM reranker. Keep on for agentic use, shortlist quality is significantly higher. Cost: a few seconds of extra latency. Disable only for high-rate exploratory searches where latency matters more than ranking quality.
date_fromNoEarliest decision date, inclusive (YYYY-MM-DD).
procedureNoFilter by procedural track (référés, QPC, EU referral, révision, tierce opposition…). Absent value = ordinary contentious procedure.
publicationNoFilter by publication, at the grain the Cour de cassation itself uses (any-of, membership is multiple: a decision matches every value whose code it carries). Judicial order: PUBLIE_RAPPORT (annual report, the strongest signal), PUBLIE_BULLETIN, LETTRE_CHAMBRE, COMMUNIQUE, INEDIT_BULLETIN. Administrative order: PUBLIE_LEBON, MENTIONNE_LEBON, INEDIT_LEBON. Lower courts carry no publication statement and match no value.
legal_domainNoFilter by legal domain (curated domain tree): 9 roots and their leaves (e.g. CIVIL_DROIT_LOCATIF). Selecting a root also matches all its leaves.
legal_articleNoRestrict to decisions citing a specific article of a specific code, as a composite key "<instrument>|<article>" where <instrument> is a slug or an exact text name, resolved like legal_instrument (e.g. "code-civil|1240", "code-de-justice-administrative|L761-1"). The instrument prefix is required: the same article number exists in several codes.
legal_instrumentNoRestrict to decisions citing one or more given codes or statutes. Accepts a slug from facets.legal_instrument (e.g. "code-civil") or an exact text name resolved server-side (e.g. "Code civil").

Output Schema

ParametersJSON Schema
NameRequiredDescription
hitsYes
queryYes
totalYes
facetsNo
pinnedNo

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover safety (readOnly, idempotent, non-destructive), so the description carries a lower burden, but it still adds significant behavioral detail: aiSummary is a machine paraphrase never quotable as the court's words, snippet is verbatim; filters are OR'd within a filter and AND'd across filters; facets expose counts and keys to reuse verbatim; and mode behavior is disclosed (semantic matching ignores negations, quoted phrases switch to keyword-only). No contradiction with annotations exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but well-structured: it opens with the core action, then the return payload, then usage guidance, then filter/facet behavior. Each sentence adds distinct information, with no filler or tautology. It is long, but given 15 parameters and the complexity of the query regimes, the length is justified.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the rich input schema, output schema, and safety annotations, the description covers everything an agent needs for correct invocation: query-regime rules, mode selection, filter semantics, facet reuse, reranker tradeoffs, and routing to get_decision for full text. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds meaningful cross-parameter semantics beyond the individual schema entries: the OR/AND filter composition rule, the instruction to reuse facet keys verbatim, and the symmetry between hit metadata fields and matching filters. This provides extra value without needing to restate every parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description leads with a specific verb and resource: 'Search decisions by meaning and keywords combined.' It explicitly names the return shortlist (title, url, overview, metadata) and distinguishes itself from the sibling get_decision, which 'reads the full text.' This leaves no ambiguity about what the tool does or how it differs from its siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear usage context: keep the query for the legal issue, put constraints in structured filters, and use get_decision for full text. It also provides strong mode-selection guidance ('auto' is right in almost all cases) and explains when to force lexical or reference modes. It does not explicitly mention search_legal_texts as an alternative, but the decision-focused scope and schema make the intended use clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_normsSearch NormsA
Read-onlyIdempotent
Inspect

Find legal articles from their subject, their wording, or an article number with no code named; returns a shortlist with highlighted snippets and the exact total. Query in French, descriptive terms (« délai de recours contentieux refus implicite »); put the code in the code filter (slug or exact name), keep the query for the subject. The response carries a facets block (code, jurisdiction): per filter name, a map of filter value to article count, reuse those keys verbatim to refine. Chain a hit into get_norm with its url, plus date when the dispute is governed by an earlier version.

ParametersJSON Schema
NameRequiredDescriptionDefault
codeNoRestrict to one code/text by its URL slug ("code-civil", as in facets.code) or exact name. Omit to search the whole navigable referential.
dateNoConsultation date (YYYY-MM-DD): searches the versions valid at that date (point-in-time, same semantics as get_norm). Omit to search the versions currently in force.
limitNoMaximum number of results (default 10).
queryYesFrench query over legal articles. Matches article titles (boosted) and bodies; alias expansion handles acronyms and usual names.
jurisdictionNoFilter by country/legal order, as an ISO 3166 alpha-2 country code: "FR" (France, the bulk of the corpus) or a foreign code ("SN", "DZ", "MA", "VN", "PE", …); plus "UE" for EU law and "INTL" for treaties/international law.

Output Schema

ParametersJSON Schema
NameRequiredDescription
hitsYes
queryYes
totalYes
facetsYes
pinnedNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and idempotentHint=true, so the description correctly doesn't repeat that. It adds valuable behavioral context: highlighted snippets, exact total, a facets block with reusable keys, and point-in-time date semantics. Minor gap: no explicit mention of pagination or result ordering, but schema covers limit.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but each sentence earns its place: purpose, query language, code filter placement, facets reuse, and chaining instructions. It front-loads the core purpose and avoids filler, making it efficient for an agent to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema (covering return structure), the description adequately covers all necessary usage context: query syntax, filter semantics, facets, chaining, and temporal behavior. It fully supports an agent calling the tool correctly without additional documentation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so parameters are well-documented. The description adds extra guidance by explaining how to use `code` (slug or exact name) and `date` (for earlier versions), which helps the agent understand parameter interplay beyond the schema definitions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Find legal articles') and the scope (by subject, wording, or article number with no code named). It explicitly differentiates from siblings like search_decisions by focusing on legal articles (norms) rather than decisions, making the tool's role unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit when-to-use guidance: query in French with descriptive terms, place the code in the `code` filter, and keep the query for the subject. It also instructs chaining hits into get_norm with url and date for earlier versions, which clarifies the workflow and alternative tool usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 6 tool updatesv1.18.0
    • Changedget_decision7 fields changed
      • removedOutput schema / properties / commentaires / items / properties / body
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null
        -}
      • addedOutput schema / properties / commentaires / items / properties / issue
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • changedOutput schema / properties / commentaires / items / properties / kind / enum
        Previous value: -[
        -  "analyse",
        -  "conclusions",
        -  "note"
        -]New value: +[
        +  "conclusions",
        +  "note"
        +]
      • addedOutput schema / properties / commentaires / items / properties / pages
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • removedOutput schema / properties / commentaires / items / properties / renvois
        Removed value: -{
        -  "items": {
        -    "type": "string"
        -  },
        -  "type": "array"
        -}
      • removedOutput schema / properties / commentaires / items / properties / rubriques
        Removed value: -{
        -  "items": {
        -    "type": "string"
        -  },
        -  "type": "array"
        -}
      • addedOutput schema / properties / commentaires / items / properties / volume
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
    • Removedget_legal_text
    • Addedget_norm
    • Changedsearch_decisions10 fields changed
      • removedInput schema / properties / jurisdiction_code
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "items": {
        -        "maxLength": 40,
        -        "type": "string"
        -      },
        -      "maxItems": 10,
        -      "type": "array"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Restrict to one or more precise court units by referential code. Code shapes: \"cc\" (Cour de cassation), \"ce\" (Conseil d'État), \"cnda\", \"cedh\", \"cjue\"; \"ca_<city>\", \"caa_<city>\", \"ta_<city>\", \"tj_<city>\", \"tcom_<city>\" (e.g. \"ca_paris\", \"ta_marseille\", \"tj_paris\", \"tcom_lyon\"). When unsure, guess with the city name in the code: the error names the nearest valid ones. Each code is a court; a room inside it is reached with seat, which also expresses this filter as a path."
        -}
      • removedInput schema / properties / jurisdiction_type
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "items": {
        -        "enum": [
        -          "TA",
        -          "CAA",
        -          "CE",
        -          "CONSTIT",
        -          "TC",
        -          "CC",
        -          "CA",
        -          "TJ",
        -          "TCOM",
        -          "CEDH",
        -          "CJUE",
        -          "CNDA",
        -          "CNIL"
        -        ],
        -        "type": "string"
        -      },
        -      "type": "array"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null,
        -  "description": "Restrict to one or more court categories: TJ (tribunal judiciaire), CA (cour d'appel), CC (Cour de cassation), TCOM (tribunal des activités économiques), TA (tribunal administratif), CAA (cour administrative d'appel), CE (Conseil d'État), CNDA (asylum), CONSTIT (Conseil constitutionnel), TC (Tribunal des conflits), CNIL (sanctions), CEDH and CJUE (European courts)."
        -}
      • changedInput schema / properties / mode / description
        Previous value: -"'auto' is the right choice in almost all cases, it picks the regime from the query shape. auto: combines meaning and keyword matching on plain queries; switches to keyword-only when the query contains ET / OU / SAUF or quoted phrases. lexical: force keyword-only matching. Every term becomes required and nothing recovers a miss, so it fits a string you expect word for word (a consecrated formula, the direction of a holding, a party named in the text) and an empty answer is then an answer. semantic: force the meaning+keyword hybrid even on quoted or operator queries (where auto would fall back to keyword-only)."New value: +"'auto' is the right choice in almost all cases, it picks the regime from the query shape. auto: combines meaning and keyword matching on plain queries; switches to keyword-only when the query contains ET / OU / SAUF or quoted phrases. lexical: force keyword-only matching. Every term becomes required and nothing recovers a miss, so it fits a string you expect word for word (a consecrated formula, the direction of a holding, a party named in the text) and an empty answer is then an answer. reference: force the lookup by identity, on the docket number, the case name, or the court and the date. semantic: force the meaning+keyword hybrid even on quoted or operator queries (where auto would fall back to keyword-only)."
      • changedInput schema / properties / mode / enum
        Previous value: -[
        -  "auto",
        -  "lexical",
        -  "semantic"
        -]New value: +[
        +  "auto",
        +  "lexical",
        +  "semantic",
        +  "reference"
        +]
      • changedInput schema / properties / seat / description
        Previous value: -"Restrict to one or more seats, as a dotted path from the court type down to the room: \"CC\", \"CC.SOCIALE\", \"CA.PARIS\", \"CA.PARIS.P5.C3\". A path matches itself and everything below it, so this one filter also covers what jurisdiction_type and jurisdiction_code express. Take the paths from facets.seat."New value: +"Restrict to one or more seats, as a dotted path from the court type down to the room: \"CA\" (a whole type), \"CA.PARIS\" and \"TJ.BORDEAUX\" (one court), \"CC.C1_CIVILE\", \"CA.PARIS.P5.C3\", \"TJ.LYON.SOCIALE\", \"TJ.PARIS.REFERES\", \"TA.MARSEILLE.C5\" (one room). A path matches itself and everything below it, so start at the type and go deeper. Types: TJ (tribunal judiciaire), CA (cour d'appel), CC (Cour de cassation), TCOM (tribunal des activités économiques), TA (tribunal administratif), CAA (cour administrative d'appel), CE (Conseil d'État), CNDA (asylum), CONSTIT (Conseil constitutionnel), TC (Tribunal des conflits), CNIL (sanctions), CEDH and CJUE (European courts). Take the paths from facets.seat."
      • removedOutput schema / properties / filterRewritten
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null
        -}
      • addedOutput schema / properties / hits / items / properties / citedArticles
        Added value: +{
        +  "additionalProperties": {
        +    "items": {
        +      "type": "string"
        +    },
        +    "type": "array"
        +  },
        +  "type": "object"
        +}
      • removedOutput schema / properties / hits / items / properties / jurisdictionCode
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "type": "string"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "default": null
        -}
      • removedOutput schema / properties / hits / items / properties / jurisdictionType
        Removed value: -{
        -  "type": "string"
        -}
      • changedOutput schema / properties / hits / items / required
        Previous value: -[
        -  "title",
        -  "url",
        -  "chars",
        -  "jurisdictionType"
        -]New value: +[
        +  "title",
        +  "url",
        +  "chars"
        +]
    • Removedsearch_legal_texts
    • Addedsearch_norms
  2. 4 tool updatesv0.1.0
    • First observedget_decision
    • First observedget_legal_text
    • First observedsearch_decisions
    • First observedsearch_legal_texts

TDQS

A4.7/5.0

Scored across 4 tools

Disambiguation5/5

The four tools divide cleanly by resource type (decisions vs. norms) and operation (search vs. fetch full content). Search tools return shortlists while get_* tools retrieve full text, so an agent can easily choose the right tool without overlap.

Naming Consistency5/5

All names use snake_case with a consistent action_entity pattern: search_decisions, get_decision, get_norm, search_norms. The plural/singular distinction tracks whether the tool returns a set or a single item, making the convention predictable.

Tool Count5/5

Four tools is well-scoped for a read-only legal research server covering decisions and norms. Each tool has a distinct and necessary role, with no redundant or missing obvious operation at this granularity.

Completeness4/5

The surface provides symmetric search and retrieval for both decisions and legal norms, including temporal versioning, citations, and facets for refinement. Minor gaps like explicit pagination controls or code-hierarchy browsing are workable through URLs and facet reuse.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers