MCP server for Dutch & European legal reasoning.
The LegalMike MCP Server gives your AI assistant Dutch and European legal reasoning with actual legal sources — legislation, case law, disciplinary and dispute-resolution decisions, EU and ECHR law, legal literature and parliamentary documents.
It is optimized to ground legal answers in retrieved text, so an assistant cites what it found rather than what it remembers.
Request access and find support at legalmike.ai or reach out to info@legalmike.ai.
You can connect LegalMike to Claude on Claude.ai, the Claude desktop app, and Claude Code.
loading...
Available on Free (1 integration), Pro, Max, Team, and Enterprise plans. See the Anthropic documentation for more details.
Run this command to add the server:
claude mcp add --transport http lmcp loading...
Add to .vscode/mcp.json in your project:
loading...
Then open Copilot Chat in Agent mode. Copilot will prompt you to authenticate with your LegalMike account via OAuth.
If OAuth isn't available, pass an API key in the
Authorization header as a Bearer token.
You can add MCP apps to ChatGPT if you have a Pro, Plus, Team, Enterprise, or Edu account.
loading...
See the OpenAI documentation for more details.
Work in progress. Contact info@legalmike.ai for early access.
Use server URL loading... with
"OAuth" as the connection mechanism. Your
client handles the authorization flow automatically.
loading...
For backend services, automated pipelines, or clients that don't
support OAuth, pass an API key as a Bearer token in the
Authorization header. No OAuth flow or client
registration is needed.
{
"mcpServers": {
"lmcp": {
"url": "loading...",
"headers": {
"Authorization": "Bearer YOUR_API_TOKEN"
}
}
}
}
API tokens are issued per organization with a configurable expiry. Contact info@legalmike.ai to request one for your organization.
LegalMike requires authentication for all tool calls. Two methods are supported.
| Method | For | How |
|---|---|---|
| OAuth 2.1 | Individual users with a LegalMike account | Your AI client handles the flow automatically (Authorization Code + PKCE). No keys to manage. |
| API Key (Bearer Token) | System-to-system integrations, backend services |
LegalMike generates a long-lived token for your
organization. Pass it as a Bearer token in the
Authorization header. Contact
info@legalmike.ai to
request one.
|
Pass the token as a Bearer token in the
Authorization header. No OAuth flow or client
registration is needed.
curl -X POST https://mcp.legalmike.ai/mcp \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"my-app","version":"1.0"}},"id":1}'
Contact info@legalmike.ai to request an API key for your organization.
| Client | OAuth 2.1 | API Key |
|---|---|---|
| ChatGPT | supported | — |
| Claude | supported | — |
| Microsoft 365 Copilot | supported | supported |
| GitHub Copilot | supported | supported |
| Custom backend / API | — | supported |
LegalMike's MCP server provides tools to search Dutch and European legal sources and synthesize grounded answers. Your AI assistant can call these tools directly — every claim in the response is backed by a citation to the original source. You don't need to learn the tools yourself; your AI client discovers and invokes them automatically based on your questions.
Loading tools...
Every call returns the answer as text and a
structuredContent object, described by the tool's
outputSchema — so you can validate it rather
than reverse-engineer it. Your MCP client exposes the schema; it
is also in GET /api/tools.
Branch on answer_type first. It is
the one field always present, and it tells you which exit you are
holding — because they carry different things. A
clarification has no citations; a raw payload has no synthesized
answer.
answer_type |
What you got |
|---|---|
answer |
A synthesized, cited legal answer. |
meta:… |
An answer about the assistant itself, not about law. |
clarification |
A question back. clarification may carry
structured options.
|
correction |
The question rests on a misconception; the text corrects it. |
rejected |
Outside the legal domain. |
non_legal |
Accepted, then found not to be a legal question after routing. |
verbatim |
Literal statute or ruling text, in verbatim. No
synthesis ran.
|
canned |
A fixed informational response; no research ran. |
raw |
Retrieved passages per source, from
legalmike_search.
|
error |
Only from legalmike_search — see below.
|
Alongside it: applied_settings (what the flow
actually used, including sources_used),
flow (the stages and sources actually traversed, in
order), ecli_citations, and verbatim.
New fields may be added — the schema allows them, so
validate permissively.
Failures follow the MCP specification's two channels. A problem
with the request itself — unknown tool, malformed call
— comes back as a JSON-RPC protocol error. Anything
that happens while answering comes back as a
tool execution error: isError: true with a
message your assistant can act on, so it can rephrase or retry
rather than simply failing.
Every failure carries a stable code. Which of the two
tools you called decides whether that code reaches you, and the
difference matters if you are writing a client:
| Tool | What you receive | How to branch |
|---|---|---|
legalmike_reason |
A tool execution error: isError: true and the
message below as text. The code is not
transmitted — it is recorded on the server’s
trace, not in the response.
|
Treat any isError as retryable and let the
model read the message — it is written to tell the
model what to try next.
|
legalmike_search |
A normal result whose payload carries
answer_type: "error" with code and
message, plus a sources array when
the failure concerns specific sources.
|
Branch on code. A caller parsing JSON cannot be
handed an exception, so this is where the machine-readable
contract is on the wire.
|
legalmike_search.
Attaching structured content to an isError result is
not expressible in the server framework today, so the answering
tool carries prose only. Messages are static and never interpolate
anything, which makes matching on them stable in practice —
with one deliberate exception: invalid_argument names
the parameter, the value you sent and the values allowed, because
that message is read first by a model that will retry, and
“a parameter is invalid” gives it nothing to retry
with.
| Code | Message (returned verbatim, in Dutch) | What to do |
|---|---|---|
no_evidence |
Ik heb geen bronnen gevonden om deze vraag op te baseren. Formuleer de vraag specifieker — noem bijvoorbeeld het rechtsgebied, een wetsartikel of een ECLI. | Retry with a narrower question — name the field of law, an article, or an ECLI. Not retryable as-is. |
sources_unavailable |
De juridische bronnen zijn tijdelijk niet bereikbaar. Probeer het over enkele minuten opnieuw. | Transient. Retry after a short delay with backoff. |
invalid_argument |
Een van de meegegeven parameters heeft een waarde die niet is toegestaan. | The only code whose message is not static: it names the parameter, the value you sent and the values allowed, so the calling model can retry with a corrected one. Fix the argument; retrying unchanged fails identically. |
retired_tool |
Deze tool bestaat niet meer. Gebruik 'legalmike_reason' voor een onderbouwd antwoord, of 'legalmike_search' voor losse bronnen. |
You called a tool that has been removed — in practice
ask, which became these two. Like
invalid_argument, the message is dynamic: it
names the tool to call instead, so a model reading it can
retry correctly. Same question, same parameters, new name.
|
upstream_timeout |
De zoekopdracht duurde te lang en is afgebroken. Een kortere of specifiekere vraag lukt vaak wel. | Retry once with a shorter or more specific question. Repeated timeouts mean the question is too broad. |
not_configured |
De server is niet volledig geconfigureerd. Neem contact op met de beheerder. | Not retryable. A server-side configuration problem — escalate. |
internal_error |
Er is een onverwachte fout opgetreden bij het beantwoorden van de vraag. Probeer het opnieuw; blijft het misgaan, meld het bij de beheerder. | Retry once. If it persists, escalate with the timestamp; details are in the server trace, never in the response. |
legalmike_search an error is
returned as a normal result like every other outcome, rather than
as an exception — a caller that is parsing JSON cannot be
handed one:
{"answer_type": "error", "kind": "error", "code": "no_evidence", "message": "..."}
When the error concerns specific sources, a
sources array names them. This payload arrives both
as text and as structuredContent, and it satisfies
the tool's declared output schema like every other exit.
Partial results. If a source is unreachable, the
answer is still synthesized from the sources that responded and
the server emits an MCP error-level log notification
naming what was missing. An answer built from fewer sources than
intended always says so.
Unexpected internal failures are deliberately opaque (Error calling tool 'legalmike_reason'). Details stay in the server's traces so nothing internal can
reach a model context.
The values below are what
restrict_to_sources accepts, on both tools.
The list you pass is a filter, not a hint
— name three sources and exactly those three are searched.
Omit the parameter (the default) and the server routes the
question itself, which is the right choice for most calls. There
is no exclude list: whatever you leave out is left out.
They are also the names returned in
applied_settings.sources_used, so what you ask for
and what you are told was searched use one vocabulary. Citations
link directly to the original documents on their official
platforms.