diff --git a/mcp-server/src/legal_mcp/server.py b/mcp-server/src/legal_mcp/server.py index 27beee0..968aa78 100644 --- a/mcp-server/src/legal_mcp/server.py +++ b/mcp-server/src/legal_mcp/server.py @@ -223,6 +223,26 @@ async def get_decision_template(case_number: str) -> str: return await drafting.get_decision_template(case_number) +@mcp.tool() +async def get_block_context( + case_number: str, + block_id: str, + instructions: str = "", +) -> str: + """קבלת הקשר מלא לכתיבת בלוק — ללא API. Claude Code כותב ושומר.""" + return await drafting.get_block_context(case_number, block_id, instructions) + + +@mcp.tool() +async def save_block_content( + case_number: str, + block_id: str, + content: str, +) -> str: + """שמירת בלוק שנכתב ע"י Claude Code ב-DB.""" + return await drafting.save_block_content(case_number, block_id, content) + + @mcp.tool() async def validate_decision(case_number: str) -> str: """בדיקת QA — 6 בדיקות איכות על ההחלטה. אם בדיקה קריטית נכשלת — ייצוא חסום.""" diff --git a/mcp-server/src/legal_mcp/services/block_writer.py b/mcp-server/src/legal_mcp/services/block_writer.py index 896b2a8..8eb4b6c 100644 --- a/mcp-server/src/legal_mcp/services/block_writer.py +++ b/mcp-server/src/legal_mcp/services/block_writer.py @@ -656,6 +656,102 @@ async def _build_previous_blocks_context(case_id: UUID, decision: dict | None) - return "\n\n".join(parts) +# ── Context-only mode (for Claude Code to write) ───────────────── + +async def get_block_context(case_id: UUID, block_id: str, instructions: str = "") -> dict: + """Return full context package for a block WITHOUT calling Claude API. + + Claude Code (or any external writer) uses this context to write the block, + then saves it via save_block_content. + """ + if block_id not in BLOCK_CONFIG: + raise ValueError(f"Unknown block: {block_id}") + + block_cfg = BLOCK_CONFIG[block_id] + case = await db.get_case(case_id) + if not case: + raise ValueError(f"Case {case_id} not found") + + decision = await db.get_decision_by_case(case_id) + + # Template blocks — return content directly + if block_id in TEMPLATE_WRITERS: + content = TEMPLATE_WRITERS[block_id](case, decision) + return { + "block_id": block_id, + "title": block_cfg["title"], + "mode": "template", + "content": content, + } + + # Build all context components + prompt_template = BLOCK_PROMPTS.get(block_id, "") + + case_context = _build_case_context(case, decision) + source_context = await _build_source_context(case_id, block_id) + claims_context = await _build_claims_context(case_id) + direction_context = _build_direction_context(decision) + plans_context = await _build_plans_context(case_id) + precedents_context = await _build_precedents_context(case_id, block_id) + style_context = await _build_style_context() + discussion_context = await _build_previous_blocks_context(case_id, decision) + + outcome = (decision or {}).get("outcome", "rejected") + structure_guidance = STRUCTURE_GUIDANCE.get(outcome, "") + + formatted_prompt = prompt_template.format( + case_context=case_context, + source_context=source_context, + claims_context=claims_context, + direction_context=direction_context, + plans_context=plans_context, + precedents_context=precedents_context, + style_context=style_context, + discussion_context=discussion_context, + structure_guidance=structure_guidance, + ) + + if instructions: + formatted_prompt += f"\n\n## הנחיות נוספות:\n{instructions}" + + # Block י requires approved direction + if block_id == "block-yod": + dir_doc = (decision or {}).get("direction_doc") or {} + if not dir_doc.get("approved"): + raise ValueError("לא ניתן לכתוב בלוק דיון ללא כיוון מאושר.") + + return { + "block_id": block_id, + "title": block_cfg["title"], + "mode": "context", + "prompt": formatted_prompt, + "source_documents": source_context, + "claims": claims_context, + "direction": direction_context, + "precedents": precedents_context, + "style_guide": style_context, + "previous_blocks": discussion_context, + } + + +async def save_block_content(case_id: UUID, block_id: str, content: str) -> dict: + """Save block content written by Claude Code (or any external writer).""" + if block_id not in BLOCK_CONFIG: + raise ValueError(f"Unknown block: {block_id}") + + block_cfg = BLOCK_CONFIG[block_id] + decision = await db.get_decision_by_case(case_id) + if not decision: + decision = await db.create_decision(case_id=case_id) + + result = _build_result(block_id, content, block_cfg) + result["generation_type"] = "claude-code" + result["model_used"] = "claude-code" + + await store_block(UUID(decision["id"]), result) + return result + + # ── Renumbering ─────────────────────────────────────────────────── async def renumber_all_blocks(decision_id: UUID) -> dict: diff --git a/mcp-server/src/legal_mcp/tools/drafting.py b/mcp-server/src/legal_mcp/tools/drafting.py index 4e4cb9e..74707f9 100644 --- a/mcp-server/src/legal_mcp/tools/drafting.py +++ b/mcp-server/src/legal_mcp/tools/drafting.py @@ -382,6 +382,50 @@ async def export_docx(case_number: str, output_path: str = "") -> str: }, ensure_ascii=False, indent=2) +async def get_block_context(case_number: str, block_id: str, instructions: str = "") -> str: + """קבלת הקשר מלא לכתיבת בלוק — ללא קריאה ל-API. Claude Code כותב את הבלוק. + + Args: + case_number: מספר תיק הערר + block_id: מזהה הבלוק (block-he, block-vav, ..., block-yod-bet) + instructions: הנחיות נוספות + """ + from legal_mcp.services import block_writer + + case = await db.get_case_by_number(case_number) + if not case: + return f"תיק {case_number} לא נמצא." + + case_id = UUID(case["id"]) + try: + ctx = await block_writer.get_block_context(case_id, block_id, instructions) + return json.dumps(ctx, default=str, ensure_ascii=False, indent=2) + except ValueError as e: + return str(e) + + +async def save_block_content(case_number: str, block_id: str, content: str) -> str: + """שמירת בלוק שנכתב ע"י Claude Code ב-DB. + + Args: + case_number: מספר תיק הערר + block_id: מזהה הבלוק + content: הטקסט שנכתב + """ + from legal_mcp.services import block_writer + + case = await db.get_case_by_number(case_number) + if not case: + return f"תיק {case_number} לא נמצא." + + case_id = UUID(case["id"]) + try: + result = await block_writer.save_block_content(case_id, block_id, content) + return json.dumps(result, default=str, ensure_ascii=False, indent=2) + except ValueError as e: + return str(e) + + async def analyze_style() -> str: """הרצת ניתוח סגנון על קורפוס ההחלטות של דפנה. מחלץ דפוסי כתיבה ושומר אותם.""" from legal_mcp.services.style_analyzer import analyze_corpus