Source code for animedex.mcp.tool_decorator
"""
Lightweight ``@mcp_tool`` decorator.
The decorator marks a callable as MCP-eligible without altering the
callable's runtime behaviour. Backends will eventually layer the
real MCP-package wiring on top of this marker; until then the
decorator gives backend authors a single, stable annotation site.
The module avoids importing the upstream ``mcp`` package so it can
be safely imported in environments where the optional ``[mcp]``
extra is not installed.
"""
from __future__ import annotations
from typing import Any, Callable
_MCP_TOOL_FLAG = "_animedex_mcp_tool"
_MCP_TOOL_NAME = "_animedex_mcp_tool_name"
[docs]
def mcp_tool(*, name: str) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""Mark a callable as an MCP-eligible tool.
The decorator stores ``name`` on the function object so
:func:`register_animedex_tools` (in :mod:`animedex.mcp.register`)
can pick it up. The returned callable is the original callable,
unchanged; no wrapping, no behaviour shift.
:param name: MCP tool identifier
(e.g. ``"animedex.anilist.search"``).
:type name: str
:return: A decorator that marks and returns the callable.
:rtype: Callable
"""
def _decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
setattr(fn, _MCP_TOOL_FLAG, True)
setattr(fn, _MCP_TOOL_NAME, name)
return fn
return _decorator
[docs]
def is_mcp_tool(fn: Callable[..., Any]) -> bool:
"""Return ``True`` when ``fn`` was decorated by :func:`mcp_tool`.
:param fn: A callable.
:type fn: Callable
:return: Whether the marker is present.
:rtype: bool
"""
return bool(getattr(fn, _MCP_TOOL_FLAG, False))
[docs]
def mcp_tool_name(fn: Callable[..., Any]) -> str:
"""Return the registered MCP tool name for ``fn``.
:param fn: A callable previously decorated by :func:`mcp_tool`.
:type fn: Callable
:return: The stored MCP tool identifier.
:rtype: str
:raises AttributeError: When ``fn`` was not decorated.
"""
return getattr(fn, _MCP_TOOL_NAME)
[docs]
def selftest() -> bool:
"""Smoke-test the decorator.
:return: ``True`` on success.
:rtype: bool
"""
@mcp_tool(name="_selftest.tool")
def _fn() -> int:
return 1
assert is_mcp_tool(_fn) is True
assert mcp_tool_name(_fn) == "_selftest.tool"
assert _fn() == 1
return True