Documentation · MCP

legali.ge MCP server

One address that gives Claude, ChatGPT and your own agents direct access to Georgian legislation, court practice and the legali.ge AI copilot — with answers that cite the exact articles.

MCP server address
https://mcp.legali.ge/mcp
  • 28 tools
  • OAuth 2.1 or API key
  • Streamable HTTP · MCP 2025-06-18

What you can do

Once connected, the assistant decides by itself when to look something up in legali.ge. You just ask in your own words.

Find the right documents

Describe a situation in any language — find_documents returns the relevant laws, the matching articles and related court practice, ranked by meaning rather than keywords.

Court practice

Decisions of the Supreme Court and the Constitutional Court of Georgia, ECtHR case law and Venice Commission opinions, searched by meaning.

Read laws and articles

Metadata, version history, table of contents and the consolidated text of any act from matsne.gov.ge, or a single article with a deep link.

Answers with citations

The legali.ge copilot retrieves statutes and practice and answers with a list of sources — the same engine as on the website.

Draft documents

20 document types — statements of claim, appeals, contracts, NDAs, powers of attorney, demand letters — drafted from a plain description, citing the governing norms.

Your documents and drafts

Upload a contract or a court letter for data extraction, ground answers in it, and edit documents in the legali.ge editor with revision history.

Try these prompts

Paste one into Claude or ChatGPT after connecting. Mentioning legali.ge helps the assistant pick the right tools.

  • Using legali.ge, find the articles of the Civil Code of Georgia and Supreme Court practice on getting a rental deposit back when the landlord refuses. Quote the articles and give links.

  • I was dismissed after three years of work without written notice. What does the Labour Code of Georgia say about compensation and the deadline for going to court? Cite the articles via legali.ge.

  • Read article 128 of the Civil Code of Georgia (document 31702) on legali.ge and explain in plain words which limitation periods apply to a claim for an unpaid loan.

  • Draft a demand letter to a shop that sold me a defective washing machine and refuses to replace it, under the Georgian law on consumer rights protection. Use legali.ge and leave placeholders for names and dates.

Quick start

The same address works in every client. You approve access once on legali.ge — your password is never shared with the app — and the connection stays active until you revoke it.

You need a legali.ge account (free — sign up). A free account includes a monthly allowance of MCP units and 10 trial AI requests.

claude.ai and Claude Desktop — Pro, Max, Team and Enterprise plans

  1. Open Settings → Connectors.
  2. Click “Add custom connector”.
  3. Name it “legali.ge” and paste the MCP server address.
  4. Click “Connect” — a legali.ge page opens: check the app name and click “Allow”.
Server address
https://mcp.legali.ge/mcp

On Team and Enterprise plans an organisation owner may have to add the connector. Menu names may differ by plan/version.

Authentication

There are two ways to authorize: OAuth for apps that support it (Claude, ChatGPT, Claude Code and most modern clients), and personal API keys for everything else.

OAuth — connect once

  1. You add the server address in the app.
  2. The app opens legali.ge in your browser. If you are signed in, you see which app asks for access and the address it will return to.
  3. You click “Allow”. The app receives tokens tied to your account — never your password.
  4. The app refreshes its tokens automatically. It stays connected as long as you use it at least once every 90 days, until you revoke it in your account.

Personal API keys

An API key lgk_… is for clients that can only send a header — scripts, servers, CI, Cursor, Claude Code without a browser. Send it as Authorization: Bearer lgk_…. The key acts as you and spends your plan’s allowance.

  • Create a separate key per device or integration, named so you recognise it; revoke it as soon as it is no longer needed.
  • The key is shown once. legali.ge stores only its SHA-256 hash, so a lost key cannot be recovered — revoke it and create a new one.
  • Keep keys in environment variables or a secret manager — never in a repository or in browser code.
Manage keys and connections →

Technical details

  • Issuer: https://mcp.legali.ge. Discovery: /.well-known/oauth-protected-resource (RFC 9728) and /.well-known/oauth-authorization-server (RFC 8414; also served as /.well-known/openid-configuration).
  • Dynamic client registration (RFC 7591): POST /oauth/register. Redirect URIs: https, http loopback (any port, RFC 8252) or a private-use scheme.
  • Authorization code with PKCE — S256 only; codes are single-use and expire in 10 minutes. Endpoints: /oauth/authorize, /oauth/token, /oauth/revoke. Scope: mcp.
  • Access token lga_…: 1 hour. Refresh token lgr_…: 90 days, rotated on every use, and every refresh extends the window — so an app in regular use never has to reconnect. A just-used refresh token is accepted again for 120 s to survive network retries.
  • A call without a token or with an expired one gets 401 with WWW-Authenticate: Bearer resource_metadata="…", which tells the client where to start the flow.
  • Revoking a connection in your account invalidates its access and refresh tokens immediately.

Tools reference

Generated from the server’s own definitions (gelaw-mcp 0.2.0). Descriptions and parameter schemas are shown in English exactly as the server sends them to the AI; the one-line summaries are ours.

On the hosted server every call needs a connection (OAuth or API key). The account badge marks tools that work with your personal data or metered databases; the others serve public data.

Research & data

find_documents

10 units 🔒 account read-only

Picks the laws, articles and court practice relevant to a question — without writing an answer.

Select the documents relevant to a situation or question — WITHOUT generating an answer. Understands the query (domains, Georgian legal terms), searches laws by meaning (vector indexes + LLM rerank), court practice (Supreme/Constitutional Court, ECtHR, Venice Commission) and the curated legal-concept map, and returns ranked laws with the matched article, practice items and anchor articles. Use it to build your own research set, then read texts with get_law / get_article. Prefer it over search_laws for natural-language queries in any language. Costs 10 MCP units.

Parameters: find_documents
NameType Req.Default Description
query string yes — Situation or question in any language (max 2000 chars).
limit integer — 10 Max laws to return. min 1 · max 30
lang string — "ru" Language for the query-understanding summary. One of: karuen
document_type string — — Optional document-type filter.
status string — — Optional status filter (e.g. 'active').
include_practice boolean — true
include_concepts boolean — true
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Situation or question in any language (max 2000 chars)."
    },
    "limit": {
      "type": "integer",
      "default": 10,
      "minimum": 1,
      "maximum": 30,
      "description": "Max laws to return."
    },
    "lang": {
      "type": "string",
      "enum": [
        "ka",
        "ru",
        "en"
      ],
      "default": "ru",
      "description": "Language for the query-understanding summary."
    },
    "document_type": {
      "type": "string",
      "description": "Optional document-type filter."
    },
    "status": {
      "type": "string",
      "description": "Optional status filter (e.g. 'active')."
    },
    "include_practice": {
      "type": "boolean",
      "default": true
    },
    "include_concepts": {
      "type": "boolean",
      "default": true
    }
  },
  "required": [
    "query"
  ],
  "additionalProperties": false
}

search_court_practice

2 units 🔒 account read-only

Searches Supreme Court, Constitutional Court, ECtHR and Venice Commission practice by meaning.

Search court practice by meaning: Georgian Supreme Court decisions, Constitutional Court judgments, ECtHR case law and Venice Commission opinions. Returns ranked items with an excerpt and the source link (ECtHR/Venice: short excerpt + official link only). Costs 2 MCP units.

Parameters: search_court_practice
NameType Req.Default Description
query string yes — Legal issue in any language.
sources array<string> — — Subset of sources; default all. One of: supremeconstcourtechrvenice
limit integer — 8 min 1 · max 20
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Legal issue in any language."
    },
    "sources": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "supreme",
          "constcourt",
          "echr",
          "venice"
        ]
      },
      "description": "Subset of sources; default all."
    },
    "limit": {
      "type": "integer",
      "default": 8,
      "minimum": 1,
      "maximum": 20
    }
  },
  "required": [
    "query"
  ],
  "additionalProperties": false
}

get_law

1 unit 🔒 account read-only

Reads a law: metadata, versions, table of contents or the consolidated text page by page.

Read a law / legal act by its matsne document id: metadata (title, type, status, registration code, issuer, dates, list of historical versions), the table of contents (articles with chunk_key for get_article) and/or the consolidated text, paged by characters (follow next_offset). Costs 1 MCP unit.

Parameters: get_law
NameType Req.Default Description
doc_id string yes — Numeric matsne document id, e.g. '31702' (Civil Code).
include array<string> — — Parts to return; default ['meta','toc']. One of: metatoctext
offset integer — 0 Text offset (characters) — pass next_offset to continue. min 0
max_chars integer — 20000 min 1000 · max 60000
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "doc_id": {
      "type": "string",
      "description": "Numeric matsne document id, e.g. '31702' (Civil Code)."
    },
    "include": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "meta",
          "toc",
          "text"
        ]
      },
      "description": "Parts to return; default ['meta','toc']."
    },
    "offset": {
      "type": "integer",
      "default": 0,
      "minimum": 0,
      "description": "Text offset (characters) — pass next_offset to continue."
    },
    "max_chars": {
      "type": "integer",
      "default": 20000,
      "minimum": 1000,
      "maximum": 60000
    }
  },
  "required": [
    "doc_id"
  ],
  "additionalProperties": false
}

get_article

1 unit 🔒 account read-only

One article of a law by number or chunk_key, with a deep link.

Read one article of a law: pass doc_id plus either chunk_key (from get_law toc / find_documents, e.g. 'art_128') or the article number ('128', '128_1'). Returns the article title, full text and a deep link. Costs 1 MCP unit.

Parameters: get_article
NameType Req.Default Description
doc_id string yes —
chunk_key string — — e.g. art_128, art_128_1, doc, att_1
article string — — Article number, e.g. '128'.
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "doc_id": {
      "type": "string"
    },
    "chunk_key": {
      "type": "string",
      "description": "e.g. art_128, art_128_1, doc, att_1"
    },
    "article": {
      "type": "string",
      "description": "Article number, e.g. '128'."
    }
  },
  "required": [
    "doc_id"
  ],
  "additionalProperties": false
}

search_laws

free read-only

Free full-text search over the Georgian legal corpus, with filters.

Full-text search over the Georgian legal corpus (matsne.gov.ge). Returns matching documents with title, date, type, status, issuer, registration code and a relevance score. Use the returned id with other tools.

Parameters: search_laws
NameType Req.Default Description
query string yes — Search terms (Georgian or transliterated).
limit integer — 20 min 1 · max 200
document_type string — — Filter by document type (e.g. 'law', 'order').
status string — — Filter by status (e.g. 'active').
issuer string — — Filter by issuing authority.
reg_code_prefix string — — Filter by registration-code prefix; comma-separated for multiple.
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "query": {
      "type": "string",
      "description": "Search terms (Georgian or transliterated)."
    },
    "limit": {
      "type": "integer",
      "default": 20,
      "minimum": 1,
      "maximum": 200
    },
    "document_type": {
      "type": "string",
      "description": "Filter by document type (e.g. 'law', 'order')."
    },
    "status": {
      "type": "string",
      "description": "Filter by status (e.g. 'active')."
    },
    "issuer": {
      "type": "string",
      "description": "Filter by issuing authority."
    },
    "reg_code_prefix": {
      "type": "string",
      "description": "Filter by registration-code prefix; comma-separated for multiple."
    }
  },
  "required": [
    "query"
  ],
  "additionalProperties": false
}

suggest_questions

free read-only

About ten natural questions about a law, in any of 9 languages.

Generate ~10 natural questions a user might ask about a specific law document, in the requested language. Good for exploring an unfamiliar law.

Parameters: suggest_questions
NameType Req.Default Description
doc_id string yes — Numeric matsne document id (optionally with a -N version suffix).
lang string — "ka" One of: karuentrzharhedefr
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "doc_id": {
      "type": "string",
      "description": "Numeric matsne document id (optionally with a -N version suffix)."
    },
    "lang": {
      "type": "string",
      "enum": [
        "ka",
        "ru",
        "en",
        "tr",
        "zh",
        "ar",
        "he",
        "de",
        "fr"
      ],
      "default": "ka"
    }
  },
  "required": [
    "doc_id"
  ],
  "additionalProperties": false
}

top_search_queries

free read-only

What people search for most on legali.ge.

The most popular search queries on legali.ge, by count. Useful to gauge what legal topics people look for.

Parameters: top_search_queries
NameType Req.Default Description
limit integer — 100 min 1 · max 1000
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "default": 100,
      "minimum": 1,
      "maximum": 1000
    }
  },
  "required": [],
  "additionalProperties": false
}

AI copilot

get_chat_response

free read-only

Fetches the result of a long AI job by request_id; waits up to ~45 s.

Fetch the result of an ask_legal_question / generate_legal_document / canvas_ai_edit job by its request_id. Waits up to ~45 s for a job that is still running (wait=false: check once). Returns {status:'processing'} if it is still not ready — call again with the same request_id.

Parameters: get_chat_response
NameType Req.Default Description
request_id string yes —
wait boolean — true
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "request_id": {
      "type": "string"
    },
    "wait": {
      "type": "boolean",
      "default": true
    }
  },
  "required": [
    "request_id"
  ],
  "additionalProperties": false
}

vote_answer

free

Rates a copilot answer thumbs up or down.

Record a thumbs up/down on a copilot answer (quality signal). Pass vote=null to clear a prior vote.

Parameters: vote_answer
NameType Req.Default Description
chat_id string yes — The request_id / chat_id of the answer.
msg_idx integer — 0 Index of the message being rated.
vote string | null — "up" One of: updownnull
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "chat_id": {
      "type": "string",
      "description": "The request_id / chat_id of the answer."
    },
    "msg_idx": {
      "type": "integer",
      "description": "Index of the message being rated.",
      "default": 0
    },
    "vote": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "up",
        "down",
        null
      ],
      "default": "up"
    }
  },
  "required": [
    "chat_id"
  ],
  "additionalProperties": false
}

Your account & documents

get_usage

free 🔒 account read-only

Shows what is left of your AI requests and MCP units, plus the price list.

Your remaining allowances: AI requests (ask / generate / canvas AI — trial, subscription, packages) and MCP data units for this month, plus the price list. Free.

No parameters.

JSON schema
inputSchema
{
  "type": "object",
  "properties": {},
  "required": [],
  "additionalProperties": false
}

list_my_chats

free 🔒 account read-only

Lists your saved chats and generated documents.

List the authenticated user's saved chats and generated documents.

No parameters.

JSON schema
inputSchema
{
  "type": "object",
  "properties": {},
  "required": [],
  "additionalProperties": false
}

get_my_chat

free 🔒 account read-only

The full history of one of your chats.

Fetch the full message history of one of the authenticated user's chats.

Parameters: get_my_chat
NameType Req.Default Description
chat_id string yes —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "chat_id": {
      "type": "string"
    }
  },
  "required": [
    "chat_id"
  ],
  "additionalProperties": false
}

list_user_documents

free 🔒 account read-only

Lists your uploaded documents and their extraction status.

List the authenticated user's uploaded personal documents and their extraction status (processing / ready / error).

No parameters.

JSON schema
inputSchema
{
  "type": "object",
  "properties": {},
  "required": [],
  "additionalProperties": false
}

get_user_document

free 🔒 account read-only

One uploaded document with extracted parties, dates, amounts and facts.

Fetch one personal document's metadata plus its extracted structured data (parties, dates, amounts, key facts, legal references, full text).

Parameters: get_user_document
NameType Req.Default Description
doc_id string yes —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "doc_id": {
      "type": "string"
    }
  },
  "required": [
    "doc_id"
  ],
  "additionalProperties": false
}

upload_user_document

free 🔒 account

Uploads a PDF, image or text (≤ 4 MB) for structured extraction.

Upload a personal legal document (PDF, JPEG, PNG, WebP or text, <=4MB). The backend extracts structured data asynchronously; poll get_user_document for status. Provide either data_b64, or (local stdio only) a filesystem path.

Parameters: upload_user_document
NameType Req.Default Description
filename string — —
content_type string — — MIME type; inferred from filename/path if omitted.
data_b64 string — — Base64-encoded file contents.
path string — — Local file path (stdio transport only).
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "filename": {
      "type": "string"
    },
    "content_type": {
      "type": "string",
      "description": "MIME type; inferred from filename/path if omitted."
    },
    "data_b64": {
      "type": "string",
      "description": "Base64-encoded file contents."
    },
    "path": {
      "type": "string",
      "description": "Local file path (stdio transport only)."
    }
  },
  "required": [],
  "additionalProperties": false
}

delete_user_document

free 🔒 account irreversible

Permanently deletes one of your uploaded documents.

Permanently delete one of the authenticated user's personal documents (irreversible).

Parameters: delete_user_document
NameType Req.Default Description
doc_id string yes —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "doc_id": {
      "type": "string"
    }
  },
  "required": [
    "doc_id"
  ],
  "additionalProperties": false
}

Document editor (canvas)

create_canvas

free 🔒 account

Creates an editable document, optionally from a template or your own text.

Create a new editable document canvas. Optionally seed it from a document type template and/or initial content. Returns the canvas metadata (including id and headRev).

Parameters: create_canvas
NameType Req.Default Description
document_type string — — Template to seed from (see generate_legal_document types).
title string — —
content string — — Initial document body (markdown).
lang string — "ka" One of: karuentrzharhedefr
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "document_type": {
      "type": "string",
      "description": "Template to seed from (see generate_legal_document types)."
    },
    "title": {
      "type": "string"
    },
    "content": {
      "type": "string",
      "description": "Initial document body (markdown)."
    },
    "lang": {
      "type": "string",
      "enum": [
        "ka",
        "ru",
        "en",
        "tr",
        "zh",
        "ar",
        "he",
        "de",
        "fr"
      ],
      "default": "ka"
    }
  },
  "required": [],
  "additionalProperties": false
}

list_canvases

free 🔒 account read-only

Lists your documents in the editor.

List the authenticated user's document canvases.

Parameters: list_canvases
NameType Req.Default Description
rebuild boolean — — Force-rebuild the index from storage.
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "rebuild": {
      "type": "boolean",
      "description": "Force-rebuild the index from storage."
    }
  },
  "required": [],
  "additionalProperties": false
}

get_canvas

free 🔒 account read-only

Reads a document: metadata, content, chat, any revision.

Fetch a canvas: metadata, content and/or chat thread. Use fields to select what to return; omit rev for the latest revision. Read headRev here before calling save_canvas / restore_canvas.

Parameters: get_canvas
NameType Req.Default Description
canvas_id string yes —
rev integer — — Specific revision; defaults to latest.
fields array<string> — — One of: metacontentchat
chat_before string — — ISO timestamp for paginating the chat thread.
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string"
    },
    "rev": {
      "type": "integer",
      "description": "Specific revision; defaults to latest."
    },
    "fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "meta",
          "content",
          "chat"
        ]
      }
    },
    "chat_before": {
      "type": "string",
      "description": "ISO timestamp for paginating the chat thread."
    }
  },
  "required": [
    "canvas_id"
  ],
  "additionalProperties": false
}

save_canvas

free 🔒 account

Saves a new revision of a document (optimistic locking by base_rev).

Save an edited canvas body as a new revision. base_rev MUST equal the current headRev (read it via get_canvas) — a mismatch is a 409 conflict. op_id is auto-generated if omitted.

Parameters: save_canvas
NameType Req.Default Description
canvas_id string yes —
base_rev integer yes — The headRev you edited from.
content string yes — The full new document body.
op_id string — — Idempotency key (UUID); auto-generated if omitted.
summary string — — Short change summary for the version list.
filled array<integer> — — Indices of placeholders filled by this edit.
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string"
    },
    "base_rev": {
      "type": "integer",
      "description": "The headRev you edited from."
    },
    "content": {
      "type": "string",
      "description": "The full new document body."
    },
    "op_id": {
      "type": "string",
      "description": "Idempotency key (UUID); auto-generated if omitted."
    },
    "summary": {
      "type": "string",
      "description": "Short change summary for the version list."
    },
    "filled": {
      "type": "array",
      "items": {
        "type": "integer"
      },
      "description": "Indices of placeholders filled by this edit."
    }
  },
  "required": [
    "canvas_id",
    "base_rev",
    "content"
  ],
  "additionalProperties": false
}

rename_canvas

free 🔒 account

Renames a document.

Rename a canvas.

Parameters: rename_canvas
NameType Req.Default Description
canvas_id string yes —
title string yes —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string"
    },
    "title": {
      "type": "string"
    }
  },
  "required": [
    "canvas_id",
    "title"
  ],
  "additionalProperties": false
}

duplicate_canvas

free 🔒 account

Copies a document into a new one.

Duplicate a canvas into a new one, optionally overriding its content/title.

Parameters: duplicate_canvas
NameType Req.Default Description
canvas_id string yes —
content string — —
title string — —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string"
    },
    "content": {
      "type": "string"
    },
    "title": {
      "type": "string"
    }
  },
  "required": [
    "canvas_id"
  ],
  "additionalProperties": false
}

delete_canvas

free 🔒 account irreversible

Permanently deletes a document.

Permanently delete a canvas (irreversible).

Parameters: delete_canvas
NameType Req.Default Description
canvas_id string yes —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string"
    }
  },
  "required": [
    "canvas_id"
  ],
  "additionalProperties": false
}

restore_canvas

free 🔒 account

Restores an earlier revision as the new head.

Restore a canvas to an earlier revision, creating a new head revision. base_rev must equal the current headRev; op_id auto-generated if omitted.

Parameters: restore_canvas
NameType Req.Default Description
canvas_id string yes —
rev integer yes — The revision to restore.
base_rev integer yes — The current headRev.
op_id string — —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string"
    },
    "rev": {
      "type": "integer",
      "description": "The revision to restore."
    },
    "base_rev": {
      "type": "integer",
      "description": "The current headRev."
    },
    "op_id": {
      "type": "string"
    }
  },
  "required": [
    "canvas_id",
    "rev",
    "base_rev"
  ],
  "additionalProperties": false
}

canvas_ai_edit

1 AI request 🔒 account

AI edit of a document: fill placeholders, redraft a selection or generate from scratch.

Ask the AI to edit a canvas in place (fill placeholders, redraft a selection, or generate from scratch with mode='generate'). The edit is committed server-side as a new revision. Blocks for the result unless wait=false; afterwards read the new content with get_canvas.

Parameters: canvas_ai_edit
NameType Req.Default Description
canvas_id string yes —
base_rev integer yes — Current headRev (from get_canvas).
question string yes — Instruction for the AI editor.
mode string — "" 'generate' to (re)generate the whole document. One of: ""generate
selection string — — Selected text the edit should target (<=3000 chars).
client_lang string — "ka" One of: karuentrzharhedefr
op_id string — —
user_docs array<string> — —
wait boolean — true
timeout number — —
JSON schema
inputSchema
{
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string"
    },
    "base_rev": {
      "type": "integer",
      "description": "Current headRev (from get_canvas)."
    },
    "question": {
      "type": "string",
      "description": "Instruction for the AI editor."
    },
    "mode": {
      "type": "string",
      "enum": [
        "",
        "generate"
      ],
      "default": "",
      "description": "'generate' to (re)generate the whole document."
    },
    "selection": {
      "type": "string",
      "description": "Selected text the edit should target (<=3000 chars)."
    },
    "client_lang": {
      "type": "string",
      "enum": [
        "ka",
        "ru",
        "en",
        "tr",
        "zh",
        "ar",
        "he",
        "de",
        "fr"
      ],
      "default": "ka"
    },
    "op_id": {
      "type": "string"
    },
    "user_docs": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "wait": {
      "type": "boolean",
      "default": true
    },
    "timeout": {
      "type": "number"
    }
  },
  "required": [
    "canvas_id",
    "base_rev",
    "question"
  ],
  "additionalProperties": false
}

Pricing & limits

MCP uses the same account and the same balance as the website. There are two wallets, and nothing is charged twice.

✨ AI requests

ask_legal_question, generate_legal_document and canvas_ai_edit spend one AI request each — the same wallet as the copilot on the website: 10 trial requests, then your subscription’s monthly requests, then request packs.

📚 MCP units

Database tools that are not free on the website — AI document selection, court-practice search and machine reading of texts — spend MCP units. Every plan includes a monthly allowance (the free tier too); unit packs never expire and are used after the allowance.

🔎 Free

Full-text search, related documents, suggested questions, popular queries, your own chats, documents and canvases, get_usage, get_chat_response.

Cost per operation

ToolWhat it doesCost
find_documents Picks the laws, articles and court practice relevant to a question — without writing an answer. 10 units
search_court_practice Searches Supreme Court, Constitutional Court, ECtHR and Venice Commission practice by meaning. 2 units
get_law Reads a law: metadata, versions, table of contents or the consolidated text page by page. 1 unit
get_article One article of a law by number or chunk_key, with a deep link. 1 unit
ask_legal_question The legali.ge AI copilot: an answer on Georgian law with cited sources. 1 AI request
generate_legal_document Drafts a legal document (20 types) from a plain description, citing the norms. 1 AI request
canvas_ai_edit AI edit of a document: fill placeholders, redraft a selection or generate from scratch. 1 AI request
All other tools free

Monthly allowances

Current plans and unit packs are listed on the tariffs page.

  • The monthly allowance resets on the 1st of each calendar month (UTC) and does not roll over. Packs are spent only after it runs out.
  • Units are charged only after a successful response — errors and timeouts are free.
  • When a wallet runs out, the tool returns an error result (isError) with a clear message and a top-up link; the assistant shows it to you. Nothing is charged automatically.
  • Ask the assistant to run get_usage to see both balances and the price list.

Building an integration or an agent for a law firm? The api plan has a large unit allowance and AI requests for automated research.

Plans and packs →

For developers

Protocol

  • Transport: MCP Streamable HTTP, revision 2025-06-18 (also negotiates 2025-03-26 and 2024-11-05). Endpoint: POST https://mcp.legali.ge/mcp.
  • Stateless: every request is authorized on its own, no Mcp-Session-Id is issued, and each JSON-RPC request gets one application/json response — no SSE stream. GET /mcp answers 405; notifications answer 202.
  • Methods: initialize, ping, tools/list, tools/call. Capabilities: tools only (listChanged: false) — no resources, prompts or sampling.
  • JSON-RPC batches (an array of messages) are accepted and answered with an array, but the 2025-06-18 spec dropped batching, so generic clients should not rely on it.
  • CORS is open (Access-Control-Allow-Origin: *), so browser-based clients can call the endpoint directly — but never ship an API key to a browser.
  • Results: content[0].text holds the JSON as text; object results are also in structuredContent. Tools carry annotations (readOnlyHint, destructiveHint, …) so clients can ask for confirmation before deleting.
Server instructions (sent to the AI)
Tools for the legali.ge Georgian legal database: select relevant laws, articles and court practice for a query, read law texts, ask the AI legal copilot (answers cite sources), draft legal documents, manage personal documents and edit document canvases. Public tools work anonymously; tools marked '(requires authentication)' need a Cognito ID token. To research yourself: find_documents → get_law / get_article / search_court_practice (metered in MCP units; get_usage shows the balance). For a finished cited answer use ask_legal_question; to produce a filing use generate_legal_document or the canvas_* tools (these spend AI requests).

Long-running AI jobs

A copilot answer usually takes 1–2 minutes and a drafted document longer — more than most gateways keep a request open. So ask_legal_question, generate_legal_document and canvas_ai_edit wait up to about 50 seconds. If the result is not ready by then, they return a normal (non-error) result:

structuredContent
{
  "status": "processing",
  "request_id": "…",
  "next": "The job is still running … Call get_chat_response with this request_id to get the result."
}

Then call get_chat_response with that request_id: it waits up to about 45 seconds more and returns the same “processing” object if the job is still running — call it again. When done, it returns status "ready" with the answer and its sources. Pass wait: false to submit without waiting (e.g. to run several jobs in parallel) and collect results later with get_chat_response. The AI request is spent once per job, not per poll.

python
import json, os, urllib.request

MCP = "https://mcp.legali.ge/mcp"
KEY = os.environ["LEGALI_KEY"]          # lgk_…  (a personal API key)

def call(name, arguments, _id=[0]):
    _id[0] += 1
    body = {"jsonrpc": "2.0", "id": _id[0], "method": "tools/call",
            "params": {"name": name, "arguments": arguments}}
    req = urllib.request.Request(MCP, data=json.dumps(body).encode(), method="POST", headers={
        "Authorization": f"Bearer {KEY}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream"})
    with urllib.request.urlopen(req, timeout=90) as r:
        result = json.load(r)["result"]
    if result.get("isError"):             # e.g. quota exhausted (status 402)
        raise RuntimeError(result["content"][0]["text"])
    return result["structuredContent"]

res = call("ask_legal_question", {"question": "landlord refuses to return the rental deposit"})
while res.get("status") == "processing":  # not ready within ~50 s
    res = call("get_chat_response", {"request_id": res["request_id"]})
print(json.dumps(res, ensure_ascii=False, indent=2))

Errors

WhatMeaningWhat to do
HTTP 401
WWW-Authenticate
Token missing, expired or revokedOAuth clients refresh the token or re-run the flow; with an API key, check that it has not been revoked.
HTTP 405GET or another non-POST request to /mcpSend POST with JSON-RPC.
-32700 -32600 -32601 -32602 -32603Parse error, invalid request, unknown method, invalid params, internal errorCheck the message format (e.g. tools/call without name → -32602).
isError: trueThe tool ran but the backend refused: text “Error: … (status N)”400 invalid arguments · 402 quota exhausted (the message names the wallet and links to top up) · 404 not found · 409 canvas revision conflict (re-read headRev with get_canvas).

Usage tips

  • Research pattern: find_documents once (10 units) → get_article for the articles you actually need (1 unit each) → search_court_practice for a specific point (2 units). Reading a whole code page by page with get_law is rarely needed.
  • search_laws is free: use it when you know the title or registration code; use find_documents for natural-language questions.
  • Cache get_law / get_article results on your side: a consolidated text changes only when the act is amended, and get_law meta lists its versions.
  • Canvas saves are optimistic: read headRev with get_canvas and pass it as base_rev; on 409, re-read and retry.
  • Check your balance with get_usage before large automated jobs.

Changelog

  • 0.2.0 — database tools metered in MCP units (find_documents, search_court_practice, get_law, get_article, get_usage); OAuth 2.1 with dynamic client registration and rotating refresh tokens; personal API keys; the mcp.legali.ge address; long AI jobs continue through get_chat_response.
  • 0.1 — first release: 22 tools (search, copilot, document drafting, personal documents, editor) with legali.ge account tokens.

Security & privacy

  • The consent page shows the app’s name and the address it will return to, so a look-alike app is visible before you click “Allow”.
  • A connected app acts as you: it can do what you can do on legali.ge — search and read, spend your AI requests and MCP units, view, create and delete your chats, documents and canvases. Connect only apps you trust.
  • Your password is never given to the app. Approving connections and managing keys requires a real signed-in session on legali.ge — an MCP token cannot approve another app.
  • Revoking a connection or key in your account takes effect immediately. If a legali.ge account is deleted or blocked, all its connections stop working.
  • The MCP server is a thin proxy over the same API as the website: it inherits every ownership check and rate limit and holds no data of its own. Each backend call carries a short-lived (10-minute) signed assertion for your account.
  • ECtHR and Venice Commission documents are returned as a short excerpt with a link to the official text, for copyright reasons.
  • Answers are generated by AI from legal sources and can contain mistakes. The official source of Georgian legislation is matsne.gov.ge; check the cited articles before relying on them.
Privacy policy →

FAQ

Which Claude and ChatGPT plans can connect?

In Claude, custom connectors are available on Pro, Max, Team and Enterprise plans (claude.ai and Claude Desktop); on Team/Enterprise an organisation owner may need to add it. In ChatGPT, custom MCP connectors are part of developer mode, which is available on some plans only — check Settings → Apps & Connectors. Claude Code, Cursor and other developer tools work with any legali.ge account.

Do I pay twice if I already have a subscription?

No. An AI answer through Claude or ChatGPT spends one request from the same balance as on the site; MCP units are only for database operations and come with your plan’s monthly allowance.

How do I disconnect an app?

Account → AI connections → “Revoke” next to the app (or the key). Access stops immediately; to connect again you approve it anew.

Which languages does it understand?

Ask in any language. The copilot answers in the language of the question, find_documents and search_court_practice accept queries in any language, and suggested questions come in 9 languages. The texts of laws and decisions are in Georgian, as published.

The assistant says the answer is still “processing”. What now?

Nothing is wrong: long answers continue in the background. The assistant should call get_chat_response with the same request_id — ask it to if it doesn’t. The request is spent once.

OAuth or API key — which should I use?

OAuth whenever the client supports it: nothing to copy, tokens refresh themselves, and you can revoke each app separately. API keys are for scripts, servers and clients that only accept a header.

Can I run the server locally?

The server also has a local stdio mode (python -m gelaw_mcp.server_stdio) authenticated with a legali.ge account, for development and self-hosted setups. Metering happens on the legali.ge backend, so prices and limits are the same; for most people the hosted address is simpler.

Is this legal advice?

No. legali.ge is a legal information service: AI answers cite sources but can be wrong, and the official text is published on matsne.gov.ge. For a decision that matters, consult a lawyer.