add bedesten module

This commit is contained in:
saidsurucu
2025-06-24 18:24:19 +03:00
parent 98abb1d658
commit 2a24f92702
6 changed files with 738 additions and 20 deletions
+53 -9
View File
@@ -9,9 +9,14 @@ Bu proje, çeşitli Türk hukuk kaynaklarına (Yargıtay, Danıştay, Emsal Kara
🎯 **Temel Özellikler** 🎯 **Temel Özellikler**
* Çeşitli Türk hukuk veritabanlarına programatik erişim için standart bir MCP arayüzü. * Çeşitli Türk hukuk veritabanlarına programatik erişim için standart bir MCP arayüzü.
* **Kapsamlı Mahkeme Daire/Kurul Filtreleme:** 79 farklı daire/kurul filtreleme seçeneği
* **Dual/Triple API Desteği:** Her mahkeme için birden fazla API kaynağı ile maksimum kapsama
* Aşağıdaki kurumların kararlarını arama ve getirme yeteneği: * Aşağıdaki kurumların kararlarını arama ve getirme yeteneği:
* **Yargıtay:** Detaylı kriterlerle karar arama ve karar metinlerini Markdown formatında getirme. * **Yargıtay:** Detaylı kriterlerle karar arama ve karar metinlerini Markdown formatında getirme. **Dual API** (Ana + Bedesten) + **52 Daire/Kurul Filtreleme** (Hukuk/Ceza Daireleri, Genel Kurullar)
* **Danıştay:** Anahtar kelime bazlı ve detaylı kriterlerle karar arama; karar metinlerini Markdown formatında getirme. * **Danıştay:** Anahtar kelime bazlı ve detaylı kriterlerle karar arama; karar metinlerini Markdown formatında getirme. **Triple API** (Keyword + Detailed + Bedesten) + **27 Daire/Kurul Filtreleme** (İdari Daireler, Vergi/İdare Kurulları, Askeri Yüksek İdare Mahkemesi)
* **Yerel Hukuk Mahkemeleri:** Bedesten API ile yerel hukuk mahkemesi kararlarına erişim
* **İstinaf Hukuk Mahkemeleri:** Bedesten API ile istinaf mahkemesi kararlarına erişim
* **Kanun Yararına Bozma (KYB):** Bedesten API ile olağanüstü kanun yoluna erişim
* **Emsal (UYAP):** Detaylı kriterlerle emsal karar arama ve karar metinlerini Markdown formatında getirme. * **Emsal (UYAP):** Detaylı kriterlerle emsal karar arama ve karar metinlerini Markdown formatında getirme.
* **Uyuşmazlık Mahkemesi:** Form tabanlı kriterlerle karar arama ve karar metinlerini (URL ile erişilen) Markdown formatında getirme. * **Uyuşmazlık Mahkemesi:** Form tabanlı kriterlerle karar arama ve karar metinlerini (URL ile erişilen) Markdown formatında getirme.
* **Anayasa Mahkemesi (Norm Denetimi):** Kapsamlı kriterlerle norm denetimi kararlarını arama; uzun karar metinlerini (5.000 karakterlik) sayfalanmış Markdown formatında getirme. * **Anayasa Mahkemesi (Norm Denetimi):** Kapsamlı kriterlerle norm denetimi kararlarını arama; uzun karar metinlerini (5.000 karakterlik) sayfalanmış Markdown formatında getirme.
@@ -75,15 +80,33 @@ Bu bölüm, Yargı MCP aracını 5ire gibi Claude Desktop dışındaki MCP istem
Bu FastMCP sunucusu aşağıdaki temel araçları sunar: Bu FastMCP sunucusu aşağıdaki temel araçları sunar:
### **Yargıtay Araçları (Dual API + 52 Daire Filtreleme)**
* **Ana API:**
* `search_yargitay_detailed(arananKelime, birimYrgKurulDaire, ...)`: Yargıtay kararlarını detaylı kriterlerle arar. **52 daire/kurul seçeneği** (Hukuk/Ceza Daireleri 1-23, Genel Kurullar, Başkanlar Kurulu)
* `get_yargitay_document_markdown(id: str)`: Belirli bir Yargıtay kararının metnini Markdown formatında getirir.
* **Bedesten API (Alternatif):**
* `search_yargitay_bedesten(phrase, birimAdi, ...)`: Bedesten API ile Yargıtay kararlarını arar. **Aynı 52 daire filtreleme**
* `get_yargitay_bedesten_document_markdown(documentId: str)`: Bedesten'den karar metni (HTML/PDF → Markdown)
* **Yargıtay Araçları:** ### **Danıştay Araçları (Triple API + 27 Daire Filtreleme)**
* `search_yargitay_detailed(search_query: YargitayDetailedSearchRequest) -> CompactYargitaySearchResult`: Yargıtay kararlarını detaylı kriterlerle arar. * **Ana API'lar:**
* `get_yargitay_document_markdown(id: str) -> YargitayDocumentMarkdown`: Belirli bir Yargıtay kararının metnini Markdown formatında getirir. * `search_danistay_by_keyword(andKelimeler, orKelimeler, ...)`: Danıştay kararlarını anahtar kelimelerle arar.
* `search_danistay_detailed(daire, esasYil, ...)`: Danıştay kararlarını detaylı kriterlerle arar.
* `get_danistay_document_markdown(id: str)`: Belirli bir Danıştay kararının metnini Markdown formatında getirir.
* **Bedesten API (Alternatif):**
* `search_danistay_bedesten(phrase, birimAdi, ...)`: Bedesten API ile Danıştay kararlarını arar. **27 daire/kurul seçeneği** (1-17. Daireler, Vergi/İdare Kurulları, Askeri Mahkemeler)
* `get_danistay_bedesten_document_markdown(documentId: str)`: Bedesten'den karar metni
* **Danıştay Araçları:** ### **Diğer Mahkemeler (Bedesten API)**
* `search_danistay_by_keyword(search_query: DanistayKeywordSearchRequest) -> CompactDanistaySearchResult`: Danıştay kararlarını anahtar kelimelerle arar. * **Yerel Hukuk Mahkemeleri:**
* `search_danistay_detailed(search_query: DanistayDetailedSearchRequest) -> CompactDanistaySearchResult`: Danıştay kararlarını detaylı kriterlerle arar. * `search_yerel_hukuk_bedesten(phrase, pageSize, ...)`: Yerel hukuk mahkemesi kararlarını arar
* `get_danistay_document_markdown(id: str) -> DanistayDocumentMarkdown`: Belirli bir Danıştay kararının metnini Markdown formatında getirir. * `get_yerel_hukuk_bedesten_document_markdown(documentId: str)`: Karar metni
* **İstinaf Hukuk Mahkemeleri:**
* `search_istinaf_hukuk_bedesten(phrase, pageSize, ...)`: İstinaf mahkemesi kararlarını arar
* `get_istinaf_hukuk_bedesten_document_markdown(documentId: str)`: Karar metni
* **Kanun Yararına Bozma (KYB):**
* `search_kyb_bedesten(phrase, pageSize, ...)`: Olağanüstü kanun yolu kararlarını arar
* `get_kyb_bedesten_document_markdown(documentId: str)`: Karar metni
* **Emsal Karar Araçları:** * **Emsal Karar Araçları:**
* `search_emsal_detailed_decisions(search_query: EmsalSearchRequest) -> CompactEmsalSearchResult`: Emsal (UYAP) kararlarını detaylı kriterlerle arar. * `search_emsal_detailed_decisions(search_query: EmsalSearchRequest) -> CompactEmsalSearchResult`: Emsal (UYAP) kararlarını detaylı kriterlerle arar.
@@ -109,6 +132,27 @@ Bu FastMCP sunucusu aşağıdaki temel araçları sunar:
    * `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.
---
### **📊 Kapsamlı İstatistikler**
- **Toplam Mahkeme/Kurum:** 11 farklı hukuki kurum
- **Toplam MCP Tool:** 30+ arama ve belge getirme aracı
- **Daire/Kurul Filtreleme:** 79 farklı seçenek (52 Yargıtay + 27 Danıştay)
- **API Kaynağı:** Dual/Triple API desteği ile maksimum kapsama
- **Tam Türk Adalet Sistemi:** Yerel mahkemelerden en yüksek mahkemelere kadar
**🏛️ Desteklenen Mahkeme Hiyerarşisi:**
```
Yerel Mahkemeler → İstinaf → Yargıtay/Danıştay → Anayasa Mahkemesi
↓ ↓ ↓ ↓
Bedesten API Bedesten API Dual/Triple API Norm+Bireysel API
```
**⚖️ Kapsamlı Daire Filtreleme:**
- **Yargıtay:** 52 seçenek (1-23 Hukuk, 1-23 Ceza, Genel Kurullar, Başkanlar Kurulu)
- **Danıştay:** 27 seçenek (1-17 Daireler, İdare/Vergi Kurulları, Askeri Mahkemeler)
- **Toplam:** 79 farklı mahkeme dairesi/kurulu
📜 **Lisans** 📜 **Lisans**
Bu proje MIT Lisansı altında lisanslanmıştır. Detaylar için `LICENSE` dosyasına bakınız. Bu proje MIT Lisansı altında lisanslanmıştır. Detaylar için `LICENSE` dosyasına bakınız.
+1
View File
@@ -0,0 +1 @@
# bedesten_mcp_module/__init__.py
+183
View File
@@ -0,0 +1,183 @@
# bedesten_mcp_module/client.py
import httpx
import base64
from typing import Optional
import logging
from markitdown import MarkItDown
import tempfile
import os
from .models import (
BedestenSearchRequest, BedestenSearchResponse,
BedestenDocumentRequest, BedestenDocumentResponse,
BedestenDocumentMarkdown, BedestenDocumentRequestData
)
logger = logging.getLogger(__name__)
class BedestenApiClient:
"""
API Client for Bedesten (bedesten.adalet.gov.tr) - Alternative legal decision search system.
Currently used for Yargıtay decisions, but can be extended for other court types.
"""
BASE_URL = "https://bedesten.adalet.gov.tr"
SEARCH_ENDPOINT = "/emsal-karar/searchDocuments"
DOCUMENT_ENDPOINT = "/emsal-karar/getDocumentContent"
def __init__(self, request_timeout: float = 60.0):
self.http_client = httpx.AsyncClient(
base_url=self.BASE_URL,
headers={
"Accept": "*/*",
"Accept-Language": "tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7",
"AdaletApplicationName": "UyapMevzuat",
"Content-Type": "application/json; charset=utf-8",
"Origin": "https://mevzuat.adalet.gov.tr",
"Referer": "https://mevzuat.adalet.gov.tr/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
},
timeout=request_timeout
)
async def search_documents(self, search_request: BedestenSearchRequest) -> BedestenSearchResponse:
"""
Search for documents using Bedesten API.
Currently supports: YARGITAYKARARI, DANISTAYKARARI, YERELHUKMAHKARARI, etc.
"""
logger.info(f"BedestenApiClient: Searching documents with phrase: {search_request.data.phrase}")
try:
response = await self.http_client.post(
self.SEARCH_ENDPOINT,
json=search_request.model_dump()
)
response.raise_for_status()
response_json = response.json()
# Parse and return the response
return BedestenSearchResponse(**response_json)
except httpx.RequestError as e:
logger.error(f"BedestenApiClient: HTTP request error during search: {e}")
raise
except Exception as e:
logger.error(f"BedestenApiClient: Error processing search response: {e}")
raise
async def get_document_as_markdown(self, document_id: str) -> BedestenDocumentMarkdown:
"""
Get document content and convert to markdown.
Handles both HTML (text/html) and PDF (application/pdf) content types.
"""
logger.info(f"BedestenApiClient: Fetching document for markdown conversion (ID: {document_id})")
try:
# Prepare request
doc_request = BedestenDocumentRequest(
data=BedestenDocumentRequestData(documentId=document_id)
)
# Get document
response = await self.http_client.post(
self.DOCUMENT_ENDPOINT,
json=doc_request.model_dump()
)
response.raise_for_status()
response_json = response.json()
doc_response = BedestenDocumentResponse(**response_json)
# Decode base64 content
content_bytes = base64.b64decode(doc_response.data.content)
mime_type = doc_response.data.mimeType
logger.info(f"BedestenApiClient: Document mime type: {mime_type}")
# Convert to markdown based on mime type
if mime_type == "text/html":
html_content = content_bytes.decode('utf-8')
markdown_content = self._convert_html_to_markdown(html_content)
elif mime_type == "application/pdf":
markdown_content = self._convert_pdf_to_markdown(content_bytes)
else:
logger.warning(f"Unsupported mime type: {mime_type}")
markdown_content = f"Unsupported content type: {mime_type}. Unable to convert to markdown."
return BedestenDocumentMarkdown(
documentId=document_id,
markdown_content=markdown_content,
source_url=f"{self.BASE_URL}/document/{document_id}",
mime_type=mime_type
)
except httpx.RequestError as e:
logger.error(f"BedestenApiClient: HTTP error fetching document {document_id}: {e}")
raise
except Exception as e:
logger.error(f"BedestenApiClient: Error processing document {document_id}: {e}")
raise
def _convert_html_to_markdown(self, html_content: str) -> Optional[str]:
"""Convert HTML to Markdown using MarkItDown"""
if not html_content:
return None
temp_file_path = None
try:
md_converter = MarkItDown()
# Write HTML to temp file
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".html", encoding="utf-8") as tmp:
tmp.write(html_content)
temp_file_path = tmp.name
# Convert
result = md_converter.convert(temp_file_path)
markdown_content = result.text_content
logger.info("Successfully converted HTML to Markdown")
return markdown_content
except Exception as e:
logger.error(f"Error converting HTML to Markdown: {e}")
return f"Error converting HTML content: {str(e)}"
finally:
if temp_file_path and os.path.exists(temp_file_path):
os.remove(temp_file_path)
def _convert_pdf_to_markdown(self, pdf_bytes: bytes) -> Optional[str]:
"""Convert PDF to Markdown using MarkItDown"""
if not pdf_bytes:
return None
temp_file_path = None
try:
# MarkItDown supports PDF with markitdown[pdf]
md_converter = MarkItDown()
# Write PDF to temp file
with tempfile.NamedTemporaryFile(mode="wb", delete=False, suffix=".pdf") as tmp:
tmp.write(pdf_bytes)
temp_file_path = tmp.name
# Convert
result = md_converter.convert(temp_file_path)
markdown_content = result.text_content
logger.info("Successfully converted PDF to Markdown")
return markdown_content
except Exception as e:
logger.error(f"Error converting PDF to Markdown: {e}")
return f"Error converting PDF content: {str(e)}. The document may be corrupted or in an unsupported format."
finally:
if temp_file_path and os.path.exists(temp_file_path):
os.remove(temp_file_path)
async def close_client_session(self):
"""Close HTTP client session"""
await self.http_client.aclose()
logger.info("BedestenApiClient: HTTP client session closed.")
+115
View File
@@ -0,0 +1,115 @@
# bedesten_mcp_module/models.py
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any, Literal, Union
# Import YargitayBirimEnum for chamber filtering
from yargitay_mcp_module.models import YargitayBirimEnum
# Danıştay Chamber/Board Options
DanistayBirimEnum = Literal[
"", # Empty string for "All" chambers
# Main Councils
"Büyük Gen.Kur.", # Grand General Assembly
"İdare Dava Daireleri Kurulu", # Administrative Cases Chambers Council
"Vergi Dava Daireleri Kurulu", # Tax Cases Chambers Council
"İçtihatları Birleştirme Kurulu", # Precedents Unification Council
"İdari İşler Kurulu", # Administrative Affairs Council
"Başkanlar Kurulu", # Presidents Council
# Chambers
"1. Daire", "2. Daire", "3. Daire", "4. Daire", "5. Daire",
"6. Daire", "7. Daire", "8. Daire", "9. Daire", "10. Daire",
"11. Daire", "12. Daire", "13. Daire", "14. Daire", "15. Daire",
"16. Daire", "17. Daire",
# Military High Administrative Court
"Askeri Yüksek İdare Mahkemesi",
"Askeri Yüksek İdare Mahkemesi Daireler Kurulu",
"Askeri Yüksek İdare Mahkemesi Başsavcılığı",
"Askeri Yüksek İdare Mahkemesi 1. Daire",
"Askeri Yüksek İdare Mahkemesi 2. Daire",
"Askeri Yüksek İdare Mahkemesi 3. Daire"
]
# Search Request Models
class BedestenSearchData(BaseModel):
pageSize: int
pageNumber: int
itemTypeList: List[str]
phrase: str
birimAdi: Optional[Union[YargitayBirimEnum, DanistayBirimEnum]] = Field(None, description="""
Chamber/Board filter (optional). Available options depend on itemTypeList:
For YARGITAYKARARI (52 options):
- None/null for ALL chambers
- 'Hukuk Genel Kurulu', '1. Hukuk Dairesi' through '23. Hukuk Dairesi'
- 'Ceza Genel Kurulu', '1. Ceza Dairesi' through '23. Ceza Dairesi'
- 'Hukuk Daireleri Başkanlar Kurulu', 'Ceza Daireleri Başkanlar Kurulu'
- 'Büyük Genel Kurulu'
For DANISTAYKARAR (27 options):
- None/null for ALL chambers
- 'Büyük Gen.Kur.', 'İdare Dava Daireleri Kurulu', 'Vergi Dava Daireleri Kurulu'
- '1. Daire' through '17. Daire'
- 'İçtihatları Birleştirme Kurulu', 'İdari İşler Kurulu', 'Başkanlar Kurulu'
- Military courts: 'Askeri Yüksek İdare Mahkemesi' variants
""")
sortFields: List[str] = ["KARAR_TARIHI"]
sortDirection: str = "desc"
class BedestenSearchRequest(BaseModel):
data: BedestenSearchData
applicationName: str = "UyapMevzuat"
paging: bool = True
# Search Response Models
class BedestenItemType(BaseModel):
name: str
description: str
class BedestenDecisionEntry(BaseModel):
documentId: str
itemType: BedestenItemType
birimId: Optional[str] = None
birimAdi: Optional[str]
esasNoYil: int
esasNoSira: int
kararNoYil: int
kararNoSira: int
kararTuru: Optional[str] = None
kararTarihi: str
kararTarihiStr: str
kesinlesmeDurumu: Optional[str] = None
kararNo: str
esasNo: str
class BedestenSearchDataResponse(BaseModel):
emsalKararList: List[BedestenDecisionEntry]
total: int
start: int
class BedestenSearchResponse(BaseModel):
data: BedestenSearchDataResponse
metadata: Dict[str, Any]
# Document Request/Response Models
class BedestenDocumentRequestData(BaseModel):
documentId: str
class BedestenDocumentRequest(BaseModel):
data: BedestenDocumentRequestData
applicationName: str = "UyapMevzuat"
class BedestenDocumentData(BaseModel):
content: str # Base64 encoded HTML or PDF
mimeType: str
version: int
class BedestenDocumentResponse(BaseModel):
data: BedestenDocumentData
metadata: Dict[str, Any]
class BedestenDocumentMarkdown(BaseModel):
documentId: str = Field(..., description="The document ID from Bedesten")
markdown_content: Optional[str] = Field(None, description="The decision content converted to Markdown")
source_url: str = Field(..., description="The source URL of the document")
mime_type: Optional[str] = Field(None, description="Original content type (text/html or application/pdf)")
+349 -5
View File
@@ -36,7 +36,13 @@ from fastmcp import FastMCP
# --- Module Imports --- # --- Module Imports ---
from yargitay_mcp_module.client import YargitayOfficialApiClient from yargitay_mcp_module.client import YargitayOfficialApiClient
from yargitay_mcp_module.models import ( from yargitay_mcp_module.models import (
YargitayDetailedSearchRequest, YargitayDocumentMarkdown, CompactYargitaySearchResult YargitayDetailedSearchRequest, YargitayDocumentMarkdown, CompactYargitaySearchResult,
YargitayBirimEnum
)
from bedesten_mcp_module.client import BedestenApiClient
from bedesten_mcp_module.models import (
BedestenSearchRequest, BedestenSearchData,
BedestenDocumentMarkdown, DanistayBirimEnum
) )
from danistay_mcp_module.client import DanistayApiClient from danistay_mcp_module.client import DanistayApiClient
from danistay_mcp_module.models import ( from danistay_mcp_module.models import (
@@ -97,6 +103,7 @@ anayasa_norm_client_instance = AnayasaMahkemesiApiClient()
anayasa_bireysel_client_instance = AnayasaBireyselBasvuruApiClient() anayasa_bireysel_client_instance = AnayasaBireyselBasvuruApiClient()
kik_client_instance = KikApiClient() kik_client_instance = KikApiClient()
rekabet_client_instance = RekabetKurumuApiClient() rekabet_client_instance = RekabetKurumuApiClient()
bedesten_client_instance = BedestenApiClient()
KARAR_TURU_ADI_TO_GUID_ENUM_MAP = { KARAR_TURU_ADI_TO_GUID_ENUM_MAP = {
@@ -120,9 +127,20 @@ async def search_yargitay_detailed(
• +"phrase1" +"phrase2" = Multiple required phrases • +"phrase1" +"phrase2" = Multiple required phrases
• +"required" -"excluded" = Include and exclude • +"required" -"excluded" = Include and exclude
Examples: arsa payı | "arsa payı" | +"arsa payı" +"bozma sebebi" | bozma*"""), Examples: arsa payı | "arsa payı" | +"arsa payı" +"bozma sebebi" | bozma*"""),
birimYrgKurulDaire: str = Field("", description="Yargitay Board Unit (e.g., 'Hukuk Genel Kurulu')."), birimYrgKurulDaire: YargitayBirimEnum = Field("", description="""
birimYrgHukukDaire: str = Field("", description="Yargitay Civil Chamber (e.g., '1. Hukuk Dairesi')."), Yargıtay chamber/board selection. Available options:
birimYrgCezaDaire: str = Field("", description="Yargitay Criminal Chamber."), • Empty string ('') for ALL chambers
• Hukuk Genel Kurulu (Civil General Assembly)
• 1. Hukuk Dairesi through 23. Hukuk Dairesi (Civil Chambers 1-23)
• Hukuk Daireleri Başkanlar Kurulu (Civil Chambers Presidents Board)
• Ceza Genel Kurulu (Criminal General Assembly)
• 1. Ceza Dairesi through 23. Ceza Dairesi (Criminal Chambers 1-23)
• Ceza Daireleri Başkanlar Kurulu (Criminal Chambers Presidents Board)
• Büyük Genel Kurulu (Grand General Assembly)
Total: 49 possible values
"""),
birimYrgHukukDaire: str = Field("", description="Legacy field - use birimYrgKurulDaire instead"),
birimYrgCezaDaire: str = Field("", description="Legacy field - use birimYrgKurulDaire instead"),
esasYil: str = Field("", description="Case year for 'Esas No'."), esasYil: str = Field("", description="Case year for 'Esas No'."),
esasIlkSiraNo: str = Field("", description="Starting sequence number for 'Esas No'."), esasIlkSiraNo: str = Field("", description="Starting sequence number for 'Esas No'."),
esasSonSiraNo: str = Field("", description="Ending sequence number for 'Esas No'."), esasSonSiraNo: str = Field("", description="Ending sequence number for 'Esas No'."),
@@ -756,6 +774,331 @@ async def get_rekabet_kurumu_document(
logger.exception(f"Error in tool 'get_rekabet_kurumu_document'. Karar ID: {karar_id}") logger.exception(f"Error in tool 'get_rekabet_kurumu_document'. Karar ID: {karar_id}")
raise raise
# --- MCP Tools for Bedesten (Alternative Yargitay Search) ---
@app.tool()
async def search_yargitay_bedesten(
phrase: str = Field(..., description="Aranacak kavram/kelime"),
pageSize: int = Field(10, ge=1, le=100, description="Sayfa başına sonuç sayısı"),
pageNumber: int = Field(1, ge=1, description="Sayfa numarası"),
birimAdi: Optional[YargitayBirimEnum] = Field(None, description="""
Yargıtay chamber/board filter (optional). Available options:
• None for ALL chambers
'Hukuk Genel Kurulu' (Civil General Assembly)
'1. Hukuk Dairesi' through '23. Hukuk Dairesi' (Civil Chambers 1-23)
'Hukuk Daireleri Başkanlar Kurulu' (Civil Chambers Presidents Board)
'Ceza Genel Kurulu' (Criminal General Assembly)
'1. Ceza Dairesi' through '23. Ceza Dairesi' (Criminal Chambers 1-23)
'Ceza Daireleri Başkanlar Kurulu' (Criminal Chambers Presidents Board)
'Büyük Genel Kurulu' (Grand General Assembly)
Total: 49 chamber options
""")
) -> dict:
"""
Searches Yargıtay decisions using Bedesten API (alternative source).
This complements search_yargitay_detailed for comprehensive coverage.
Always use BOTH Yargıtay search tools for complete results.
Returns a simplified response with decision list and metadata.
"""
search_data = BedestenSearchData(
pageSize=pageSize,
pageNumber=pageNumber,
itemTypeList=["YARGITAYKARARI"], # Only Yargıtay decisions
phrase=phrase,
birimAdi=birimAdi
)
search_request = BedestenSearchRequest(data=search_data)
logger.info(f"Tool 'search_yargitay_bedesten' called: phrase='{phrase}', birimAdi='{birimAdi}', page={pageNumber}")
try:
response = await bedesten_client_instance.search_documents(search_request)
# Return simplified response format
return {
"decisions": [d.model_dump() for d in response.data.emsalKararList],
"total_records": response.data.total,
"requested_page": pageNumber,
"page_size": pageSize
}
except Exception as e:
logger.exception("Error in tool 'search_yargitay_bedesten'")
raise
@app.tool()
async def get_yargitay_bedesten_document_markdown(
documentId: str = Field(..., description="Document ID from Bedesten search results")
) -> BedestenDocumentMarkdown:
"""
Retrieves a Yargıtay decision document from Bedesten API and converts to Markdown.
Supports both HTML and PDF content types.
Use documentId from search_yargitay_bedesten results.
"""
logger.info(f"Tool 'get_yargitay_bedesten_document_markdown' called for ID: {documentId}")
if not documentId or not documentId.strip():
raise ValueError("Document ID must be a non-empty string.")
try:
return await bedesten_client_instance.get_document_as_markdown(documentId)
except Exception as e:
logger.exception("Error in tool 'get_yargitay_bedesten_document_markdown'")
raise
# --- MCP Tools for Bedesten (Alternative Danıştay Search) ---
@app.tool()
async def search_danistay_bedesten(
phrase: str = Field(..., description="Aranacak kavram/kelime"),
pageSize: int = Field(10, ge=1, le=100, description="Sayfa başına sonuç sayısı"),
pageNumber: int = Field(1, ge=1, description="Sayfa numarası"),
birimAdi: Optional[DanistayBirimEnum] = Field(None, description="""
Danıştay chamber/board filter (optional). Available options:
• None for ALL chambers
• Main Councils: 'Büyük Gen.Kur.', 'İdare Dava Daireleri Kurulu', 'Vergi Dava Daireleri Kurulu'
• Chambers: '1. Daire' through '17. Daire' (17 administrative chambers)
• Special Councils: 'İçtihatları Birleştirme Kurulu', 'İdari İşler Kurulu', 'Başkanlar Kurulu'
• Military: 'Askeri Yüksek İdare Mahkemesi' and its chambers/councils
Total: 27 chamber options
""")
) -> dict:
"""
Searches Danıştay decisions using Bedesten API (alternative source).
This complements existing Danıştay search tools (search_danistay_by_keyword, search_danistay_detailed)
for comprehensive coverage. Always use ALL Danıştay search tools for complete results.
Returns a simplified response with decision list and metadata.
"""
search_data = BedestenSearchData(
pageSize=pageSize,
pageNumber=pageNumber,
itemTypeList=["DANISTAYKARAR"], # Only Danıştay decisions
phrase=phrase,
birimAdi=birimAdi
)
search_request = BedestenSearchRequest(data=search_data)
logger.info(f"Tool 'search_danistay_bedesten' called: phrase='{phrase}', birimAdi='{birimAdi}', page={pageNumber}")
try:
response = await bedesten_client_instance.search_documents(search_request)
# Return simplified response format
return {
"decisions": [d.model_dump() for d in response.data.emsalKararList],
"total_records": response.data.total,
"requested_page": pageNumber,
"page_size": pageSize
}
except Exception as e:
logger.exception("Error in tool 'search_danistay_bedesten'")
raise
@app.tool()
async def get_danistay_bedesten_document_markdown(
documentId: str = Field(..., description="Document ID from Bedesten search results")
) -> BedestenDocumentMarkdown:
"""
Retrieves a Danıştay decision document from Bedesten API and converts to Markdown.
Supports both HTML and PDF content types.
Use documentId from search_danistay_bedesten results.
"""
logger.info(f"Tool 'get_danistay_bedesten_document_markdown' called for ID: {documentId}")
if not documentId or not documentId.strip():
raise ValueError("Document ID must be a non-empty string.")
try:
return await bedesten_client_instance.get_document_as_markdown(documentId)
except Exception as e:
logger.exception("Error in tool 'get_danistay_bedesten_document_markdown'")
raise
# --- MCP Tools for Bedesten (Yerel Hukuk Mahkemesi Search) ---
@app.tool()
async def search_yerel_hukuk_bedesten(
phrase: str = Field(..., description="Aranacak kavram/kelime"),
pageSize: int = Field(10, ge=1, le=100, description="Sayfa başına sonuç sayısı"),
pageNumber: int = Field(1, ge=1, description="Sayfa numarası")
) -> dict:
"""
Searches Yerel Hukuk Mahkemesi (Local Civil Court) decisions using Bedesten API.
This provides access to local court decisions that are not available through other APIs.
Currently the only available tool for searching Yerel Hukuk Mahkemesi decisions.
Returns a simplified response with decision list and metadata.
"""
search_data = BedestenSearchData(
pageSize=pageSize,
pageNumber=pageNumber,
itemTypeList=["YERELHUKUK"], # Local Civil Court decisions
phrase=phrase
)
search_request = BedestenSearchRequest(data=search_data)
logger.info(f"Tool 'search_yerel_hukuk_bedesten' called: phrase='{phrase}', page={pageNumber}")
try:
response = await bedesten_client_instance.search_documents(search_request)
# Return simplified response format
return {
"decisions": [d.model_dump() for d in response.data.emsalKararList],
"total_records": response.data.total,
"requested_page": pageNumber,
"page_size": pageSize
}
except Exception as e:
logger.exception("Error in tool 'search_yerel_hukuk_bedesten'")
raise
@app.tool()
async def get_yerel_hukuk_bedesten_document_markdown(
documentId: str = Field(..., description="Document ID from Bedesten search results")
) -> BedestenDocumentMarkdown:
"""
Retrieves a Yerel Hukuk Mahkemesi decision document from Bedesten API and converts to Markdown.
Supports both HTML and PDF content types.
Use documentId from search_yerel_hukuk_bedesten results.
"""
logger.info(f"Tool 'get_yerel_hukuk_bedesten_document_markdown' called for ID: {documentId}")
if not documentId or not documentId.strip():
raise ValueError("Document ID must be a non-empty string.")
try:
return await bedesten_client_instance.get_document_as_markdown(documentId)
except Exception as e:
logger.exception("Error in tool 'get_yerel_hukuk_bedesten_document_markdown'")
raise
# --- MCP Tools for Bedesten (İstinaf Hukuk Mahkemesi Search) ---
@app.tool()
async def search_istinaf_hukuk_bedesten(
phrase: str = Field(..., description="Aranacak kavram/kelime"),
pageSize: int = Field(10, ge=1, le=100, description="Sayfa başına sonuç sayısı"),
pageNumber: int = Field(1, ge=1, description="Sayfa numarası")
) -> dict:
"""
Searches İstinaf Hukuk Mahkemesi (Civil Court of Appeals) decisions using Bedesten API.
This provides access to appellate court decisions that are not available through other APIs.
Currently the only available tool for searching İstinaf Hukuk Mahkemesi decisions.
İstinaf courts are intermediate appellate courts in the Turkish judicial system,
handling appeals from local civil courts before cases reach Yargıtay.
Returns a simplified response with decision list and metadata.
"""
search_data = BedestenSearchData(
pageSize=pageSize,
pageNumber=pageNumber,
itemTypeList=["ISTINAFHUKUK"], # Civil Court of Appeals decisions
phrase=phrase
)
search_request = BedestenSearchRequest(data=search_data)
logger.info(f"Tool 'search_istinaf_hukuk_bedesten' called: phrase='{phrase}', page={pageNumber}")
try:
response = await bedesten_client_instance.search_documents(search_request)
# Return simplified response format
return {
"decisions": [d.model_dump() for d in response.data.emsalKararList],
"total_records": response.data.total,
"requested_page": pageNumber,
"page_size": pageSize
}
except Exception as e:
logger.exception("Error in tool 'search_istinaf_hukuk_bedesten'")
raise
@app.tool()
async def get_istinaf_hukuk_bedesten_document_markdown(
documentId: str = Field(..., description="Document ID from Bedesten search results")
) -> BedestenDocumentMarkdown:
"""
Retrieves an İstinaf Hukuk Mahkemesi decision document from Bedesten API and converts to Markdown.
Supports both HTML and PDF content types.
Use documentId from search_istinaf_hukuk_bedesten results.
"""
logger.info(f"Tool 'get_istinaf_hukuk_bedesten_document_markdown' called for ID: {documentId}")
if not documentId or not documentId.strip():
raise ValueError("Document ID must be a non-empty string.")
try:
return await bedesten_client_instance.get_document_as_markdown(documentId)
except Exception as e:
logger.exception("Error in tool 'get_istinaf_hukuk_bedesten_document_markdown'")
raise
# --- MCP Tools for Bedesten (Kanun Yararına Bozma Search) ---
@app.tool()
async def search_kyb_bedesten(
phrase: str = Field(..., description="Aranacak kavram/kelime"),
pageSize: int = Field(10, ge=1, le=100, description="Sayfa başına sonuç sayısı"),
pageNumber: int = Field(1, ge=1, description="Sayfa numarası")
) -> dict:
"""
Searches Kanun Yararına Bozma (KYB - Extraordinary Appeal) decisions using Bedesten API.
This provides access to extraordinary appeal decisions that are not available through other APIs.
Currently the only available tool for searching KYB decisions.
KYB decisions are extraordinary appeals in the Turkish judicial system,
where the Public Prosecutor's Office can request review of finalized decisions
in favor of the law and defendants.
Returns a simplified response with decision list and metadata.
"""
search_data = BedestenSearchData(
pageSize=pageSize,
pageNumber=pageNumber,
itemTypeList=["KYB"], # Kanun Yararına Bozma decisions
phrase=phrase
)
search_request = BedestenSearchRequest(data=search_data)
logger.info(f"Tool 'search_kyb_bedesten' called: phrase='{phrase}', page={pageNumber}")
try:
response = await bedesten_client_instance.search_documents(search_request)
# Return simplified response format
return {
"decisions": [d.model_dump() for d in response.data.emsalKararList],
"total_records": response.data.total,
"requested_page": pageNumber,
"page_size": pageSize
}
except Exception as e:
logger.exception("Error in tool 'search_kyb_bedesten'")
raise
@app.tool()
async def get_kyb_bedesten_document_markdown(
documentId: str = Field(..., description="Document ID from Bedesten search results")
) -> BedestenDocumentMarkdown:
"""
Retrieves a Kanun Yararına Bozma (KYB) decision document from Bedesten API and converts to Markdown.
Supports both HTML and PDF content types.
Use documentId from search_kyb_bedesten results.
"""
logger.info(f"Tool 'get_kyb_bedesten_document_markdown' called for ID: {documentId}")
if not documentId or not documentId.strip():
raise ValueError("Document ID must be a non-empty string.")
try:
return await bedesten_client_instance.get_document_as_markdown(documentId)
except Exception as e:
logger.exception("Error in tool 'get_kyb_bedesten_document_markdown'")
raise
# --- Application Shutdown Handling --- # --- Application Shutdown Handling ---
def perform_cleanup(): def perform_cleanup():
logger.info("MCP Server performing cleanup...") logger.info("MCP Server performing cleanup...")
@@ -775,7 +1118,8 @@ def perform_cleanup():
globals().get('anayasa_norm_client_instance'), globals().get('anayasa_norm_client_instance'),
globals().get('anayasa_bireysel_client_instance'), globals().get('anayasa_bireysel_client_instance'),
globals().get('kik_client_instance'), globals().get('kik_client_instance'),
globals().get('rekabet_client_instance') globals().get('rekabet_client_instance'),
globals().get('bedesten_client_instance')
] ]
async def close_all_clients_async(): async def close_all_clients_async():
tasks = [] tasks = []
+37 -6
View File
@@ -1,7 +1,32 @@
# yargitay_mcp_module/models.py # yargitay_mcp_module/models.py
from pydantic import BaseModel, Field, HttpUrl from pydantic import BaseModel, Field, HttpUrl
from typing import List, Optional, Dict, Any from typing import List, Optional, Dict, Any, Literal
# Yargıtay Chamber/Board Options
YargitayBirimEnum = Literal[
"", # Empty string for "All" chambers
# Hukuk (Civil) Chambers
"Hukuk Genel Kurulu",
"1. Hukuk Dairesi", "2. Hukuk Dairesi", "3. Hukuk Dairesi", "4. Hukuk Dairesi",
"5. Hukuk Dairesi", "6. Hukuk Dairesi", "7. Hukuk Dairesi", "8. Hukuk Dairesi",
"9. Hukuk Dairesi", "10. Hukuk Dairesi", "11. Hukuk Dairesi", "12. Hukuk Dairesi",
"13. Hukuk Dairesi", "14. Hukuk Dairesi", "15. Hukuk Dairesi", "16. Hukuk Dairesi",
"17. Hukuk Dairesi", "18. Hukuk Dairesi", "19. Hukuk Dairesi", "20. Hukuk Dairesi",
"21. Hukuk Dairesi", "22. Hukuk Dairesi", "23. Hukuk Dairesi",
"Hukuk Daireleri Başkanlar Kurulu",
# Ceza (Criminal) Chambers
"Ceza Genel Kurulu",
"1. Ceza Dairesi", "2. Ceza Dairesi", "3. Ceza Dairesi", "4. Ceza Dairesi",
"5. Ceza Dairesi", "6. Ceza Dairesi", "7. Ceza Dairesi", "8. Ceza Dairesi",
"9. Ceza Dairesi", "10. Ceza Dairesi", "11. Ceza Dairesi", "12. Ceza Dairesi",
"13. Ceza Dairesi", "14. Ceza Dairesi", "15. Ceza Dairesi", "16. Ceza Dairesi",
"17. Ceza Dairesi", "18. Ceza Dairesi", "19. Ceza Dairesi", "20. Ceza Dairesi",
"21. Ceza Dairesi", "22. Ceza Dairesi", "23. Ceza Dairesi",
"Ceza Daireleri Başkanlar Kurulu",
# General Assembly
"Büyük Genel Kurulu"
]
class YargitayDetailedSearchRequest(BaseModel): class YargitayDetailedSearchRequest(BaseModel):
""" """
@@ -10,11 +35,17 @@ class YargitayDetailedSearchRequest(BaseModel):
Based on the payload provided by the user. Based on the payload provided by the user.
""" """
arananKelime: Optional[str] = Field("", description="Keyword to search for.") arananKelime: Optional[str] = Field("", description="Keyword to search for.")
# Department/Board selection. Based on user provided payload. # Department/Board selection - Complete Yargıtay chamber hierarchy
# birimYrg* fields seem to be the ones used for filtering. birimYrgKurulDaire: YargitayBirimEnum = Field("", description="""
birimYrgKurulDaire: Optional[str] = Field("", description="Yargitay Board Unit (e.g., 'Hukuk Genel Kurulu').") Yargıtay chamber/board selection. Options include:
birimYrgHukukDaire: Optional[str] = Field("", description="Yargitay Civil Chamber (e.g., '1. Hukuk Dairesi').") - Empty string ('') for ALL chambers
birimYrgCezaDaire: Optional[str] = Field("", description="Yargitay Criminal Chamber.") - Civil: 'Hukuk Genel Kurulu', '1. Hukuk Dairesi' through '23. Hukuk Dairesi', 'Hukuk Daireleri Başkanlar Kurulu'
- Criminal: 'Ceza Genel Kurulu', '1. Ceza Dairesi' through '23. Ceza Dairesi', 'Ceza Daireleri Başkanlar Kurulu'
- General: 'Büyük Genel Kurulu'
Total: 49 possible values (including empty string for all chambers)
""")
birimYrgHukukDaire: Optional[str] = Field("", description="Legacy field - use birimYrgKurulDaire instead")
birimYrgCezaDaire: Optional[str] = Field("", description="Legacy field - use birimYrgKurulDaire instead")
esasYil: Optional[str] = Field("", description="Case year for 'Esas No'.") esasYil: Optional[str] = Field("", description="Case year for 'Esas No'.")
esasIlkSiraNo: Optional[str] = Field("", description="Starting sequence number for 'Esas No'.") esasIlkSiraNo: Optional[str] = Field("", description="Starting sequence number for 'Esas No'.")