get_cbgenelge_content
Retrieve and convert Turkish Presidential Circulars from PDF to Markdown format for analysis, summarization, or question answering.
Instructions
Retrieve the full content of a Turkish Presidential Circular (Cumhurbaşkanlığı Genelgesi) in Markdown format.
This tool fetches the PDF document and converts it to Markdown. Presidential Circulars are available only as PDF files. Use 'search_cbgenelge' first to find the circular number and Official Gazette date.
IMPORTANT: You must provide the 'resmi_gazete_tarihi' (Official Gazette date) from the search results. This is required to construct the correct PDF URL.
Returns:
Full circular content formatted as Markdown (converted from PDF)
Ready for analysis, summarization, or question answering
Example usage:
Search for circulars: search_cbgenelge(baslangic_tarihi="2025")
Get full content: get_cbgenelge_content(mevzuat_no="16", resmi_gazete_tarihi="20/09/2025", mevzuat_tertip="5")
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mevzuat_no | Yes | The Presidential Circular number from search results (e.g., '16', '15') | |
| resmi_gazete_tarihi | Yes | Official Gazette date from search results in DD/MM/YYYY format (e.g., '20/09/2025') | |
| mevzuat_tertip | No | Circular series from search results (e.g., '5') | 5 |
Implementation Reference
- mevzuat_mcp_server.py:954-1009 (handler)The handler and registration for the 'get_cbgenelge_content' MCP tool using FastMCP @app.tool() decorator. Defines input schema via Pydantic Field descriptions, logs parameters, delegates to MevzuatApiClientNew.get_content with mevzuat_tur=22, handles errors, and returns MevzuatArticleContent.@app.tool() async def get_cbgenelge_content( mevzuat_no: str = Field( ..., description="The Presidential Circular number from search results (e.g., '16', '15')" ), resmi_gazete_tarihi: str = Field( ..., description="Official Gazette date from search results in DD/MM/YYYY format (e.g., '20/09/2025')" ), mevzuat_tertip: str = Field( "5", description="Circular series from search results (e.g., '5')" ) ) -> MevzuatArticleContent: """ Retrieve the full content of a Turkish Presidential Circular (Cumhurbaşkanlığı Genelgesi) in Markdown format. This tool fetches the PDF document and converts it to Markdown. Presidential Circulars are available only as PDF files. Use 'search_cbgenelge' first to find the circular number and Official Gazette date. IMPORTANT: You must provide the 'resmi_gazete_tarihi' (Official Gazette date) from the search results. This is required to construct the correct PDF URL. Returns: - Full circular content formatted as Markdown (converted from PDF) - Ready for analysis, summarization, or question answering Example usage: 1. Search for circulars: search_cbgenelge(baslangic_tarihi="2025") 2. Get full content: get_cbgenelge_content(mevzuat_no="16", resmi_gazete_tarihi="20/09/2025", mevzuat_tertip="5") """ logger.info(f"Tool 'get_cbgenelge_content' called: {mevzuat_no}, RG date: {resmi_gazete_tarihi}, tertip: {mevzuat_tertip}") try: result = await mevzuat_client.get_content( mevzuat_no=mevzuat_no, mevzuat_tur=22, # Cumhurbaşkanlığı Genelgesi mevzuat_tertip=mevzuat_tertip, resmi_gazete_tarihi=resmi_gazete_tarihi ) if result.error_message: logger.warning(f"Error fetching circular content: {result.error_message}") return result except Exception as e: logger.exception(f"Error in tool 'get_cbgenelge_content' for {mevzuat_no}") return MevzuatArticleContent( madde_id=mevzuat_no, mevzuat_id=mevzuat_no, markdown_content="", error_message=f"An unexpected error occurred: {str(e)}" )
- mevzuat_client.py:557-724 (helper)Core helper function MevzuatApiClientNew.get_content that implements the PDF download and markdown conversion for CB Genelgesi (tur=22). Constructs special PDF URL using formatted resmi_gazete_tarihi, downloads PDF, optionally uses Mistral OCR for text extraction from images, falls back to markitdown PDF conversion, supports caching.async def get_content( self, mevzuat_no: str, mevzuat_tur: int = 1, mevzuat_tertip: str = "3", resmi_gazete_tarihi: Optional[str] = None ) -> MevzuatArticleContent: """ Download and extract content from legislation. Tries HTML scraping first (most reliable), then falls back to file downloads. For Presidential Decisions (tur=20) and Circulars (tur=22), skip HTML and go directly to PDF. Args: mevzuat_no: Legislation number mevzuat_tur: Legislation type code (1=Kanun, 20=CB Kararı, 22=CB Genelgesi, etc.) mevzuat_tertip: Series number resmi_gazete_tarihi: Official Gazette date (DD/MM/YYYY) - required for CB Genelgesi (tur=22) """ # CB Kararları (tur=20) and CB Genelgesi (tur=22) are PDF-only, skip HTML scraping if mevzuat_tur not in [20, 22]: # Try HTML scraping first (most reliable method for other types) result = await self.get_content_from_html(mevzuat_no, mevzuat_tur, mevzuat_tertip) if result.markdown_content: return result logger.info(f"HTML scraping returned no content for {mevzuat_no}, trying file downloads") else: if mevzuat_tur == 20: logger.info("CB Kararı detected (tur=20), skipping HTML scraping, going directly to PDF") elif mevzuat_tur == 22: logger.info("CB Genelgesi detected (tur=22), skipping HTML scraping, going directly to PDF") cache_key = f"doc:{mevzuat_tur}.{mevzuat_tertip}.{mevzuat_no}" if self._cache_enabled else None if cache_key and self._cache: cached_content = self._cache.get(cache_key) if cached_content: logger.debug(f"Cache hit: {mevzuat_no}") return MevzuatArticleContent( madde_id=mevzuat_no, mevzuat_id=mevzuat_no, markdown_content=cached_content ) # Construct URLs based on mevzuat type if mevzuat_tur == 22: # CB Genelgesi - special PDF URL format if not resmi_gazete_tarihi: return MevzuatArticleContent( madde_id=mevzuat_no, mevzuat_id=mevzuat_no, markdown_content="", error_message="resmi_gazete_tarihi is required for CB Genelgesi (tur=22)" ) # Convert DD/MM/YYYY to YYYYMMDD parts = resmi_gazete_tarihi.split('/') if len(parts) == 3: date_str = f"{parts[2]}{parts[1].zfill(2)}{parts[0].zfill(2)}" else: return MevzuatArticleContent( madde_id=mevzuat_no, mevzuat_id=mevzuat_no, markdown_content="", error_message=f"Invalid date format: {resmi_gazete_tarihi}. Expected DD/MM/YYYY" ) pdf_url = self.GENELGE_PDF_URL_TEMPLATE.format(date=date_str, no=mevzuat_no) doc_url = None # Genelge has no DOC version elif mevzuat_tur == 20: # CB Kararı - PDF only, no DOC doc_url = None # CB Kararları have no DOC version pdf_url = self.PDF_URL_TEMPLATE.format(tur=mevzuat_tur, tertip=mevzuat_tertip, no=mevzuat_no) else: doc_url = self.DOC_URL_TEMPLATE.format(tur=mevzuat_tur, tertip=mevzuat_tertip, no=mevzuat_no) pdf_url = self.PDF_URL_TEMPLATE.format(tur=mevzuat_tur, tertip=mevzuat_tertip, no=mevzuat_no) # Try DOC first (skip for CB Genelgesi and CB Kararı which have no DOC version) if doc_url: try: logger.info(f"Trying DOC: {doc_url}") # Use separate headers for document download doc_headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', 'Accept': 'application/msword, */*', } response = await self._http_client.get(doc_url, headers=doc_headers) response.raise_for_status() doc_bytes = response.content logger.info(f"Downloaded DOC: {len(doc_bytes)} bytes") # DOC files from mevzuat.gov.tr are actually HTML if len(doc_bytes) < 100: logger.warning(f"DOC file too small ({len(doc_bytes)} bytes), likely empty") raise Exception("DOC file is empty or too small") doc_stream = io.BytesIO(doc_bytes) result = self._md_converter.convert_stream(doc_stream, file_extension=".doc") markdown_content = result.text_content.strip() if result and result.text_content else "" if markdown_content: logger.info(f"DOC conversion successful for {mevzuat_no}") if cache_key and self._cache: self._cache.put(cache_key, markdown_content) return MevzuatArticleContent( madde_id=mevzuat_no, mevzuat_id=mevzuat_no, markdown_content=markdown_content ) except Exception as e: logger.info(f"DOC failed, trying PDF: {e}") # Try PDF fallback try: logger.info(f"Trying PDF: {pdf_url}") # For CB Kararı (tur=20) and CB Genelgesi (tur=22), ensure we have session cookies if mevzuat_tur in [20, 22]: await self._ensure_session() # Create temporary client with cookies to avoid deprecation warning async with httpx.AsyncClient( headers=self.HEADERS, cookies=self._cookies, timeout=self._http_client.timeout, follow_redirects=True ) as temp_client: response = await temp_client.get(pdf_url) else: response = await self._http_client.get(pdf_url) response.raise_for_status() pdf_bytes = response.content markdown_content = "" # For CB Kararı (tur=20) and CB Genelgesi (tur=22), use Mistral OCR (handles images + text) if mevzuat_tur in [20, 22] and self._mistral_client: doc_type = "CB Kararı" if mevzuat_tur == 20 else "CB Genelgesi" logger.info(f"Using Mistral OCR for {doc_type} PDF") markdown_content = await self._ocr_pdf_with_mistral(pdf_bytes, pdf_url) # Fallback to markitdown if OCR fails if not markdown_content: logger.warning("Mistral OCR failed, falling back to markitdown") pdf_stream = io.BytesIO(pdf_bytes) result = self._md_converter.convert_stream(pdf_stream, file_extension=".pdf") markdown_content = result.text_content.strip() if result and result.text_content else "" else: # Use markitdown for other types pdf_stream = io.BytesIO(pdf_bytes) result = self._md_converter.convert_stream(pdf_stream, file_extension=".pdf") markdown_content = result.text_content.strip() if result and result.text_content else "" if markdown_content: logger.info(f"PDF conversion successful for {mevzuat_no}") if cache_key and self._cache: self._cache.put(cache_key, markdown_content) return MevzuatArticleContent( madde_id=mevzuat_no, mevzuat_id=mevzuat_no, markdown_content=markdown_content ) except Exception as e: logger.error(f"PDF also failed: {e}") return MevzuatArticleContent( madde_id=mevzuat_no, mevzuat_id=mevzuat_no, markdown_content="", error_message=f"Both DOC and PDF download/conversion failed for {mevzuat_tur}.{mevzuat_tertip}.{mevzuat_no}" )
- mevzuat_models.py:119-125 (schema)Pydantic schema model MevzuatArticleContent returned by the tool, containing the extracted markdown_content of the Presidential Circular and optional error_message.class MevzuatArticleContent(BaseModel): """Model for the content of legislation (reused from old models).""" madde_id: str mevzuat_id: str markdown_content: str error_message: Optional[str] = None