From ae5d590cca00506fd8b2a1d9c8ae5387171c0d37 Mon Sep 17 00:00:00 2001 From: saidsurucu Date: Sun, 3 May 2026 01:41:35 +0300 Subject: [PATCH] fix(sayistay): surface clear error when upstream WAF returns 418 (#23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verified 2026-05-03 against a real Chrome browser: POSTs to /KararlarGenelKurul/DataTablesList consistently return HTTP 418 with the WAF block page "Bilgi Güvenliği Politikaları Gereği Kısıtlanmıştır", regardless of headers, cookies, CSRF token, or form payload. The block is server-side at sayistay.gov.tr and cannot be worked around client-side. The Temyiz Kurulu and Daire endpoints are unaffected (29k/22k records still return normally). Detect the 418 + WAF marker in all three search methods and raise a clear RuntimeError explaining it is an upstream restriction, instead of the cryptic "Client error '418 I'm a teapot'" that hides the real situation from MCP clients. Co-Authored-By: Claude Opus 4.7 (1M context) --- sayistay_mcp_module/client.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/sayistay_mcp_module/client.py b/sayistay_mcp_module/client.py index 234c981..5b48e8b 100644 --- a/sayistay_mcp_module/client.py +++ b/sayistay_mcp_module/client.py @@ -42,11 +42,17 @@ class SayistayApiClient: """ BASE_URL = "https://www.sayistay.gov.tr" - + # Search endpoints for each decision type GENEL_KURUL_ENDPOINT = "/KararlarGenelKurul/DataTablesList" - TEMYIZ_KURULU_ENDPOINT = "/KararlarTemyiz/DataTablesList" + TEMYIZ_KURULU_ENDPOINT = "/KararlarTemyiz/DataTablesList" DAIRE_ENDPOINT = "/KararlarDaire/DataTablesList" + + # Marker present in the upstream WAF block page (also returns HTTP 418). + # Verified 2026-05-03 against real Chrome — the block targets POSTs to + # the DataTablesList endpoints regardless of headers/cookies/CSRF, so + # we surface a specific error instead of the generic "I'm a teapot". + _WAF_BLOCK_MARKER = "Bilgi Güvenliği Politikaları Gereği Kısıtlanmıştır" # Page endpoints for session initialization and document access GENEL_KURUL_PAGE = "/KararlarGenelKurul" @@ -141,6 +147,23 @@ class SayistayApiClient: return enum_value + def _raise_if_waf_blocked(self, response: httpx.Response, endpoint_label: str) -> None: + """ + Sayıştay's upstream WAF returns HTTP 418 with a Turkish HTML block + page for POSTs to the DataTablesList endpoints. This affects every + client (verified with real Chrome on 2026-05-03), so there is no + client-side workaround. Detect it and raise a clear error. + """ + if response.status_code == 418 or self._WAF_BLOCK_MARKER in response.text: + raise RuntimeError( + f"Sayıştay upstream WAF blocked the {endpoint_label} request " + f"(HTTP {response.status_code} from {response.request.url}). " + "This is a server-side restriction at sayistay.gov.tr — affects " + "all clients including a real browser — and cannot be worked " + "around from yargi-mcp. Try again later or contact Sayıştay if " + "the block persists." + ) + def _build_datatables_params(self, start: int, length: int, draw: int = 1) -> List[Tuple[str, str]]: """Build standard DataTables parameters for all endpoints.""" params = [ @@ -384,9 +407,10 @@ class SayistayApiClient: data=encoded_data, headers=headers ) + self._raise_if_waf_blocked(response, "Genel Kurul") response.raise_for_status() response_json = response.json() - + # Parse response decisions = [] for item in response_json.get('data', []): @@ -443,6 +467,7 @@ class SayistayApiClient: data=encoded_data, headers=headers ) + self._raise_if_waf_blocked(response, "Temyiz Kurulu") response.raise_for_status() response_json = response.json() @@ -502,6 +527,7 @@ class SayistayApiClient: data=encoded_data, headers=headers ) + self._raise_if_waf_blocked(response, "Daire") response.raise_for_status() response_json = response.json()