optimize token usage

This commit is contained in:
saidsurucu
2025-07-14 15:45:19 +03:00
parent 54e8d61f83
commit e34d81be26
11 changed files with 489 additions and 277 deletions
+20
View File
@@ -192,3 +192,23 @@ scripts/deploy-flyio.sh
docs/DEPLOYMENT_FLYIO.md docs/DEPLOYMENT_FLYIO.md
setup_jwt_template.py setup_jwt_template.py
mcp_server_main.py.backup mcp_server_main.py.backup
mcp_overhead_content.json
ANTHROPIC_TEST_README.md
extract_mcp_overhead.py
mcp_overhead_content.txt
mcp_overhead_summary.txt
run_http_server.py
run_local_test.py
# MCP overhead analysis files
mcp_overhead_*.json
mcp_overhead_*.txt
mcp_test_results_*.json
mcp_quick_test_*.json
# General text files (temporary notes, etc)
*.txt
analyze_playwright_mcp.py
measure_mcp_directly.py
playwright_mcp_overhead.json
simple_test.py
+49 -49
View File
@@ -90,59 +90,59 @@ class AnayasaNormDenetimiSearchRequest(BaseModel):
keywords_any: Optional[List[str]] = Field(default_factory=list, description="Keywords for OR logic (HerhangiBirKelimeAra[]).") keywords_any: Optional[List[str]] = Field(default_factory=list, description="Keywords for OR logic (HerhangiBirKelimeAra[]).")
keywords_exclude: Optional[List[str]] = Field(default_factory=list, description="Keywords to exclude (BulunmayanKelimeAra[]).") keywords_exclude: Optional[List[str]] = Field(default_factory=list, description="Keywords to exclude (BulunmayanKelimeAra[]).")
period: Optional[AnayasaDonemEnum] = Field(default=AnayasaDonemEnum.TUMU, description="Constitutional period (Donemler_id).") period: Optional[AnayasaDonemEnum] = Field(default=AnayasaDonemEnum.TUMU, description="Constitutional period (Donemler_id).")
case_number_esas: Optional[str] = Field(None, description="Case registry number (EsasNo), e.g., '2023/123'.") case_number_esas: str = Field("", description="Case registry number (EsasNo), e.g., '2023/123'.")
decision_number_karar: Optional[str] = Field(None, description="Decision number (KararNo), e.g., '2023/456'.") decision_number_karar: str = Field("", description="Decision number (KararNo), e.g., '2023/456'.")
first_review_date_start: Optional[str] = Field(None, description="First review start date (IlkIncelemeTarihiIlk), format DD/MM/YYYY.") first_review_date_start: str = Field("", description="First review start date (IlkIncelemeTarihiIlk), format DD/MM/YYYY.")
first_review_date_end: Optional[str] = Field(None, description="First review end date (IlkIncelemeTarihiSon), format DD/MM/YYYY.") first_review_date_end: str = Field("", description="First review end date (IlkIncelemeTarihiSon), format DD/MM/YYYY.")
decision_date_start: Optional[str] = Field(None, description="Decision start date (KararTarihiIlk), format DD/MM/YYYY.") decision_date_start: str = Field("", description="Decision start date (KararTarihiIlk), format DD/MM/YYYY.")
decision_date_end: Optional[str] = Field(None, description="Decision end date (KararTarihiSon), format DD/MM/YYYY.") decision_date_end: str = Field("", description="Decision end date (KararTarihiSon), format DD/MM/YYYY.")
application_type: Optional[AnayasaBasvuruTuruEnum] = Field(default=AnayasaBasvuruTuruEnum.TUMU, description="Type of application (BasvuruTurler_id).") application_type: Optional[AnayasaBasvuruTuruEnum] = Field(default=AnayasaBasvuruTuruEnum.TUMU, description="Type of application (BasvuruTurler_id).")
applicant_general_name: Optional[str] = Field(None, description="General applicant name (BasvuranGeneller_id).") applicant_general_name: str = Field("", description="General applicant name (BasvuranGeneller_id).")
applicant_specific_name: Optional[str] = Field(None, description="Specific applicant name (BasvuranOzeller_id).") applicant_specific_name: str = Field("", description="Specific applicant name (BasvuranOzeller_id).")
official_gazette_date_start: Optional[str] = Field(None, description="Official Gazette start date (ResmiGazeteTarihiIlk), format DD/MM/YYYY.") official_gazette_date_start: str = Field("", description="Official Gazette start date (ResmiGazeteTarihiIlk), format DD/MM/YYYY.")
official_gazette_date_end: Optional[str] = Field(None, description="Official Gazette end date (ResmiGazeteTarihiSon), format DD/MM/YYYY.") official_gazette_date_end: str = Field("", description="Official Gazette end date (ResmiGazeteTarihiSon), format DD/MM/YYYY.")
official_gazette_number_start: Optional[str] = Field(None, description="Official Gazette starting number (ResmiGazeteSayisiIlk).") official_gazette_number_start: str = Field("", description="Official Gazette starting number (ResmiGazeteSayisiIlk).")
official_gazette_number_end: Optional[str] = Field(None, description="Official Gazette ending number (ResmiGazeteSayisiSon).") official_gazette_number_end: str = Field("", description="Official Gazette ending number (ResmiGazeteSayisiSon).")
has_press_release: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Press release available (BasinDuyurusu).") has_press_release: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Press release available (BasinDuyurusu).")
has_dissenting_opinion: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Dissenting opinion exists (KarsiOy).") has_dissenting_opinion: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Dissenting opinion exists (KarsiOy).")
has_different_reasoning: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Different reasoning exists (FarkliGerekce).") has_different_reasoning: Optional[AnayasaVarYokEnum] = Field(default=AnayasaVarYokEnum.TUMU, description="Different reasoning exists (FarkliGerekce).")
attending_members_names: Optional[List[str]] = Field(default_factory=list, description="List of attending members' exact names (Uyeler_id[]).") attending_members_names: Optional[List[str]] = Field(default_factory=list, description="List of attending members' exact names (Uyeler_id[]).")
rapporteur_name: Optional[str] = Field(None, description="Rapporteur's exact name (Raportorler_id).") rapporteur_name: str = Field("", description="Rapporteur's exact name (Raportorler_id).")
norm_type: Optional[AnayasaNormTuruEnum] = Field(default=AnayasaNormTuruEnum.TUMU, description="Type of the reviewed norm (NormunTurler_id).") norm_type: Optional[AnayasaNormTuruEnum] = Field(default=AnayasaNormTuruEnum.TUMU, description="Type of the reviewed norm (NormunTurler_id).")
norm_id_or_name: Optional[str] = Field(None, description="Number or name of the norm (NormunNumarasiAdlar_id).") norm_id_or_name: str = Field("", description="Number or name of the norm (NormunNumarasiAdlar_id).")
norm_article: Optional[str] = Field(None, description="Article number of the norm (NormunMaddeNumarasi).") norm_article: str = Field("", description="Article number of the norm (NormunMaddeNumarasi).")
review_outcomes: Optional[List[AnayasaIncelemeSonucuEnum]] = Field(default_factory=list, description="List of review types and outcomes (IncelemeTuruKararSonuclar_id[]).") review_outcomes: Optional[List[AnayasaIncelemeSonucuEnum]] = Field(default_factory=list, description="List of review types and outcomes (IncelemeTuruKararSonuclar_id[]).")
reason_for_final_outcome: Optional[AnayasaSonucGerekcesiEnum] = Field(default=AnayasaSonucGerekcesiEnum.TUMU, description="Main reason for the decision outcome (KararSonucununGerekcesi).") reason_for_final_outcome: Optional[AnayasaSonucGerekcesiEnum] = Field(default=AnayasaSonucGerekcesiEnum.TUMU, description="Main reason for the decision outcome (KararSonucununGerekcesi).")
basis_constitution_article_numbers: Optional[List[str]] = Field(default_factory=list, description="List of supporting Constitution article numbers (DayanakHukmu[]).") basis_constitution_article_numbers: Optional[List[str]] = Field(default_factory=list, description="List of supporting Constitution article numbers (DayanakHukmu[]).")
results_per_page: Optional[int] = Field(10, ge=1, le=10, description="Results per page.") results_per_page: int = Field(10, ge=1, le=10, description="Results per page.")
page_to_fetch: Optional[int] = Field(1, ge=1, description="Page number to fetch for results list.") page_to_fetch: int = Field(1, ge=1, description="Page number to fetch for results list.")
sort_by_criteria: Optional[str] = Field("KararTarihi", description="Sort criteria. Options: 'KararTarihi', 'YayinTarihi', 'Toplam' (keyword count).") sort_by_criteria: str = Field("KararTarihi", description="Sort criteria. Options: 'KararTarihi', 'YayinTarihi', 'Toplam' (keyword count).")
class AnayasaReviewedNormInfo(BaseModel): class AnayasaReviewedNormInfo(BaseModel):
"""Details of a norm reviewed within an AYM decision summary.""" """Details of a norm reviewed within an AYM decision summary."""
norm_name_or_number: Optional[str] = None norm_name_or_number: str = Field("", description="Norm name or number")
article_number: Optional[str] = None article_number: str = Field("", description="Article number")
review_type_and_outcome: Optional[str] = None review_type_and_outcome: str = Field("", description="Review type and outcome")
outcome_reason: Optional[str] = None outcome_reason: str = Field("", description="Outcome reason")
basis_constitution_articles_cited: List[str] = Field(default_factory=list) basis_constitution_articles_cited: List[str] = Field(default_factory=list)
postponement_period: Optional[str] = None postponement_period: str = Field("", description="Postponement period")
class AnayasaDecisionSummary(BaseModel): class AnayasaDecisionSummary(BaseModel):
"""Model for a single Anayasa Mahkemesi (Norm Denetimi) decision summary from search results.""" """Model for a single Anayasa Mahkemesi (Norm Denetimi) decision summary from search results."""
decision_reference_no: Optional[str] = None decision_reference_no: str = Field("", description="Decision reference number")
decision_page_url: Optional[HttpUrl] = None decision_page_url: str = Field("", description="Decision page URL")
keywords_found_count: Optional[int] = None keywords_found_count: int = Field(0, description="Keywords found count")
application_type_summary: Optional[str] = None application_type_summary: str = Field("", description="Application type summary")
applicant_summary: Optional[str] = None applicant_summary: str = Field("", description="Applicant summary")
decision_outcome_summary: Optional[str] = None decision_outcome_summary: str = Field("", description="Decision outcome summary")
decision_date_summary: Optional[str] = None decision_date_summary: str = Field("", description="Decision date summary")
reviewed_norms: List[AnayasaReviewedNormInfo] = Field(default_factory=list) reviewed_norms: List[AnayasaReviewedNormInfo] = Field(default_factory=list)
class AnayasaSearchResult(BaseModel): class AnayasaSearchResult(BaseModel):
"""Model for the overall search result for Anayasa Mahkemesi Norm Denetimi decisions.""" """Model for the overall search result for Anayasa Mahkemesi Norm Denetimi decisions."""
decisions: List[AnayasaDecisionSummary] decisions: List[AnayasaDecisionSummary]
total_records_found: Optional[int] = None total_records_found: int = Field(0, description="Total records found")
retrieved_page_number: Optional[int] = None retrieved_page_number: int = Field(1, description="Retrieved page number")
class AnayasaDocumentMarkdown(BaseModel): class AnayasaDocumentMarkdown(BaseModel):
""" """
@@ -150,10 +150,10 @@ class AnayasaDocumentMarkdown(BaseModel):
and pagination information. and pagination information.
""" """
source_url: HttpUrl source_url: HttpUrl
decision_reference_no_from_page: Optional[str] = Field(None, description="E.K. No parsed from the document page.") decision_reference_no_from_page: str = Field("", description="E.K. No parsed from the document page.")
decision_date_from_page: Optional[str] = Field(None, description="Decision date parsed from the document page.") decision_date_from_page: str = Field("", description="Decision date parsed from the document page.")
official_gazette_info_from_page: Optional[str] = Field(None, description="Official Gazette info parsed from the document page.") official_gazette_info_from_page: str = Field("", description="Official Gazette info parsed from the document page.")
markdown_chunk: Optional[str] = Field(None, description="A 5,000 character chunk of the Markdown content.") # Corrected chunk size markdown_chunk: str = Field("", description="A 5,000 character chunk of the Markdown content.") # Corrected chunk size
current_page: int = Field(description="The current page number of the markdown chunk (1-indexed).") current_page: int = Field(description="The current page number of the markdown chunk (1-indexed).")
total_pages: int = Field(description="Total number of pages for the full markdown content.") total_pages: int = Field(description="Total number of pages for the full markdown content.")
is_paginated: bool = Field(description="True if the full markdown content is split into multiple pages.") is_paginated: bool = Field(description="True if the full markdown content is split into multiple pages.")
@@ -168,27 +168,27 @@ class AnayasaBireyselReportSearchRequest(BaseModel):
class AnayasaBireyselReportDecisionDetail(BaseModel): class AnayasaBireyselReportDecisionDetail(BaseModel):
"""Details of a specific right/claim within a Bireysel Başvuru decision summary in a report.""" """Details of a specific right/claim within a Bireysel Başvuru decision summary in a report."""
hak: Optional[str] = Field(None, description="İhlal edildiği iddia edilen hak (örneğin, Mülkiyet hakkı).") hak: str = Field("", description="İhlal edildiği iddia edilen hak (örneğin, Mülkiyet hakkı).")
mudahale_iddiasi: Optional[str] = Field(None, description="İhlale neden olan müdahale iddiası.") mudahale_iddiasi: str = Field("", description="İhlale neden olan müdahale iddiası.")
sonuc: Optional[str] = Field(None, description="İnceleme sonucu (örneğin, İhlal, Düşme).") sonuc: str = Field("", description="İnceleme sonucu (örneğin, İhlal, Düşme).")
giderim: Optional[str] = Field(None, description="Kararlaştırılan giderim (örneğin, Yeniden yargılama).") giderim: str = Field("", description="Kararlaştırılan giderim (örneğin, Yeniden yargılama).")
class AnayasaBireyselReportDecisionSummary(BaseModel): class AnayasaBireyselReportDecisionSummary(BaseModel):
"""Model for a single Anayasa Mahkemesi (Bireysel Başvuru) decision summary from a 'Karar Arama Raporu'.""" """Model for a single Anayasa Mahkemesi (Bireysel Başvuru) decision summary from a 'Karar Arama Raporu'."""
title: Optional[str] = Field(None, description="Başvurunun başlığı (e.g., 'HASAN DURMUŞ Başvurusuna İlişkin Karar').") title: str = Field("", description="Başvurunun başlığı (e.g., 'HASAN DURMUŞ Başvurusuna İlişkin Karar').")
decision_reference_no: Optional[str] = Field(None, description="Başvuru Numarası (e.g., '2019/19126').") decision_reference_no: str = Field("", description="Başvuru Numarası (e.g., '2019/19126').")
decision_page_url: Optional[HttpUrl] = Field(None, description="URL to the full decision page.") decision_page_url: str = Field("", description="URL to the full decision page.")
decision_type_summary: Optional[str] = Field(None, description="Karar Türü (Başvuru Sonucu) (e.g., 'Esas (İhlal)').") decision_type_summary: str = Field("", description="Karar Türü (Başvuru Sonucu) (e.g., 'Esas (İhlal)').")
decision_making_body: Optional[str] = Field(None, description="Kararı Veren Birim (e.g., 'Genel Kurul', 'Birinci Bölüm').") decision_making_body: str = Field("", description="Kararı Veren Birim (e.g., 'Genel Kurul', 'Birinci Bölüm').")
application_date_summary: Optional[str] = Field(None, description="Başvuru Tarihi (DD/MM/YYYY).") application_date_summary: str = Field("", description="Başvuru Tarihi (DD/MM/YYYY).")
decision_date_summary: Optional[str] = Field(None, description="Karar Tarihi (DD/MM/YYYY).") decision_date_summary: str = Field("", description="Karar Tarihi (DD/MM/YYYY).")
application_subject_summary: Optional[str] = Field(None, description="Başvuru konusunun özeti.") application_subject_summary: str = Field("", description="Başvuru konusunun özeti.")
details: List[AnayasaBireyselReportDecisionDetail] = Field(default_factory=list, description="İncelenen haklar ve sonuçlarına ilişkin detaylar.") details: List[AnayasaBireyselReportDecisionDetail] = Field(default_factory=list, description="İncelenen haklar ve sonuçlarına ilişkin detaylar.")
class AnayasaBireyselReportSearchResult(BaseModel): class AnayasaBireyselReportSearchResult(BaseModel):
"""Model for the overall search result for Anayasa Mahkemesi 'Karar Arama Raporu'.""" """Model for the overall search result for Anayasa Mahkemesi 'Karar Arama Raporu'."""
decisions: List[AnayasaBireyselReportDecisionSummary] decisions: List[AnayasaBireyselReportDecisionSummary]
total_records_found: Optional[int] = Field(None, description="Raporda bulunan toplam karar sayısı.") total_records_found: int = Field(0, description="Raporda bulunan toplam karar sayısı.")
retrieved_page_number: int = Field(description="Alınan rapor sayfa numarası.") retrieved_page_number: int = Field(description="Alınan rapor sayfa numarası.")
+2 -2
View File
@@ -76,8 +76,8 @@ class DanistayApiClient:
mevzuatNumarasi=params.mevzuatNumarasi or "", mevzuatNumarasi=params.mevzuatNumarasi or "",
mevzuatAdi=params.mevzuatAdi or "", mevzuatAdi=params.mevzuatAdi or "",
madde=params.madde or "", madde=params.madde or "",
siralama=params.siralama, siralama="1",
siralamaDirection=params.siralamaDirection, siralamaDirection="desc",
pageSize=params.pageSize, pageSize=params.pageSize,
pageNumber=params.pageNumber pageNumber=params.pageNumber
) )
+19 -21
View File
@@ -51,20 +51,18 @@ class DanistayDetailedSearchRequestData(BaseModel): # Internal data model for de
class DanistayDetailedSearchRequest(DanistayBaseSearchRequest): # MCP tool will accept this class DanistayDetailedSearchRequest(DanistayBaseSearchRequest): # MCP tool will accept this
"""Model for detailed search request for Danistay.""" """Model for detailed search request for Danistay."""
daire: Optional[str] = Field(None, description="Chamber") daire: str = Field("", description="Chamber")
esasYil: Optional[str] = Field(None, description="Case year") esasYil: str = Field("", description="Case year")
esasIlkSiraNo: Optional[str] = Field(None, description="Start case no") esasIlkSiraNo: str = Field("", description="Start case no")
esasSonSiraNo: Optional[str] = Field(None, description="End case no") esasSonSiraNo: str = Field("", description="End case no")
kararYil: Optional[str] = Field(None, description="Decision year") kararYil: str = Field("", description="Decision year")
kararIlkSiraNo: Optional[str] = Field(None, description="Start decision no") kararIlkSiraNo: str = Field("", description="Start decision no")
kararSonSiraNo: Optional[str] = Field(None, description="End decision no") kararSonSiraNo: str = Field("", description="End decision no")
baslangicTarihi: Optional[str] = Field(None, description="Start date") baslangicTarihi: str = Field("", description="Start date")
bitisTarihi: Optional[str] = Field(None, description="End date") bitisTarihi: str = Field("", description="End date")
mevzuatNumarasi: Optional[str] = Field(None, description="Law number") mevzuatNumarasi: str = Field("", description="Law number")
mevzuatAdi: Optional[str] = Field(None, description="Law name") mevzuatAdi: str = Field("", description="Law name")
madde: Optional[str] = Field(None, description="Article") madde: str = Field("", description="Article")
siralama: str = Field("1", description="Sort by")
siralamaDirection: str = Field("desc", description="Direction")
# Add a general keyword field if detailed search also supports it # Add a general keyword field if detailed search also supports it
# arananKelime: Optional[str] = Field(None, description="General keyword for detailed search.") # arananKelime: Optional[str] = Field(None, description="General keyword for detailed search.")
@@ -76,11 +74,11 @@ class DanistayApiDecisionEntry(BaseModel):
id: str id: str
# The API response for keyword search uses "daireKurul", detailed search example uses "daire". # The API response for keyword search uses "daireKurul", detailed search example uses "daire".
# We use an alias to handle both and map to a consistent field name "chamber". # We use an alias to handle both and map to a consistent field name "chamber".
chamber: Optional[str] = Field(None, alias="daire", description="Chamber") chamber: str = Field("", alias="daire", description="Chamber")
esasNo: Optional[str] = Field(None) esasNo: str = Field("", description="Case number")
kararNo: Optional[str] = Field(None) kararNo: str = Field("", description="Decision number")
kararTarihi: Optional[str] = Field(None) kararTarihi: str = Field("", description="Decision date")
arananKelime: Optional[str] = Field(None, description="Keyword") arananKelime: str = Field("", description="Keyword")
# index: Optional[int] = None # Present in response, can be added if needed by MCP tool # index: Optional[int] = None # Present in response, can be added if needed by MCP tool
# siraNo: Optional[int] = None # Present in detailed response, can be added # siraNo: Optional[int] = None # Present in detailed response, can be added
@@ -93,7 +91,7 @@ class DanistayApiResponseInnerData(BaseModel):
data: List[DanistayApiDecisionEntry] data: List[DanistayApiDecisionEntry]
recordsTotal: int recordsTotal: int
recordsFiltered: int recordsFiltered: int
draw: Optional[int] = Field(None, description="Draw counter") draw: int = Field(0, description="Draw counter")
class DanistayApiResponse(BaseModel): class DanistayApiResponse(BaseModel):
"""Model for the complete search response from the Danistay API.""" """Model for the complete search response from the Danistay API."""
@@ -103,7 +101,7 @@ class DanistayApiResponse(BaseModel):
class DanistayDocumentMarkdown(BaseModel): class DanistayDocumentMarkdown(BaseModel):
"""Model for a Danistay decision document, containing only Markdown content.""" """Model for a Danistay decision document, containing only Markdown content."""
id: str id: str
markdown_content: Optional[str] = Field(None, description="The decision content (Karar İçeriği) converted to Markdown.") markdown_content: str = Field("", description="The decision content (Karar İçeriği) converted to Markdown.")
source_url: HttpUrl source_url: HttpUrl
class CompactDanistaySearchResult(BaseModel): class CompactDanistaySearchResult(BaseModel):
+22 -22
View File
@@ -12,8 +12,8 @@ class EmsalDetailedSearchRequestData(BaseModel):
""" """
arananKelime: Optional[str] = "" arananKelime: Optional[str] = ""
Bam_Hukuk_Mahkemeleri: Optional[str] = Field(None, alias="Bam Hukuk Mahkemeleri") Bam_Hukuk_Mahkemeleri: str = Field("", alias="Bam Hukuk Mahkemeleri")
Hukuk_Mahkemeleri: Optional[str] = Field(None, alias="Hukuk Mahkemeleri") Hukuk_Mahkemeleri: str = Field("", alias="Hukuk Mahkemeleri")
# Add other specific court type fields from the form if they are separate keys in payload # Add other specific court type fields from the form if they are separate keys in payload
# E.g., "Ceza Mahkemeleri", "İdari Mahkemeler" etc. # E.g., "Ceza Mahkemeleri", "İdari Mahkemeler" etc.
@@ -36,22 +36,22 @@ class EmsalDetailedSearchRequestData(BaseModel):
class EmsalSearchRequest(BaseModel): # This is the model the MCP tool will accept class EmsalSearchRequest(BaseModel): # This is the model the MCP tool will accept
"""Model for Emsal detailed search request, with user-friendly field names.""" """Model for Emsal detailed search request, with user-friendly field names."""
keyword: Optional[str] = Field(None, description="Keyword") keyword: str = Field("", description="Keyword")
selected_bam_civil_court: Optional[str] = Field(None, description="BAM Civil Court") selected_bam_civil_court: str = Field("", description="BAM Civil Court")
selected_civil_court: Optional[str] = Field(None, description="Civil Court") selected_civil_court: str = Field("", description="Civil Court")
selected_regional_civil_chambers: Optional[List[str]] = Field(default_factory=list, description="Regional chambers") selected_regional_civil_chambers: List[str] = Field(default_factory=list, description="Regional chambers")
case_year_esas: Optional[str] = Field(None, description="Case year") case_year_esas: str = Field("", description="Case year")
case_start_seq_esas: Optional[str] = Field(None, description="Start case no") case_start_seq_esas: str = Field("", description="Start case no")
case_end_seq_esas: Optional[str] = Field(None, description="End case no") case_end_seq_esas: str = Field("", description="End case no")
decision_year_karar: Optional[str] = Field(None, description="Decision year") decision_year_karar: str = Field("", description="Decision year")
decision_start_seq_karar: Optional[str] = Field(None, description="Start decision no") decision_start_seq_karar: str = Field("", description="Start decision no")
decision_end_seq_karar: Optional[str] = Field(None, description="End decision no") decision_end_seq_karar: str = Field("", description="End decision no")
start_date: Optional[str] = Field(None, description="Start date (DD.MM.YYYY)") start_date: str = Field("", description="Start date (DD.MM.YYYY)")
end_date: Optional[str] = Field(None, description="End date (DD.MM.YYYY)") end_date: str = Field("", description="End date (DD.MM.YYYY)")
sort_criteria: str = Field("1", description="Sort by") sort_criteria: str = Field("1", description="Sort by")
sort_direction: str = Field("desc", description="Direction") sort_direction: str = Field("desc", description="Direction")
@@ -63,12 +63,12 @@ class EmsalSearchRequest(BaseModel): # This is the model the MCP tool will accep
class EmsalApiDecisionEntry(BaseModel): class EmsalApiDecisionEntry(BaseModel):
"""Model for an individual decision entry from the Emsal API search response.""" """Model for an individual decision entry from the Emsal API search response."""
id: str id: str
daire: Optional[str] = Field(None, description="Chamber") daire: str = Field("", description="Chamber")
esasNo: Optional[str] = Field(None) esasNo: str = Field("", description="Case number")
kararNo: Optional[str] = Field(None) kararNo: str = Field("", description="Decision number")
kararTarihi: Optional[str] = Field(None) kararTarihi: str = Field("", description="Decision date")
arananKelime: Optional[str] = Field(None, description="Keyword") arananKelime: str = Field("", description="Keyword")
durum: Optional[str] = Field(None, description="Status") durum: str = Field("", description="Status")
# index: Optional[int] = None # Present in Emsal response, can be added if tool needs it # index: Optional[int] = None # Present in Emsal response, can be added if tool needs it
document_url: Optional[HttpUrl] = Field(None, description="Document URL") document_url: Optional[HttpUrl] = Field(None, description="Document URL")
@@ -80,7 +80,7 @@ class EmsalApiResponseInnerData(BaseModel):
data: List[EmsalApiDecisionEntry] data: List[EmsalApiDecisionEntry]
recordsTotal: int recordsTotal: int
recordsFiltered: int recordsFiltered: int
draw: Optional[int] = Field(None, description="Draw counter (Çizim Sayıcısı) from API, usually for DataTables.") draw: int = Field(0, description="Draw counter (Çizim Sayıcısı) from API, usually for DataTables.")
class EmsalApiResponse(BaseModel): class EmsalApiResponse(BaseModel):
"""Model for the complete search response from the Emsal API.""" """Model for the complete search response from the Emsal API."""
@@ -90,7 +90,7 @@ class EmsalApiResponse(BaseModel):
class EmsalDocumentMarkdown(BaseModel): class EmsalDocumentMarkdown(BaseModel):
"""Model for an Emsal decision document, containing only Markdown content.""" """Model for an Emsal decision document, containing only Markdown content."""
id: str id: str
markdown_content: Optional[str] = Field(None, description="The decision content (Karar İçeriği) converted to Markdown.") markdown_content: str = Field("", description="The decision content (Karar İçeriği) converted to Markdown.")
source_url: HttpUrl source_url: HttpUrl
class CompactEmsalSearchResult(BaseModel): class CompactEmsalSearchResult(BaseModel):
+13 -13
View File
@@ -13,16 +13,16 @@ class KikKararTipi(str, Enum):
class KikSearchRequest(BaseModel): class KikSearchRequest(BaseModel):
"""Model for KIK Decision search criteria.""" """Model for KIK Decision search criteria."""
karar_tipi: KikKararTipi = Field(KikKararTipi.UYUSMAZLIK, description="Type") karar_tipi: KikKararTipi = Field(KikKararTipi.UYUSMAZLIK, description="Type")
karar_no: Optional[str] = Field(None, description="No") karar_no: str = Field("", description="No")
karar_tarihi_baslangic: Optional[str] = Field(None, description="Start", pattern=r"^\d{2}\.\d{2}\.\d{4}$") karar_tarihi_baslangic: str = Field("", description="Start", pattern=r"^\d{2}\.\d{2}\.\d{4}$|^$")
karar_tarihi_bitis: Optional[str] = Field(None, description="End", pattern=r"^\d{2}\.\d{2}\.\d{4}$") karar_tarihi_bitis: str = Field("", description="End", pattern=r"^\d{2}\.\d{2}\.\d{4}$|^$")
resmi_gazete_sayisi: Optional[str] = Field(None, description="Gazette") resmi_gazete_sayisi: str = Field("", description="Gazette")
resmi_gazete_tarihi: Optional[str] = Field(None, description="Date", pattern=r"^\d{2}\.\d{2}\.\d{4}$") resmi_gazete_tarihi: str = Field("", description="Date", pattern=r"^\d{2}\.\d{2}\.\d{4}$|^$")
basvuru_konusu_ihale: Optional[str] = Field(None, description="Subject") basvuru_konusu_ihale: str = Field("", description="Subject")
basvuru_sahibi: Optional[str] = Field(None, description="Applicant") basvuru_sahibi: str = Field("", description="Applicant")
ihaleyi_yapan_idare: Optional[str] = Field(None, description="Entity") ihaleyi_yapan_idare: str = Field("", description="Entity")
yil: Optional[str] = Field(None, description="Year") yil: str = Field("", description="Year")
karar_metni: Optional[str] = Field(None, description="Text") karar_metni: str = Field("", description="Text")
page: int = Field(1, ge=1, description="Page") page: int = Field(1, ge=1, description="Page")
class KikDecisionEntry(BaseModel): class KikDecisionEntry(BaseModel):
@@ -32,9 +32,9 @@ class KikDecisionEntry(BaseModel):
karar_tipi: KikKararTipi = Field(..., description="Decision type") karar_tipi: KikKararTipi = Field(..., description="Decision type")
karar_tarihi_str: str = Field(..., alias="kararTarihi", description="Date") karar_tarihi_str: str = Field(..., alias="kararTarihi", description="Date")
idare_str: Optional[str] = Field(None, alias="idare", description="Entity") idare_str: str = Field("", alias="idare", description="Entity")
basvuru_sahibi_str: Optional[str] = Field(None, alias="basvuruSahibi", description="Applicant") basvuru_sahibi_str: str = Field("", alias="basvuruSahibi", description="Applicant")
ihale_konusu_str: Optional[str] = Field(None, alias="ihaleKonusu", description="Subject") ihale_konusu_str: str = Field("", alias="ihaleKonusu", description="Subject")
@computed_field @computed_field
@property @property
+326 -129
View File
@@ -4,9 +4,14 @@ import atexit
import logging import logging
import os import os
import httpx import httpx
import json
import time
from collections import defaultdict
from pydantic import HttpUrl, Field from pydantic import HttpUrl, Field
from typing import Optional, Dict, List, Literal, Any, Union from typing import Optional, Dict, List, Literal, Any, Union
import urllib.parse import urllib.parse
import tiktoken
from fastmcp.server.middleware import Middleware, MiddlewareContext
# --- Logging Configuration Start --- # --- Logging Configuration Start ---
LOG_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logs") LOG_DIRECTORY = os.path.join(os.path.dirname(os.path.abspath(__file__)), "logs")
@@ -32,6 +37,197 @@ root_logger.addHandler(console_handler)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# --- Logging Configuration End --- # --- Logging Configuration End ---
# --- Token Counting Middleware ---
class TokenCountingMiddleware(Middleware):
"""Middleware for counting input/output tokens using tiktoken."""
def __init__(self, model: str = "cl100k_base"):
"""Initialize token counting middleware.
Args:
model: Tiktoken model name (cl100k_base for GPT-4/Claude compatibility)
"""
self.encoder = tiktoken.get_encoding(model)
self.model = model
self.token_stats = defaultdict(lambda: {"input": 0, "output": 0, "calls": 0})
self.logger = logging.getLogger("token_counter")
# Create separate log file for token metrics
token_log_path = os.path.join(LOG_DIRECTORY, "token_metrics.log")
token_handler = logging.FileHandler(token_log_path, mode='a', encoding='utf-8')
token_formatter = logging.Formatter('%(asctime)s - %(message)s')
token_handler.setFormatter(token_formatter)
token_handler.setLevel(logging.INFO)
self.logger.addHandler(token_handler)
self.logger.setLevel(logging.INFO)
def count_tokens(self, text: str) -> int:
"""Count tokens in text using tiktoken."""
if not text:
return 0
try:
return len(self.encoder.encode(str(text)))
except Exception as e:
logger.warning(f"Token counting failed: {e}")
return 0
def extract_text_content(self, data: Any) -> str:
"""Extract text content from various data types."""
if isinstance(data, str):
return data
elif isinstance(data, dict):
# Extract text from common response fields
text_parts = []
for key, value in data.items():
if isinstance(value, str):
text_parts.append(value)
elif isinstance(value, list):
for item in value:
if isinstance(item, str):
text_parts.append(item)
elif isinstance(item, dict) and 'text' in item:
text_parts.append(str(item['text']))
return ' '.join(text_parts)
elif isinstance(data, list):
text_parts = []
for item in data:
text_parts.append(self.extract_text_content(item))
return ' '.join(text_parts)
else:
return str(data)
def log_token_usage(self, operation: str, input_tokens: int, output_tokens: int,
tool_name: str = None, duration_ms: float = None):
"""Log token usage with structured format."""
log_data = {
"operation": operation,
"tool_name": tool_name,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"total_tokens": input_tokens + output_tokens,
"duration_ms": duration_ms,
"timestamp": time.time()
}
# Update statistics
key = tool_name if tool_name else operation
self.token_stats[key]["input"] += input_tokens
self.token_stats[key]["output"] += output_tokens
self.token_stats[key]["calls"] += 1
# Log as JSON for easy parsing
self.logger.info(json.dumps(log_data))
# Also log human-readable format to main logger
logger.info(f"Token Usage - {operation}" +
(f" ({tool_name})" if tool_name else "") +
f": {input_tokens} in + {output_tokens} out = {input_tokens + output_tokens} total")
async def on_call_tool(self, context: MiddlewareContext, call_next):
"""Count tokens for tool calls."""
start_time = time.perf_counter()
# Extract tool name and arguments
tool_name = getattr(context.message, 'name', 'unknown_tool')
tool_args = getattr(context.message, 'arguments', {})
# Count input tokens (tool arguments)
input_text = self.extract_text_content(tool_args)
input_tokens = self.count_tokens(input_text)
try:
# Execute the tool
result = await call_next(context)
# Count output tokens (tool result)
output_text = self.extract_text_content(result)
output_tokens = self.count_tokens(output_text)
# Calculate duration
duration_ms = (time.perf_counter() - start_time) * 1000
# Log token usage
self.log_token_usage("tool_call", input_tokens, output_tokens,
tool_name, duration_ms)
return result
except Exception as e:
duration_ms = (time.perf_counter() - start_time) * 1000
self.log_token_usage("tool_call_error", input_tokens, 0,
tool_name, duration_ms)
raise
async def on_read_resource(self, context: MiddlewareContext, call_next):
"""Count tokens for resource reads."""
start_time = time.perf_counter()
# Extract resource URI
resource_uri = getattr(context.message, 'uri', 'unknown_resource')
try:
# Execute the resource read
result = await call_next(context)
# Count output tokens (resource content)
output_text = self.extract_text_content(result)
output_tokens = self.count_tokens(output_text)
# Calculate duration
duration_ms = (time.perf_counter() - start_time) * 1000
# Log token usage (no input tokens for resource reads)
self.log_token_usage("resource_read", 0, output_tokens,
resource_uri, duration_ms)
return result
except Exception as e:
duration_ms = (time.perf_counter() - start_time) * 1000
self.log_token_usage("resource_read_error", 0, 0,
resource_uri, duration_ms)
raise
async def on_get_prompt(self, context: MiddlewareContext, call_next):
"""Count tokens for prompt retrievals."""
start_time = time.perf_counter()
# Extract prompt name
prompt_name = getattr(context.message, 'name', 'unknown_prompt')
try:
# Execute the prompt retrieval
result = await call_next(context)
# Count output tokens (prompt content)
output_text = self.extract_text_content(result)
output_tokens = self.count_tokens(output_text)
# Calculate duration
duration_ms = (time.perf_counter() - start_time) * 1000
# Log token usage
self.log_token_usage("prompt_get", 0, output_tokens,
prompt_name, duration_ms)
return result
except Exception as e:
duration_ms = (time.perf_counter() - start_time) * 1000
self.log_token_usage("prompt_get_error", 0, 0,
prompt_name, duration_ms)
raise
def get_token_stats(self) -> Dict[str, Any]:
"""Get current token usage statistics."""
return dict(self.token_stats)
def reset_token_stats(self):
"""Reset token usage statistics."""
self.token_stats.clear()
# --- End Token Counting Middleware ---
# Create FastMCP app directly without authentication wrapper # Create FastMCP app directly without authentication wrapper
from fastmcp import FastMCP from fastmcp import FastMCP
@@ -113,6 +309,11 @@ from kvkk_mcp_module.models import (
app = create_app() app = create_app()
# --- Add Token Counting Middleware ---
token_counter = TokenCountingMiddleware()
app.add_middleware(token_counter)
logger.info("Token counting middleware added to MCP server")
# --- Tool Documentation Resources --- # --- Tool Documentation Resources ---
@app.resource("docs://tools/yargitay") @app.resource("docs://tools/yargitay")
async def get_yargitay_tools_documentation() -> str: async def get_yargitay_tools_documentation() -> str:
@@ -863,7 +1064,7 @@ KARAR_TURU_ADI_TO_GUID_ENUM_MAP = {
# --- MCP Tools for Yargitay --- # --- MCP Tools for Yargitay ---
@app.tool( @app.tool(
description="Search Yargıtay decisions using primary API with 52 chamber filtering and advanced operators. Before using, read docs://tools/yargitay", description="Search Yargıtay decisions with 52 chamber filtering and advanced operators",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -871,10 +1072,8 @@ KARAR_TURU_ADI_TO_GUID_ENUM_MAP = {
} }
) )
async def search_yargitay_detailed( async def search_yargitay_detailed(
arananKelime: str = Field("", description="Search keyword with OR/AND/wildcard operators"), arananKelime: str = Field("", description="Turkish search keyword. Supports +required -excluded \"exact phrase\" operators"),
birimYrgKurulDaire: str = Field("ALL", description="Chamber selection (52 options: Civil/Criminal chambers, General Assemblies)"), birimYrgKurulDaire: str = Field("ALL", description="Chamber selection (52 options: Civil/Criminal chambers, General Assemblies)"),
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'."),
@@ -883,8 +1082,6 @@ async def search_yargitay_detailed(
kararSonSiraNo: str = Field("", description="Ending sequence number for 'Karar No'."), kararSonSiraNo: str = Field("", description="Ending sequence number for 'Karar No'."),
baslangicTarihi: str = Field("", description="Start date for decision search (DD.MM.YYYY)."), baslangicTarihi: str = Field("", description="Start date for decision search (DD.MM.YYYY)."),
bitisTarihi: str = Field("", description="End date for decision search (DD.MM.YYYY)."), bitisTarihi: str = Field("", description="End date for decision search (DD.MM.YYYY)."),
siralama: str = Field("3", description="Sorting criteria (1: Esas No, 2: Karar No, 3: Karar Tarihi)."),
siralamaDirection: str = Field("desc", description="Sorting direction ('asc' or 'desc')."),
pageSize: int = Field(10, ge=1, le=10, description="Number of results per page."), pageSize: int = Field(10, ge=1, le=10, description="Number of results per page."),
pageNumber: int = Field(1, ge=1, description="Page number to retrieve.") pageNumber: int = Field(1, ge=1, description="Page number to retrieve.")
) -> CompactYargitaySearchResult: ) -> CompactYargitaySearchResult:
@@ -897,8 +1094,6 @@ async def search_yargitay_detailed(
search_query = YargitayDetailedSearchRequest( search_query = YargitayDetailedSearchRequest(
arananKelime=arananKelime, arananKelime=arananKelime,
birimYrgKurulDaire=birimYrgKurulDaire, birimYrgKurulDaire=birimYrgKurulDaire,
birimYrgHukukDaire=birimYrgHukukDaire,
birimYrgCezaDaire=birimYrgCezaDaire,
esasYil=esasYil, esasYil=esasYil,
esasIlkSiraNo=esasIlkSiraNo, esasIlkSiraNo=esasIlkSiraNo,
esasSonSiraNo=esasSonSiraNo, esasSonSiraNo=esasSonSiraNo,
@@ -907,8 +1102,8 @@ async def search_yargitay_detailed(
kararSonSiraNo=kararSonSiraNo, kararSonSiraNo=kararSonSiraNo,
baslangicTarihi=baslangicTarihi, baslangicTarihi=baslangicTarihi,
bitisTarihi=bitisTarihi, bitisTarihi=bitisTarihi,
siralama=siralama, siralama="3",
siralamaDirection=siralamaDirection, siralamaDirection="desc",
pageSize=pageSize, pageSize=pageSize,
pageNumber=pageNumber pageNumber=pageNumber
) )
@@ -941,7 +1136,7 @@ async def search_yargitay_detailed(
raise raise
@app.tool( @app.tool(
description="Retrieve full text of a Yargıtay decision in Markdown format. Before using, read docs://tools/yargitay", description="Get Yargıtay decision text in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -959,7 +1154,7 @@ async def get_yargitay_document_markdown(id: str) -> YargitayDocumentMarkdown:
# --- MCP Tools for Danistay --- # --- MCP Tools for Danistay ---
@app.tool( @app.tool(
description="Search Danıştay decisions using keyword logic with AND/OR/NOT operators. Before using, read docs://tools/danistay", description="Search Danıştay decisions with keyword logic (AND/OR/NOT operators)",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1001,7 +1196,7 @@ async def search_danistay_by_keyword(
raise raise
@app.tool( @app.tool(
description="Search Danıştay decisions with detailed criteria including chamber selection and case numbers. Before using, read docs://tools/danistay", description="Search Danıştay decisions with detailed criteria (chamber selection, case numbers)",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1009,20 +1204,18 @@ async def search_danistay_by_keyword(
} }
) )
async def search_danistay_detailed( async def search_danistay_detailed(
daire: Optional[str] = Field(None, description="Chamber/Department name (e.g., '1. Daire')."), daire: str = Field("", description="Chamber/Department name (e.g., '1. Daire')."),
esasYil: Optional[str] = Field(None, description="Case year for 'Esas No'."), esasYil: str = Field("", description="Case year for 'Esas No'."),
esasIlkSiraNo: Optional[str] = Field(None, description="Starting sequence for 'Esas No'."), esasIlkSiraNo: str = Field("", description="Starting sequence for 'Esas No'."),
esasSonSiraNo: Optional[str] = Field(None, description="Ending sequence for 'Esas No'."), esasSonSiraNo: str = Field("", description="Ending sequence for 'Esas No'."),
kararYil: Optional[str] = Field(None, description="Decision year for 'Karar No'."), kararYil: str = Field("", description="Decision year for 'Karar No'."),
kararIlkSiraNo: Optional[str] = Field(None, description="Starting sequence for 'Karar No'."), kararIlkSiraNo: str = Field("", description="Starting sequence for 'Karar No'."),
kararSonSiraNo: Optional[str] = Field(None, description="Ending sequence for 'Karar No'."), kararSonSiraNo: str = Field("", description="Ending sequence for 'Karar No'."),
baslangicTarihi: Optional[str] = Field(None, description="Start date for decision (DD.MM.YYYY)."), baslangicTarihi: str = Field("", description="Start date for decision (DD.MM.YYYY)."),
bitisTarihi: Optional[str] = Field(None, description="End date for decision (DD.MM.YYYY)."), bitisTarihi: str = Field("", description="End date for decision (DD.MM.YYYY)."),
mevzuatNumarasi: Optional[str] = Field(None, description="Legislation number."), mevzuatNumarasi: str = Field("", description="Legislation number."),
mevzuatAdi: Optional[str] = Field(None, description="Legislation name."), mevzuatAdi: str = Field("", description="Legislation name."),
madde: Optional[str] = Field(None, description="Article number."), madde: str = Field("", description="Article number."),
siralama: str = Field("1", description="Sorting criteria (e.g., 1: Esas No, 3: Karar Tarihi)."),
siralamaDirection: str = Field("desc", description="Sorting direction ('asc' or 'desc')."),
pageNumber: int = Field(1, ge=1, description="Page number."), pageNumber: int = Field(1, ge=1, description="Page number."),
pageSize: int = Field(10, ge=1, le=10, description="Results per page.") pageSize: int = Field(10, ge=1, le=10, description="Results per page.")
) -> CompactDanistaySearchResult: ) -> CompactDanistaySearchResult:
@@ -1041,8 +1234,8 @@ async def search_danistay_detailed(
mevzuatNumarasi=mevzuatNumarasi, mevzuatNumarasi=mevzuatNumarasi,
mevzuatAdi=mevzuatAdi, mevzuatAdi=mevzuatAdi,
madde=madde, madde=madde,
siralama=siralama, siralama="3",
siralamaDirection=siralamaDirection, siralamaDirection="desc",
pageNumber=pageNumber, pageNumber=pageNumber,
pageSize=pageSize pageSize=pageSize
) )
@@ -1063,7 +1256,7 @@ async def search_danistay_detailed(
raise raise
@app.tool( @app.tool(
description="Retrieve full text of a Danıştay decision in Markdown format. Before using, read docs://tools/danistay", description="Get Danıştay decision text in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1081,7 +1274,7 @@ async def get_danistay_document_markdown(id: str) -> DanistayDocumentMarkdown:
# --- MCP Tools for Emsal --- # --- MCP Tools for Emsal ---
@app.tool( @app.tool(
description="Search Emsal precedent decisions with detailed criteria across Turkish courts. Before using, read docs://tools/emsal", description="Search Emsal precedent decisions with detailed criteria",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1089,18 +1282,18 @@ async def get_danistay_document_markdown(id: str) -> DanistayDocumentMarkdown:
} }
) )
async def search_emsal_detailed_decisions( async def search_emsal_detailed_decisions(
keyword: Optional[str] = Field(None, description="Keyword to search."), keyword: str = Field("", description="Keyword to search."),
selected_bam_civil_court: Optional[str] = Field(None, description="Selected BAM Civil Court."), selected_bam_civil_court: str = Field("", description="Selected BAM Civil Court."),
selected_civil_court: Optional[str] = Field(None, description="Selected Civil Court."), selected_civil_court: str = Field("", description="Selected Civil Court."),
selected_regional_civil_chambers: List[str] = Field(default_factory=list, description="Selected Regional Civil Chambers."), selected_regional_civil_chambers: List[str] = Field(default_factory=list, description="Selected Regional Civil Chambers."),
case_year_esas: Optional[str] = Field(None, description="Case year for 'Esas No'."), case_year_esas: str = Field("", description="Case year for 'Esas No'."),
case_start_seq_esas: Optional[str] = Field(None, description="Starting sequence for 'Esas No'."), case_start_seq_esas: str = Field("", description="Starting sequence for 'Esas No'."),
case_end_seq_esas: Optional[str] = Field(None, description="Ending sequence for 'Esas No'."), case_end_seq_esas: str = Field("", description="Ending sequence for 'Esas No'."),
decision_year_karar: Optional[str] = Field(None, description="Decision year for 'Karar No'."), decision_year_karar: str = Field("", description="Decision year for 'Karar No'."),
decision_start_seq_karar: Optional[str] = Field(None, description="Starting sequence for 'Karar No'."), decision_start_seq_karar: str = Field("", description="Starting sequence for 'Karar No'."),
decision_end_seq_karar: Optional[str] = Field(None, description="Ending sequence for 'Karar No'."), decision_end_seq_karar: str = Field("", description="Ending sequence for 'Karar No'."),
start_date: Optional[str] = Field(None, description="Start date for decision (DD.MM.YYYY)."), start_date: str = Field("", description="Start date for decision (DD.MM.YYYY)."),
end_date: Optional[str] = Field(None, description="End date for decision (DD.MM.YYYY)."), end_date: str = Field("", description="End date for decision (DD.MM.YYYY)."),
sort_criteria: str = Field("1", description="Sorting criteria (e.g., 1: Esas No)."), sort_criteria: str = Field("1", description="Sorting criteria (e.g., 1: Esas No)."),
sort_direction: str = Field("desc", description="Sorting direction ('asc' or 'desc')."), sort_direction: str = Field("desc", description="Sorting direction ('asc' or 'desc')."),
page_number: int = Field(1, ge=1, description="Page number (accepts int)."), page_number: int = Field(1, ge=1, description="Page number (accepts int)."),
@@ -1144,7 +1337,7 @@ async def search_emsal_detailed_decisions(
raise raise
@app.tool( @app.tool(
description="Retrieve full text of an Emsal precedent decision in Markdown format. Before using, read docs://tools/emsal", description="Get Emsal precedent decision text in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1162,7 +1355,7 @@ async def get_emsal_document_markdown(id: str) -> EmsalDocumentMarkdown:
# --- MCP Tools for Uyusmazlik --- # --- MCP Tools for Uyusmazlik ---
@app.tool( @app.tool(
description="Search Uyuşmazlık Mahkemesi decisions for jurisdictional disputes between court systems. Before using, read docs://tools/uyusmazlik", description="Search Uyuşmazlık Mahkemesi decisions for jurisdictional disputes",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1233,7 +1426,7 @@ async def search_uyusmazlik_decisions(
raise raise
@app.tool( @app.tool(
description="Retrieve full text of an Uyuşmazlık Mahkemesi decision from URL in Markdown format. Before using, read docs://tools/uyusmazlik", description="Get Uyuşmazlık Mahkemesi decision text from URL in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1254,7 +1447,7 @@ async def get_uyusmazlik_document_markdown_from_url(
# --- MCP Tools for Anayasa Mahkemesi (Norm Denetimi) --- # --- MCP Tools for Anayasa Mahkemesi (Norm Denetimi) ---
@app.tool( @app.tool(
description="Search Constitutional Court norm control decisions with comprehensive filtering and legal criteria. Before using, read docs://tools/constitutional_court", description="Search Constitutional Court norm control decisions with comprehensive filtering",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1262,34 +1455,34 @@ async def get_uyusmazlik_document_markdown_from_url(
} }
) )
async def search_anayasa_norm_denetimi_decisions( async def search_anayasa_norm_denetimi_decisions(
keywords_all: List[str] = Field(default_factory=list, description="AND keywords"), keywords_all: List[str] = Field(default_factory=list, description="AND"),
keywords_any: List[str] = Field(default_factory=list, description="OR keywords"), keywords_any: List[str] = Field(default_factory=list, description="OR"),
keywords_exclude: List[str] = Field(default_factory=list, description="Exclude keywords"), keywords_exclude: List[str] = Field(default_factory=list, description="NOT"),
period: Literal["ALL", "1", "2"] = Field("ALL", description="Period"), period: Literal["ALL", "1", "2"] = Field("ALL", description="Period"),
case_number_esas: Optional[str] = Field(None, description="Case number"), case_number_esas: str = Field("", description="Case number"),
decision_number_karar: Optional[str] = Field(None, description="Decision number"), decision_number_karar: str = Field("", description="Decision number"),
first_review_date_start: Optional[str] = Field(None, description="Review start date"), first_review_date_start: str = Field("", description="Start"),
first_review_date_end: Optional[str] = Field(None, description="Review end date"), first_review_date_end: str = Field("", description="End"),
decision_date_start: Optional[str] = Field(None, description="Decision start"), decision_date_start: str = Field("", description="Start"),
decision_date_end: Optional[str] = Field(None, description="Decision end"), decision_date_end: str = Field("", description="End"),
application_type: Literal["ALL", "1", "2", "3"] = Field("ALL", description="App type"), application_type: Literal["ALL", "1", "2", "3"] = Field("ALL", description="App type"),
applicant_general_name: Optional[str] = Field(None, description="Applicant"), applicant_general_name: str = Field("", description="Applicant"),
applicant_specific_name: Optional[str] = Field(None, description="Specific name"), applicant_specific_name: str = Field("", description="Name"),
official_gazette_date_start: Optional[str] = Field(None, description="Gazette start"), official_gazette_date_start: str = Field("", description="Start"),
official_gazette_date_end: Optional[str] = Field(None, description="Gazette end"), official_gazette_date_end: str = Field("", description="End"),
official_gazette_number_start: Optional[str] = Field(None, description="Gazette no start"), official_gazette_number_start: str = Field("", description="Start"),
official_gazette_number_end: Optional[str] = Field(None, description="Gazette no end"), official_gazette_number_end: str = Field("", description="End"),
has_press_release: Literal["ALL", "0", "1"] = Field("ALL", description="Press release"), has_press_release: Literal["ALL", "0", "1"] = Field("ALL", description="Press"),
has_dissenting_opinion: Literal["ALL", "0", "1"] = Field("ALL", description="Dissenting"), has_dissenting_opinion: Literal["ALL", "0", "1"] = Field("ALL", description="Dissenting"),
has_different_reasoning: Literal["ALL", "0", "1"] = Field("ALL", description="Different reason"), has_different_reasoning: Literal["ALL", "0", "1"] = Field("ALL", description="Reason"),
attending_members_names: List[str] = Field(default_factory=list, description="Members"), attending_members_names: List[str] = Field(default_factory=list, description="Members"),
rapporteur_name: Optional[str] = Field(None, description="Rapporteur"), rapporteur_name: str = Field("", description="Rapporteur"),
norm_type: Literal["ALL", "1", "2", "14", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "0", "13"] = Field("ALL", description="Norm type"), norm_type: Literal["ALL", "1", "2", "14", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "0", "13"] = Field("ALL", description="Norm type"),
norm_id_or_name: Optional[str] = Field(None, description="Norm name"), norm_id_or_name: str = Field("", description="Norm name"),
norm_article: Optional[str] = Field(None, description="Article"), norm_article: str = Field("", description="Article"),
review_outcomes: List[Literal["ALL", "1", "2", "3", "4", "5", "6", "7", "8", "12"]] = Field(default_factory=list, description="Outcomes"), review_outcomes: List[Literal["ALL", "1", "2", "3", "4", "5", "6", "7", "8", "12"]] = Field(default_factory=list, description="Outcomes"),
reason_for_final_outcome: Literal["ALL", "29", "1", "2", "30", "3", "4", "27", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26"] = Field("ALL", description="Reason"), reason_for_final_outcome: Literal["ALL", "29", "1", "2", "30", "3", "4", "27", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26"] = Field("ALL", description="Reason"),
basis_constitution_article_numbers: List[str] = Field(default_factory=list, description="Articles"), basis_constitution_article_numbers: List[str] = Field(default_factory=list, description="Arts"),
results_per_page: int = Field(10, description="Count"), results_per_page: int = Field(10, description="Count"),
page_to_fetch: int = Field(1, ge=1, description="Page"), page_to_fetch: int = Field(1, ge=1, description="Page"),
sort_by_criteria: str = Field("KararTarihi", description="Sort") sort_by_criteria: str = Field("KararTarihi", description="Sort")
@@ -1348,7 +1541,7 @@ async def search_anayasa_norm_denetimi_decisions(
raise raise
@app.tool( @app.tool(
description="Retrieve full text of a Constitutional Court norm control decision in paginated Markdown format. Before using, read docs://tools/constitutional_court", description="Get Constitutional Court norm control decision text in paginated Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1371,7 +1564,7 @@ async def get_anayasa_norm_denetimi_document_markdown(
# --- MCP Tools for Anayasa Mahkemesi (Bireysel Başvuru Karar Raporu & Belgeler) --- # --- MCP Tools for Anayasa Mahkemesi (Bireysel Başvuru Karar Raporu & Belgeler) ---
@app.tool( @app.tool(
description="Search Constitutional Court individual application decisions for human rights violation reports. Before using, read docs://tools/constitutional_court", description="Search Constitutional Court individual application decisions for human rights violations",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1397,7 +1590,7 @@ async def search_anayasa_bireysel_basvuru_report(
raise raise
@app.tool( @app.tool(
description="Parameter description", description="Get Constitutional Court individual application decision text in paginated Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1427,7 +1620,7 @@ async def get_anayasa_bireysel_basvuru_document_markdown(
# --- MCP Tools for KIK (Kamu İhale Kurulu) --- # --- MCP Tools for KIK (Kamu İhale Kurulu) ---
@app.tool( @app.tool(
description="Search Public Procurement Authority (KİK) decisions for procurement law and administrative disputes. Before using, read docs://tools/kik", description="Search Public Procurement Authority (KİK) decisions for procurement law disputes",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1436,21 +1629,23 @@ async def get_anayasa_bireysel_basvuru_document_markdown(
) )
async def search_kik_decisions( async def search_kik_decisions(
karar_tipi: Literal["rbUyusmazlik", "rbDuzenleyici", "rbMahkeme"] = Field("rbUyusmazlik", description="Type of KIK Decision."), karar_tipi: Literal["rbUyusmazlik", "rbDuzenleyici", "rbMahkeme"] = Field("rbUyusmazlik", description="Type of KIK Decision."),
karar_no: Optional[str] = Field(None, description="Decision Number (e.g., '2024/UH.II-1766')."), karar_no: str = Field("", description="Decision Number (e.g., '2024/UH.II-1766')."),
karar_tarihi_baslangic: Optional[str] = Field(None, description="Decision Date Start (DD.MM.YYYY)."), karar_tarihi_baslangic: str = Field("", description="Decision Date Start (DD.MM.YYYY)."),
karar_tarihi_bitis: Optional[str] = Field(None, description="Decision Date End (DD.MM.YYYY)."), karar_tarihi_bitis: str = Field("", description="Decision Date End (DD.MM.YYYY)."),
basvuru_sahibi: Optional[str] = Field(None, description="Applicant."), basvuru_sahibi: str = Field("", description="Applicant."),
ihaleyi_yapan_idare: Optional[str] = Field(None, description="Procuring Entity."), ihaleyi_yapan_idare: str = Field("", description="Procuring Entity."),
basvuru_konusu_ihale: Optional[str] = Field(None, description="Tender subject of the application."), basvuru_konusu_ihale: str = Field("", description="Tender subject of the application."),
karar_metni: Optional[str] = Field(None, description=""" karar_metni: str = Field("", description="""
Keyword/phrase in decision text. Advanced search operators supported: Keyword/phrase in decision text. Advanced search operators supported:
- word1+word2 = AND logic (+anayasa +mahkeme -> both words required) - word1+word2 = AND logic (+anayasa +mahkeme -> both words required)
- +"required" -"excluded" = Include and exclude (+ihale -"iptal") - +"required" -"excluded" = Include and exclude (+ihale -"iptal")
Examples: anayasa | +anayasa +mahkeme | +ihale -"iptal" - "exact phrase" = Exact match ("kamu yararı" for exact phrase)
- OR/AND/wildcard operators supported
Examples: anayasa | +anayasa +mahkeme | +ihale -"iptal" | "tam cümle"
"""), """),
yil: Optional[str] = Field(None, description="Year of the decision."), yil: str = Field("", description="Year of the decision."),
resmi_gazete_tarihi: Optional[str] = Field(None, description="Official Gazette Date (DD.MM.YYYY)."), resmi_gazete_tarihi: str = Field("", description="Official Gazette Date (DD.MM.YYYY)."),
resmi_gazete_sayisi: Optional[str] = Field(None, description="Official Gazette Number."), resmi_gazete_sayisi: str = Field("", description="Official Gazette Number."),
page: int = Field(1, ge=1, description="Results page number.") page: int = Field(1, ge=1, description="Results page number.")
) -> KikSearchResult: ) -> KikSearchResult:
"""Search Public Procurement Authority (KIK) decisions.""" """Search Public Procurement Authority (KIK) decisions."""
@@ -1486,7 +1681,7 @@ async def search_kik_decisions(
return KikSearchResult(decisions=[], total_records=0, current_page=current_page_val) return KikSearchResult(decisions=[], total_records=0, current_page=current_page_val)
@app.tool( @app.tool(
description="Retrieve full text of a Public Procurement Authority (KİK) decision in paginated Markdown format. Before using, read docs://tools/kik", description="Get Public Procurement Authority (KİK) decision text in paginated Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1526,7 +1721,7 @@ async def get_kik_document_markdown(
is_paginated=False is_paginated=False
) )
@app.tool( @app.tool(
description="Search Competition Authority (Rekabet Kurumu) decisions for competition law and antitrust research. Before using, read docs://tools/rekabet", description="Search Competition Authority (Rekabet Kurumu) decisions for competition law and antitrust",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1534,10 +1729,10 @@ async def get_kik_document_markdown(
} }
) )
async def search_rekabet_kurumu_decisions( async def search_rekabet_kurumu_decisions(
sayfaAdi: Optional[str] = Field(None, description="Search in decision title (Başlık)."), sayfaAdi: str = Field("", description="Search in decision title (Başlık)."),
YayinlanmaTarihi: Optional[str] = Field(None, description="Publication date (Yayım Tarihi), e.g., DD.MM.YYYY."), YayinlanmaTarihi: str = Field("", description="Publication date (Yayım Tarihi), e.g., DD.MM.YYYY."),
PdfText: Optional[str] = Field( PdfText: str = Field(
None, "",
description='Search in decision text. Use "\\"kesin cümle\\"" for precise matching.' description='Search in decision text. Use "\\"kesin cümle\\"" for precise matching.'
), ),
KararTuru: Literal[ KararTuru: Literal[
@@ -1548,8 +1743,8 @@ async def search_rekabet_kurumu_decisions(
"Özelleştirme", "Özelleştirme",
"Rekabet İhlali" "Rekabet İhlali"
] = Field("ALL", description="Parameter description"), ] = Field("ALL", description="Parameter description"),
KararSayisi: Optional[str] = Field(None, description="Decision number (Karar Sayısı)."), KararSayisi: str = Field("", description="Decision number (Karar Sayısı)."),
KararTarihi: Optional[str] = Field(None, description="Decision date (Karar Tarihi), e.g., DD.MM.YYYY."), KararTarihi: str = Field("", description="Decision date (Karar Tarihi), e.g., DD.MM.YYYY."),
page: int = Field(1, ge=1, description="Page number to fetch for the results list.") page: int = Field(1, ge=1, description="Page number to fetch for the results list.")
) -> RekabetSearchResult: ) -> RekabetSearchResult:
"""Search Competition Authority decisions.""" """Search Competition Authority decisions."""
@@ -1582,7 +1777,7 @@ async def search_rekabet_kurumu_decisions(
return RekabetSearchResult(decisions=[], retrieved_page_number=page, total_records_found=0, total_pages=0) return RekabetSearchResult(decisions=[], retrieved_page_number=page, total_records_found=0, total_pages=0)
@app.tool( @app.tool(
description="Retrieve full text of a Competition Authority decision in paginated Markdown format. Before using, read docs://tools/rekabet", description="Get Competition Authority decision text in paginated Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1606,7 +1801,7 @@ 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="Search Yargıtay, Danıştay, Local Courts, Appeals Courts, and KYB decisions", description="Search multiple Turkish courts (Yargıtay, Danıştay, Local Courts, Appeals Courts, KYB)",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1614,7 +1809,7 @@ async def get_rekabet_kurumu_document(
} }
) )
async def search_bedesten_unified( async def search_bedesten_unified(
phrase: str = Field(..., description="Search phrase. Use \"exact phrase\" for precise matching"), phrase: str = Field(..., description="Turkish search phrase. Supports +required -excluded \"exact phrase\" operators"),
court_types: List[BedestenCourtTypeEnum] = Field( court_types: List[BedestenCourtTypeEnum] = Field(
default=["YARGITAYKARARI", "DANISTAYKARAR"], default=["YARGITAYKARARI", "DANISTAYKARAR"],
description="Court types: YARGITAYKARARI, DANISTAYKARAR, YERELHUKUK, ISTINAFHUKUK, KYB" description="Court types: YARGITAYKARARI, DANISTAYKARAR, YERELHUKUK, ISTINAFHUKUK, KYB"
@@ -1622,8 +1817,8 @@ async def search_bedesten_unified(
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"),
birimAdi: Optional[Union[YargitayBirimEnum, DanistayBirimEnum]] = Field(None, description="Chamber filter (optional)"), birimAdi: Optional[Union[YargitayBirimEnum, DanistayBirimEnum]] = Field(None, description="Chamber filter (optional)"),
kararTarihiStart: Optional[str] = Field(None, description="Start date (ISO 8601 format)"), kararTarihiStart: str = Field("", description="Start date (ISO 8601 format)"),
kararTarihiEnd: Optional[str] = Field(None, description="End date (ISO 8601 format)") kararTarihiEnd: str = Field("", description="End date (ISO 8601 format)")
) -> dict: ) -> dict:
"""Search Turkish legal databases via unified Bedesten API.""" """Search Turkish legal databases via unified Bedesten API."""
search_data = BedestenSearchData( search_data = BedestenSearchData(
@@ -1665,7 +1860,7 @@ async def search_bedesten_unified(
raise raise
@app.tool( @app.tool(
description="Retrieve legal decision document from Bedesten API in Markdown format", description="Get legal decision document from Bedesten API in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True "idempotentHint": True
@@ -1689,7 +1884,7 @@ async def get_bedesten_document_markdown(
# --- MCP Tools for Sayıştay (Turkish Court of Accounts) --- # --- MCP Tools for Sayıştay (Turkish Court of Accounts) ---
@app.tool( @app.tool(
description="Search Sayıştay Genel Kurul decisions for audit and accountability regulations. Before using, read docs://tools/sayistay", description="Search Sayıştay Genel Kurul decisions for audit and accountability regulations",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1697,11 +1892,11 @@ async def get_bedesten_document_markdown(
} }
) )
async def search_sayistay_genel_kurul( async def search_sayistay_genel_kurul(
karar_no: Optional[str] = Field(None, description="Decision number to search for (e.g., '5415')"), karar_no: str = Field("", description="Decision number to search for (e.g., '5415')"),
karar_ek: Optional[str] = Field(None, description="Decision appendix number (max 99, e.g., '1')"), karar_ek: str = Field("", description="Decision appendix number (max 99, e.g., '1')"),
karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"), karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"),
karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"), karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"),
karar_tamami: Optional[str] = Field(None, description="See docs for details"), karar_tamami: str = Field("", description="Full text search"),
start: int = Field(0, description="Starting record for pagination (0-based)"), start: int = Field(0, description="Starting record for pagination (0-based)"),
length: int = Field(10, description="Number of records per page (1-100)") length: int = Field(10, description="Number of records per page (1-100)")
) -> GenelKurulSearchResponse: ) -> GenelKurulSearchResponse:
@@ -1724,7 +1919,7 @@ async def search_sayistay_genel_kurul(
raise raise
@app.tool( @app.tool(
description="Search Sayıştay Temyiz Kurulu decisions with chamber filtering and comprehensive search criteria. Before using, read docs://tools/sayistay", description="Search Sayıştay Temyiz Kurulu decisions with chamber filtering and comprehensive criteria",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1732,16 +1927,16 @@ async def search_sayistay_genel_kurul(
} }
) )
async def search_sayistay_temyiz_kurulu( async def search_sayistay_temyiz_kurulu(
ilam_dairesi: DaireEnum = Field("ALL", description="See docs for details"), ilam_dairesi: DaireEnum = Field("ALL", description="Audit chamber selection"),
yili: Optional[str] = Field(None, description="See docs for details"), yili: str = Field("", description="Year (YYYY)"),
karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"), karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"),
karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"), karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"),
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="See docs for details"), kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"),
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)"), ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"),
dosya_no: Optional[str] = Field(None, description="File number for the case"), dosya_no: str = Field("", description="File number for the case"),
temyiz_tutanak_no: Optional[str] = Field(None, description="Appeals board meeting minutes number"), temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number"),
temyiz_karar: Optional[str] = Field(None, description="See docs for details"), temyiz_karar: str = Field("", description="Appeals decision text"),
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="See docs for details"), web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"),
start: int = Field(0, description="Starting record for pagination (0-based)"), start: int = Field(0, description="Starting record for pagination (0-based)"),
length: int = Field(10, description="Number of records per page (1-100)") length: int = Field(10, description="Number of records per page (1-100)")
) -> TemyizKuruluSearchResponse: ) -> TemyizKuruluSearchResponse:
@@ -1769,7 +1964,7 @@ async def search_sayistay_temyiz_kurulu(
raise raise
@app.tool( @app.tool(
description="Search Sayıştay Daire decisions with chamber filtering and subject categorization. Before using, read docs://tools/sayistay", description="Search Sayıştay Daire decisions with chamber filtering and subject categorization",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -1777,14 +1972,14 @@ async def search_sayistay_temyiz_kurulu(
} }
) )
async def search_sayistay_daire( async def search_sayistay_daire(
yargilama_dairesi: DaireEnum = Field("ALL", description="See docs for details"), yargilama_dairesi: DaireEnum = Field("ALL", description="Chamber selection"),
karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"), karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"),
karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"), karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"),
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)"), ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"),
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="See docs for details"), kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"),
hesap_yili: Optional[str] = Field(None, description="See docs for details"), hesap_yili: str = Field("", description="Fiscal year"),
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="See docs for details"), web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"),
web_karar_metni: Optional[str] = Field(None, description="See docs for details"), web_karar_metni: str = Field("", description="Decision text search"),
start: int = Field(0, description="Starting record for pagination (0-based)"), start: int = Field(0, description="Starting record for pagination (0-based)"),
length: int = Field(10, description="Number of records per page (1-100)") length: int = Field(10, description="Number of records per page (1-100)")
) -> DaireSearchResponse: ) -> DaireSearchResponse:
@@ -1810,7 +2005,7 @@ async def search_sayistay_daire(
raise raise
@app.tool( @app.tool(
description="Retrieve Sayıştay Genel Kurul decision document in Markdown format. Before using, read docs://tools/sayistay", description="Get Sayıştay Genel Kurul decision document in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": False, "openWorldHint": False,
@@ -1833,7 +2028,7 @@ async def get_sayistay_genel_kurul_document_markdown(
raise raise
@app.tool( @app.tool(
description="Retrieve Sayıştay Temyiz Kurulu decision document in Markdown format. Before using, read docs://tools/sayistay", description="Get Sayıştay Temyiz Kurulu decision document in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": False, "openWorldHint": False,
@@ -1856,7 +2051,7 @@ async def get_sayistay_temyiz_kurulu_document_markdown(
raise raise
@app.tool( @app.tool(
description="Retrieve Sayıştay Daire decision document in Markdown format. Before using, read docs://tools/sayistay", description="Get Sayıştay Daire decision document in Markdown format",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": False, "openWorldHint": False,
@@ -2085,7 +2280,7 @@ async def check_government_servers_health() -> Dict[str, Any]:
# --- MCP Tools for KVKK --- # --- MCP Tools for KVKK ---
@app.tool( @app.tool(
description="Search KVKK decisions using Brave Search API for data protection authority decisions. Before using, read docs://tools/kvkk", description="Search KVKK data protection authority decisions",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": True, "openWorldHint": True,
@@ -2093,7 +2288,7 @@ async def check_government_servers_health() -> Dict[str, Any]:
} }
) )
async def search_kvkk_decisions( async def search_kvkk_decisions(
keywords: str = Field(..., description="Turkish keywords for KVKK decision search"), keywords: str = Field(..., description="Turkish keywords. Supports +required -excluded \"exact phrase\" operators"),
page: int = Field(1, ge=1, le=50, description="Page number for results (1-50)."), page: int = Field(1, ge=1, le=50, description="Page number for results (1-50)."),
pageSize: int = Field(10, ge=1, le=20, description="Number of results per page (1-20).") pageSize: int = Field(10, ge=1, le=20, description="Number of results per page (1-20).")
) -> KvkkSearchResult: ) -> KvkkSearchResult:
@@ -2122,7 +2317,7 @@ async def search_kvkk_decisions(
) )
@app.tool( @app.tool(
description="Retrieve KVKK decision document in Markdown format with metadata extraction. Before using, read docs://tools/kvkk", description="Get KVKK decision document in Markdown format with metadata extraction",
annotations={ annotations={
"readOnlyHint": True, "readOnlyHint": True,
"openWorldHint": False, "openWorldHint": False,
@@ -2130,7 +2325,7 @@ async def search_kvkk_decisions(
} }
) )
async def get_kvkk_document_markdown( async def get_kvkk_document_markdown(
decision_url: str = Field(..., description="See docs for details"), decision_url: str = Field(..., description="KVKK decision URL from search results"),
page_number: Union[int, str] = Field(1, description="Page number for paginated Markdown content (1-indexed, accepts int). Default is 1 (first 5,000 characters).") page_number: Union[int, str] = Field(1, description="Page number for paginated Markdown content (1-indexed, accepts int). Default is 1 (first 5,000 characters).")
) -> KvkkDocumentMarkdown: ) -> KvkkDocumentMarkdown:
"""Get KVKK decision as paginated Markdown.""" """Get KVKK decision as paginated Markdown."""
@@ -2499,6 +2694,8 @@ async def fetch(
logger.exception(f"Error fetching ChatGPT Deep Research document {id}") logger.exception(f"Error fetching ChatGPT Deep Research document {id}")
raise raise
# --- Token Metrics Tool Removed for Optimization ---
def ensure_playwright_browsers(): def ensure_playwright_browsers():
"""Ensure Playwright browsers are installed for KIK tool functionality.""" """Ensure Playwright browsers are installed for KIK tool functionality."""
try: try:
+1
View File
@@ -30,6 +30,7 @@ dependencies = [
"pypdf>=5.5.0", "pypdf>=5.5.0",
"fastapi>=0.115.14", "fastapi>=0.115.14",
"PyJWT>=2.8.0", "PyJWT>=2.8.0",
"tiktoken>=0.5.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]
+16 -16
View File
@@ -25,31 +25,31 @@ class RekabetKararTuruAdiEnum(str, Enum):
class RekabetKurumuSearchRequest(BaseModel): class RekabetKurumuSearchRequest(BaseModel):
"""Model for Rekabet Kurumu (Turkish Competition Authority) search request.""" """Model for Rekabet Kurumu (Turkish Competition Authority) search request."""
sayfaAdi: Optional[str] = Field(None, description="Title") sayfaAdi: str = Field("", description="Title")
YayinlanmaTarihi: Optional[str] = Field(None, description="Date") YayinlanmaTarihi: str = Field("", description="Date")
PdfText: Optional[str] = Field(None, description="Text") PdfText: str = Field("", description="Text")
KararTuruID: Optional[RekabetKararTuruGuidEnum] = Field(RekabetKararTuruGuidEnum.TUMU, description="Type") KararTuruID: RekabetKararTuruGuidEnum = Field(RekabetKararTuruGuidEnum.TUMU, description="Type")
KararSayisi: Optional[str] = Field(None, description="No") KararSayisi: str = Field("", description="No")
KararTarihi: Optional[str] = Field(None, description="Date") KararTarihi: str = Field("", description="Date")
page: int = Field(1, ge=1, description="Page") page: int = Field(1, ge=1, description="Page")
class RekabetDecisionSummary(BaseModel): class RekabetDecisionSummary(BaseModel):
"""Model for a single Rekabet Kurumu decision summary from search results.""" """Model for a single Rekabet Kurumu decision summary from search results."""
publication_date: Optional[str] = Field(None, description="Pub date") publication_date: str = Field("", description="Pub date")
decision_number: Optional[str] = Field(None, description="Number") decision_number: str = Field("", description="Number")
decision_date: Optional[str] = Field(None, description="Date") decision_date: str = Field("", description="Date")
decision_type_text: Optional[str] = Field(None, description="Type") decision_type_text: str = Field("", description="Type")
title: Optional[str] = Field(None, description="Title") title: str = Field("", description="Title")
decision_url: Optional[HttpUrl] = Field(None, description="URL") decision_url: str = Field("", description="URL")
karar_id: Optional[str] = Field(None, description="ID") karar_id: str = Field("", description="ID")
related_cases_url: Optional[HttpUrl] = Field(None, description="Cases URL") related_cases_url: str = Field("", description="Cases URL")
class RekabetSearchResult(BaseModel): class RekabetSearchResult(BaseModel):
"""Model for the overall search result for Rekabet Kurumu decisions.""" """Model for the overall search result for Rekabet Kurumu decisions."""
decisions: List[RekabetDecisionSummary] decisions: List[RekabetDecisionSummary]
total_records_found: Optional[int] = Field(None, description="Total") total_records_found: int = Field(0, description="Total")
retrieved_page_number: int = Field(description="Page") retrieved_page_number: int = Field(description="Page")
total_pages: Optional[int] = Field(None, description="Pages") total_pages: int = Field(0, description="Pages")
class RekabetDocument(BaseModel): class RekabetDocument(BaseModel):
""" """
+21 -21
View File
@@ -16,14 +16,14 @@ class GenelKurulSearchRequest(BaseModel):
of the Turkish Court of Accounts, typically addressing interpretation of of the Turkish Court of Accounts, typically addressing interpretation of
audit and accountability regulations. audit and accountability regulations.
""" """
karar_no: Optional[str] = Field(None, description="Decision no") karar_no: str = Field("", description="Decision no")
karar_ek: Optional[str] = Field(None, description="Appendix no") karar_ek: str = Field("", description="Appendix no")
karar_tarih_baslangic: Optional[str] = Field(None, description="Start year (YYYY)") karar_tarih_baslangic: str = Field("", description="Start year (YYYY)")
karar_tarih_bitis: Optional[str] = Field(None, description="End year") karar_tarih_bitis: str = Field("", description="End year")
karar_tamami: Optional[str] = Field(None, description="Value") karar_tamami: str = Field("", description="Value")
# DataTables pagination # DataTables pagination
start: int = Field(0, description="Starting record for pagination (0-based)") start: int = Field(0, description="Starting record for pagination (0-based)")
@@ -56,19 +56,19 @@ class TemyizKuruluSearchRequest(BaseModel):
""" """
ilam_dairesi: DaireEnum = Field("ALL", description="Value") ilam_dairesi: DaireEnum = Field("ALL", description="Value")
yili: Optional[str] = Field(None, description="Value") yili: str = Field("", description="Value")
karar_tarih_baslangic: Optional[str] = Field(None, description="Value") karar_tarih_baslangic: str = Field("", description="Value")
karar_tarih_bitis: Optional[str] = Field(None, description="End year") karar_tarih_bitis: str = Field("", description="End year")
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Value") kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Value")
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)") ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)")
dosya_no: Optional[str] = Field(None, description="File number for the case") dosya_no: str = Field("", description="File number for the case")
temyiz_tutanak_no: Optional[str] = Field(None, description="Appeals board meeting minutes number") temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number")
temyiz_karar: Optional[str] = Field(None, description="Value") temyiz_karar: str = Field("", description="Value")
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Value") web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Value")
@@ -103,19 +103,19 @@ class DaireSearchRequest(BaseModel):
""" """
yargilama_dairesi: DaireEnum = Field("ALL", description="Value") yargilama_dairesi: DaireEnum = Field("ALL", description="Value")
karar_tarih_baslangic: Optional[str] = Field(None, description="Value") karar_tarih_baslangic: str = Field("", description="Value")
karar_tarih_bitis: Optional[str] = Field(None, description="End year") karar_tarih_bitis: str = Field("", description="End year")
ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)") ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)")
kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Value") kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Value")
hesap_yili: Optional[str] = Field(None, description="Value") hesap_yili: str = Field("", description="Value")
web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Value") web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Value")
web_karar_metni: Optional[str] = Field(None, description="Value") web_karar_metni: str = Field("", description="Value")
# DataTables pagination # DataTables pagination
start: int = Field(0, description="Starting record for pagination (0-based)") start: int = Field(0, description="Starting record for pagination (0-based)")
@@ -127,7 +127,7 @@ class DaireDecision(BaseModel):
yargilama_dairesi: int = Field(..., description="Chamber number (1-8)") yargilama_dairesi: int = Field(..., description="Chamber number (1-8)")
karar_tarih: str = Field(..., description="Decision date in DD.MM.YYYY format") karar_tarih: str = Field(..., description="Decision date in DD.MM.YYYY format")
karar_no: str = Field(..., description="Decision number") karar_no: str = Field(..., description="Decision number")
ilam_no: Optional[str] = Field(None, description="Audit report number (may be null)") ilam_no: str = Field("", description="Audit report number (may be null)")
madde_no: int = Field(..., description="Article/item number within the decision") madde_no: int = Field(..., description="Article/item number within the decision")
kamu_idaresi_turu: str = Field(..., description="Public administration type") kamu_idaresi_turu: str = Field(..., description="Public administration type")
hesap_yili: int = Field(..., description="Account year being audited") hesap_yili: int = Field(..., description="Account year being audited")
@@ -155,6 +155,6 @@ class SayistayDocumentMarkdown(BaseModel):
decision_id: str = Field(..., description="Unique decision identifier") decision_id: str = Field(..., description="Unique decision identifier")
decision_type: str = Field(..., description="Value") decision_type: str = Field(..., description="Value")
source_url: str = Field(..., description="Original URL where the document was retrieved") source_url: str = Field(..., description="Original URL where the document was retrieved")
markdown_content: Optional[str] = Field(None, description="Full decision text converted to Markdown format") markdown_content: str = Field("", description="Full decision text converted to Markdown format")
retrieval_date: Optional[str] = Field(None, description="Date when document was retrieved (ISO format)") retrieval_date: str = Field("", description="Date when document was retrieved (ISO format)")
error_message: Optional[str] = Field(None, description="Error message if document retrieval failed") error_message: str = Field("", description="Error message if document retrieval failed")
-4
View File
@@ -37,8 +37,6 @@ class YargitayDetailedSearchRequest(BaseModel):
arananKelime: Optional[str] = Field("", description="Turkish keywords (supports +word -word \"phrase\" operators)") arananKelime: Optional[str] = Field("", description="Turkish keywords (supports +word -word \"phrase\" operators)")
# Department/Board selection - Complete Court of Cassation chamber hierarchy # Department/Board selection - Complete Court of Cassation chamber hierarchy
birimYrgKurulDaire: Optional[str] = Field("ALL", description="Chamber (ALL or specific chamber name)") birimYrgKurulDaire: Optional[str] = Field("ALL", description="Chamber (ALL or specific chamber name)")
birimYrgHukukDaire: Optional[str] = Field("", description="Legacy field")
birimYrgCezaDaire: Optional[str] = Field("", description="Legacy field")
esasYil: Optional[str] = Field("", description="Case year (YYYY)") esasYil: Optional[str] = Field("", description="Case year (YYYY)")
esasIlkSiraNo: Optional[str] = Field("", description="Start case no") esasIlkSiraNo: Optional[str] = Field("", description="Start case no")
@@ -51,8 +49,6 @@ class YargitayDetailedSearchRequest(BaseModel):
baslangicTarihi: Optional[str] = Field("", description="Start date (DD.MM.YYYY)") baslangicTarihi: Optional[str] = Field("", description="Start date (DD.MM.YYYY)")
bitisTarihi: Optional[str] = Field("", description="End date (DD.MM.YYYY)") bitisTarihi: Optional[str] = Field("", description="End date (DD.MM.YYYY)")
siralama: Optional[str] = Field("3", description="Sort by (1=case, 2=decision, 3=date)")
siralamaDirection: Optional[str] = Field("desc", description="Direction (asc/desc)")
pageSize: int = Field(10, ge=1, le=10, description="Results per page (1-100)") pageSize: int = Field(10, ge=1, le=10, description="Results per page (1-100)")
pageNumber: int = Field(1, ge=1, description="Page number (1-indexed)") pageNumber: int = Field(1, ge=1, description="Page number (1-indexed)")