diff --git a/mcp-server/src/legal_mcp/server.py b/mcp-server/src/legal_mcp/server.py index 7f0098d..20f7ff2 100644 --- a/mcp-server/src/legal_mcp/server.py +++ b/mcp-server/src/legal_mcp/server.py @@ -116,6 +116,13 @@ async def case_delete(case_number: str, remove_files: bool = False) -> str: return await cases.case_delete(case_number, remove_files) +@mcp.tool() +async def case_get_final_text(case_number: str, max_chars: int = 0) -> str: + """קליטת טקסט ההחלטה הסופית (`סופי-{case}.docx` בתיקיית exports). + max_chars: 0=הכל, אחרת חיתוך לאורך הנתון. שימושי ל-Hermes Knowledge Curator.""" + return await cases.case_get_final_text(case_number, max_chars) + + # Precedent attachments (user-supplied legal support for the compose phase) @mcp.tool() async def precedent_attach( diff --git a/mcp-server/src/legal_mcp/tools/cases.py b/mcp-server/src/legal_mcp/tools/cases.py index 7c07717..0219de5 100644 --- a/mcp-server/src/legal_mcp/tools/cases.py +++ b/mcp-server/src/legal_mcp/tools/cases.py @@ -13,7 +13,7 @@ from uuid import UUID import httpx from legal_mcp import config -from legal_mcp.services import audit, db, git_sync, practice_area as pa +from legal_mcp.services import audit, db, extractor, git_sync, practice_area as pa logger = logging.getLogger(__name__) @@ -370,3 +370,55 @@ async def case_delete(case_number: str, remove_files: bool = False) -> str: result["removed_files"] = True return json.dumps(result, ensure_ascii=False, indent=2) + + +async def case_get_final_text(case_number: str, max_chars: int = 0) -> str: + """קליטת טקסט ההחלטה הסופית (`סופי-{case}.docx` בתיקיית exports). + + בניגוד ל-`document_get_text` שעובד על שורות בטבלת `documents`, + הקובץ הסופי הוא רק קובץ בתיקייה (נוצר על ידי `api_mark_final`). + + Args: + case_number: מספר תיק הערר + max_chars: אם >0, חתוך את הטקסט המוחזר לאורך הזה. 0 = הכל. + """ + case_dir = config.find_case_dir(case_number) + final_path = case_dir / "exports" / f"סופי-{case_number}.docx" + + if not final_path.exists(): + return json.dumps({ + "status": "not_found", + "case_number": case_number, + "expected_path": str(final_path), + "hint": ( + "ההחלטה הסופית עדיין לא סומנה כ'סופית' ב-UI. " + "דפנה צריכה ללחוץ 'סמן כסופי' על קובץ הטיוטה הנכון." + ), + }, ensure_ascii=False, indent=2) + + try: + text, page_count, _ = await extractor.extract_text(str(final_path)) + except Exception as e: + logger.exception("case_get_final_text: extraction failed for %s", case_number) + return json.dumps({ + "status": "error", + "case_number": case_number, + "file_path": str(final_path), + "error": str(e), + }, ensure_ascii=False, indent=2) + + text = text or "" + truncated = False + if max_chars > 0 and len(text) > max_chars: + text = text[:max_chars] + truncated = True + + return json.dumps({ + "status": "ok", + "case_number": case_number, + "file_path": str(final_path), + "text_length": len(text), + "page_count": page_count, + "truncated": truncated, + "text": text, + }, ensure_ascii=False, indent=2)