diff --git a/.gitignore b/.gitignore index f0fc7a1..e996eb1 100644 --- a/.gitignore +++ b/.gitignore @@ -192,3 +192,23 @@ scripts/deploy-flyio.sh docs/DEPLOYMENT_FLYIO.md setup_jwt_template.py 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 diff --git a/anayasa_mcp_module/models.py b/anayasa_mcp_module/models.py index d9bdd77..904004e 100644 --- a/anayasa_mcp_module/models.py +++ b/anayasa_mcp_module/models.py @@ -90,59 +90,59 @@ class AnayasaNormDenetimiSearchRequest(BaseModel): 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[]).") 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'.") - decision_number_karar: Optional[str] = Field(None, 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_end: Optional[str] = Field(None, 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_end: Optional[str] = Field(None, description="Decision end date (KararTarihiSon), format DD/MM/YYYY.") + case_number_esas: str = Field("", description="Case registry number (EsasNo), e.g., '2023/123'.") + decision_number_karar: str = Field("", description="Decision number (KararNo), e.g., '2023/456'.") + first_review_date_start: str = Field("", description="First review start date (IlkIncelemeTarihiIlk), format DD/MM/YYYY.") + first_review_date_end: str = Field("", description="First review end date (IlkIncelemeTarihiSon), format DD/MM/YYYY.") + decision_date_start: str = Field("", description="Decision start date (KararTarihiIlk), 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).") - applicant_general_name: Optional[str] = Field(None, description="General applicant name (BasvuranGeneller_id).") - applicant_specific_name: Optional[str] = Field(None, 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_end: Optional[str] = Field(None, 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_end: Optional[str] = Field(None, description="Official Gazette ending number (ResmiGazeteSayisiSon).") + applicant_general_name: str = Field("", description="General applicant name (BasvuranGeneller_id).") + applicant_specific_name: str = Field("", description="Specific applicant name (BasvuranOzeller_id).") + official_gazette_date_start: str = Field("", description="Official Gazette start date (ResmiGazeteTarihiIlk), format DD/MM/YYYY.") + official_gazette_date_end: str = Field("", description="Official Gazette end date (ResmiGazeteTarihiSon), format DD/MM/YYYY.") + official_gazette_number_start: str = Field("", description="Official Gazette starting number (ResmiGazeteSayisiIlk).") + 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_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).") 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_id_or_name: Optional[str] = Field(None, description="Number or name of the norm (NormunNumarasiAdlar_id).") - norm_article: Optional[str] = Field(None, description="Article number of the norm (NormunMaddeNumarasi).") + norm_id_or_name: str = Field("", description="Number or name of the norm (NormunNumarasiAdlar_id).") + 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[]).") 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[]).") - results_per_page: Optional[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.") - sort_by_criteria: Optional[str] = Field("KararTarihi", description="Sort criteria. Options: 'KararTarihi', 'YayinTarihi', 'Toplam' (keyword count).") + results_per_page: int = Field(10, ge=1, le=10, description="Results per page.") + page_to_fetch: int = Field(1, ge=1, description="Page number to fetch for results list.") + sort_by_criteria: str = Field("KararTarihi", description="Sort criteria. Options: 'KararTarihi', 'YayinTarihi', 'Toplam' (keyword count).") class AnayasaReviewedNormInfo(BaseModel): """Details of a norm reviewed within an AYM decision summary.""" - norm_name_or_number: Optional[str] = None - article_number: Optional[str] = None - review_type_and_outcome: Optional[str] = None - outcome_reason: Optional[str] = None + norm_name_or_number: str = Field("", description="Norm name or number") + article_number: str = Field("", description="Article number") + review_type_and_outcome: str = Field("", description="Review type and outcome") + outcome_reason: str = Field("", description="Outcome reason") 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): """Model for a single Anayasa Mahkemesi (Norm Denetimi) decision summary from search results.""" - decision_reference_no: Optional[str] = None - decision_page_url: Optional[HttpUrl] = None - keywords_found_count: Optional[int] = None - application_type_summary: Optional[str] = None - applicant_summary: Optional[str] = None - decision_outcome_summary: Optional[str] = None - decision_date_summary: Optional[str] = None + decision_reference_no: str = Field("", description="Decision reference number") + decision_page_url: str = Field("", description="Decision page URL") + keywords_found_count: int = Field(0, description="Keywords found count") + application_type_summary: str = Field("", description="Application type summary") + applicant_summary: str = Field("", description="Applicant summary") + decision_outcome_summary: str = Field("", description="Decision outcome summary") + decision_date_summary: str = Field("", description="Decision date summary") reviewed_norms: List[AnayasaReviewedNormInfo] = Field(default_factory=list) class AnayasaSearchResult(BaseModel): """Model for the overall search result for Anayasa Mahkemesi Norm Denetimi decisions.""" decisions: List[AnayasaDecisionSummary] - total_records_found: Optional[int] = None - retrieved_page_number: Optional[int] = None + total_records_found: int = Field(0, description="Total records found") + retrieved_page_number: int = Field(1, description="Retrieved page number") class AnayasaDocumentMarkdown(BaseModel): """ @@ -150,10 +150,10 @@ class AnayasaDocumentMarkdown(BaseModel): and pagination information. """ source_url: HttpUrl - decision_reference_no_from_page: Optional[str] = Field(None, 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.") - official_gazette_info_from_page: Optional[str] = Field(None, 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 + decision_reference_no_from_page: str = Field("", description="E.K. No parsed from the document page.") + decision_date_from_page: str = Field("", description="Decision date parsed from the document page.") + official_gazette_info_from_page: str = Field("", description="Official Gazette info parsed from the document page.") + 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).") 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.") @@ -168,27 +168,27 @@ class AnayasaBireyselReportSearchRequest(BaseModel): class AnayasaBireyselReportDecisionDetail(BaseModel): """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ı).") - mudahale_iddiasi: Optional[str] = Field(None, description="İhlale neden olan müdahale iddiası.") - sonuc: Optional[str] = Field(None, description="İnceleme sonucu (örneğin, İhlal, Düşme).") - giderim: Optional[str] = Field(None, description="Kararlaştırılan giderim (örneğin, Yeniden yargılama).") + hak: str = Field("", description="İhlal edildiği iddia edilen hak (örneğin, Mülkiyet hakkı).") + mudahale_iddiasi: str = Field("", description="İhlale neden olan müdahale iddiası.") + sonuc: str = Field("", description="İnceleme sonucu (örneğin, İhlal, Düşme).") + giderim: str = Field("", description="Kararlaştırılan giderim (örneğin, Yeniden yargılama).") class AnayasaBireyselReportDecisionSummary(BaseModel): """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').") - decision_reference_no: Optional[str] = Field(None, description="Başvuru Numarası (e.g., '2019/19126').") - decision_page_url: Optional[HttpUrl] = Field(None, 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_making_body: Optional[str] = Field(None, 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).") - decision_date_summary: Optional[str] = Field(None, description="Karar Tarihi (DD/MM/YYYY).") - application_subject_summary: Optional[str] = Field(None, description="Başvuru konusunun özeti.") + title: str = Field("", description="Başvurunun başlığı (e.g., 'HASAN DURMUŞ Başvurusuna İlişkin Karar').") + decision_reference_no: str = Field("", description="Başvuru Numarası (e.g., '2019/19126').") + decision_page_url: str = Field("", description="URL to the full decision page.") + decision_type_summary: str = Field("", description="Karar Türü (Başvuru Sonucu) (e.g., 'Esas (İhlal)').") + decision_making_body: str = Field("", description="Kararı Veren Birim (e.g., 'Genel Kurul', 'Birinci Bölüm').") + application_date_summary: str = Field("", description="Başvuru Tarihi (DD/MM/YYYY).") + decision_date_summary: str = Field("", description="Karar Tarihi (DD/MM/YYYY).") + 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.") class AnayasaBireyselReportSearchResult(BaseModel): """Model for the overall search result for Anayasa Mahkemesi 'Karar Arama Raporu'.""" 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ı.") diff --git a/danistay_mcp_module/client.py b/danistay_mcp_module/client.py index ae03b40..e3d6883 100644 --- a/danistay_mcp_module/client.py +++ b/danistay_mcp_module/client.py @@ -76,8 +76,8 @@ class DanistayApiClient: mevzuatNumarasi=params.mevzuatNumarasi or "", mevzuatAdi=params.mevzuatAdi or "", madde=params.madde or "", - siralama=params.siralama, - siralamaDirection=params.siralamaDirection, + siralama="1", + siralamaDirection="desc", pageSize=params.pageSize, pageNumber=params.pageNumber ) diff --git a/danistay_mcp_module/models.py b/danistay_mcp_module/models.py index 0529aaa..8c657a9 100644 --- a/danistay_mcp_module/models.py +++ b/danistay_mcp_module/models.py @@ -51,20 +51,18 @@ class DanistayDetailedSearchRequestData(BaseModel): # Internal data model for de class DanistayDetailedSearchRequest(DanistayBaseSearchRequest): # MCP tool will accept this """Model for detailed search request for Danistay.""" - daire: Optional[str] = Field(None, description="Chamber") - esasYil: Optional[str] = Field(None, description="Case year") - esasIlkSiraNo: Optional[str] = Field(None, description="Start case no") - esasSonSiraNo: Optional[str] = Field(None, description="End case no") - kararYil: Optional[str] = Field(None, description="Decision year") - kararIlkSiraNo: Optional[str] = Field(None, description="Start decision no") - kararSonSiraNo: Optional[str] = Field(None, description="End decision no") - baslangicTarihi: Optional[str] = Field(None, description="Start date") - bitisTarihi: Optional[str] = Field(None, description="End date") - mevzuatNumarasi: Optional[str] = Field(None, description="Law number") - mevzuatAdi: Optional[str] = Field(None, description="Law name") - madde: Optional[str] = Field(None, description="Article") - siralama: str = Field("1", description="Sort by") - siralamaDirection: str = Field("desc", description="Direction") + daire: str = Field("", description="Chamber") + esasYil: str = Field("", description="Case year") + esasIlkSiraNo: str = Field("", description="Start case no") + esasSonSiraNo: str = Field("", description="End case no") + kararYil: str = Field("", description="Decision year") + kararIlkSiraNo: str = Field("", description="Start decision no") + kararSonSiraNo: str = Field("", description="End decision no") + baslangicTarihi: str = Field("", description="Start date") + bitisTarihi: str = Field("", description="End date") + mevzuatNumarasi: str = Field("", description="Law number") + mevzuatAdi: str = Field("", description="Law name") + madde: str = Field("", description="Article") # Add a general keyword field if detailed search also supports it # arananKelime: Optional[str] = Field(None, description="General keyword for detailed search.") @@ -76,11 +74,11 @@ class DanistayApiDecisionEntry(BaseModel): id: str # 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". - chamber: Optional[str] = Field(None, alias="daire", description="Chamber") - esasNo: Optional[str] = Field(None) - kararNo: Optional[str] = Field(None) - kararTarihi: Optional[str] = Field(None) - arananKelime: Optional[str] = Field(None, description="Keyword") + chamber: str = Field("", alias="daire", description="Chamber") + esasNo: str = Field("", description="Case number") + kararNo: str = Field("", description="Decision number") + kararTarihi: str = Field("", description="Decision date") + arananKelime: str = Field("", description="Keyword") # 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 @@ -93,7 +91,7 @@ class DanistayApiResponseInnerData(BaseModel): data: List[DanistayApiDecisionEntry] recordsTotal: int recordsFiltered: int - draw: Optional[int] = Field(None, description="Draw counter") + draw: int = Field(0, description="Draw counter") class DanistayApiResponse(BaseModel): """Model for the complete search response from the Danistay API.""" @@ -103,7 +101,7 @@ class DanistayApiResponse(BaseModel): class DanistayDocumentMarkdown(BaseModel): """Model for a Danistay decision document, containing only Markdown content.""" 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 class CompactDanistaySearchResult(BaseModel): diff --git a/emsal_mcp_module/models.py b/emsal_mcp_module/models.py index fc54d49..c94f0dd 100644 --- a/emsal_mcp_module/models.py +++ b/emsal_mcp_module/models.py @@ -12,8 +12,8 @@ class EmsalDetailedSearchRequestData(BaseModel): """ arananKelime: Optional[str] = "" - Bam_Hukuk_Mahkemeleri: Optional[str] = Field(None, alias="Bam Hukuk Mahkemeleri") - Hukuk_Mahkemeleri: Optional[str] = Field(None, alias="Hukuk Mahkemeleri") + Bam_Hukuk_Mahkemeleri: str = Field("", alias="Bam 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 # 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 """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_civil_court: Optional[str] = Field(None, description="Civil Court") - selected_regional_civil_chambers: Optional[List[str]] = Field(default_factory=list, description="Regional chambers") + selected_bam_civil_court: str = Field("", description="BAM Civil Court") + selected_civil_court: str = Field("", description="Civil Court") + selected_regional_civil_chambers: List[str] = Field(default_factory=list, description="Regional chambers") - case_year_esas: Optional[str] = Field(None, description="Case year") - case_start_seq_esas: Optional[str] = Field(None, description="Start case no") - case_end_seq_esas: Optional[str] = Field(None, description="End case no") + case_year_esas: str = Field("", description="Case year") + case_start_seq_esas: str = Field("", description="Start case no") + case_end_seq_esas: str = Field("", description="End case no") - decision_year_karar: Optional[str] = Field(None, description="Decision year") - decision_start_seq_karar: Optional[str] = Field(None, description="Start decision no") - decision_end_seq_karar: Optional[str] = Field(None, description="End decision no") + decision_year_karar: str = Field("", description="Decision year") + decision_start_seq_karar: str = Field("", description="Start decision no") + decision_end_seq_karar: str = Field("", description="End decision no") - start_date: Optional[str] = Field(None, description="Start date (DD.MM.YYYY)") - end_date: Optional[str] = Field(None, description="End date (DD.MM.YYYY)") + start_date: str = Field("", description="Start date (DD.MM.YYYY)") + end_date: str = Field("", description="End date (DD.MM.YYYY)") sort_criteria: str = Field("1", description="Sort by") 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): """Model for an individual decision entry from the Emsal API search response.""" id: str - daire: Optional[str] = Field(None, description="Chamber") - esasNo: Optional[str] = Field(None) - kararNo: Optional[str] = Field(None) - kararTarihi: Optional[str] = Field(None) - arananKelime: Optional[str] = Field(None, description="Keyword") - durum: Optional[str] = Field(None, description="Status") + daire: str = Field("", description="Chamber") + esasNo: str = Field("", description="Case number") + kararNo: str = Field("", description="Decision number") + kararTarihi: str = Field("", description="Decision date") + arananKelime: str = Field("", description="Keyword") + durum: str = Field("", description="Status") # index: Optional[int] = None # Present in Emsal response, can be added if tool needs it document_url: Optional[HttpUrl] = Field(None, description="Document URL") @@ -80,7 +80,7 @@ class EmsalApiResponseInnerData(BaseModel): data: List[EmsalApiDecisionEntry] recordsTotal: 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): """Model for the complete search response from the Emsal API.""" @@ -90,7 +90,7 @@ class EmsalApiResponse(BaseModel): class EmsalDocumentMarkdown(BaseModel): """Model for an Emsal decision document, containing only Markdown content.""" 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 class CompactEmsalSearchResult(BaseModel): diff --git a/kik_mcp_module/models.py b/kik_mcp_module/models.py index 395f84a..6b6632b 100644 --- a/kik_mcp_module/models.py +++ b/kik_mcp_module/models.py @@ -13,16 +13,16 @@ class KikKararTipi(str, Enum): class KikSearchRequest(BaseModel): """Model for KIK Decision search criteria.""" karar_tipi: KikKararTipi = Field(KikKararTipi.UYUSMAZLIK, description="Type") - karar_no: Optional[str] = Field(None, description="No") - karar_tarihi_baslangic: Optional[str] = Field(None, 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}$") - resmi_gazete_sayisi: Optional[str] = Field(None, description="Gazette") - resmi_gazete_tarihi: Optional[str] = Field(None, description="Date", pattern=r"^\d{2}\.\d{2}\.\d{4}$") - basvuru_konusu_ihale: Optional[str] = Field(None, description="Subject") - basvuru_sahibi: Optional[str] = Field(None, description="Applicant") - ihaleyi_yapan_idare: Optional[str] = Field(None, description="Entity") - yil: Optional[str] = Field(None, description="Year") - karar_metni: Optional[str] = Field(None, description="Text") + karar_no: str = Field("", description="No") + karar_tarihi_baslangic: str = Field("", description="Start", 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: str = Field("", description="Gazette") + resmi_gazete_tarihi: str = Field("", description="Date", pattern=r"^\d{2}\.\d{2}\.\d{4}$|^$") + basvuru_konusu_ihale: str = Field("", description="Subject") + basvuru_sahibi: str = Field("", description="Applicant") + ihaleyi_yapan_idare: str = Field("", description="Entity") + yil: str = Field("", description="Year") + karar_metni: str = Field("", description="Text") page: int = Field(1, ge=1, description="Page") class KikDecisionEntry(BaseModel): @@ -32,9 +32,9 @@ class KikDecisionEntry(BaseModel): karar_tipi: KikKararTipi = Field(..., description="Decision type") karar_tarihi_str: str = Field(..., alias="kararTarihi", description="Date") - idare_str: Optional[str] = Field(None, alias="idare", description="Entity") - basvuru_sahibi_str: Optional[str] = Field(None, alias="basvuruSahibi", description="Applicant") - ihale_konusu_str: Optional[str] = Field(None, alias="ihaleKonusu", description="Subject") + idare_str: str = Field("", alias="idare", description="Entity") + basvuru_sahibi_str: str = Field("", alias="basvuruSahibi", description="Applicant") + ihale_konusu_str: str = Field("", alias="ihaleKonusu", description="Subject") @computed_field @property diff --git a/mcp_server_main.py b/mcp_server_main.py index 570f02e..6f093bf 100644 --- a/mcp_server_main.py +++ b/mcp_server_main.py @@ -4,9 +4,14 @@ import atexit import logging import os import httpx +import json +import time +from collections import defaultdict from pydantic import HttpUrl, Field from typing import Optional, Dict, List, Literal, Any, Union import urllib.parse +import tiktoken +from fastmcp.server.middleware import Middleware, MiddlewareContext # --- Logging Configuration Start --- 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__) # --- 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 from fastmcp import FastMCP @@ -113,6 +309,11 @@ from kvkk_mcp_module.models import ( 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 --- @app.resource("docs://tools/yargitay") async def get_yargitay_tools_documentation() -> str: @@ -863,7 +1064,7 @@ KARAR_TURU_ADI_TO_GUID_ENUM_MAP = { # --- MCP Tools for Yargitay --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -871,10 +1072,8 @@ KARAR_TURU_ADI_TO_GUID_ENUM_MAP = { } ) 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)"), - 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'."), esasIlkSiraNo: str = Field("", description="Starting 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'."), baslangicTarihi: str = Field("", description="Start 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."), pageNumber: int = Field(1, ge=1, description="Page number to retrieve.") ) -> CompactYargitaySearchResult: @@ -897,8 +1094,6 @@ async def search_yargitay_detailed( search_query = YargitayDetailedSearchRequest( arananKelime=arananKelime, birimYrgKurulDaire=birimYrgKurulDaire, - birimYrgHukukDaire=birimYrgHukukDaire, - birimYrgCezaDaire=birimYrgCezaDaire, esasYil=esasYil, esasIlkSiraNo=esasIlkSiraNo, esasSonSiraNo=esasSonSiraNo, @@ -907,8 +1102,8 @@ async def search_yargitay_detailed( kararSonSiraNo=kararSonSiraNo, baslangicTarihi=baslangicTarihi, bitisTarihi=bitisTarihi, - siralama=siralama, - siralamaDirection=siralamaDirection, + siralama="3", + siralamaDirection="desc", pageSize=pageSize, pageNumber=pageNumber ) @@ -941,7 +1136,7 @@ async def search_yargitay_detailed( raise @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={ "readOnlyHint": True, "idempotentHint": True @@ -959,7 +1154,7 @@ async def get_yargitay_document_markdown(id: str) -> YargitayDocumentMarkdown: # --- MCP Tools for Danistay --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1001,7 +1196,7 @@ async def search_danistay_by_keyword( raise @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1009,20 +1204,18 @@ async def search_danistay_by_keyword( } ) async def search_danistay_detailed( - daire: Optional[str] = Field(None, description="Chamber/Department name (e.g., '1. Daire')."), - esasYil: Optional[str] = Field(None, description="Case year for 'Esas No'."), - esasIlkSiraNo: Optional[str] = Field(None, description="Starting sequence for 'Esas No'."), - esasSonSiraNo: Optional[str] = Field(None, description="Ending sequence for 'Esas No'."), - kararYil: Optional[str] = Field(None, description="Decision year for 'Karar No'."), - kararIlkSiraNo: Optional[str] = Field(None, description="Starting sequence for 'Karar No'."), - kararSonSiraNo: Optional[str] = Field(None, description="Ending sequence for 'Karar No'."), - baslangicTarihi: Optional[str] = Field(None, description="Start date for decision (DD.MM.YYYY)."), - bitisTarihi: Optional[str] = Field(None, description="End date for decision (DD.MM.YYYY)."), - mevzuatNumarasi: Optional[str] = Field(None, description="Legislation number."), - mevzuatAdi: Optional[str] = Field(None, description="Legislation name."), - madde: Optional[str] = Field(None, 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')."), + daire: str = Field("", description="Chamber/Department name (e.g., '1. Daire')."), + esasYil: str = Field("", description="Case year for 'Esas No'."), + esasIlkSiraNo: str = Field("", description="Starting sequence for 'Esas No'."), + esasSonSiraNo: str = Field("", description="Ending sequence for 'Esas No'."), + kararYil: str = Field("", description="Decision year for 'Karar No'."), + kararIlkSiraNo: str = Field("", description="Starting sequence for 'Karar No'."), + kararSonSiraNo: str = Field("", description="Ending sequence for 'Karar No'."), + baslangicTarihi: str = Field("", description="Start date for decision (DD.MM.YYYY)."), + bitisTarihi: str = Field("", description="End date for decision (DD.MM.YYYY)."), + mevzuatNumarasi: str = Field("", description="Legislation number."), + mevzuatAdi: str = Field("", description="Legislation name."), + madde: str = Field("", description="Article number."), pageNumber: int = Field(1, ge=1, description="Page number."), pageSize: int = Field(10, ge=1, le=10, description="Results per page.") ) -> CompactDanistaySearchResult: @@ -1041,8 +1234,8 @@ async def search_danistay_detailed( mevzuatNumarasi=mevzuatNumarasi, mevzuatAdi=mevzuatAdi, madde=madde, - siralama=siralama, - siralamaDirection=siralamaDirection, + siralama="3", + siralamaDirection="desc", pageNumber=pageNumber, pageSize=pageSize ) @@ -1063,7 +1256,7 @@ async def search_danistay_detailed( raise @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={ "readOnlyHint": True, "idempotentHint": True @@ -1081,7 +1274,7 @@ async def get_danistay_document_markdown(id: str) -> DanistayDocumentMarkdown: # --- MCP Tools for Emsal --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1089,18 +1282,18 @@ async def get_danistay_document_markdown(id: str) -> DanistayDocumentMarkdown: } ) async def search_emsal_detailed_decisions( - keyword: Optional[str] = Field(None, description="Keyword to search."), - selected_bam_civil_court: Optional[str] = Field(None, description="Selected BAM Civil Court."), - selected_civil_court: Optional[str] = Field(None, description="Selected Civil Court."), + keyword: str = Field("", description="Keyword to search."), + selected_bam_civil_court: str = Field("", description="Selected BAM 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."), - case_year_esas: Optional[str] = Field(None, description="Case year for 'Esas No'."), - case_start_seq_esas: Optional[str] = Field(None, description="Starting sequence for 'Esas No'."), - case_end_seq_esas: Optional[str] = Field(None, description="Ending sequence for 'Esas No'."), - decision_year_karar: Optional[str] = Field(None, description="Decision year for 'Karar No'."), - decision_start_seq_karar: Optional[str] = Field(None, description="Starting sequence for 'Karar No'."), - decision_end_seq_karar: Optional[str] = Field(None, description="Ending sequence for 'Karar No'."), - start_date: Optional[str] = Field(None, description="Start date for decision (DD.MM.YYYY)."), - end_date: Optional[str] = Field(None, description="End date for decision (DD.MM.YYYY)."), + case_year_esas: str = Field("", description="Case year for 'Esas No'."), + case_start_seq_esas: str = Field("", description="Starting sequence for 'Esas No'."), + case_end_seq_esas: str = Field("", description="Ending sequence for 'Esas No'."), + decision_year_karar: str = Field("", description="Decision year for 'Karar No'."), + decision_start_seq_karar: str = Field("", description="Starting sequence for 'Karar No'."), + decision_end_seq_karar: str = Field("", description="Ending sequence for 'Karar No'."), + start_date: str = Field("", description="Start 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_direction: str = Field("desc", description="Sorting direction ('asc' or 'desc')."), page_number: int = Field(1, ge=1, description="Page number (accepts int)."), @@ -1144,7 +1337,7 @@ async def search_emsal_detailed_decisions( raise @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={ "readOnlyHint": True, "idempotentHint": True @@ -1162,7 +1355,7 @@ async def get_emsal_document_markdown(id: str) -> EmsalDocumentMarkdown: # --- MCP Tools for Uyusmazlik --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1233,7 +1426,7 @@ async def search_uyusmazlik_decisions( raise @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={ "readOnlyHint": True, "idempotentHint": True @@ -1254,7 +1447,7 @@ async def get_uyusmazlik_document_markdown_from_url( # --- MCP Tools for Anayasa Mahkemesi (Norm Denetimi) --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1262,34 +1455,34 @@ async def get_uyusmazlik_document_markdown_from_url( } ) async def search_anayasa_norm_denetimi_decisions( - keywords_all: List[str] = Field(default_factory=list, description="AND keywords"), - keywords_any: List[str] = Field(default_factory=list, description="OR keywords"), - keywords_exclude: List[str] = Field(default_factory=list, description="Exclude keywords"), + keywords_all: List[str] = Field(default_factory=list, description="AND"), + keywords_any: List[str] = Field(default_factory=list, description="OR"), + keywords_exclude: List[str] = Field(default_factory=list, description="NOT"), period: Literal["ALL", "1", "2"] = Field("ALL", description="Period"), - case_number_esas: Optional[str] = Field(None, description="Case number"), - decision_number_karar: Optional[str] = Field(None, description="Decision number"), - first_review_date_start: Optional[str] = Field(None, description="Review start date"), - first_review_date_end: Optional[str] = Field(None, description="Review end date"), - decision_date_start: Optional[str] = Field(None, description="Decision start"), - decision_date_end: Optional[str] = Field(None, description="Decision end"), + case_number_esas: str = Field("", description="Case number"), + decision_number_karar: str = Field("", description="Decision number"), + first_review_date_start: str = Field("", description="Start"), + first_review_date_end: str = Field("", description="End"), + decision_date_start: str = Field("", description="Start"), + decision_date_end: str = Field("", description="End"), application_type: Literal["ALL", "1", "2", "3"] = Field("ALL", description="App type"), - applicant_general_name: Optional[str] = Field(None, description="Applicant"), - applicant_specific_name: Optional[str] = Field(None, description="Specific name"), - official_gazette_date_start: Optional[str] = Field(None, description="Gazette start"), - official_gazette_date_end: Optional[str] = Field(None, description="Gazette end"), - official_gazette_number_start: Optional[str] = Field(None, description="Gazette no start"), - official_gazette_number_end: Optional[str] = Field(None, description="Gazette no end"), - has_press_release: Literal["ALL", "0", "1"] = Field("ALL", description="Press release"), + applicant_general_name: str = Field("", description="Applicant"), + applicant_specific_name: str = Field("", description="Name"), + official_gazette_date_start: str = Field("", description="Start"), + official_gazette_date_end: str = Field("", description="End"), + official_gazette_number_start: str = Field("", description="Start"), + official_gazette_number_end: str = Field("", description="End"), + has_press_release: Literal["ALL", "0", "1"] = Field("ALL", description="Press"), 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"), - 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_id_or_name: Optional[str] = Field(None, description="Norm name"), - norm_article: Optional[str] = Field(None, description="Article"), + norm_id_or_name: str = Field("", description="Norm name"), + 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"), 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"), page_to_fetch: int = Field(1, ge=1, description="Page"), sort_by_criteria: str = Field("KararTarihi", description="Sort") @@ -1348,7 +1541,7 @@ async def search_anayasa_norm_denetimi_decisions( raise @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={ "readOnlyHint": 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) --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1397,7 +1590,7 @@ async def search_anayasa_bireysel_basvuru_report( raise @app.tool( - description="Parameter description", + description="Get Constitutional Court individual application decision text in paginated Markdown format", annotations={ "readOnlyHint": True, "idempotentHint": True @@ -1427,7 +1620,7 @@ async def get_anayasa_bireysel_basvuru_document_markdown( # --- MCP Tools for KIK (Kamu İhale Kurulu) --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1436,21 +1629,23 @@ async def get_anayasa_bireysel_basvuru_document_markdown( ) async def search_kik_decisions( 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_tarihi_baslangic: Optional[str] = Field(None, description="Decision Date Start (DD.MM.YYYY)."), - karar_tarihi_bitis: Optional[str] = Field(None, description="Decision Date End (DD.MM.YYYY)."), - basvuru_sahibi: Optional[str] = Field(None, description="Applicant."), - ihaleyi_yapan_idare: Optional[str] = Field(None, description="Procuring Entity."), - basvuru_konusu_ihale: Optional[str] = Field(None, description="Tender subject of the application."), - karar_metni: Optional[str] = Field(None, description=""" + karar_no: str = Field("", description="Decision Number (e.g., '2024/UH.II-1766')."), + karar_tarihi_baslangic: str = Field("", description="Decision Date Start (DD.MM.YYYY)."), + karar_tarihi_bitis: str = Field("", description="Decision Date End (DD.MM.YYYY)."), + basvuru_sahibi: str = Field("", description="Applicant."), + ihaleyi_yapan_idare: str = Field("", description="Procuring Entity."), + basvuru_konusu_ihale: str = Field("", description="Tender subject of the application."), + karar_metni: str = Field("", description=""" Keyword/phrase in decision text. Advanced search operators supported: - word1+word2 = AND logic (+anayasa +mahkeme -> both words required) - +"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."), - resmi_gazete_tarihi: Optional[str] = Field(None, description="Official Gazette Date (DD.MM.YYYY)."), - resmi_gazete_sayisi: Optional[str] = Field(None, description="Official Gazette Number."), + yil: str = Field("", description="Year of the decision."), + resmi_gazete_tarihi: str = Field("", description="Official Gazette Date (DD.MM.YYYY)."), + resmi_gazete_sayisi: str = Field("", description="Official Gazette Number."), page: int = Field(1, ge=1, description="Results page number.") ) -> KikSearchResult: """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) @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={ "readOnlyHint": True, "idempotentHint": True @@ -1526,7 +1721,7 @@ async def get_kik_document_markdown( is_paginated=False ) @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1534,10 +1729,10 @@ async def get_kik_document_markdown( } ) async def search_rekabet_kurumu_decisions( - sayfaAdi: Optional[str] = Field(None, description="Search in decision title (Başlık)."), - YayinlanmaTarihi: Optional[str] = Field(None, description="Publication date (Yayım Tarihi), e.g., DD.MM.YYYY."), - PdfText: Optional[str] = Field( - None, + sayfaAdi: str = Field("", description="Search in decision title (Başlık)."), + YayinlanmaTarihi: str = Field("", description="Publication date (Yayım Tarihi), e.g., DD.MM.YYYY."), + PdfText: str = Field( + "", description='Search in decision text. Use "\\"kesin cümle\\"" for precise matching.' ), KararTuru: Literal[ @@ -1548,8 +1743,8 @@ async def search_rekabet_kurumu_decisions( "Özelleştirme", "Rekabet İhlali" ] = Field("ALL", description="Parameter description"), - KararSayisi: Optional[str] = Field(None, description="Decision number (Karar Sayısı)."), - KararTarihi: Optional[str] = Field(None, description="Decision date (Karar Tarihi), e.g., DD.MM.YYYY."), + KararSayisi: str = Field("", description="Decision number (Karar Sayısı)."), + 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.") ) -> RekabetSearchResult: """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) @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={ "readOnlyHint": True, "idempotentHint": True @@ -1606,7 +1801,7 @@ async def get_rekabet_kurumu_document( # --- MCP Tools for Bedesten (Unified Search Across All Courts) --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1614,7 +1809,7 @@ async def get_rekabet_kurumu_document( } ) 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( default=["YARGITAYKARARI", "DANISTAYKARAR"], 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)"), pageNumber: int = Field(1, ge=1, description="Page number"), birimAdi: Optional[Union[YargitayBirimEnum, DanistayBirimEnum]] = Field(None, description="Chamber filter (optional)"), - kararTarihiStart: Optional[str] = Field(None, description="Start date (ISO 8601 format)"), - kararTarihiEnd: Optional[str] = Field(None, description="End date (ISO 8601 format)") + kararTarihiStart: str = Field("", description="Start date (ISO 8601 format)"), + kararTarihiEnd: str = Field("", description="End date (ISO 8601 format)") ) -> dict: """Search Turkish legal databases via unified Bedesten API.""" search_data = BedestenSearchData( @@ -1665,7 +1860,7 @@ async def search_bedesten_unified( raise @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={ "readOnlyHint": True, "idempotentHint": True @@ -1689,7 +1884,7 @@ async def get_bedesten_document_markdown( # --- MCP Tools for Sayıştay (Turkish Court of Accounts) --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1697,11 +1892,11 @@ async def get_bedesten_document_markdown( } ) async def search_sayistay_genel_kurul( - karar_no: Optional[str] = Field(None, 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_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"), - karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"), - karar_tamami: Optional[str] = Field(None, description="See docs for details"), + karar_no: str = Field("", description="Decision number to search for (e.g., '5415')"), + karar_ek: str = Field("", description="Decision appendix number (max 99, e.g., '1')"), + karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), + karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), + karar_tamami: str = Field("", description="Full text search"), start: int = Field(0, description="Starting record for pagination (0-based)"), length: int = Field(10, description="Number of records per page (1-100)") ) -> GenelKurulSearchResponse: @@ -1724,7 +1919,7 @@ async def search_sayistay_genel_kurul( raise @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1732,16 +1927,16 @@ async def search_sayistay_genel_kurul( } ) async def search_sayistay_temyiz_kurulu( - ilam_dairesi: DaireEnum = Field("ALL", description="See docs for details"), - yili: Optional[str] = Field(None, description="See docs for details"), - karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"), - karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"), - kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="See docs for details"), - ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)"), - dosya_no: Optional[str] = Field(None, description="File number for the case"), - temyiz_tutanak_no: Optional[str] = Field(None, description="Appeals board meeting minutes number"), - temyiz_karar: Optional[str] = Field(None, description="See docs for details"), - web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="See docs for details"), + ilam_dairesi: DaireEnum = Field("ALL", description="Audit chamber selection"), + yili: str = Field("", description="Year (YYYY)"), + karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), + karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), + kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"), + ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"), + dosya_no: str = Field("", description="File number for the case"), + temyiz_tutanak_no: str = Field("", description="Appeals board meeting minutes number"), + temyiz_karar: str = Field("", description="Appeals decision text"), + web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"), start: int = Field(0, description="Starting record for pagination (0-based)"), length: int = Field(10, description="Number of records per page (1-100)") ) -> TemyizKuruluSearchResponse: @@ -1769,7 +1964,7 @@ async def search_sayistay_temyiz_kurulu( raise @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={ "readOnlyHint": True, "openWorldHint": True, @@ -1777,14 +1972,14 @@ async def search_sayistay_temyiz_kurulu( } ) async def search_sayistay_daire( - yargilama_dairesi: DaireEnum = Field("ALL", description="See docs for details"), - karar_tarih_baslangic: Optional[str] = Field(None, description="See docs for details"), - karar_tarih_bitis: Optional[str] = Field(None, description="See docs for details"), - ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)"), - kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="See docs for details"), - hesap_yili: Optional[str] = Field(None, description="See docs for details"), - web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="See docs for details"), - web_karar_metni: Optional[str] = Field(None, description="See docs for details"), + yargilama_dairesi: DaireEnum = Field("ALL", description="Chamber selection"), + karar_tarih_baslangic: str = Field("", description="Start date (DD.MM.YYYY)"), + karar_tarih_bitis: str = Field("", description="End date (DD.MM.YYYY)"), + ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)"), + kamu_idaresi_turu: KamuIdaresiTuruEnum = Field("ALL", description="Public admin type"), + hesap_yili: str = Field("", description="Fiscal year"), + web_karar_konusu: WebKararKonusuEnum = Field("ALL", description="Decision subject"), + web_karar_metni: str = Field("", description="Decision text search"), start: int = Field(0, description="Starting record for pagination (0-based)"), length: int = Field(10, description="Number of records per page (1-100)") ) -> DaireSearchResponse: @@ -1810,7 +2005,7 @@ async def search_sayistay_daire( raise @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={ "readOnlyHint": True, "openWorldHint": False, @@ -1833,7 +2028,7 @@ async def get_sayistay_genel_kurul_document_markdown( raise @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={ "readOnlyHint": True, "openWorldHint": False, @@ -1856,7 +2051,7 @@ async def get_sayistay_temyiz_kurulu_document_markdown( raise @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={ "readOnlyHint": True, "openWorldHint": False, @@ -2085,7 +2280,7 @@ async def check_government_servers_health() -> Dict[str, Any]: # --- MCP Tools for KVKK --- @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={ "readOnlyHint": True, "openWorldHint": True, @@ -2093,7 +2288,7 @@ async def check_government_servers_health() -> Dict[str, Any]: } ) 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)."), pageSize: int = Field(10, ge=1, le=20, description="Number of results per page (1-20).") ) -> KvkkSearchResult: @@ -2122,7 +2317,7 @@ async def search_kvkk_decisions( ) @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={ "readOnlyHint": True, "openWorldHint": False, @@ -2130,7 +2325,7 @@ async def search_kvkk_decisions( } ) 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).") ) -> KvkkDocumentMarkdown: """Get KVKK decision as paginated Markdown.""" @@ -2499,6 +2694,8 @@ async def fetch( logger.exception(f"Error fetching ChatGPT Deep Research document {id}") raise +# --- Token Metrics Tool Removed for Optimization --- + def ensure_playwright_browsers(): """Ensure Playwright browsers are installed for KIK tool functionality.""" try: diff --git a/pyproject.toml b/pyproject.toml index d22095f..a2a2ada 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dependencies = [ "pypdf>=5.5.0", "fastapi>=0.115.14", "PyJWT>=2.8.0", + "tiktoken>=0.5.0", ] [project.optional-dependencies] diff --git a/rekabet_mcp_module/models.py b/rekabet_mcp_module/models.py index 576d33a..892131c 100644 --- a/rekabet_mcp_module/models.py +++ b/rekabet_mcp_module/models.py @@ -25,31 +25,31 @@ class RekabetKararTuruAdiEnum(str, Enum): class RekabetKurumuSearchRequest(BaseModel): """Model for Rekabet Kurumu (Turkish Competition Authority) search request.""" - sayfaAdi: Optional[str] = Field(None, description="Title") - YayinlanmaTarihi: Optional[str] = Field(None, description="Date") - PdfText: Optional[str] = Field(None, description="Text") - KararTuruID: Optional[RekabetKararTuruGuidEnum] = Field(RekabetKararTuruGuidEnum.TUMU, description="Type") - KararSayisi: Optional[str] = Field(None, description="No") - KararTarihi: Optional[str] = Field(None, description="Date") + sayfaAdi: str = Field("", description="Title") + YayinlanmaTarihi: str = Field("", description="Date") + PdfText: str = Field("", description="Text") + KararTuruID: RekabetKararTuruGuidEnum = Field(RekabetKararTuruGuidEnum.TUMU, description="Type") + KararSayisi: str = Field("", description="No") + KararTarihi: str = Field("", description="Date") page: int = Field(1, ge=1, description="Page") class RekabetDecisionSummary(BaseModel): """Model for a single Rekabet Kurumu decision summary from search results.""" - publication_date: Optional[str] = Field(None, description="Pub date") - decision_number: Optional[str] = Field(None, description="Number") - decision_date: Optional[str] = Field(None, description="Date") - decision_type_text: Optional[str] = Field(None, description="Type") - title: Optional[str] = Field(None, description="Title") - decision_url: Optional[HttpUrl] = Field(None, description="URL") - karar_id: Optional[str] = Field(None, description="ID") - related_cases_url: Optional[HttpUrl] = Field(None, description="Cases URL") + publication_date: str = Field("", description="Pub date") + decision_number: str = Field("", description="Number") + decision_date: str = Field("", description="Date") + decision_type_text: str = Field("", description="Type") + title: str = Field("", description="Title") + decision_url: str = Field("", description="URL") + karar_id: str = Field("", description="ID") + related_cases_url: str = Field("", description="Cases URL") class RekabetSearchResult(BaseModel): """Model for the overall search result for Rekabet Kurumu decisions.""" 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") - total_pages: Optional[int] = Field(None, description="Pages") + total_pages: int = Field(0, description="Pages") class RekabetDocument(BaseModel): """ diff --git a/sayistay_mcp_module/models.py b/sayistay_mcp_module/models.py index 93a74a5..e32c842 100644 --- a/sayistay_mcp_module/models.py +++ b/sayistay_mcp_module/models.py @@ -16,14 +16,14 @@ class GenelKurulSearchRequest(BaseModel): of the Turkish Court of Accounts, typically addressing interpretation of audit and accountability regulations. """ - karar_no: Optional[str] = Field(None, description="Decision no") - karar_ek: Optional[str] = Field(None, description="Appendix no") + karar_no: str = Field("", description="Decision 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 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") - 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") - ilam_no: Optional[str] = Field(None, description="Audit report number (İlam No, max 50 chars)") - dosya_no: Optional[str] = Field(None, description="File number for the case") - temyiz_tutanak_no: Optional[str] = Field(None, description="Appeals board meeting minutes number") + ilam_no: str = Field("", description="Audit report number (İlam No, max 50 chars)") + dosya_no: str = Field("", description="File number for the case") + 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") @@ -103,19 +103,19 @@ class DaireSearchRequest(BaseModel): """ 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") - hesap_yili: Optional[str] = Field(None, description="Value") + hesap_yili: str = Field("", 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 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)") karar_tarih: str = Field(..., description="Decision date in DD.MM.YYYY format") 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") kamu_idaresi_turu: str = Field(..., description="Public administration type") 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_type: str = Field(..., description="Value") 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") - retrieval_date: Optional[str] = Field(None, description="Date when document was retrieved (ISO format)") - error_message: Optional[str] = Field(None, description="Error message if document retrieval failed") \ No newline at end of file + markdown_content: str = Field("", description="Full decision text converted to Markdown format") + retrieval_date: str = Field("", description="Date when document was retrieved (ISO format)") + error_message: str = Field("", description="Error message if document retrieval failed") \ No newline at end of file diff --git a/yargitay_mcp_module/models.py b/yargitay_mcp_module/models.py index 9ea8ecc..d14afec 100644 --- a/yargitay_mcp_module/models.py +++ b/yargitay_mcp_module/models.py @@ -37,8 +37,6 @@ class YargitayDetailedSearchRequest(BaseModel): arananKelime: Optional[str] = Field("", description="Turkish keywords (supports +word -word \"phrase\" operators)") # Department/Board selection - Complete Court of Cassation chamber hierarchy 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)") 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)") 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)") pageNumber: int = Field(1, ge=1, description="Page number (1-indexed)")