Skip to main content
Glama

zerochan_search

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

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)

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