Skip to main content
Glama

zerochan_search

Read-onlyIdempotent

Search Zerochan anime images by tags, with options for multi-tag queries, strict primary tag filtering, and results sorting by recent or popular content.

Instructions

Search Zerochan entries by one or more tags.

Supports single-tag, multi-tag, and strict-mode queries. Zerochan tag names
use Title Case with spaces (e.g. 'Hatsune Miku', not 'hatsune_miku').

Multi-tag example: tags=['Hatsune Miku', 'Flower'] → /Hatsune+Miku,Flower?json
Strict mode: tags=['Rem'] + strict=True → /Rem?json&strict (only entries where Rem is primary tag)

Note: Strict mode only works with a single tag. Passing multiple tags with strict=True
will ignore the strict flag.

Args:
    params (SearchByTagInput): Input parameters including:
        - username (str): Your Zerochan username (required)
        - tags (list[str]): One or more tag names (Title Case preferred)
        - strict (bool): If True and single tag, use strict mode (default: False)
        - page (int): Page number (default: 1)
        - limit (int): Results per page, 1–250 (default: 20)
        - sort (SortOrder): 'id' for recent, 'fav' for popular (default: 'id')
        - dimensions (Optional[Dimensions]): Filter by image shape
        - color (Optional[str]): Filter by dominant color name
        - response_format (ResponseFormat): 'markdown' or 'json' (default: 'markdown')

Returns:
    str: Matching entries in the requested format.
         Markdown: formatted table with ID, tags, dimensions, favorites, links.
         JSON: raw API response with all available fields.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The main handler function for zerochan_search tool. Accepts SearchByTagInput params, builds URL path from tags, constructs query parameters, calls zerochan_get helper, and returns formatted results in either Markdown or JSON format.
    async def zerochan_search(params: SearchByTagInput) -> str:
        """Search Zerochan entries by one or more tags.
    
        Supports single-tag, multi-tag, and strict-mode queries. Zerochan tag names
        use Title Case with spaces (e.g. 'Hatsune Miku', not 'hatsune_miku').
    
        Multi-tag example: tags=['Hatsune Miku', 'Flower'] → /Hatsune+Miku,Flower?json
        Strict mode: tags=['Rem'] + strict=True → /Rem?json&strict (only entries where Rem is primary tag)
    
        Note: Strict mode only works with a single tag. Passing multiple tags with strict=True
        will ignore the strict flag.
    
        Args:
            params (SearchByTagInput): Input parameters including:
                - username (str): Your Zerochan username (required)
                - tags (list[str]): One or more tag names (Title Case preferred)
                - strict (bool): If True and single tag, use strict mode (default: False)
                - page (int): Page number (default: 1)
                - limit (int): Results per page, 1–250 (default: 20)
                - sort (SortOrder): 'id' for recent, 'fav' for popular (default: 'id')
                - dimensions (Optional[Dimensions]): Filter by image shape
                - color (Optional[str]): Filter by dominant color name
                - response_format (ResponseFormat): 'markdown' or 'json' (default: 'markdown')
    
        Returns:
            str: Matching entries in the requested format.
                 Markdown: formatted table with ID, tags, dimensions, favorites, links.
                 JSON: raw API response with all available fields.
        """
        # Build URL path: tags are joined with commas, spaces replaced with +
        encoded_tags = [t.replace(" ", "+") for t in params.tags]
        tag_path = ",".join(encoded_tags)
        path = f"/{tag_path}"
    
        query: dict = {
            "p": params.page,
            "l": params.limit,
            "s": params.sort.value,
        }
        if params.dimensions is not None:
            query["d"] = params.dimensions.value
        if params.color:
            query["c"] = params.color
    
        # Strict mode only works with a single tag
        use_strict = params.strict and len(params.tags) == 1
        if use_strict:
            query["strict"] = True
    
        try:
            data = await zerochan_get(path, query)
        except Exception as e:
            return handle_api_error(e)
    
        items = data.get("items", data) if isinstance(data, dict) else data
        source = " + ".join(params.tags) + (" [strict]" if use_strict else "")
    
        if params.response_format == ResponseFormat.JSON:
            return json.dumps(data, indent=2, ensure_ascii=False)
    
        return format_post_list_markdown(items if isinstance(items, list) else [], source)
  • server.py:299-308 (registration)
    Tool registration using @mcp.tool decorator with name='zerochan_search', title, and annotations indicating it's read-only, non-destructive, idempotent, and open-world.
    @mcp.tool(
        name="zerochan_search",
        annotations={
            "title": "Search Zerochan by Tag(s)",
            "readOnlyHint": True,
            "destructiveHint": False,
            "idempotentHint": True,
            "openWorldHint": True,
        }
    )
  • SearchByTagInput Pydantic BaseModel defining the input schema for zerochan_search. Includes tags (list of strings), strict mode, pagination, limit, sort, dimensions, color filter, and response_format fields with validators.
    class SearchByTagInput(BaseModel):
        """Input for searching Zerochan entries by one or more tags."""
        model_config = ConfigDict(str_strip_whitespace=True, extra="forbid")
    
        tags: list[str] = Field(
            ...,
            description="One or more tags to filter by (e.g. ['Hatsune Miku'] or ['Hatsune Miku', 'Flower']). Tags are joined with commas in the URL.",
            min_length=1
        )
        strict: bool = Field(
            default=False,
            description="If True, use strict mode — only returns entries where the FIRST tag is the primary tag. Only works with a single tag."
        )
        page: int = Field(default=1, description="Page number for pagination", ge=1)
        limit: int = Field(default=DEFAULT_LIMIT, description="Number of results per page (1–250)", ge=1, le=MAX_LIMIT)
        sort: SortOrder = Field(default=SortOrder.RECENT, description="Sort order: 'id' = most recent, 'fav' = most popular")
        dimensions: Optional[Dimensions] = Field(default=None, description="Filter by image dimensions: large, huge, landscape, portrait, square")
        color: Optional[str] = Field(default=None, description="Filter by dominant color, e.g. 'red', 'blue', 'green'", max_length=32)
        response_format: ResponseFormat = Field(default=ResponseFormat.MARKDOWN, description="Output format: 'markdown' or 'json'")
    
        @field_validator("tags")
        @classmethod
        def validate_tags(cls, v: list[str]) -> list[str]:
            cleaned = [t.strip() for t in v if t.strip()]
            if not cleaned:
                raise ValueError("At least one non-empty tag is required.")
            return cleaned
  • zerochan_get HTTP helper function that performs GET requests to Zerochan API. Handles User-Agent header with username, adds json=True parameter, and returns parsed JSON response or raises appropriate exceptions.
    async def zerochan_get(path: str, params: dict) -> dict:
        """
        Perform a GET request to the Zerochan API.
    
        Args:
            path: URL path (e.g. '/Hatsune+Miku')
            params: Query string parameters (json=True always included)
    
        Returns:
            Parsed JSON response dict
    
        Raises:
            ValueError: If ZEROCHAN_USERNAME env var is not set
            httpx.HTTPStatusError: On non-2xx responses
            httpx.TimeoutException: On request timeout
        """
        if not ZEROCHAN_USERNAME:
            raise ValueError(
                "ZEROCHAN_USERNAME is not set. Add it to your MCP config: "
                '"env": {"ZEROCHAN_USERNAME": "YourUsername"}'
            )
        params["json"] = True
        url = f"{ZEROCHAN_BASE_URL}{path}"
        user_agent = f"zerochan-mcp - {ZEROCHAN_USERNAME}"
    
        async with httpx.AsyncClient(timeout=15.0) as client:
            response = await client.get(url, params=params, headers={"User-Agent": user_agent})
            response.raise_for_status()
            return response.json()
  • format_post_list_markdown helper function that formats a list of Zerochan post items into a Markdown table with columns for ID, tags, dimensions, favorites, and full image links.
    def format_post_list_markdown(items: list, source: str) -> str:
        """Format a list of post summaries as a Markdown table."""
        if not items:
            return "No results found."
    
        lines = [f"### Zerochan Results from `{source}`\n"]
        lines.append(f"| ID | Tags | Dimensions | Favorites | Full Image |")
        lines.append(f"|---|---|---|---|---|")
    
        for item in items:
            entry_id = item.get("id", "N/A")
            tags = ", ".join(item.get("tags", [])[:5])
            if len(item.get("tags", [])) > 5:
                tags += f" (+{len(item['tags']) - 5} more)"
            width = item.get("width", "?")
            height = item.get("height", "?")
            fav = item.get("fav", "?")
            full_url = item.get("full", f"https://www.zerochan.net/{entry_id}")
            lines.append(f"| [{entry_id}]({full_url}) | {tags} | {width}×{height} | {fav} | [View]({full_url}) |")
    
        return "\n".join(lines)
Behavior4/5

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

Annotations already provide readOnlyHint=true, destructiveHint=false, openWorldHint=true, and idempotentHint=true, indicating safe, non-destructive, open-ended, and idempotent behavior. The description adds valuable context beyond annotations, such as tag naming conventions ('Title Case with spaces'), strict mode rules, and output format details, enhancing transparency without contradictions.

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

Conciseness4/5

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

The description is well-structured and front-loaded with the core purpose, followed by usage details and parameter explanations. It is appropriately sized but includes some redundancy (e.g., repeating parameter details that could be inferred from schema), slightly reducing efficiency.

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, rich annotations, and detailed input schema, the description is complete. It covers purpose, usage, parameters, and output behavior, and with an output schema present, it need not explain return values in depth. The description provides all necessary context for effective tool invocation.

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

Parameters5/5

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

The input schema has 0% description coverage, so the description fully compensates by detailing all parameters in the 'Args' section, including their purposes, defaults, constraints, and examples. It adds meaning beyond the schema's structure, such as explaining tag formatting, strict mode behavior, and output format implications.

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's purpose: 'Search Zerochan entries by one or more tags.' It specifies the verb ('Search'), resource ('Zerochan entries'), and scope ('by one or more tags'), distinguishing it from siblings like 'zerochan_browse' and 'zerochan_get_entry' by focusing on tag-based search rather than browsing or fetching specific entries.

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 usage, such as supporting 'single-tag, multi-tag, and strict-mode queries,' and explains strict mode limitations. However, it does not explicitly state when to use this tool versus its siblings (e.g., 'zerochan_browse' or 'zerochan_get_entry'), missing explicit alternatives or exclusions.

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/citronlegacy/zero-chan-mcp'

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