Files
legal-ai/mcp-server/src/legal_mcp/tools/precedents.py
Chaim 684a4cfd3b
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m41s
Fix 500 error on precedents API — add default=str to json.dumps
UUID and datetime objects from PostgreSQL RETURNING * were not
serializable. All other tool files already used default=str.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:11:30 +00:00

96 lines
3.5 KiB
Python

"""MCP tools for attached legal precedents (user-supplied case-law quotes).
These complement the existing `case_law` table (which is populated from
structured sources and is what the block-writer RAG searches) by storing
free-text citations the chair attaches during the compose phase.
"""
from __future__ import annotations
import json
from pathlib import Path
from uuid import UUID
from legal_mcp.services import db
async def precedent_attach(
case_number: str,
quote: str,
citation: str,
section_id: str = "",
chair_note: str = "",
pdf_document_id: str = "",
) -> str:
"""צירוף פסיקה תומכת לתיק ערר.
Args:
case_number: מספר תיק הערר
quote: הציטוט המדויק שיוכנס להחלטה
citation: מראה המקום (ערר 1126-08-25 ... נ' ... (נבו 9.3.2026))
section_id: מזהה הטענה/סוגיה (threshold_1, issue_3); ריק = כללי לתיק
chair_note: הערה אופציונלית — למה הציטוט תומך בעמדה
pdf_document_id: מזהה קובץ PDF מצורף (אופציונלי)
"""
case = await db.get_case_by_number(case_number)
if not case:
return json.dumps({"error": f"תיק {case_number} לא נמצא."}, ensure_ascii=False)
pdf_uuid: UUID | None = None
if pdf_document_id:
try:
pdf_uuid = UUID(pdf_document_id)
except ValueError:
return json.dumps({"error": "pdf_document_id לא תקין"}, ensure_ascii=False)
row = await db.create_case_precedent(
case_id=UUID(case["id"]),
quote=quote,
citation=citation,
section_id=section_id or None,
chair_note=chair_note,
pdf_document_id=pdf_uuid,
practice_area=case.get("practice_area"),
)
return json.dumps(row, ensure_ascii=False, indent=2, default=str)
async def precedent_list(case_number: str) -> str:
"""רשימת כל הפסיקות שצורפו לתיק, ממוינות לפי סעיף ואז לפי זמן יצירה."""
case = await db.get_case_by_number(case_number)
if not case:
return json.dumps({"error": f"תיק {case_number} לא נמצא."}, ensure_ascii=False)
rows = await db.list_case_precedents(UUID(case["id"]))
return json.dumps(rows, ensure_ascii=False, indent=2, default=str)
async def precedent_remove(precedent_id: str) -> str:
"""הסרת פסיקה מצורפת. קובץ ה-PDF (אם צורף) נשאר ב-documents לצורך audit."""
try:
pid = UUID(precedent_id)
except ValueError:
return json.dumps({"error": "precedent_id לא תקין"}, ensure_ascii=False)
ok = await db.delete_case_precedent(pid)
return json.dumps(
{"deleted": ok, "precedent_id": precedent_id}, ensure_ascii=False,
)
async def precedent_search_library(
query: str, practice_area: str = "", limit: int = 10,
) -> str:
"""חיפוש בספרייה הרוחבית — כל הפסיקות שצורפו אי-פעם בכל התיקים.
Args:
query: מחרוזת חיפוש (מתחרה מול citation ומול quote)
practice_area: אופציונלי — סינון לתחום משפטי מסוים
limit: מספר תוצאות מקסימלי
"""
if not query or len(query.strip()) < 2:
return json.dumps([], ensure_ascii=False)
rows = await db.search_precedent_library(query.strip(), practice_area, limit)
return json.dumps(rows, ensure_ascii=False, indent=2, default=str)