Skip to main content
Glama
saidsurucu

Mevzuat MCP

by saidsurucu

search_within_cbk

Search within specific Turkish Presidential Decrees using advanced query operators to find relevant articles without loading entire documents, returning only matching content sorted by relevance.

Instructions

Search for a keyword within a specific Presidential Decree's articles with advanced query operators.

This tool is optimized for large Presidential Decrees. Instead of loading the entire decree into context, it:

  1. Fetches the full content

  2. Splits it into individual articles (madde)

  3. Returns only the articles that match the search query

  4. Sorts results by relevance score (based on match count)

Query Syntax (operators must be uppercase):

  • Simple keyword: organize

  • Exact phrase: "organize suç"

  • AND operator: organize AND suç (both terms must be present)

  • OR operator: organize OR terör (at least one term must be present)

  • NOT operator: organize NOT terör (first term present, second must not be)

  • Combinations: "organize suç" AND ceza NOT terör

Returns formatted text with:

  • Article number and title

  • Relevance score (higher = more matches)

  • Full article content for matching articles

Example use cases:

  • Search for "organize" in CBK 1 (Judicial Reform)

  • Search for "suç AND ceza" in specific decree

  • Search for "devlet OR kamu" in administrative decrees

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
mevzuat_noYesThe Presidential Decree number to search within (e.g., '1', '32')
keywordYesSearch query supporting advanced operators: simple keyword ("organize"), exact phrase ("organize suç"), AND/OR/NOT operators (organize AND suç, suç OR ceza, organize NOT terör). Operators must be uppercase.
mevzuat_tertipNoDecree series from search results (e.g., '5')5
case_sensitiveNoWhether to match case when searching (default: False)
max_resultsNoMaximum number of matching articles to return (1-50, default: 25)

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The handler function for the MCP tool 'search_within_cbk'. It validates inputs using Pydantic Fields, fetches Presidential Decree (CBK) content from mevzuat_client, uses search_articles_by_keyword to find matching articles, creates ArticleSearchResult, formats with format_search_results, and returns formatted matching articles or error messages.
    async def search_within_cbk(
        mevzuat_no: str = Field(
            ...,
            description="The Presidential Decree number to search within (e.g., '1', '32')"
        ),
        keyword: str = Field(
            ...,
            description='Search query supporting advanced operators: simple keyword ("organize"), exact phrase ("organize suç"), AND/OR/NOT operators (organize AND suç, suç OR ceza, organize NOT terör). Operators must be uppercase.'
        ),
        mevzuat_tertip: str = Field(
            "5",
            description="Decree series from search results (e.g., '5')"
        ),
        case_sensitive: bool = Field(
            False,
            description="Whether to match case when searching (default: False)"
        ),
        max_results: int = Field(
            25,
            ge=1,
            le=50,
            description="Maximum number of matching articles to return (1-50, default: 25)"
        )
    ) -> str:
        """
        Search for a keyword within a specific Presidential Decree's articles with advanced query operators.
    
        This tool is optimized for large Presidential Decrees.
        Instead of loading the entire decree into context, it:
        1. Fetches the full content
        2. Splits it into individual articles (madde)
        3. Returns only the articles that match the search query
        4. Sorts results by relevance score (based on match count)
    
        Query Syntax (operators must be uppercase):
        - Simple keyword: organize
        - Exact phrase: "organize suç"
        - AND operator: organize AND suç (both terms must be present)
        - OR operator: organize OR terör (at least one term must be present)
        - NOT operator: organize NOT terör (first term present, second must not be)
        - Combinations: "organize suç" AND ceza NOT terör
    
        Returns formatted text with:
        - Article number and title
        - Relevance score (higher = more matches)
        - Full article content for matching articles
    
        Example use cases:
        - Search for "organize" in CBK 1 (Judicial Reform)
        - Search for "suç AND ceza" in specific decree
        - Search for "devlet OR kamu" in administrative decrees
        """
        logger.info(f"Tool 'search_within_cbk' called: {mevzuat_no}, keyword: '{keyword}'")
    
        try:
            # Get full content
            content_result = await mevzuat_client.get_content(
                mevzuat_no=mevzuat_no,
                mevzuat_tur=19,  # Cumhurbaşkanlığı Kararnamesi
                mevzuat_tertip=mevzuat_tertip
            )
    
            if content_result.error_message:
                return f"Error fetching decree content: {content_result.error_message}"
    
            # Search within articles
            matches = search_articles_by_keyword(
                markdown_content=content_result.markdown_content,
                keyword=keyword,
                case_sensitive=case_sensitive,
                max_results=max_results
            )
    
            result = ArticleSearchResult(
                mevzuat_no=mevzuat_no,
                mevzuat_tur=19,
                keyword=keyword,
                total_matches=len(matches),
                matching_articles=matches
            )
    
            if len(matches) == 0:
                return f"No articles found containing '{keyword}' in CBK {mevzuat_no}"
    
            return format_search_results(result)
    
        except Exception as e:
            logger.exception(f"Error in tool 'search_within_cbk' for {mevzuat_no}")
            return f"An unexpected error occurred: {str(e)}"
  • Key helper function implementing the article-level search logic. Parses markdown into articles using split_into_articles, evaluates queries with AND/OR/NOT/exact phrases via _matches_query, generates previews, creates MaddeMatch objects, scores by match count, sorts, and limits results.
    def search_articles_by_keyword(
        markdown_content: str,
        keyword: str,
        case_sensitive: bool = False,
        max_results: int = 50
    ) -> List[MaddeMatch]:
        """
        Search for keyword within articles with support for advanced operators.
    
        Query syntax:
        - Simple keyword: "yatırımcı"
        - Exact phrase: "mali sıkıntı"
        - AND operator: yatırımcı AND tazmin
        - OR operator: yatırımcı OR müşteri
        - NOT operator: yatırımcı NOT kurum
        - Combinations: "mali sıkıntı" AND yatırımcı NOT kurum
    
        Args:
            markdown_content: Full legislation content in markdown
            keyword: Search query with optional operators (AND, OR, NOT, "exact phrase")
            case_sensitive: Whether to match case
            max_results: Maximum number of matching articles to return
    
        Returns:
            List of matching articles sorted by relevance (score based on match count)
        """
        articles = split_into_articles(markdown_content)
        matches = []
    
        for article in articles:
            content = article['madde_content']
    
            # Check if article matches query
            matches_query, score = _matches_query(content, keyword, case_sensitive)
    
            if matches_query and score > 0:
                # Generate preview (first occurrence of a search term)
                search_content = content if case_sensitive else content.lower()
                search_keyword = keyword if case_sensitive else keyword.lower()
    
                # Try to find first quoted phrase or first word
                preview_terms = re.findall(r'"([^"]*)"', search_keyword)
                if not preview_terms:
                    # Use first word (excluding operators)
                    words = re.split(r'\s+(?:AND|OR|NOT)\s+', search_keyword)
                    preview_terms = [w.strip() for w in words if w.strip() and w.strip() not in ('AND', 'OR', 'NOT')]
    
                preview = ""
                if preview_terms:
                    first_term = preview_terms[0] if case_sensitive else preview_terms[0].lower()
                    if first_term in search_content:
                        keyword_pos = search_content.find(first_term)
                        start = max(0, keyword_pos - 100)
                        end = min(len(content), keyword_pos + len(first_term) + 100)
                        preview = content[start:end]
    
                        if start > 0:
                            preview = "..." + preview
                        if end < len(content):
                            preview = preview + "..."
    
                if not preview:
                    preview = content[:200] + "..."
    
                matches.append(MaddeMatch(
                    madde_no=article['madde_no'],
                    madde_title=article['madde_title'],
                    madde_content=content,
                    match_count=score,
                    preview=preview
                ))
    
        # Sort by score (most relevant first)
        matches.sort(key=lambda x: x.match_count, reverse=True)
    
        return matches[:max_results]
  • Helper function to parse legislation markdown into structured articles (madde_no, title, content) using regex to detect headers like '**MADDE 1 –**'.
    def split_into_articles(markdown_content: str) -> List[Dict[str, str]]:
        """
        Split markdown content into individual articles.
    
        Returns list of dicts with keys: madde_no, madde_title, madde_content
        """
        articles = []
    
        # Split by article headers: **MADDE X –** or **MADDE X**- or **Madde X –**
        # Regex to match all formats (case-insensitive for MADDE/Madde):
        # - **MADDE 1 –** (dash inside **) - used in some laws
        # - **MADDE 1**- (dash outside **) - used in regulations
        # - **Madde 1 –** (title case) - used in some laws like CMK
        pattern = r'\*\*(?:MADDE|Madde)\s+(\d+)(?:\s*[–-])?\*\*\s*-?'
    
        # Find all article positions
        matches = list(re.finditer(pattern, markdown_content))
    
        if not matches:
            return []
    
        for i, match in enumerate(matches):
            madde_no = match.group(1)
            start_pos = match.start()
    
            # Find end position (start of next article or end of content)
            if i < len(matches) - 1:
                end_pos = matches[i + 1].start()
            else:
                end_pos = len(markdown_content)
    
            # Extract full article content
            article_text = markdown_content[start_pos:end_pos].strip()
    
            # Try to extract title (usually follows the article number)
            # Pattern: **MADDE X –** (1) or **Title** after article number
            title = ""
            lines = article_text.split('\n', 3)
            if len(lines) > 1:
                # Check if second line is a title (surrounded by **)
                second_line = lines[1].strip()
                if second_line.startswith('**') and second_line.endswith('**'):
                    title = second_line.strip('*').strip()
    
            articles.append({
                'madde_no': madde_no,
                'madde_title': title,
                'madde_content': article_text
            })
    
        return articles
  • Helper function to format ArticleSearchResult into a readable string with article headers, titles, match counts, and full contents.
    def format_search_results(result: ArticleSearchResult) -> str:
        """Format search results as readable text."""
        output = []
        output.append(f"Keyword: '{result.keyword}'")
        output.append(f"Total matching articles: {result.total_matches}")
        output.append("")
    
        for i, match in enumerate(result.matching_articles, 1):
            output.append(f"=== MADDE {match.madde_no} ===")
            if match.madde_title:
                output.append(f"Title: {match.madde_title}")
            output.append(f"Matches: {match.match_count}")
            output.append("")
            output.append("Full content:")
            output.append(match.madde_content)
            output.append("")
    
        return "\n".join(output)
  • Pydantic models used internally by the tool: MaddeMatch for individual article matches and ArticleSearchResult aggregating results for formatting.
    class MaddeMatch(BaseModel):
        """A single article match result."""
        madde_no: str  # e.g., "1", "15", "142"
        madde_title: str  # e.g., "Amaç", "Tanımlar"
        madde_content: str  # Full article text
        match_count: int  # Number of keyword occurrences
        preview: str  # Short preview showing keyword in context
    
    
    class ArticleSearchResult(BaseModel):
        """Search results within a legislation."""
        mevzuat_no: str
        mevzuat_tur: int
        keyword: str
        total_matches: int
        matching_articles: List[MaddeMatch]
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden and excels at disclosing behavioral traits. It explains the four-step process (fetch, split, return matches, sort), specifies it's optimized for large decrees to avoid context overload, describes the return format (article number, title, relevance score, content), and mentions result limits (max_results parameter).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured and front-loaded with the core purpose. Every sentence adds value: the first states the purpose, subsequent sections explain the optimization approach, query syntax, return format, and provide concrete examples. No wasted words or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (search with advanced operators, article-level matching, relevance scoring) and the presence of an output schema, the description is complete. It explains the behavioral approach, query syntax, return format, and provides examples - all essential context that complements the structured schema data.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds some value by elaborating on the 'keyword' parameter with detailed query syntax examples and operators, but doesn't provide additional meaning for other parameters beyond what's already in the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches for keywords within specific Presidential Decree articles using advanced query operators. It distinguishes itself from siblings by specifying it's optimized for large decrees and returns only matching articles with relevance scoring, unlike broader search tools like 'search_cbk'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use this tool ('optimized for large Presidential Decrees') and includes example use cases. However, it doesn't explicitly state when NOT to use it or directly compare it to alternatives like 'search_cbk' or other 'search_within_' tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/saidsurucu/mevzuat-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server