This is the API of the cat (amazing.cat), a search engine for case law, government agency acts and laws of Russia for AI agents. Programs call the cat’s tools directly over MCP; to search through an AI agent, connect the free amazingcat MCP server (“the cat”) to your agent using the instructions.
YOUR_AMAZINGCAT_TOKEN — your form-issued token; Python and TypeScript read it from the AMAZINGCAT_TOKEN environment variable. Send it as Authorization: Bearer. Keep it on your server, out of URLs and browser code. AI applications using OAuth connect through browser authorization.
Access
Per 60 seconds
Per UTC day
Free
60
300
The rate limit uses a rolling 60-second window; UTC days start at 00:00 UTC. The daily limit applies per token; all free tokens from one network together also get at most 300 requests per UTC day. One request can return several documents.
When throttled, the server supplies Retry-After. tools/call can signal a quota refusal with HTTP 200; it is not a search result. Check status, Retry-After and errors inside the response.
MCP and responses
Streamable HTTP endpoint: https://mcp.amazingcat.net/mcp. Send Content-Type: application/json and Accept: application/json, text/event-stream for POST. JSON-RPC returns result; a tool response is in result.content[].text. It is a JSON string requiring a second parse.
For text/event-stream collect the data: lines of one event and parse the event whose id matches your request. notifications/initialized has no id and receives HTTP 202. Tool names and arguments are listed in tools/list.
HTTP
Action
401
Check Authorization: Bearer and the token.
404
Unknown or closed session: initialize again, then send notifications/initialized.
429
Wait for Retry-After and retry the request.
200
Check Retry-After, JSON-RPC error and tool errors; HTTP 200 may contain a quota refusal.
Session: what YOUR_MCP_SESSION_ID means and where to get it
YOUR_MCP_SESSION_ID — the value of the Mcp-Session-Id response header returned by initialize. It identifies the connection; it is not an access token. Copy only the value, without the header name or surrounding spaces. Send it with Bearer in subsequent calls in this session.
The sequence below resolves the Air Code: initialize with visible response headers (-D -), save the identifier, send notifications/initialized, then tools/call.
Read the identifier using response.headers["Mcp-Session-Id"].
"""Python 3.10+: pip install requests; set AMAZINGCAT_TOKEN from the browser form."""
import json, os
import requests
http = requests.Session()
http.headers.update({"Authorization": "Bearer " + os.environ["AMAZINGCAT_TOKEN"],
"Accept": "application/json, text/event-stream"})
URL = "https://mcp.amazingcat.net/mcp"
def rpc(method, params=None, notification=False):
body = {"jsonrpc": "2.0", "method": method, "params": params or {}}
if not notification: body["id"] = method
with http.post(URL, json=body, stream=True, timeout=(15, 120)) as response:
response.raise_for_status()
if response.headers.get("Retry-After"): raise RuntimeError("Quota pause: " + response.headers["Retry-After"])
# initialize returns this response header; subsequent calls send it back.
if "Mcp-Session-Id" in response.headers:
http.headers["Mcp-Session-Id"] = response.headers["Mcp-Session-Id"]
if response.status_code == 202: return None
if "text/event-stream" in response.headers.get("Content-Type", ""):
response.encoding = "utf-8"; data = []; message = None
for line in response.iter_lines(decode_unicode=True):
if line.startswith("data:"): data.append(line[5:].lstrip())
elif not line and data:
event = json.loads("\n".join(data)); data = []
if event.get("id") == body.get("id"): message = event; break
if message is None: raise RuntimeError("No matching JSON-RPC response")
else: message = response.json()
if "error" in message: raise RuntimeError(message["error"])
return message["result"]
init = rpc("initialize", {"protocolVersion": "2025-06-18", "capabilities": {},
"clientInfo": {"name": "cat-session", "version": "1"}})
session_id = http.headers["Mcp-Session-Id"]
http.headers["MCP-Protocol-Version"] = init["protocolVersion"]
rpc("notifications/initialized", notification=True)
reply = rpc("tools/call", {"name": "law_find", "arguments": {"query": "Воздушный кодекс", "limit": 5}})
if reply.get("isError"): raise RuntimeError(reply)
law = json.loads(next(b["text"] for b in reply["content"] if b["type"] == "text"))
if law.get("error") or law.get("ошибка"): raise RuntimeError(law)
print(json.dumps({"session_received": bool(session_id), "candidates": len(law["candidates"]),
"first": law["candidates"][0]["name"]}, ensure_ascii=False))
http.close()
No fixed session lifetime is advertised. After closing the session or restarting the server, initialize again. An unknown or closed identifier returns HTTP 404 with JSON-RPC error.code=-32600; observed messages are “Session not found” and “Not Found: Session has been terminated”. Remove the old Mcp-Session-Id, repeat initialize and notifications/initialized, then retry the call.
Session calls consume the request quota, including notifications/initialized, tools/list and tools/call. initialize does not consume quota. The number of returned documents does not change the cost of a call.
Clients
All four clients perform the first step of the award-comparison example: resolve the Air Code before reading Articles 103, 105, 107 and 108. limit=5 allows code-name resolution.
"""Small synchronous MCP client. pip install requests"""
import itertools
import json
import os
import random
import time
import requests
class MCP:
def __init__(self, token, endpoint="https://mcp.amazingcat.net/mcp", rpm=60):
self.endpoint = endpoint
self.rpm = min(60, rpm)
self.last = 0.0
self.ids = itertools.count(1)
self.http = requests.Session()
self.http.headers.update({
"Authorization": f"Bearer {token}",
"Accept": "application/json, text/event-stream",
"Content-Type": "application/json",
})
def rpc(self, method, params=None, notification=False):
payload = {"jsonrpc": "2.0", "method": method}
if params is not None:
payload["params"] = params
if not notification:
payload["id"] = next(self.ids)
for attempt in range(5):
time.sleep(max(0, 60 / self.rpm - (time.monotonic() - self.last)))
self.last = time.monotonic()
try:
with self.http.post(self.endpoint, json=payload, stream=True,
timeout=(15, 120)) as response:
# This API also signals tool-call quota exhaustion with HTTP 200.
wait = response.headers.get("Retry-After")
if wait or response.status_code in (429, 502, 503, 504):
delay = int(wait) if wait and wait.isdigit() else min(30, 2 ** attempt)
if attempt == 4:
raise RuntimeError(f"Retry budget exhausted; HTTP {response.status_code}")
# A daily limit must be scheduled for later, not kept in a retry loop.
if delay > 120:
raise RuntimeError(f"Quota exhausted; retry after {delay} seconds")
time.sleep(delay + random.uniform(0, 0.5))
continue
if response.status_code == 404 and method != "initialize" and self.http.headers.get("Mcp-Session-Id") and attempt < 4:
self.http.headers.pop("Mcp-Session-Id", None)
self.initialize()
continue
response.raise_for_status() # 401: replace/refresh credentials, do not retry.
sid = response.headers.get("Mcp-Session-Id")
if sid:
self.http.headers["Mcp-Session-Id"] = sid
if response.status_code == 202:
return None
if "text/event-stream" in response.headers.get("Content-Type", ""):
response.encoding = "utf-8"
data = []
message = None
for line in response.iter_lines(decode_unicode=True, delimiter="\n"):
line = line.rstrip("\r")
if line.startswith("data:"):
data.append(line[5:].lstrip(" "))
elif not line and data:
event = json.loads("\n".join(data))
data = []
if event.get("id") == payload.get("id"):
message = event
break
if message is None:
raise RuntimeError("SSE ended without the matching JSON-RPC response")
else:
message = response.json()
except (requests.ConnectionError, requests.Timeout):
if attempt == 4:
raise
time.sleep(min(30, 2 ** attempt) + random.uniform(0, 0.5))
continue
if "error" in message:
raise RuntimeError(message["error"])
return message["result"]
raise RuntimeError("Retry budget exhausted")
def initialize(self):
result = self.rpc("initialize", {
"protocolVersion": "2025-06-18", "capabilities": {},
"clientInfo": {"name": "amazingcat-api-example", "version": "1.0"},
})
self.http.headers["MCP-Protocol-Version"] = result["protocolVersion"]
self.rpc("notifications/initialized", notification=True)
return result
def call(self, name, arguments):
for attempt in range(5):
result = self.rpc("tools/call", {"name": name, "arguments": arguments})
if result.get("isError"):
raise RuntimeError(result.get("content"))
blocks = [b["text"] for b in result.get("content", []) if b.get("type") == "text"]
if not blocks:
raise RuntimeError("No text content; inspect the MCP result")
try:
value = json.loads(blocks[0])
except json.JSONDecodeError:
# Plain text can be a quota message, never interpret it as zero results.
raise RuntimeError(blocks[0]) from None
if isinstance(value, dict) and value.get("error") == "server_busy":
if attempt < 4:
time.sleep(value.get("retry_after_seconds", 5) + random.uniform(0, 0.5))
continue
if isinstance(value, dict) and (value.get("error") or value.get("ошибка")):
raise RuntimeError(value)
return value
raise RuntimeError("Server busy; retry later")
def close(self):
if "Mcp-Session-Id" in self.http.headers:
self.http.delete(self.endpoint, timeout=(15, 30)).close()
self.http.close()
def main():
client = MCP(os.environ["AMAZINGCAT_TOKEN"])
try:
client.initialize()
result = client.call("law_find", {"query": "Воздушный кодекс", "limit": 5})
print(json.dumps({"tool": "law_find", "candidates": len(result["candidates"]),
"first": result["candidates"][0]["name"]}, ensure_ascii=False))
finally:
client.close()
if __name__ == "__main__":
main()
"""Python 3.10+. pip install -r requirements.txt; set AMAZINGCAT_TOKEN."""
import asyncio
import json
import os
from datetime import timedelta
import httpx
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
async def main():
# Keep the token on your server. Never embed it in a browser bundle.
async with httpx.AsyncClient(
headers={"Authorization": f"Bearer {os.environ['AMAZINGCAT_TOKEN']}"},
timeout=httpx.Timeout(120.0, connect=15.0),
) as http:
async with streamable_http_client(
"https://mcp.amazingcat.net/mcp", http_client=http
) as (read, write, session_id):
async with ClientSession(
read, write, read_timeout_seconds=timedelta(seconds=120)
) as session:
# initialize() also sends notifications/initialized.
# The transport handles session/version headers and JSON/SSE.
initialized = await session.initialize()
result = await session.call_tool(
"law_find", {"query": "Воздушный кодекс", "limit": 5}
)
if result.isError:
raise RuntimeError("Tool returned an MCP error; inspect it privately")
text = next((b.text for b in result.content if b.type == "text"), None)
# HTTP 200 does not by itself prove success: plain text can be a quota error.
if text is None:
raise RuntimeError("Tool returned no text content")
try:
data = json.loads(text)
except json.JSONDecodeError:
raise RuntimeError("Tool returned non-JSON text; check quota/errors") from None
if not isinstance(data, dict) or data.get("error") or data.get("ошибка"):
raise RuntimeError("Tool returned an application error")
# Print counters, avoiding personal names in the sample output.
print(json.dumps({"tool": "law_find", "candidates": len(data["candidates"]),
"first": data["candidates"][0]["name"]}, ensure_ascii=False))
# terminate_on_close=True (the default) sends DELETE for the MCP session.
if __name__ == "__main__":
asyncio.run(main())
Python SDK: download sdk.py and requirements.txt, then run python -m pip install -r requirements.txt. TypeScript: download client.ts and package.json, then npm install and npm start. raw.py and session.py need requests; curl.sh needs curl, awk and Python 3. requirements.txt · package.json.
End-to-end examples
The human asks an AI agent. The agent reads tool descriptions, identifies provisions, selects filters and takes identifiers from the cat’s replies. MCP accepts structured tools/call arguments. Replies below show selected fields; an ellipsis marks a shortened passage.
A decision by case number and court
Connect the cat
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
Ask the cat what the Omsk Regional Court held in case No. 33-2696/2024 and provide a link to the appellate ruling.
The agent prepares the search
The request identifies a specific appellate ruling. The agent resolves the court name to its code, retrieves the case record, then reads the operative part. No substantive-law filter is needed.
Step 1 · oblsud_courts
Select the exact Omsk Regional Court entry: code 55OS0000. The substring lookup also returns other court names.
{
"act_id": 1963772,
"act_key": "55OS0000|doc|27016959|1",
"номер": "33-2696/2024",
"суд": "Омский областной суд (Омская область)",
"дата": "2024-05-06",
"часть": "resolution",
"текст": " Руководствуясь статьями 328, 329 Гражданского процессуального кодекса Российской Федерации, судебная коллегия\n ОПРЕДЕЛИЛА:\n решение Кировского районного суда г. Омска от 26 декабря 2023 года оставить без изменения, апелляционную жалобу К*******а А.В. - без удовлетворения.…\nМотивированное определение изготовлено в окончательной форме 14.06.2024.",
"источник": "https://oblsud.oms.sudrf.ru/modules.php?name=sud_delo&name_op=doc&number=27016959&delo_id=5&new=5&text_number=1&srv_num=1"
}
The agent replies to the human
The Kirovsky District Court of Omsk judgment dated 26 December 2023 was upheld and the appeal dismissed. The case number is 33-2696/2024. The reasoned ruling was prepared on 14 June 2024. The document link is in the response field источник.
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
Ask the cat to find Alexey Valeryevich Kuznetsov’s claim against PJSC Aeroflot in the Kirovsky District Court of Omsk, heard by Judge Bondarenko.
The agent prepares the search
The agent identifies the claimant, defendant, court and judge. It resolves the court code, then the claimant’s party_id and the judge’s judge_id. raysud_search accepts these filters and text terms; raysud_case retrieves the matched case by act_id. A legal-provision filter is unnecessary for identifying the case. Names in the reply remain masked.
Step 1 · raysud_courts
The agent selects the Kirovsky District Court of Omsk. Its directory entry supplies court code 55RS0001.
{
"case_id": 28067732,
"номер": "2-249/2026 (2-5812/2025;) ~ М-4929/2025",
"судья": "Бондаренко Е.В.",
"стороны": "Отношения, связанные с защитой прав потребителей → О защите прав потребителей → - из договоров в сфере: → транспортных услуг ИСТЕЦ(ЗАЯВИТЕЛЬ): К******в А*****й В********ч ОТВЕТЧИК: ПАО Аэрофлот",
"результат": "Иск (заявление, жалоба) УДОВЛЕТВОРЕН ЧАСТИЧНО",
"ссылка_на_карточку": "https://kirovcourt--oms.sudrf.ru/modules.php?name=sud_delo&srv_num=1&name_op=case&case_id=330839593&case_uid=1a022b3b-f02a-45f3-aa4b-ecee9fab773c&delo_id=1540005&new="
}
The agent replies to the human
Case No. 2-249/2026 (2-5812/2025;) ~ М-4929/2025 was found. The record names Judge Bondarenko E.V., defendant PJSC Aeroflot and claimant К******в А*****й В********ч. The claim was granted in part. The reply includes the case-record link. A matching masked name alone does not establish identity.
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
How many district-court cases has PJSC Aeroflot lost over cancelled Ekaterinburg–Phuket bookings? Ask the cat to start with the outcome distribution and check five decisions.
The agent prepares the search
The agent identifies a dispute over cancelled flight bookings. It checks Air Code Articles 103 and 105 (contract and ticket), 107 (termination by the carrier) and 108 (withdrawal by the passenger); these grounds differ. It also checks Civil Code Articles 310, 393 and 786 and Consumer Protection Law Articles 13 and 15. It then uses raysud_search directly and groups outcomes with raysud_aggregate. Aggregation counts decisions, not distinct cases. To count lost cases, the agent checks the parties and operative parts and combines decisions from the same case. The provisions guide legal analysis; they do not replace the carrier and route in the search.
Step 1 · law_find
Request up to five candidates; candidates[0].name supplies the law name for the next call.
{
"articles": [
{
"article": "103",
"text": "Статья 103. Договор воздушной перевозки пассажира. Договор воздушной перевозки груза. Договор воздушной перевозки почты\n1. По договору воздушной перевозки пассажира перевозчик обязуется перевезти пассажира воздушного судна в пункт назначения с предоставлением ему места на воздушном судне, совершающем рейс, указанный в билете, а в случае сдачи пассажиром воздушного судна багажа …"
},
{
"article": "105",
"text": "Статья 105. Перевозочные документы\n1. К перевозочным документам относятся билет, багажная квитанция, грузовая накладная, почтовая накладная, иные документы, используемые при оказании услуг по воздушной перевозке пассажиров, багажа, груза, почты и предусмотренные нормативными правовыми актами федерального органа исполнительной власти, уполномоченного в области транспорта.\n2. Дог…"
},
{
"article": "107",
"text": "Статья 107. Прекращение по инициативе перевозчика действия договора воздушной перевозки пассажира, договора воздушной перевозки груза\n1. Перевозчик может в одностороннем порядке расторгнуть договор воздушной перевозки пассажира, договор воздушной перевозки груза в следующих случаях:\n1) нарушение пассажиром, грузовладельцем, грузоотправителем паспортных, таможенных, санитарных и…"
},
{
"article": "108",
"text": "Статья 108. Прекращение по инициативе пассажира действия договора воздушной перевозки пассажира\n1. В случае отказа пассажира от воздушной перевозки по причинам, не предусмотренным пунктом 2 настоящей статьи, возврат пассажиру провозной платы осуществляется в следующем порядке:\n1) если пассажиром заключен договор воздушной перевозки пассажира, предусматривающий условие о возврат…"
}
]
}
Step 3 · law_get_text
Check the rules on unilateral refusal to perform obligations, damages and passenger carriage. ГК is the abbreviation for the Civil Code.
{
"articles": [
{
"article": "310",
"text": "Статья 310. Недопустимость одностороннего отказа от исполнения обязательства\n1. Односторонний отказ от исполнения обязательства и одностороннее изменение его условий не допускаются, за исключением случаев, предусмотренных настоящим Кодексом, другими законами или иными правовыми актами.\n2. Одностороннее изменение условий обязательства, связанного с осуществлением всеми его сторо…"
},
{
"article": "393",
"text": "Статья 393. Обязанность должника возместить убытки\n1. Должник обязан возместить кредитору убытки, причиненные неисполнением или ненадлежащим исполнением обязательства.\nЕсли иное не установлено законом, использование кредитором иных способов защиты нарушенных прав, предусмотренных законом или договором, не лишает его права требовать от должника возмещения убытков, причиненных не…"
},
{
"article": "786",
"text": "Статья 786. Договор перевозки пассажира\n1. По договору перевозки пассажира перевозчик обязуется перевезти пассажира в пункт назначения, а в случае сдачи пассажиром багажа также доставить багаж в пункт назначения и выдать его управомоченному на получение багажа лицу; пассажир обязуется уплатить установленную плату за проезд, а при сдаче багажа и за провоз багажа.\n2. Заключение д…"
}
]
}
Step 4 · law_get_text
Check liability and compensation for non-pecuniary damage; the agent must assess their applicability to the facts.
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "law_get_text",
"arguments": {
"act": "Закон о защите прав потребителей",
"articles": "13,15",
"max_chars": 13000
}
}
}
The cat’s reply — selected fields:
{
"articles": [
{
"article": "13",
"text": "Статья 13. Ответственность изготовителя (исполнителя, продавца, уполномоченной организации или уполномоченного индивидуального предпринимателя, импортера , владельца агрегатора ) за нарушение прав потребителей\n(Наименование в редакции федеральных законов от 21.12.2004 № 171-ФЗ , от 31.07.2025 № 290-ФЗ )\n1. За нарушение прав потребителей изготовитель (исполнитель, продавец, упол…"
},
{
"article": "15",
"text": "Статья 15. Компенсация морального вреда\nМоральный вред, причиненный потребителю вследствие нарушения изготовителем (исполнителем, продавцом, уполномоченной организацией или уполномоченным индивидуальным предпринимателем, импортером , владельцем агрегатора ) прав потребителя, предусмотренных законами и правовыми актами Российской Федерации, регулирующими отношения в области защи…"
}
]
}
Step 5 · raysud_aggregate
Group the same text query by outcome. limit=10 limits the number of groups. The unit is a court decision, so these counts alone do not answer how many cases the defendant lost.
Search decisions mentioning the carrier, route and booking. The user asks to start with five decisions, so limit=5. The reply supplies case numbers and act_id values.
Build act_ids from the five search results. Read the operative parts to identify the defendant and the relief awarded. Compare court/case-number pairs so that multiple decisions in one case are not counted as separate losses.
{
"показано": 5,
"акты": [
{
"act_id": 37900030,
"номер_дела": "2-2268/2024 ~ М-1224/2024",
"текст": "…Взыскать с публичного акционерного общества «Аэрофлот -Российские авиалинии» (ОГРН № ) в пользу К******а Д****а В*********а ( ДД.ММ.ГГГГ года рождения, СНИЛС № ) компенсацию морального вреда в размере 10 000 руб., штраф в размере 5 000 руб.\n Взыскать с публичного акционерного общества «Аэрофлот - Российские авиалинии» (ОГРН № ) в пользу С*******й М***и С*******ы ( ДД.ММ.ГГГГ года рождения, СНИЛС № ) компенсацию морального вреда в размере 10 000 руб., штраф в размере 5 000 руб.\n Взыскать с публичного акционерного общества «Аэрофлот - Российские авиалинии» (ОГРН № ) государственную пошлину в размере 600 руб.\n Возложить обязанность на К******а Д****а В*********а, С******у М***ю С*******у оплатить договоры воздушной перевозки пассажиров общей стоимостью 39 574 руб.\n В удовлетворении остальной части исковых требований отказать.\n Решение может быть обжаловано в апелляционном порядке в Пермский краевой суд в месячный срок со дня составления мо…"
},
{
"act_id": 19299446,
"номер_дела": "2-1455/2024 ~ М-923/2024",
"текст": "…Взыскать с Публичного акционерного общества «Аэрофлот» (ИНН 7712040126) в пользу З******й И. Д. (паспорт РФ [паспортные данные скрыты]) убытки в сумме 32 968,00 руб., неустойку за просрочку возмещения убытков в сумме 32 968,00 руб., компенсацию морального вреда в сумме 5 000,00 руб., штраф в размере 36 468,00 руб., а всего 107 404,00 руб.\n Взыскать с Публичного акционерного общества «Аэрофлот» (ИНН 7712040126) в пользу З*****а В. Л. (паспорт РФ [паспортные данные скрыты]) убытки в сумме 32 968,00 руб., неустойку за просрочку возмещения убытков в сумме 32 968,00 руб., компенсацию морального вреда в сумме 5 000,00 руб., штраф в размере 36 468,00 руб., а всего 107 404,00 руб.\n В удовлетворении остальной части исковых требований о взыскании морального вреда – отказать. \n Взыскать с Публичного акционерного общества «Аэрофлот» (ИНН 7712040126) в доход местного бюджета государственную пошлину в размере 4 137,00 руб.\n Решение может быть обжалов…"
},
{
"act_id": 21221376,
"номер_дела": "2-1284/2024 ~ М-375/2024",
"текст": "…Взыскать с Публичного акционерного общества «Аэрофлот – российские авиалинии» (ОГРН 1027700092661) в пользу Д***********й С.А. компенсацию морального вреда в размере 10 000 руб., штраф в сумме 5 000 руб.\n Взыскать с Публичного акционерного общества «Аэрофлот – Российские авиалинии» (ОГРН 1027700092661) в доход бюджета государственную пошлину в размере 600 руб.\n Возложить обязанность на Д**********у С.А. оплатить договор воздушной перевозки пассажиров общей стоимостью 29955 руб.\n В удовлетворении остальной части исковых требований Д***********й С.А. отказать.\n Решение в течение месяца со дня принятия в окончательной форме может быть обжаловано в апелляционном порядке в Пермский краевой суд через Кировский районный суд г.Перми.\n Судья Н.М. Швец \n Мотивированное решение составлено 01 августа 2024 года"
},
{
"act_id": 16201876,
"номер_дела": "2-1208/2024 ~ М-2945/2023",
"текст": "…Взыскать с Публичного акционерного общества «Аэрофлот - российские авиалинии» (ПАО «Аэрофлот») (ИНН 7712040126, ОГРН 1027700092661) в пользу Б****а ФИО12 …сумму убытков в размере 115 879 руб., неустойку, предусмотренную ч.5 ст. 28 Закона РФ от 07.02.1992 № 2300-1 «О защите прав потребителей», в размере 76 480 руб. 14 коп., компенсацию морального вреда в размере 5 000 руб., штраф в размере 98 679 руб. 57 коп., а также судебные расходы по направлению почтовых отправлений в размере 425 руб. 10 коп., итого 296 463 (двести девяносто шесть тысяч четыреста шестьдесят три) рубля 81 копейка. \n В удовлетворении остальной части заявленных требований истцам отказать.\n Взыскать с Публичного акционерного общества «Аэрофлот - российские авиалинии» (ПАО «Аэрофлот») (ИНН 7712040126, ОГРН 1027700092661) в пользу местного бюджета госпошлину в размере 5 347 (пять тысяч триста сорок семь) рублей 18 копеек.\n Решение может быть обжаловано в Свердловский областной суд в течение месяца со дня принятия решения суда в окончательной форме путем подачи апелляционной жалобы через Сысертский районный суд, вынесший реше…"
},
{
"act_id": 32036828,
"номер_дела": "2-1083/2024 (2-5747/2023;) ~ М-5033/2023",
"текст": "…Взыскать с публичного акционерного общества «Аэрофлот - российские авиалинии» в пользу ФИО3 , ФИО2 компенсацию морального вреда по 10 000 руб. в пользу каждого, штраф по 5000 руб. в пользу каждого.\n Взыскать с публичного акционерного общества «Аэрофлот - российские авиалинии» в доход местного бюджета государственную пошлину в размере 300 руб.\n Обязать ФИО3 , ФИО2 оплатить договоры воздушной перевозки стоимостью 18 625 руб. каждый.\n Решение суда может быть обжаловано в Пермский краевой суд через Ленинский районный суд г. Перми в течение месяца со дня принятия решения в окончательной форме.\n Копия верна\n Судья Ю.Н.Шпигарь\n Мотивированное решение изготовлено 07.03.2024"
}
],
"next_cursor": null
}
The agent replies to the human
First step: the text query returned 201 court decisions: 118 in cases where the claim was granted in part, 18 granted, four dismissed, and 61 with another outcome. In the five decisions read, claims against Aeroflot were granted in part: five distinct cases with awards to claimants. The figure 136 (118 + 18) is not a count of lost cases. Decisions must first be grouped by case and checked for the defendant and the relief awarded. Continue from the next_cursor returned by search.
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
What is the largest amount awarded to a single claimant in claims against PJSC Aeroflot over cancelled Ekaterinburg–Phuket bookings? Ask the cat to start by comparing five district-court judgments.
The agent prepares the search
The agent identifies the carrier, route and the amount actually awarded. It checks the provisions on carriage contracts, termination, damages and consumer protection. raysud_aggregate groups decisions by outcome but does not compute monetary maxima. The agent therefore retrieves five judgments with raysud_search and reads their operative parts with raysud_acts. Identifiers come from search; it compares one award per claimant in each case.
Step 1 · law_find
Request up to five candidates; candidates[0].name supplies the law name for the next call.
{
"articles": [
{
"article": "103",
"text": "Статья 103. Договор воздушной перевозки пассажира. Договор воздушной перевозки груза. Договор воздушной перевозки почты\n1. По договору воздушной перевозки пассажира перевозчик обязуется перевезти пассажира воздушного судна в пункт назначения с предоставлением ему места на воздушном судне, совершающем рейс, указанный в билете, а в случае сдачи пассажиром воздушного судна багажа …"
},
{
"article": "105",
"text": "Статья 105. Перевозочные документы\n1. К перевозочным документам относятся билет, багажная квитанция, грузовая накладная, почтовая накладная, иные документы, используемые при оказании услуг по воздушной перевозке пассажиров, багажа, груза, почты и предусмотренные нормативными правовыми актами федерального органа исполнительной власти, уполномоченного в области транспорта.\n2. Дог…"
},
{
"article": "107",
"text": "Статья 107. Прекращение по инициативе перевозчика действия договора воздушной перевозки пассажира, договора воздушной перевозки груза\n1. Перевозчик может в одностороннем порядке расторгнуть договор воздушной перевозки пассажира, договор воздушной перевозки груза в следующих случаях:\n1) нарушение пассажиром, грузовладельцем, грузоотправителем паспортных, таможенных, санитарных и…"
},
{
"article": "108",
"text": "Статья 108. Прекращение по инициативе пассажира действия договора воздушной перевозки пассажира\n1. В случае отказа пассажира от воздушной перевозки по причинам, не предусмотренным пунктом 2 настоящей статьи, возврат пассажиру провозной платы осуществляется в следующем порядке:\n1) если пассажиром заключен договор воздушной перевозки пассажира, предусматривающий условие о возврат…"
}
]
}
Step 3 · law_get_text
Check the rules on unilateral refusal to perform obligations, damages and passenger carriage. ГК is the abbreviation for the Civil Code.
{
"articles": [
{
"article": "310",
"text": "Статья 310. Недопустимость одностороннего отказа от исполнения обязательства\n1. Односторонний отказ от исполнения обязательства и одностороннее изменение его условий не допускаются, за исключением случаев, предусмотренных настоящим Кодексом, другими законами или иными правовыми актами.\n2. Одностороннее изменение условий обязательства, связанного с осуществлением всеми его сторо…"
},
{
"article": "393",
"text": "Статья 393. Обязанность должника возместить убытки\n1. Должник обязан возместить кредитору убытки, причиненные неисполнением или ненадлежащим исполнением обязательства.\nЕсли иное не установлено законом, использование кредитором иных способов защиты нарушенных прав, предусмотренных законом или договором, не лишает его права требовать от должника возмещения убытков, причиненных не…"
},
{
"article": "786",
"text": "Статья 786. Договор перевозки пассажира\n1. По договору перевозки пассажира перевозчик обязуется перевезти пассажира в пункт назначения, а в случае сдачи пассажиром багажа также доставить багаж в пункт назначения и выдать его управомоченному на получение багажа лицу; пассажир обязуется уплатить установленную плату за проезд, а при сдаче багажа и за провоз багажа.\n2. Заключение д…"
}
]
}
Step 4 · law_get_text
Check liability and compensation for non-pecuniary damage; the agent must assess their applicability to the facts.
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "law_get_text",
"arguments": {
"act": "Закон о защите прав потребителей",
"articles": "13,15",
"max_chars": 13000
}
}
}
The cat’s reply — selected fields:
{
"articles": [
{
"article": "13",
"text": "Статья 13. Ответственность изготовителя (исполнителя, продавца, уполномоченной организации или уполномоченного индивидуального предпринимателя, импортера , владельца агрегатора ) за нарушение прав потребителей\n(Наименование в редакции федеральных законов от 21.12.2004 № 171-ФЗ , от 31.07.2025 № 290-ФЗ )\n1. За нарушение прав потребителей изготовитель (исполнитель, продавец, упол…"
},
{
"article": "15",
"text": "Статья 15. Компенсация морального вреда\nМоральный вред, причиненный потребителю вследствие нарушения изготовителем (исполнителем, продавцом, уполномоченной организацией или уполномоченным индивидуальным предпринимателем, импортером , владельцем агрегатора ) прав потребителя, предусмотренных законами и правовыми актами Российской Федерации, регулирующими отношения в области защи…"
}
]
}
Step 5 · raysud_search
Search decisions mentioning the carrier, route and booking. The user asks to start with five decisions, so limit=5. The reply supplies case numbers and act_id values.
Take act_ids from search. Read the five operative parts and sum awards to a single claimant: damages, the statutory penalty, compensation for non-pecuniary damage, the consumer-law penalty and awarded costs. Exclude ticket payments owed by the claimants and court fees payable to the state.
{
"показано": 5,
"акты": [
{
"act_id": 37900030,
"номер_дела": "2-2268/2024 ~ М-1224/2024",
"текст": "…Взыскать с публичного акционерного общества «Аэрофлот -Российские авиалинии» (ОГРН № ) в пользу К******а Д****а В*********а ( ДД.ММ.ГГГГ года рождения, СНИЛС № ) компенсацию морального вреда в размере 10 000 руб., штраф в размере 5 000 руб.\n Взыскать с публичного акционерного общества «Аэрофлот - Российские авиалинии» (ОГРН № ) в пользу С*******й М***и С*******ы ( ДД.ММ.ГГГГ года рождения, СНИЛС № ) компенсацию морального вреда в размере 10 000 руб., штраф в размере 5 000 руб.\n Взыскать с публичного акционерного общества «Аэрофлот - Российские авиалинии» (ОГРН № ) государственную пошлину в размере 600 руб.\n Возложить обязанность на К******а Д****а В*********а, С******у М***ю С*******у оплатить договоры воздушной перевозки пассажиров общей стоимостью 39 574 руб.\n В удовлетворении остальной части исковых требований отказать.\n Решение может быть обжаловано в апелляционном порядке в Пермский краевой суд в месячный срок со дня составления мо…"
},
{
"act_id": 19299446,
"номер_дела": "2-1455/2024 ~ М-923/2024",
"текст": "…Взыскать с Публичного акционерного общества «Аэрофлот» (ИНН 7712040126) в пользу З******й И. Д. (паспорт РФ [паспортные данные скрыты]) убытки в сумме 32 968,00 руб., неустойку за просрочку возмещения убытков в сумме 32 968,00 руб., компенсацию морального вреда в сумме 5 000,00 руб., штраф в размере 36 468,00 руб., а всего 107 404,00 руб.\n Взыскать с Публичного акционерного общества «Аэрофлот» (ИНН 7712040126) в пользу З*****а В. Л. (паспорт РФ [паспортные данные скрыты]) убытки в сумме 32 968,00 руб., неустойку за просрочку возмещения убытков в сумме 32 968,00 руб., компенсацию морального вреда в сумме 5 000,00 руб., штраф в размере 36 468,00 руб., а всего 107 404,00 руб.\n В удовлетворении остальной части исковых требований о взыскании морального вреда – отказать. \n Взыскать с Публичного акционерного общества «Аэрофлот» (ИНН 7712040126) в доход местного бюджета государственную пошлину в размере 4 137,00 руб.\n Решение может быть обжалов…"
},
{
"act_id": 21221376,
"номер_дела": "2-1284/2024 ~ М-375/2024",
"текст": "…Взыскать с Публичного акционерного общества «Аэрофлот – российские авиалинии» (ОГРН 1027700092661) в пользу Д***********й С.А. компенсацию морального вреда в размере 10 000 руб., штраф в сумме 5 000 руб.\n Взыскать с Публичного акционерного общества «Аэрофлот – Российские авиалинии» (ОГРН 1027700092661) в доход бюджета государственную пошлину в размере 600 руб.\n Возложить обязанность на Д**********у С.А. оплатить договор воздушной перевозки пассажиров общей стоимостью 29955 руб.\n В удовлетворении остальной части исковых требований Д***********й С.А. отказать.\n Решение в течение месяца со дня принятия в окончательной форме может быть обжаловано в апелляционном порядке в Пермский краевой суд через Кировский районный суд г.Перми.\n Судья Н.М. Швец \n Мотивированное решение составлено 01 августа 2024 года"
},
{
"act_id": 16201876,
"номер_дела": "2-1208/2024 ~ М-2945/2023",
"текст": "…Взыскать с Публичного акционерного общества «Аэрофлот - российские авиалинии» (ПАО «Аэрофлот») (ИНН 7712040126, ОГРН 1027700092661) в пользу Б****а ФИО12 …сумму убытков в размере 115 879 руб., неустойку, предусмотренную ч.5 ст. 28 Закона РФ от 07.02.1992 № 2300-1 «О защите прав потребителей», в размере 76 480 руб. 14 коп., компенсацию морального вреда в размере 5 000 руб., штраф в размере 98 679 руб. 57 коп., а также судебные расходы по направлению почтовых отправлений в размере 425 руб. 10 коп., итого 296 463 (двести девяносто шесть тысяч четыреста шестьдесят три) рубля 81 копейка. \n В удовлетворении остальной части заявленных требований истцам отказать.\n Взыскать с Публичного акционерного общества «Аэрофлот - российские авиалинии» (ПАО «Аэрофлот») (ИНН 7712040126, ОГРН 1027700092661) в пользу местного бюджета госпошлину в размере 5 347 (пять тысяч триста сорок семь) рублей 18 копеек.\n Решение может быть обжаловано в Свердловский областной суд в течение месяца со дня принятия решения суда в окончательной форме путем подачи апелляционной жалобы через Сысертский районный суд, вынесший реше…"
},
{
"act_id": 32036828,
"номер_дела": "2-1083/2024 (2-5747/2023;) ~ М-5033/2023",
"текст": "…Взыскать с публичного акционерного общества «Аэрофлот - российские авиалинии» в пользу ФИО3 , ФИО2 компенсацию морального вреда по 10 000 руб. в пользу каждого, штраф по 5000 руб. в пользу каждого.\n Взыскать с публичного акционерного общества «Аэрофлот - российские авиалинии» в доход местного бюджета государственную пошлину в размере 300 руб.\n Обязать ФИО3 , ФИО2 оплатить договоры воздушной перевозки стоимостью 18 625 руб. каждый.\n Решение суда может быть обжаловано в Пермский краевой суд через Ленинский районный суд г. Перми в течение месяца со дня принятия решения в окончательной форме.\n Копия верна\n Судья Ю.Н.Шпигарь\n Мотивированное решение изготовлено 07.03.2024"
}
],
"next_cursor": null
}
The agent replies to the human
Among the five judgments read, the largest award to a single claimant is RUB 296,463.81, in case No. 2-1208/2024 before the Sysertsky District Court: RUB 115,879 damages + RUB 76,480.14 statutory penalty + RUB 5,000 compensation for non-pecuniary damage + RUB 98,679.57 consumer-law penalty + RUB 425.10 postage costs. The RUB 5,347.18 court fee payable to the state is excluded. This compares five judgments. To establish the maximum across the matched cases, the agent continues from next_cursor and checks subsequent appeals.
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
Ask the cat to find Supreme Court case law under Article 112 of the Civil Procedure Code: when can late receipt of a court decision justify reinstating the time limit for a cassation appeal? Explain one relevant ruling.
The agent prepares the search
The agent identifies the procedural time limit, the cassation appeal and Article 112 of the Civil Procedure Code. It requests five search results with search_practice, then reads one using get_case. The doc_id comes from search; the conclusion concerns the facts of the selected ruling.
Step 1 · search_practice
Request five search results and select one relevant ruling for the requested detailed example. ВС is the supported Supreme Court corpus code.
{
"query": "восстановление срока кассационной жалобы позднее получение копии судебного акта",
"article": "112 ГПК",
"court_filter": "ВС",
"results": [
{
"doc_id": 1993400,
"court": "ВС",
"date": "2021-04-06",
"case_number": "56-КГ21-1-К9",
"position": "ВС РФ, руководствуясь ст. 46 Конституции РФ, ст. 112, 329, 376.1 ГПК РФ, разъяснениями постановления Пленума ВС РФ №29 и Обзора судебной практики №4 (2019), указал, что позднее получение мотивированного судебного акта (апелляционное определение направлено сторонам только 20.05.2020) при добросовестности заявителя и разумности сроков последующих действий является уважительной причиной для восстановления срока. Отказ в восстановлении противоречит п…",
"source_url": "https://www.vsrf.ru/lk/practice/stor_pdf/1993400"
},
{
"doc_id": 2065454,
"court": "ВС",
"date": "2021-11-30",
"case_number": "5-КГ21-138-К2",
"position": "ВС, сославшись на ст.46 Конституции РФ, ст.112, 329, 376.1, 67, 71 ГПК РФ и разъяснения (п.15 постановления Пленума ВС №17, Обзор судебной практики ВС №4 (2019)), указал, что позднее получение мотивированного судебного акта при добросовестности заявителя и разумности сроков может быть уважительной причиной. По делу резолютивная часть апелляционного определения вынесена 28.09.2020, дело поступило в суд первой инстанции 09.12.2020, истица получила …",
"source_url": "https://www.vsrf.ru/lk/practice/stor_pdf/2065454"
}
]
}
3 additional entries are omitted from this abbreviated reply.
Step 2 · get_case
doc_id comes from results[0]: decision No. 56-КГ21-1-К9 dated 6 April 2021.
{
"doc_id": 1993400,
"court": "ВС",
"case_number": "56-КГ21-1-К9",
"date": "2021-04-06",
"position": "ВС РФ, руководствуясь ст. 46 Конституции РФ, ст. 112, 329, 376.1 ГПК РФ, разъяснениями постановления Пленума ВС РФ №29 и Обзора судебной практики №4 (2019), указал, что позднее получение мотивированного судебного акта (апелляционное определение направлено сторонам только 20.05.2020) при добросовестности заявителя и разумности сроков последующих действий является уважительной причиной для восстановления срока. Отказ в восстановлении противоречит праву на судебную защиту.",
"result": "Определения Девятого КСОЮ от 30.07.2020 и от 08.09.2020 отменены. Восстановлен ООО СПК «ГлавМонтажОбъединение» пропущенный процессуальный срок подачи кассационной жалобы. Дело направлено в Девятый КСОЮ для выполнения действий по ст. 378 ГПК РФ.",
"source_url": "https://www.vsrf.ru/lk/practice/stor_pdf/1993400"
}
The agent replies to the human
In ruling No. 56-КГ21-1-К9 of 6 April 2021, the Supreme Court reinstated the time limit: the appellate ruling was sent only on 20 May 2020. The Court considered late receipt, the applicant’s good faith and the time taken to act afterward. An agent would check these circumstances in the new case. Document: https://www.vsrf.ru/lk/practice/stor_pdf/1993400.
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
Ask the cat for Article 219 of the Code of Administrative Judicial Procedure of the Russian Federation as of 1 January 2025: what is the time limit for challenging a bailiff’s action or inaction?
The agent prepares the search
The agent identifies an administrative judicial claim under KAS, rather than an administrative offence under KoAP, Article 219 and date 2025-01-01. It resolves the code and uses law_get_text(as_of) for historical text, rather than get_norm_text for the current revision. It checks the article’s revision date.
Step 1 · law_find
Use the KAS title from candidates[0].name. Up to five candidates allow the agent to resolve the code.
{
"as_of": "2025-01-01",
"articles": [
{
"article": "219",
"as_of": "2025-01-01",
"redaction_date": "20240819",
"valid_until": "20250108",
"text": "…\n3. Административное исковое заявление о признании незаконными решений Федеральной службы судебных приставов, а также решений , действий (бездействия) судебного пристава-исполнителя может быть подано в суд в течение десяти дней со дня, когда гражданину, организации, иному лицу стало известно о нарушении их прав, свобод и законных интересов. (В редакции Федерального закона от 21.12.2021 № 417-ФЗ )\n4. Административное исковое заявление об оспаривании решений, действий (бездействия) исполнительного органа субъекта Российской Федерации, органа местного самоуправления по вопросам, связанным с согласованием места и времени проведения публичного мероприятия (собрания, митинга, демонстрации, шествия, пикетирования), а также с вынесенным этими органами предупреждением в отношении целей такого публичного мероприятия и формы его проведения, может быть подано в суд в течение десяти дней со дня, когда гражданину, организации, иному лицу стало известно о нарушении их прав, свобод и законных интересов. (В редакции Федерального закона от 08.08.2024 № 232-ФЗ )\n5. Пропуск установленного срока обращения в суд не является основанием для отказа в принят…"
}
]
}
The agent replies to the human
Article 219(3) of the Code of Administrative Judicial Procedure gives ten days to challenge a bailiff’s decision, action or inaction, measured from learning of the violation of rights, freedoms and lawful interests. This is the text as of 1 January 2025. For a particular case, establish when the claimant learned of the violation and whether there are grounds to restore a missed deadline.
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
Ask the cat to review this sentence for a civil submission: «Суд просто проигнорировал мои доводы, хотя они могли повлиять на результат дела». Give three Supreme Court wording examples and one revision preserving its meaning.
The agent prepares the search
The agent distinguishes language editing from case-law research. It checks length limits with wording_stats and sends the complete sentence to wording_review for civil proceedings and three examples. The passages help replace conversational wording; facts, provisions and conclusions from the source cases are not imported into the draft.
Step 1 · wording_stats
These limits govern wording-review input: sentence count and length.
sentences contains the user’s text; proceeding=civil follows the submission type, and per_sentence=3 follows the requested number of examples. Frequencies describe usage, not legal correctness.
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "wording_review",
"arguments": {
"sentences": [
"Суд просто проигнорировал мои доводы, хотя они могли повлиять на результат дела."
],
"proceeding": "civil",
"per_sentence": 3,
"frequencies": true
}
}
}
The cat’s reply — selected fields:
{
"status": "ok",
"results": [
{
"n": 1,
"sentence": "Суд просто проигнорировал мои доводы, хотя они могли повлиять на результат дела.",
"examples": [
{
"fragment": "Между тем эти доводы заявителя, имеющие существенное значение для правильного разрешения спора, суд оставил без внимания.",
"act": {
"court": "Верховный Суд Российской Федерации",
"act_number": "18-В10-43",
"act_date": "2010-07-13",
"source_url": "https://www.vsrf.ru/lk/practice/stor_pdf/320448"
}
},
{
"fragment": "Однако эти доводы сторон спора судом первой инстанции были проигнорированы.",
"act": {
"court": "Верховный Суд Российской Федерации",
"act_number": "4-КГ24-74-К1",
"act_date": "2025-01-20",
"source_url": "https://www.vsrf.ru/lk/practice/stor_pdf/2440836"
}
},
{
"fragment": "Данные обстоятельства имели существенное значение для правильного разрешения спора, но были проигнорированы судами.",
"act": {
"court": "Верховный Суд Российской Федерации",
"act_number": "18-КГ25-221-К4",
"act_date": "2025-07-18",
"source_url": "https://www.vsrf.ru/lk/practice/stor_pdf/2479318"
}
}
],
"words": [
{
"text": "дела",
"lemma": "деть",
"acts": 1
},
{
"text": "проигнорировал",
"lemma": "проигнорировать",
"acts": 145
},
{
"text": "повлиять",
"lemma": "повлиять",
"acts": 6667
},
{
"text": "результат",
"lemma": "результат",
"acts": 8792
},
{
"text": "могли",
"lemma": "мочь",
"acts": 14947
},
{
"text": "доводы",
"lemma": "довод",
"acts": 23589
},
{
"text": "дела",
"lemma": "дело",
"acts": 26381
}
]
}
]
}
The agent replies to the human
Suggested revision: «Суд оставил без внимания доводы заявителя, которые могли иметь значение для разрешения спора». It preserves the court as the actor and the conditional “could”; it adds no unconditional duty to reverse the decision. The cat’s response above supplies three examples and their source links.
curl --fail-with-body 'https://mcp.amazingcat.net/mcp' \
-H 'Authorization: Bearer YOUR_AMAZINGCAT_TOKEN' \
-H 'Mcp-Session-Id: YOUR_MCP_SESSION_ID' \
-H 'MCP-Protocol-Version: 2025-06-18' \
-H 'Accept: application/json, text/event-stream' \
-H 'Content-Type: application/json' \
--data-raw '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "wording_review", "arguments": {"sentences": ["Суд просто проигнорировал мои доводы, хотя они могли повлиять на результат дела."], "proceeding": "civil", "per_sentence": 3, "frequencies": true}}}'
Wording review
wording_review retrieves Supreme Court language examples and expression frequencies; the agent replaces awkward wording and proposes a revision. wording_stats reports accepted text sizes.
The cat returns a QR code that can be inserted into a footnote at the bottom of a PDF or DOCX page. A phone camera opens the document URL encoded in the QR.
Decision records for the Supreme Court of the Russian Federation, the Constitutional Court of the Russian Federation, Supreme Court Plenum resolutions, the historical Supreme Commercial Court corpus and reviews of judicial practice contain source_url and a qr object. They are returned by find_term, search_practice, find_by_norm and get_case.
Field
Meaning
source_url
Document URL on the court website: a PDF or document page.
qr.qr_image_url
PNG URL; download the image without a Bearer token.
qr.qr_encodes
URL encoded in the QR. It equals source_url in this example.
qr.qr_note
A brief explanation of the QR.
find_term and search_practice return decision records in results[i]; find_by_norm uses corpora[corpus]["документы"][i], where corpus is a corpus-group key; get_case returns the top-level decision record. Other families use their own schemas: read the PNG address specifically from qr.qr_image_url.
Connect the cat
Connect the free amazingcat MCP server (“the cat”) to your agent using the instructions. Without this connection, the agent cannot call the cat.
The human asks the agent
Ask the cat to find Supreme Court case law on litigation costs and provide a QR code for one relevant ruling for a PDF footnote.
The agent prepares the search
The agent requests five search results, selects one ruling and retrieves it using the returned doc_id. It takes the document URL and PNG URL from source_url and qr.
Step 1 · find_term
Request five search results to select a relevant ruling; the first suitable decision’s doc_id is in results[0].
The agent downloads the PNG from qr.qr_image_url and inserts it into a footnote with the caption “Source: ruling of the Supreme Court of the Russian Federation”, the date, case number and source_url. Scanning the QR code opens qr.qr_encodes.
After decoding the response, use card["source_url"] for the caption and link, and card["qr"]["qr_image_url"] for the image. Download the PNG and embed it in the document.
PDF footnote · HTML for WeasyPrint
Use 24 × 24 mm on the page, with 2 mm of white space around it. Keep the image square. This example’s source PNG is 148 × 148 pixels; pixel dimensions depend on the URL length.
<!doctype html><html lang="ru"><meta charset="utf-8">
<title>QR в сноске</title>
<style>
@page { size: A4; margin: 22mm 25mm;
@footnote { border-top: .25pt solid; padding-top: 3mm; } }
body { font: 12pt/1.5 "DejaVu Sans", sans-serif; }
.note { float: footnote; font-size: 8pt; line-height: 1.3; }
.note::footnote-call { content: counter(footnote); vertical-align: super; font-size: 8pt; }
.note::footnote-marker { content: counter(footnote) ". "; }
.note img { width: 24mm; height: 24mm; padding: 2mm; background: white;
vertical-align: top; margin-right: 3mm; }
.source { display: inline-block; width: 115mm; vertical-align: top; overflow-wrap: anywhere; }
</style>
<h1>Ссылка на судебный акт</h1>
<p>Судебный акт указан в сноске<span class="note"><img src="qr.png" alt="QR на источник">
<span class="source">Источник: Определение Верховного Суда Российской Федерации от 02.10.2026 № 305-ЭС26-10137. <a href="https://www.vsrf.ru/lk/practice/stor_pdf_ec/2571526">https://www.vsrf.ru/lk/practice/stor_pdf_ec/2571526</a></span></span>.</p>
<p>QR-код внизу страницы содержит прямую ссылку на документ на сайте суда.</p>
</html>
float: footnote places the numbered note at the bottom of its page. The local qr.png is embedded in the PDF; the caption link stays clickable.
DOCX footnote
In Word, choose References → Insert Footnote. Insert the downloaded PNG into the note, set width and height to 2.4 cm, keep white space around it, and add a “Source: …” caption linking to the document.
For automated generation, download qr_footnote.py, raw.py and qr_requirements.txt. The script creates HTML, PDF and DOCX with a native Word footnote and an embedded PNG.
python -m pip install -r qr_requirements.txt
python qr_footnote.py --doc-id 2571526 --court ВС \
--caption "Определение Верховного Суда Российской Федерации от 02.10.2026 № 305-ЭС26-10137" \
--output qr-note
In kas_search, court=1…9 identifies a cassation court; court=0 searches without selecting a court. In oblsud_* and raysud_*, court is a directory code from *_courts. Supply both court and case number; numbers are not unique between courts.
kind=civil selects civil proceedings, admin a KAS administrative claim, and koap an administrative-offence case. Check each family’s inputSchema. A session argument accepted by some tools does not replace the Mcp-Session-Id header.
query expresses a topic; text and phrase specify text search, and norms specifies provisions. For kas_search and oblsud_search, take the next cursor from page.next_cursor. For raysud_search, use the top-level next_cursor. Repeat the same filters with that cursor. In law_get_text follow pagination.next_request when truncated.
Participant names in the cat’s replies are masked; preserve the returned mask. Judges’ names may remain visible. Take a document source from its reply; do not assume every family supplies QR.
Batch integration
Export civil rulings of the Seventh Cassation Court of General Jurisdiction mentioning Article 112 of the Civil Procedure Code of the Russian Federation, dated 2023–2025, to CSV.
batch.py · Python 3.10+ and requests. Run one process in a dedicated directory.
"""Python 3.10+: pip install requests; export AMAZINGCAT_TOKEN='your browser-issued token'."""
import csv, json, os, time
from datetime import datetime, timezone
from pathlib import Path
import requests
URL = "https://mcp.amazingcat.net/mcp"
# Seventh cassation court, civil acts mentioning Article 112 GPK, dated 2023–2025.
QUERY = {"court": 7, "kind": "civil", "norms": "ГПК 112", "year_from": 2023,
"year_to": 2025, "limit": 10, "detail": "brief"}
state_path, csv_path = Path("kas-export.state.json"), Path("kas-export.csv")
state = json.loads(state_path.read_text()) if state_path.exists() else {"cursor": 0, "calls": 0}
if time.time() < state.get("retry_not_before", 0):
raise SystemExit("Quota pause; resume in " + str(int(state["retry_not_before"] - time.time()) + 1) + " seconds")
http = requests.Session()
http.headers.update({"Authorization": "Bearer " + os.environ["AMAZINGCAT_TOKEN"],
"Accept": "application/json, text/event-stream"})
last = 0.0
def save():
tmp = state_path.with_suffix(".tmp")
tmp.write_text(json.dumps(state)); tmp.replace(state_path)
def rpc(method, params=None):
global last
body = {"jsonrpc": "2.0", "method": method, "params": params or {}}
if method != "notifications/initialized": body["id"] = method
for attempt in range(5):
day = datetime.now(timezone.utc).date().isoformat()
if state.get("day") != day: state.update(day=day, calls=0)
# Count every attempt except initialize; include the initialized notification.
if method != "initialize":
if state["calls"] >= 300: raise SystemExit("300 requests reached; resume on the next UTC day")
state["calls"] += 1; save()
time.sleep(max(0, 1.05 - (time.monotonic() - last))); last = time.monotonic()
with http.post(URL, json=body, stream=True, timeout=(15, 120)) as r:
# The server can signal a quota delay with HTTP 429 or HTTP 200 + Retry-After.
if r.status_code == 429 or r.headers.get("Retry-After"):
delay = int(r.headers.get("Retry-After", "60"))
state["retry_not_before"] = time.time() + delay; save()
if delay > 60: raise SystemExit("Quota pause; checkpoint saved. Resume after Retry-After: " + str(delay))
time.sleep(delay); continue
if r.status_code == 404: raise SystemExit("Session lost; run the script again to initialize and resume")
r.raise_for_status()
if r.headers.get("Mcp-Session-Id"): http.headers["Mcp-Session-Id"] = r.headers["Mcp-Session-Id"]
if r.status_code == 202: return None
if "text/event-stream" in r.headers.get("Content-Type", ""):
r.encoding = "utf-8"; data = []; reply = None
for line in r.iter_lines(decode_unicode=True):
if line.startswith("data:"): data.append(line[5:].lstrip())
elif not line and data:
event = json.loads("\n".join(data)); data = []
if event.get("id") == body.get("id"): reply = event; break
if reply is None: raise RuntimeError("SSE ended without a response")
else: reply = r.json()
if "error" in reply: raise RuntimeError(reply["error"])
return reply["result"]
raise SystemExit("Retry limit reached; checkpoint saved")
if state.get("done"): raise SystemExit("Export already completed: " + str(csv_path))
if state.get("cursor", 0) and not csv_path.exists(): raise SystemExit("CSV missing; restore it before resuming")
init = rpc("initialize", {"protocolVersion": "2025-06-18", "capabilities": {},
"clientInfo": {"name": "cat-csv", "version": "1"}})
http.headers["MCP-Protocol-Version"] = init["protocolVersion"]
rpc("notifications/initialized")
seen = {row["act_id"] for row in csv.DictReader(csv_path.open(encoding="utf-8"))} if csv_path.exists() else set()
with csv_path.open("a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["act_id", "суд", "дата", "номер_определения", "класс_исхода"])
if f.tell() == 0: writer.writeheader()
pages = 0
while True:
result = rpc("tools/call", {"name": "kas_search", "arguments": {**QUERY, "cursor": state["cursor"]}})
if result.get("isError"): raise RuntimeError(result)
data = json.loads(next(b["text"] for b in result["content"] if b["type"] == "text"))
if data.get("error") or data.get("ошибка") or data.get("частично"): raise RuntimeError("Search failed; cursor unchanged")
for row in data["результаты"]:
if str(row["act_id"]) not in seen:
writer.writerow({k: row.get(k, "") for k in writer.fieldnames}); seen.add(str(row["act_id"]))
f.flush(); os.fsync(f.fileno()) # Save rows before advancing the checkpoint; deduplicate on resume.
cursor = data["page"]["next_cursor"]
if cursor is not None and cursor <= state["cursor"]: raise RuntimeError("Cursor did not advance; checkpoint unchanged")
state.update(cursor=cursor or 0, done=cursor is None); save()
pages += 1; print(f"rows={len(seen)} found={data['найдено']} next_cursor={cursor}")
if state["done"] or pages == int(os.environ.get("AMAZINGCAT_MAX_PAGES", "0")): break
Small run: two pages of ten rulings. AMAZINGCAT_MAX_PAGES=2 stops the demonstration after two pages; without it, the loop continues until the result ends or quota requires a pause. A new run resumes from the saved cursor.
Quota counts requests, not CSV rows; the initialized notification also consumes quota.
For 429 or Retry-After retry the same page after the specified pause; a long pause stops the script for later resumption.
kas-export.state.json stores the cursor and the script’s UTC-day request count; kas-export.csv stores rows. Keep both files and the same filters when resuming.
The script paces calls by 1.05 seconds and stops after 300 counted attempts per UTC day. Other calls using the same token also consume the shared quota; the server’s response takes precedence.