Skip to main content
Glama
Ukenn2112

Bangumi TV MCP Service

by Ukenn2112

get_character_subjects

Retrieve a list of subjects (e.g., anime, games) where a specific character appears by providing the character ID. Supports querying through Bangumi TV MCP Service for accurate results.

Instructions

List subjects (e.g., anime, games) where a character appears.

Args:
    character_id: The ID of the character.

Returns:
    Formatted list of related subjects or an error message.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
character_idYes

Implementation Reference

  • The main handler function for the 'get_character_subjects' tool. It is decorated with @mcp.tool() which also serves as registration. Fetches related subjects for a given character ID from the Bangumi API endpoint /v0/characters/{character_id}/subjects, handles API errors, validates response format, formats each subject with name, type, and role info, and returns a formatted string summary.
    @mcp.tool()
    async def get_character_subjects(character_id: int) -> str:
        """
        List subjects (e.g., anime, games) where a character appears.
    
        Args:
            character_id: The ID of the character.
    
        Returns:
            Formatted list of related subjects or an error message.
        """
        response = await make_bangumi_request(
            method="GET", path=f"/v0/characters/{character_id}/subjects"
        )
    
        error_msg = handle_api_error_response(response)
        if error_msg:
            return error_msg
    
        # Expecting a list of subjects
        if not isinstance(response, list):
            return f"Unexpected API response format for get_character_subjects: {response}"
    
        related_subjects = response
        if not related_subjects:
            return f"No subjects found related to character ID {character_id}."
    
        formatted_results = []
        for rel_subject in related_subjects:
            name = rel_subject.get("name")
            name_cn = rel_subject.get("name_cn")
            rel_id = rel_subject.get("id")
            rel_type_int = rel_subject.get("type")
            try:
                rel_type_str = (
                    SubjectType(rel_type_int).name
                    if rel_type_int is not None
                    else "Unknown Type"
                )
            except ValueError:
                rel_type_str = f"Unknown Type ({rel_type_int})"
    
            staff_info = rel_subject.get(
                "staff"
            )  # Staff refers to the role of the char in the subject e.g. "主角"
    
            formatted_results.append(
                f"Subject ID: {rel_id}, Name: {name_cn or name}, Type: {rel_type_str}, Role/Staff (in subject): {staff_info}"
            )
    
        return "Subjects This Character Appears In:\n" + "\n---\n".join(formatted_results)
  • Helper function used by get_character_subjects (and others) to format individual subject summaries with type, name, score, rank, summary, and image.
    def format_subject_summary(subject: Dict[str, Any]) -> str:
        """Formats a subject dictionary into a readable summary string."""
        name = subject.get("name")
        name_cn = subject.get("name_cn")
        subject_type = subject.get("type")
        subject_id = subject.get("id")
        score = subject.get("rating", {}).get("score")  # Access Nested Score
        rank = subject.get("rating", {}).get("rank")  # Access Nested Rank
        summary = subject.get("short_summary") or subject.get("summary", "")
    
        try:
            type_str = (
                SubjectType(subject_type).name
                if subject_type is not None
                else "Unknown Type"
            )
        except ValueError:
            type_str = f"Unknown Type ({subject_type})"
    
        formatted_string = f"[{type_str}] {name_cn or name} (ID: {subject_id})\n"
        if score is not None:
            formatted_string += f"  Score: {score}\n"
        if rank is not None:
            formatted_string += f"  Rank: {rank}\n"
        if summary:
            formatted_summary = summary  # [:200] + '...' if len(summary) > 200 else summary
            formatted_string += f"  Summary: {formatted_summary}\n"
    
        # Add images URL if available (for potential LLM multi-modal future use or user info)
        images = subject.get("images")
        if images and images.get("common"):
            formatted_string += f"  Image: {images.get('common')}\n"  # Or 'grid', 'large', 'medium', 'small' depending on preference
    
        return formatted_string
  • Core helper function used by all Bangumi API tools, including get_character_subjects, to make HTTP requests with authentication, error handling, and debug logging.
    async def make_bangumi_request(
        method: str,
        path: str,
        query_params: Optional[Dict[str, Any]] = None,
        json_body: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
    ) -> Any:
        """Make a request to the Bangumi API with proper headers and error handling."""
        request_headers = headers.copy() if headers else {}
        request_headers["User-Agent"] = USER_AGENT
        request_headers["Accept"] = "application/json"
    
        if BANGUMI_TOKEN:
            request_headers["Authorization"] = f"Bearer {BANGUMI_TOKEN}"
    
        url = f"{BANGUMI_API_BASE}{path}"
    
        async with httpx.AsyncClient() as client:
            try:
                print(
                    f"DEBUG: Making {method} request to {url} with params={query_params}, json={json_body}"
                )
                response = await client.request(
                    method=method,
                    url=url,
                    params=query_params,
                    json=json_body,
                    headers=request_headers,
                    timeout=30.0,
                )
                response.raise_for_status()
                # Return the raw JSON response, let the calling tool handle its structure (dict or list)
                json_response = response.json()
                print(
                    f"DEBUG: Received response (type: {type(json_response)}, keys/length: {list(json_response.keys()) if isinstance(json_response, dict) else len(json_response) if isinstance(json_response, list) else 'N/A'})"
                )
                return json_response
            except httpx.HTTPStatusError as e:
                error_msg = (
                    f"HTTP error occurred: {e.response.status_code} - {e.response.text}"
                )
                print(f"ERROR: {error_msg}")
                # Try to parse the error response body if it's JSON
                try:
                    error_details = e.response.json()
                    return {
                        "error": error_msg,
                        "status_code": e.response.status_code,
                        "details": error_details,
                    }
                except json.JSONDecodeError:
                    return {
                        "error": error_msg,
                        "status_code": e.response.status_code,
                        "details": e.response.text,
                    }
            except httpx.RequestError as e:
                error_msg = f"An error occurred while requesting {e.request.url!r}: {e}"
                print(f"ERROR: {error_msg}")
                return {"error": error_msg}
            except Exception as e:
                error_msg = f"An unexpected error occurred: {e}"
                print(f"ERROR: {error_msg}")
                return {"error": error_msg}
  • Helper function used by get_character_subjects (and all tools) to detect and format API error responses from make_bangumi_request or direct API errors.
    def handle_api_error_response(response: Any) -> Optional[str]:
        """
        Checks if the API response indicates an error and returns a formatted error message.
        Handles both dictionary-based errors and returns from make_bangumi_request on failure.
        """
        # Check for error structure returned by make_bangumi_request on HTTPStatusError or RequestError
        if isinstance(response, dict) and (
            "error" in response or "status_code" in response
        ):
            # This is an error dictionary created by our helper
            status_code = response.get("status_code", "N/A")
            error_msg = response.get("error", "Unknown error during request.")
            details = response.get("details", "")
            return f"Bangumi API Request Error (Status {status_code}): {error_msg}. Details: {details}".strip()
    
        # Check for error structure returned by Bangumi API itself (often dictionaries)
        # Safely check if the response is a dictionary before accessing its keys
        if isinstance(response, dict):
            if "title" in response and "description" in response:
                # This looks like a common Bangumi error response structure
                error_title = response.get("title", "API Error")
                error_description = response.get("description", "No description provided.")
                # The API might return a status code in the body too, or rely on HTTP status
                return f"Bangumi API Error: {error_title}. {error_description}".strip()
    
            # Check if it's a dictionary but *not* empty and *doesn't* look like a success response from list endpoints
            # Check for specific error fields if structure varies
            # Add more checks here if other error dictionary formats are observed
            # Example: if "message" in response and "code" in response: return f"API Error {response['code']}: {response['message']}"
            pass  # If it's a dictionary but doesn't match known error formats, assume it's a valid data response for now
    
        # If it's not a dictionary, or it's a dictionary that doesn't match known error formats, assume it's not an error
        return None
  • main.py:1005-1005 (registration)
    The @mcp.tool() decorator registers the get_character_subjects function as an MCP tool.
    @mcp.tool()

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/Ukenn2112/BangumiMCP'

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