Merge pull request #28 from hburaktasyurek/tool-limit-alignment

Tool Routing: align descriptions with runtime behavior and reduce Bedesten request overhead
This commit is contained in:
Said Sürücü
2026-06-16 11:53:09 +03:00
committed by GitHub
2 changed files with 97 additions and 151 deletions
+14 -13
View File
@@ -313,7 +313,7 @@ API anahtarınızı [openrouter.ai/keys](https://openrouter.ai/keys) adresinden
<details> <details>
<summary>🛠️ <strong>Kullanılabilir Araçlar (MCP Tools)</strong></summary> <summary>🛠️ <strong>Kullanılabilir Araçlar (MCP Tools)</strong></summary>
Bu FastMCP sunucusu **24 temel MCP aracı** + **1 opsiyonel semantik arama aracı** sunar (token verimliliği için optimize edilmiş): Bu FastMCP sunucusu **26 aktif MCP aracı** + **1 opsiyonel semantik arama aracı** sunar (token verimliliği için optimize edilmiş):
### **Yargıtay Araçları (Birleşik Bedesten API - Token Optimized)** ### **Yargıtay Araçları (Birleşik Bedesten API - Token Optimized)**
*Not: Yargıtay araçları token verimliliği için birleşik Bedesten API'ye entegre edilmiştir* *Not: Yargıtay araçları token verimliliği için birleşik Bedesten API'ye entegre edilmiştir*
@@ -338,8 +338,8 @@ Bu FastMCP sunucusu **24 temel MCP aracı** + **1 opsiyonel semantik arama arac
8. `get_anayasa_document_unified(document_url, page_number)`: AYM kararlarını birleşik belge getirme - **sayfalanmış Markdown** içeriği 8. `get_anayasa_document_unified(document_url, page_number)`: AYM kararlarını birleşik belge getirme - **sayfalanmış Markdown** içeriği
### **KİK (Kamu İhale Kurulu) Araçları** ### **KİK (Kamu İhale Kurulu) Araçları**
9. `search_kik_decisions(karar_tipi, ...)`: KİK (Kamu İhale Kurulu) kararlarını arar. 9. `search_kik_v2_decisions(decision_type, karar_metni, karar_no, basvuran, idare_adi, baslangic_tarihi, bitis_tarihi)`: KİK v2 API ile uyuşmazlık, düzenleyici ve mahkeme kararlarını arar.
10. `get_kik_document_markdown(karar_id, page_number)`: Belirli bir KİK kararını, Base64 ile encode edilmiş `karar_id`'sini kullanarak alır ve **sayfalanmış Markdown** içeriğini getirir. 10. `get_kik_v2_document_markdown(gundemMaddesiId)`: Arama sonucundaki `gundemMaddesiId` ile KİK karar metnini Markdown formatında getirir.
### **Rekabet Kurumu Araçları** ### **Rekabet Kurumu Araçları**
    * `search_rekabet_kurumu_decisions(KararTuru: Literal[...], ...) -> RekabetSearchResult`: Rekabet Kurumu kararlarını arar. `KararTuru` için kullanıcı dostu isimler kullanılır (örn: "Birleşme ve Devralma").     * `search_rekabet_kurumu_decisions(KararTuru: Literal[...], ...) -> RekabetSearchResult`: Rekabet Kurumu kararlarını arar. `KararTuru` için kullanıcı dostu isimler kullanılır (örn: "Birleşme ve Devralma").
    * `get_rekabet_kurumu_document(karar_id: str, page_number: Optional[int] = 1) -> RekabetDocument`: Belirli bir Rekabet Kurumu kararını `karar_id` ile alır. Kararın PDF formatındaki orijinalinden istenen sayfayı ayıklar ve Markdown formatında döndürür.     * `get_rekabet_kurumu_document(karar_id: str, page_number: Optional[int] = 1) -> RekabetDocument`: Belirli bir Rekabet Kurumu kararını `karar_id` ile alır. Kararın PDF formatındaki orijinalinden istenen sayfayı ayıklar ve Markdown formatında döndürür.
@@ -347,16 +347,12 @@ Bu FastMCP sunucusu **24 temel MCP aracı** + **1 opsiyonel semantik arama arac
--- ---
* **Sayıştay Araçları (3 Karar Türü + 8 Daire Filtreleme):** * **Sayıştay Araçları (Birleşik API, 3 Karar Türü + 8 Daire Filtreleme):**
* `search_sayistay_genel_kurul(karar_no, karar_tarih_baslangic, karar_tamami, ...)`: Sayıştay Genel Kurul (yorumlayıcı) kararlarını arar. **Tarih aralığı** (2006-2024) + **İçerik arama** (400 karakter) * `search_sayistay_unified(decision_type, start, length, ...)`: `genel_kurul`, `temyiz_kurulu` veya `daire` kararlarını tek araçla arar. `length` 1-100 aralığındadır.
* `search_sayistay_temyiz_kurulu(ilam_dairesi, kamu_idaresi_turu, temyiz_karar, ...)`: Temyiz Kurulu (itiraz) kararlarını arar. **8 Daire filtreleme** + **Kurum türü** + **Konu sınıflandırması** * `get_sayistay_document_unified(decision_id, decision_type)`: Birleşik arama sonucundaki karar ID'si ve karar türüyle tam metni Markdown formatında getirir.
* `search_sayistay_daire(yargilama_dairesi, web_karar_metni, hesap_yili, ...)`: Daire (ilk derece denetim) kararlarını arar. **8 Daire filtreleme** + **Hesap yılı** + **İçerik arama**
* `get_sayistay_genel_kurul_document_markdown(decision_id: str)`: Genel Kurul kararının tam metnini Markdown formatında getirir
* `get_sayistay_temyiz_kurulu_document_markdown(decision_id: str)`: Temyiz Kurulu kararının tam metnini Markdown formatında getirir
* `get_sayistay_daire_document_markdown(decision_id: str)`: Daire kararının tam metnini Markdown formatında getirir
* **KVKK Araçları (Brave Search API + Türkçe Arama):** * **KVKK Araçları (Brave Search API + Türkçe Arama):**
* `search_kvkk_decisions(keywords, page, pageSize, ...)`: KVKK (Kişisel Verilerin Korunması Kurulu) kararlarını Brave Search API ile arar. **Türkçe arama** + **Site hedeflemeli** (`site:kvkk.gov.tr "karar özeti"`) + **Sayfalama desteği** * `search_kvkk_decisions(keywords, page)`: KVKK (Kişisel Verilerin Korunması Kurulu) kararlarını Brave Search API ile arar. **Türkçe arama** + **Site hedeflemeli** (`site:kvkk.gov.tr "karar özeti"`) + **Sayfalama desteği**. Sonuç sayısı sunucuda 10 olarak sabitlenmiştir.
* `get_kvkk_document_markdown(decision_url: str, page_number: Optional[int] = 1)`: KVKK kararının tam metnini **sayfalanmış Markdown** formatında getirir (5.000 karakterlik sayfa) * `get_kvkk_document_markdown(decision_url: str, page_number: Optional[int] = 1)`: KVKK kararının tam metnini **sayfalanmış Markdown** formatında getirir (5.000 karakterlik sayfa)
### BDDK Araçları ### BDDK Araçları
@@ -368,10 +364,15 @@ Bu FastMCP sunucusu **24 temel MCP aracı** + **1 opsiyonel semantik arama arac
* `get_gib_ozelge_document_markdown(ozelge_id: int, page_number: int = 1)`: Belirli bir özelgenin tam metnini **sayfalanmış Markdown** formatında getirir (5.000 karakterlik sayfa) + **Metadata başlık bloğu** (Başlık, Sayı, Tarih, Kanun, Kaynak URL) * `get_gib_ozelge_document_markdown(ozelge_id: int, page_number: int = 1)`: Belirli bir özelgenin tam metnini **sayfalanmış Markdown** formatında getirir (5.000 karakterlik sayfa) + **Metadata başlık bloğu** (Başlık, Sayı, Tarih, Kanun, Kaynak URL)
### Sigorta Tahkim Komisyonu Araçları (Tavily Search API + PDF) ### Sigorta Tahkim Komisyonu Araçları (Tavily Search API + PDF)
* `search_sigorta_tahkim_decisions(keywords, page, pageSize)`: Sigorta Tahkim Komisyonu kararlarını Tavily Search API ile arar. **Site hedeflemeli** (`sigortatahkim.org`) + **Sayfalama desteği** * `search_sigorta_tahkim_decisions(keywords, page)`: Sigorta Tahkim Komisyonu kararlarını Tavily Search API ile arar. **Site hedeflemeli** (`sigortatahkim.org`) + **Sayfalama desteği**. Sonuç sayısı sunucuda 10 olarak sabitlenmiştir.
* `get_sigorta_tahkim_document_markdown(issue_number: str, page_number: int)`: Hakem Karar Dergisi sayısının PDF'ini indirip **sayfalanmış Markdown** formatında getirir (5.000 karakterlik sayfa). 64 sayı (2010-2025) * `get_sigorta_tahkim_document_markdown(issue_number: str, page_number: int)`: Hakem Karar Dergisi sayısının PDF'ini indirip **sayfalanmış Markdown** formatında getirir (5.000 karakterlik sayfa). 64 sayı (2010-2025)
* `search_within_sigorta_tahkim_issue(issue_number: str, keyword: str, max_results: int)`: Belirli bir dergi sayısı içindeki kararları anahtar kelime ile arar. **Türkçe İ/I desteği** + **Relevance scoring** + **Excerpt** ile sonuç * `search_within_sigorta_tahkim_issue(issue_number: str, keyword: str, max_results: int)`: Belirli bir dergi sayısı içindeki kararları anahtar kelime ile arar. **Türkçe İ/I desteği** + **Relevance scoring** + **Excerpt** ile sonuç
### Yardımcı ve Uyumluluk Araçları
* `check_government_servers_health()`: Yargı kaynaklarının erişilebilirliğini kontrol eder.
* `search(query)`: ChatGPT Deep Research uyumluluğu için Bedesten destekli kaynaklarda arama yapar.
* `fetch(id)`: ChatGPT Deep Research uyumluluğu için tek bir Bedesten belge ID'sinin tam metnini getirir.
</details> </details>
--- ---
@@ -387,7 +388,7 @@ Bu FastMCP sunucusu **24 temel MCP aracı** + **1 opsiyonel semantik arama arac
**GENEL İSTATİSTİKLER:** **GENEL İSTATİSTİKLER:**
- **Toplam Mahkeme/Kurum:** 15 farklı hukuki kurum (GİB Özelgeleri ve Sigorta Tahkim Komisyonu dahil) - **Toplam Mahkeme/Kurum:** 15 farklı hukuki kurum (GİB Özelgeleri ve Sigorta Tahkim Komisyonu dahil)
- **Toplam MCP Tool:** 24 temel araç + 1 opsiyonel semantik arama aracı - **Toplam MCP Tool:** 26 aktif araç + 1 opsiyonel semantik arama aracı
- **Daire/Kurul Filtreleme:** 87 farklı seçenek (52 Yargıtay + 27 Danıştay + 8 Sayıştay) - **Daire/Kurul Filtreleme:** 87 farklı seçenek (52 Yargıtay + 27 Danıştay + 8 Sayıştay)
- **Tarih Filtreleme:** Birleşik Bedesten API aracında ISO 8601 formatında tam tarih aralığı desteği - **Tarih Filtreleme:** Birleşik Bedesten API aracında ISO 8601 formatında tam tarih aralığı desteği
- **Kesin Cümle Arama:** Birleşik Bedesten API aracında çift tırnak ile tam cümle arama (`"\"mülkiyet kararı\""` formatı) - **Kesin Cümle Arama:** Birleşik Bedesten API aracında çift tırnak ile tam cümle arama (`"\"mülkiyet kararı\""` formatı)
+83 -138
View File
@@ -818,7 +818,10 @@ async def get_uyusmazlik_document_markdown_from_url(
# --- Unified MCP Tools for Anayasa Mahkemesi --- # --- Unified MCP Tools for Anayasa Mahkemesi ---
@app.tool( @app.tool(
description="Use this when searching Turkish Constitutional Court decisions. Supports both norm control (legislation review) and individual applications (rights violations).", description=(
"Use this when searching Turkish Constitutional Court decision records. Supports norm control decisions "
"and individual application decisions. Norm control filters include reviewed norm metadata; results are court decisions."
),
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1095,7 +1098,11 @@ async def get_rekabet_kurumu_document(
# --- MCP Tools for Bedesten (Unified Search Across All Courts) --- # --- MCP Tools for Bedesten (Unified Search Across All Courts) ---
@app.tool( @app.tool(
description="Use this when searching across multiple Turkish courts in a single query. Supports Yargıtay, Danıştay, Local Courts, Appeals Courts, and KYB.", description=(
"Use this for Turkish court decision records from Yargıtay, Danıştay, Local Courts, Appeals Courts, and KYB via Bedesten. "
"Prefer narrow court_types over all courts. pageSize is intentionally fixed to 10 results per page. "
"Bedesten is upstream rate-limited; avoid parallel repeated calls and wait retry_after seconds after 429 responses."
),
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1119,7 +1126,7 @@ For best results, use exact phrases with quotes for legal terms."""),
description="Court types: YARGITAYKARARI, DANISTAYKARAR, YERELHUKUK, ISTINAFHUKUK, KYB" description="Court types: YARGITAYKARARI, DANISTAYKARAR, YERELHUKUK, ISTINAFHUKUK, KYB"
), ),
# pageSize: int = Field(10, ge=1, le=10, description="Results per page (1-10)"), # pageSize: int = Field(10, ge=1, le=10, description="Results per page (1-10)"),
pageNumber: int = Field(1, ge=1, description="Page number"), pageNumber: int = Field(1, ge=1, description="Page number. Each page returns 10 results; pageSize is fixed by the server."),
birimAdi: BirimAdiEnum = Field("ALL", description=""" birimAdi: BirimAdiEnum = Field("ALL", description="""
Chamber filter (optional). Abbreviated values with Turkish names: Chamber filter (optional). Abbreviated values with Turkish names:
• Yargıtay: H1-H23 (1-23. Hukuk Dairesi), C1-C23 (1-23. Ceza Dairesi), HGK (Hukuk Genel Kurulu), CGK (Ceza Genel Kurulu), BGK (Büyük Genel Kurulu), HBK (Hukuk Daireleri Başkanlar Kurulu), CBK (Ceza Daireleri Başkanlar Kurulu) • Yargıtay: H1-H23 (1-23. Hukuk Dairesi), C1-C23 (1-23. Ceza Dairesi), HGK (Hukuk Genel Kurulu), CGK (Ceza Genel Kurulu), BGK (Büyük Genel Kurulu), HBK (Hukuk Daireleri Başkanlar Kurulu), CBK (Ceza Daireleri Başkanlar Kurulu)
@@ -1231,7 +1238,10 @@ For best results, use exact phrases with quotes for legal terms."""),
raise raise
@app.tool( @app.tool(
description="Use this when retrieving full text of any Bedesten-supported court decision. Returns clean Markdown format.", description=(
"Use this when retrieving full text of a Bedesten search result by documentId. "
"Counts against the same Bedesten upstream rate limit as search; after 429, wait retry_after seconds before retrying."
),
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -2187,7 +2197,11 @@ async def get_bddk_document_markdown(
# --- MCP Tools for GİB (Gelir İdaresi Başkanlığı / Revenue Administration) Özelgeler --- # --- MCP Tools for GİB (Gelir İdaresi Başkanlığı / Revenue Administration) Özelgeler ---
@app.tool( @app.tool(
description="Search Turkish GİB özelgeler (Revenue Administration tax rulings) - 18k+ rulings on VAT, income tax, corporate tax, stamp duty interpretations. Supports keyword, date range, and law number filtering.", description=(
"Search Turkish GİB özelge records (Revenue Administration tax rulings) - 18k+ rulings on VAT, "
"income tax, corporate tax, stamp duty interpretations. kanunNo filters the related law number; "
"returned documents are özelge/tax ruling records."
),
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -2197,7 +2211,7 @@ async def get_bddk_document_markdown(
async def search_gib_ozelge( async def search_gib_ozelge(
keywords: str = Field("", description="Turkish keywords searched in title, kanunNo and description (e.g., 'KDV oranı', 'kurumlar vergisi istisna')"), keywords: str = Field("", description="Turkish keywords searched in title, kanunNo and description (e.g., 'KDV oranı', 'kurumlar vergisi istisna')"),
ozelgeNo: str = Field("", description="Exact özelge reference number (e.g., 'E-40247694-130-15524')"), ozelgeNo: str = Field("", description="Exact özelge reference number (e.g., 'E-40247694-130-15524')"),
kanunNo: str = Field("", description="Law number filter, e.g. '3065' for KDV, '193' for Gelir Vergisi"), kanunNo: str = Field("", description="Related law number filter for özelge records, e.g. '3065' for KDV, '193' for Gelir Vergisi"),
ozelgeStartDate: str = Field("", description="Start date YYYY-MM-DD (e.g., '2024-01-01') or full ISO 8601"), ozelgeStartDate: str = Field("", description="Start date YYYY-MM-DD (e.g., '2024-01-01') or full ISO 8601"),
ozelgeEndDate: str = Field("", description="End date YYYY-MM-DD (e.g., '2024-12-31') or full ISO 8601"), ozelgeEndDate: str = Field("", description="End date YYYY-MM-DD (e.g., '2024-12-31') or full ISO 8601"),
page: int = Field(1, ge=1, description="Page number (1-indexed)"), page: int = Field(1, ge=1, description="Page number (1-indexed)"),
@@ -2429,54 +2443,42 @@ async def search_within_sigorta_tahkim_issue(
# --- ChatGPT Deep Research Compatible Tools --- # --- ChatGPT Deep Research Compatible Tools ---
def get_preview_text(markdown_content: str, skip_chars: int = 100, preview_chars: int = 200) -> str: def build_bedesten_title(decision: Any, court_name: str) -> str:
""" """Build a compact title from Bedesten search metadata without fetching the document."""
Extract a preview of document text by skipping headers and showing meaningful content. title_parts = [court_name]
if getattr(decision, "birimAdi", None):
Args: title_parts.append(str(decision.birimAdi))
markdown_content: Full document content in markdown format if getattr(decision, "esasNo", None):
skip_chars: Number of characters to skip from the beginning (default: 100) title_parts.append(f"Esas: {decision.esasNo}")
preview_chars: Number of characters to show in preview (default: 200) if getattr(decision, "kararNo", None):
title_parts.append(f"Karar: {decision.kararNo}")
Returns: if getattr(decision, "kararTarihiStr", None):
Preview text suitable for ChatGPT Deep Research title_parts.append(f"Tarih: {decision.kararTarihiStr}")
""" return " - ".join(title_parts) if title_parts else f"{court_name} - Document {decision.documentId}"
if not markdown_content:
return ""
def build_bedesten_metadata_preview(decision: Any, court_name: str) -> str:
# Remove common markdown artifacts and clean up """Return Deep Research preview text using only search-result metadata."""
cleaned_content = markdown_content.strip() preview_parts = [f"Kaynak: {court_name}"]
if getattr(decision, "birimAdi", None):
# Skip the first N characters (usually headers, metadata) preview_parts.append(f"Daire/Kurul: {decision.birimAdi}")
if len(cleaned_content) > skip_chars: if getattr(decision, "esasNo", None):
content_start = cleaned_content[skip_chars:] preview_parts.append(f"Esas No: {decision.esasNo}")
else: if getattr(decision, "kararNo", None):
content_start = cleaned_content preview_parts.append(f"Karar No: {decision.kararNo}")
if getattr(decision, "kararTarihiStr", None):
# Get the next N characters for preview preview_parts.append(f"Karar Tarihi: {decision.kararTarihiStr}")
if len(content_start) > preview_chars: preview_parts.append("Tam metin için fetch aracını bu sonucun id değeriyle çağırın.")
preview = content_start[:preview_chars] return ". ".join(preview_parts)
else:
preview = content_start
# Clean up the preview - remove incomplete sentences at the end
preview = preview.strip()
# If preview ends mid-sentence, try to end at last complete sentence
if preview and not preview.endswith('.'):
last_period = preview.rfind('.')
if last_period > 50: # Only if there's a reasonable sentence
preview = preview[:last_period + 1]
# Add ellipsis if content was truncated
if len(content_start) > preview_chars:
preview += "..."
return preview.strip()
@app.tool( @app.tool(
description="Only for ChatGPT Deep Research. Use this when searching across all Turkish legal databases in a single query. Returns results in ChatGPT Deep Research compatible format (id, title, text, url). Supports: +term (must have), -term (exclude), \"exact phrase\", term1 OR term2. Use Turkish keywords for best results.", description=(
"Only for ChatGPT Deep Research. Searches Bedesten-supported Turkish court databases and returns "
"OpenAI Deep Research compatible results (id, title, text, url). For regular MCP use, prefer "
"search_bedesten_unified. This tool does not fetch document bodies during search so one query stays "
"within Bedesten upstream rate limits; call fetch only for selected result IDs."
),
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -2506,21 +2508,21 @@ async def search(
try: try:
# Search all court types via unified Bedesten API # Search all court types via unified Bedesten API
court_types = [ court_types = [
("YARGITAYKARARI", "Yargıtay", "yargitay_bedesten"), ("YARGITAYKARARI", "Yargıtay"),
("DANISTAYKARAR", "Danıştay", "danistay_bedesten"), ("DANISTAYKARAR", "Danıştay"),
("YERELHUKUK", "Yerel Hukuk Mahkemesi", "yerel_hukuk_bedesten"), ("YERELHUKUK", "Yerel Hukuk Mahkemesi"),
("ISTINAFHUKUK", "İstinaf Hukuk Mahkemesi", "istinaf_hukuk_bedesten"), ("ISTINAFHUKUK", "İstinaf Hukuk Mahkemesi"),
("KYB", "Kanun Yararına Bozma", "kyb_bedesten") ("KYB", "Kanun Yararına Bozma")
] ]
for item_type, court_name, id_prefix in court_types: for item_type, court_name in court_types:
try: try:
search_results = await bedesten_client_instance.search_documents( search_results = await bedesten_client_instance.search_documents(
BedestenSearchRequest( BedestenSearchRequest(
data=BedestenSearchData( data=BedestenSearchData(
phrase=query, # Use query as-is to support both regular and exact phrase searches phrase=query, # Use query as-is to support both regular and exact phrase searches
itemTypeList=[item_type], itemTypeList=[item_type],
pageSize=10, pageSize=5,
pageNumber=1 pageNumber=1
) )
) )
@@ -2531,49 +2533,15 @@ async def search(
logger.warning(f"No data returned from Bedesten API for {court_name}") logger.warning(f"No data returned from Bedesten API for {court_name}")
continue continue
# Add results from this court type (limit to top 5 per court) # Add results from metadata only. Fetching every document preview
# would turn one Deep Research search into ~30 Bedesten requests.
for decision in search_results.data.emsalKararList[:5]: for decision in search_results.data.emsalKararList[:5]:
# For ChatGPT Deep Research, fetch document content for preview results.append({
try: "id": decision.documentId,
# Fetch document content for preview "title": build_bedesten_title(decision, court_name),
doc = await bedesten_client_instance.get_document_as_markdown(decision.documentId) "text": build_bedesten_metadata_preview(decision, court_name),
"url": f"https://mevzuat.adalet.gov.tr/ictihat/{decision.documentId}"
# Generate preview text (skip first 100 chars, show next 200) })
preview_text = get_preview_text(doc.markdown_content, skip_chars=100, preview_chars=200)
# Build title from metadata
title_parts = []
if decision.birimAdi:
title_parts.append(decision.birimAdi)
if decision.esasNo:
title_parts.append(f"Esas: {decision.esasNo}")
if decision.kararNo:
title_parts.append(f"Karar: {decision.kararNo}")
if decision.kararTarihiStr:
title_parts.append(f"Tarih: {decision.kararTarihiStr}")
if title_parts:
title = " - ".join(title_parts)
else:
title = f"{court_name} - Document {decision.documentId}"
# Add to results in OpenAI format
results.append({
"id": decision.documentId,
"title": title,
"text": preview_text,
"url": f"https://mevzuat.adalet.gov.tr/ictihat/{decision.documentId}"
})
except Exception as e:
logger.warning(f"Could not fetch preview for document {decision.documentId}: {e}")
# Add minimal result without preview
results.append({
"id": decision.documentId,
"title": f"{court_name} - Document {decision.documentId}",
"text": "Document preview not available",
"url": f"https://mevzuat.adalet.gov.tr/ictihat/{decision.documentId}"
})
if search_results.data: if search_results.data:
logger.info(f"Found {len(search_results.data.emsalKararList)} results from {court_name}") logger.info(f"Found {len(search_results.data.emsalKararList)} results from {court_name}")
@@ -2592,7 +2560,7 @@ async def search(
# Danıştay Official API - use search_danistay_by_keyword instead # Danıştay Official API - use search_danistay_by_keyword instead
# Constitutional Court - use search_anayasa_norm_denetimi_decisions instead # Constitutional Court - use search_anayasa_norm_denetimi_decisions instead
# Competition Authority - use search_rekabet_kurumu_decisions instead # Competition Authority - use search_rekabet_kurumu_decisions instead
# Public Procurement Authority - use search_kik_decisions instead # Public Procurement Authority - use search_kik_v2_decisions instead
# Court of Accounts - use search_sayistay_* tools instead # Court of Accounts - use search_sayistay_* tools instead
# UYAP Emsal - use search_emsal_detailed_decisions instead # UYAP Emsal - use search_emsal_detailed_decisions instead
# Jurisdictional Disputes Court - use search_uyusmazlik_decisions instead # Jurisdictional Disputes Court - use search_uyusmazlik_decisions instead
@@ -2629,7 +2597,11 @@ async def search(
raise raise
@app.tool( @app.tool(
description="Only for ChatGPT Deep Research. Use this when retrieving a Turkish legal document by ID. Returns full document in ChatGPT Deep Research compatible format (id, title, text, url, metadata).", description=(
"Only for ChatGPT Deep Research. Retrieves one Turkish legal document by numeric Bedesten ID. "
"For regular MCP use, prefer get_bedesten_document_markdown. This performs one Bedesten document request "
"and avoids an extra metadata lookup to respect upstream rate limits."
),
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": False, # Retrieves specific documents, not exploring "openWorldHint": False, # Retrieves specific documents, not exploring
@@ -2664,41 +2636,13 @@ async def fetch(
# Use the numeric ID directly with Bedesten API # Use the numeric ID directly with Bedesten API
doc = await bedesten_client_instance.get_document_as_markdown(id) doc = await bedesten_client_instance.get_document_as_markdown(id)
# Try to get additional metadata by searching for this specific document
title = f"Turkish Legal Document {id}" title = f"Turkish Legal Document {id}"
try: if doc.markdown_content:
# Quick search to get metadata for better title for line in doc.markdown_content.splitlines():
search_results = await bedesten_client_instance.search_documents( cleaned_line = line.strip().lstrip("#").strip()
BedestenSearchRequest( if cleaned_line:
data=BedestenSearchData( title = cleaned_line[:160]
phrase=id, # Search by document ID break
itemTypeList=["YARGITAYKARARI", "DANISTAYKARAR", "YERELHUKUK", "ISTINAFHUKUK", "KYB"],
pageSize=1,
pageNumber=1
)
)
)
if search_results.data and search_results.data.emsalKararList:
decision = search_results.data.emsalKararList[0]
if decision.documentId == id:
# Build a proper title from metadata
title_parts = []
if decision.birimAdi:
title_parts.append(decision.birimAdi)
if decision.esasNo:
title_parts.append(f"Esas: {decision.esasNo}")
if decision.kararNo:
title_parts.append(f"Karar: {decision.kararNo}")
if decision.kararTarihiStr:
title_parts.append(f"Tarih: {decision.kararTarihiStr}")
if title_parts:
title = " - ".join(title_parts)
else:
title = f"Turkish Legal Decision {id}"
except Exception as e:
logger.warning(f"Could not fetch metadata for document {id}: {e}")
return { return {
"id": id, "id": id,
@@ -2711,7 +2655,8 @@ async def fetch(
"source_url": doc.source_url, "source_url": doc.source_url,
"mime_type": doc.mime_type, "mime_type": doc.mime_type,
"api_source": "Bedesten Unified API", "api_source": "Bedesten Unified API",
"chatgpt_deep_research": True "chatgpt_deep_research": True,
"rate_limit_optimized": True
} }
} }
@@ -2775,4 +2720,4 @@ def main():
logger.info(f"{app.name} server has shut down.") logger.info(f"{app.name} server has shut down.")
if __name__ == "__main__": if __name__ == "__main__":
main() main()