feat(mcp): FU-14 פרוסה 1 — get_appraiser_facts (GAP-44) + limit-caps (GAP-53)
תוספתי בלבד, אפס שבירת-תאימות. שני invariants מחוזה-כלי-ה-MCP (X9): GAP-44 (INV-TOOL4, סימטריית extract/get): נוסף get_appraiser_facts — ה-get המקביל ל-extract_appraiser_facts. קורא list_appraiser_facts + detect_appraiser_conflicts מה-DB ללא חילוץ-LLM יקר ולא-דטרמיניסטי. מחזיר count=0 (לא שגיאה) אם טרם חולץ. GAP-53 (INV-TOOL5, limit-caps / OWASP API4:2023): נוסף _clamp_limit (תקרה 200, non-positive→max) על ~13 כלי list/search ב-server.py (case_list, search_*, precedent_library_list, halachot_pending, missing_precedent_list, list_*_citations…). list_chair_feedback קיבל param limit חדש (server→workflow→db עם LIMIT) — היה ללא תקרה כלל. לא הוסף get_appraiser_facts ל-frontmatter של סוכנים (INV-AG3 "לא עודף" — ההוראות עוד לא מפנות אליו; חיווט = follow-up). נותר ב-FU-14: GAP-45/48/49/50/51/52. עודכנו docs/spec/X9 (INV-TOOL4/5) ו-gap-audit (סטטוס פרוסה 1). אומת: py_compile על 4 קבצי הקוד. אימות runtime (restart MCP server) נדחה עד שהחילוץ הפעיל של היו"ר יסתיים. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -84,10 +84,24 @@ async def case_create(
|
||||
)
|
||||
|
||||
|
||||
# INV-TOOL5 / GAP-53: hard cap on list/search result sizes (OWASP API4:2023 —
|
||||
# Unrestricted Resource Consumption). Non-positive is treated as "max", not "all".
|
||||
_MAX_LIMIT = 200
|
||||
|
||||
|
||||
def _clamp_limit(limit: int, hard_max: int = _MAX_LIMIT) -> int:
|
||||
"""Clamp a caller-supplied result limit to [1, hard_max]."""
|
||||
try:
|
||||
n = int(limit)
|
||||
except (TypeError, ValueError):
|
||||
return hard_max
|
||||
return hard_max if n <= 0 else min(n, hard_max)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def case_list(status: str = "", limit: int = 50) -> str:
|
||||
"""רשימת תיקי ערר. סינון אופציונלי לפי סטטוס (new/in_progress/drafted/reviewed/final)."""
|
||||
return await cases.case_list(status, limit)
|
||||
return await cases.case_list(status, _clamp_limit(limit))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@@ -162,7 +176,7 @@ async def precedent_search_library(
|
||||
) -> str:
|
||||
"""חיפוש בציטוטים שדפנה צירפה ידנית לתיקים בעבר (case_precedents).
|
||||
שונה מ-search_precedent_library שמחפש בקורפוס הפסיקה הסמכותית."""
|
||||
return await precedents.precedent_search_library(query, practice_area, limit)
|
||||
return await precedents.precedent_search_library(query, practice_area, _clamp_limit(limit))
|
||||
|
||||
|
||||
# ── External Precedent Library — authoritative case-law corpus ─────
|
||||
@@ -214,7 +228,7 @@ async def precedent_library_list(
|
||||
"""
|
||||
return await plib.precedent_library_list(
|
||||
practice_area, court, precedent_level, source_type, search,
|
||||
source_kind, limit,
|
||||
source_kind, _clamp_limit(limit),
|
||||
)
|
||||
|
||||
|
||||
@@ -273,7 +287,7 @@ async def style_corpus_enrich(corpus_id: str, overwrite: bool = False) -> str:
|
||||
@mcp.tool()
|
||||
async def style_corpus_pending_enrichment(limit: int = 50) -> str:
|
||||
"""רשימת החלטות בקורפוס הסגנון שעדיין חסרות summary/outcome/key_principles — מועמדות לחילוץ."""
|
||||
return await train_tools.list_corpus_pending_enrichment(limit)
|
||||
return await train_tools.list_corpus_pending_enrichment(_clamp_limit(limit))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@@ -296,7 +310,7 @@ async def search_precedent_library(
|
||||
"""חיפוש סמנטי בקורפוס הפסיקה הסמכותית. מחזיר הלכות (מאושרות בלבד) + קטעי טקסט. השתמש כש-legal-writer צריך לצטט פסיקה מחייבת בבלוק י (CREAC: rule + explanation)."""
|
||||
return await plib.search_precedent_library(
|
||||
query, practice_area, court, precedent_level, appeal_subtype,
|
||||
None, subject_tag, limit, include_halachot,
|
||||
None, subject_tag, _clamp_limit(limit), include_halachot,
|
||||
)
|
||||
|
||||
|
||||
@@ -320,7 +334,7 @@ async def halacha_review(
|
||||
@mcp.tool()
|
||||
async def halachot_pending(limit: int = 100) -> str:
|
||||
"""תור ההלכות הממתינות לאישור."""
|
||||
return await plib.halachot_pending(limit)
|
||||
return await plib.halachot_pending(_clamp_limit(limit))
|
||||
|
||||
|
||||
# Documents
|
||||
@@ -439,7 +453,7 @@ async def search_decisions(
|
||||
) -> str:
|
||||
"""חיפוש סמנטי בהחלטות קודמות ובמסמכים — מסונן לפי תחום משפטי."""
|
||||
return await search.search_decisions(
|
||||
query, limit, section_type, practice_area, appeal_subtype, case_number,
|
||||
query, _clamp_limit(limit), section_type, practice_area, appeal_subtype, case_number,
|
||||
)
|
||||
|
||||
|
||||
@@ -450,7 +464,7 @@ async def search_case_documents(
|
||||
limit: int = 10,
|
||||
) -> str:
|
||||
"""חיפוש סמנטי בתוך מסמכי תיק ספציפי."""
|
||||
return await search.search_case_documents(case_number, query, limit)
|
||||
return await search.search_case_documents(case_number, query, _clamp_limit(limit))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
@@ -463,7 +477,7 @@ async def find_similar_cases(
|
||||
) -> str:
|
||||
"""מציאת תיקים דומים על בסיס תיאור — מסונן לפי תחום משפטי."""
|
||||
return await search.find_similar_cases(
|
||||
description, limit, practice_area, appeal_subtype, case_number,
|
||||
description, _clamp_limit(limit), practice_area, appeal_subtype, case_number,
|
||||
)
|
||||
|
||||
|
||||
@@ -496,7 +510,7 @@ async def search_internal_decisions(
|
||||
כשרוצים להרחיב מעבר לטקסט המקורי. default False.
|
||||
"""
|
||||
return await search.search_internal_decisions(
|
||||
query, practice_area, appeal_subtype, district, chair_name, limit, include_halachot,
|
||||
query, practice_area, appeal_subtype, district, chair_name, _clamp_limit(limit), include_halachot,
|
||||
include_cited_by=include_cited_by,
|
||||
)
|
||||
|
||||
@@ -571,6 +585,12 @@ async def extract_appraiser_facts(case_number: str) -> str:
|
||||
return await drafting.extract_appraiser_facts(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_appraiser_facts(case_number: str) -> str:
|
||||
"""קריאת עובדות-השמאי שכבר חולצו (facts + סתירות) — ללא חילוץ-מחדש יקר. ה-get המקביל ל-extract_appraiser_facts."""
|
||||
return await drafting.get_appraiser_facts(case_number)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def write_interim_draft(case_number: str, instructions: str = "") -> str:
|
||||
"""כתיבת ארבעת הבלוקים לטיוטת ביניים (רקע, תכניות+היתרים, טענות, הליכים) — אותו skill וטמפלט."""
|
||||
@@ -813,7 +833,7 @@ async def missing_precedent_list(
|
||||
case_number=case_number,
|
||||
status=status,
|
||||
legal_topic=legal_topic,
|
||||
limit=limit,
|
||||
limit=_clamp_limit(limit),
|
||||
)
|
||||
|
||||
|
||||
@@ -878,7 +898,7 @@ async def list_internal_citations(
|
||||
return await cit_tools.list_internal_citations(
|
||||
case_law_id=case_law_id,
|
||||
linked_only=linked_only,
|
||||
limit=limit,
|
||||
limit=_clamp_limit(limit),
|
||||
)
|
||||
|
||||
|
||||
@@ -894,7 +914,7 @@ async def list_incoming_citations(
|
||||
"""
|
||||
return await cit_tools.list_incoming_citations(
|
||||
case_law_id=case_law_id,
|
||||
limit=limit,
|
||||
limit=_clamp_limit(limit),
|
||||
)
|
||||
|
||||
|
||||
@@ -917,9 +937,10 @@ async def list_chair_feedback(
|
||||
case_number: str = "",
|
||||
category: str = "",
|
||||
unresolved_only: bool = True,
|
||||
limit: int = 100,
|
||||
) -> str:
|
||||
"""הצגת הערות יו"ר שתועדו — אפשר לסנן לפי תיק, קטגוריה, מטופלות."""
|
||||
return await workflow.list_chair_feedback(case_number, category, unresolved_only)
|
||||
return await workflow.list_chair_feedback(case_number, category, unresolved_only, _clamp_limit(limit))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
|
||||
Reference in New Issue
Block a user