Files
AstrBot/astrbot/api/tools.py
LIghtJUNction f2c0c2a9de feat: add _internal modules for MCP, Skills, and Tools
This commit adds the _internal package structure for AstrBot's
standardized MCP & Skills support:

astrbot/_internal/mcp/:
- MCPClient for MCP server connections
- MCPTool wrapper for MCP tools
- MCP configuration management

astrbot/_internal/skills/:
- SkillManager for skill lifecycle
- Skill parser and loader
- SkillToToolConverter for tool-based skills
- Prompt builder for skills

astrbot/_internal/tools/:
- ToolSchema, FunctionTool, ToolSet base definitions
- FunctionToolManager for tool registry
- Builtin tools (cron, send_message, kb_query)
- Tool providers (internal, plugin, computer)

astrbot/api/:
- Public API for tools (ToolRegistry, tool decorator)
- Public API for MCP (get_mcp_servers, register_mcp_server)
- Public API for skills (get_skill_manager, skill_to_tool)
2026-03-23 12:48:34 +08:00

111 lines
3.2 KiB
Python

"""
Tools Public API for AstrBot.
Example:
from astrbot.api.tools import tool, get_registry
@tool(name="weather", description="Get weather", parameters={...})
async def get_weather(city: str) -> str:
return f"Weather in {city} is sunny"
registry = get_registry()
tools = registry.list_tools()
"""
from __future__ import annotations
from collections.abc import Awaitable, Callable
from functools import wraps
from typing import Any
from astrbot.core.agent.tool import FunctionTool
__all__ = ["FunctionTool", "ToolRegistry", "get_registry", "tool"]
class ToolRegistry:
"""Simple wrapper around FunctionToolManager for tool registration."""
_instance: ToolRegistry | None = None
def __init__(self) -> None:
from astrbot.core.provider.register import llm_tools as func_tool_manager
self._manager = func_tool_manager
@classmethod
def get_instance(cls) -> ToolRegistry:
"""Get the singleton ToolRegistry instance."""
if cls._instance is None:
cls._instance = cls()
return cls._instance
def register(self, tool: FunctionTool) -> None:
"""Register a FunctionTool."""
self._manager.func_list.append(tool)
def unregister(self, name: str) -> bool:
"""Unregister a tool by name. Returns True if found and removed."""
for i, f in enumerate(self._manager.func_list):
if f.name == name:
self._manager.func_list.pop(i)
return True
return False
def list_tools(self) -> list[FunctionTool]:
"""List all registered tools."""
return self._manager.func_list.copy()
def get_tool(self, name: str) -> FunctionTool | None:
"""Get a tool by name."""
return self._manager.get_func(name)
def get_registry() -> ToolRegistry:
"""Get the global ToolRegistry instance."""
return ToolRegistry.get_instance()
def tool(
name: str,
description: str,
parameters: dict[str, Any] | None = None,
) -> Callable[
[Callable[..., Awaitable[str | None]]], Callable[..., Awaitable[str | None]]
]:
"""Decorator to register an async function as a tool.
Args:
name: Tool name (used by LLM to invoke it)
description: What the tool does
parameters: JSON Schema for parameters (optional)
Example:
@tool(name="weather", description="Get weather for a city", parameters={...})
async def get_weather(city: str) -> str:
return f"The weather in {city} is sunny"
"""
if parameters is None:
parameters = {"type": "object", "properties": {}}
def decorator(
func: Callable[..., Awaitable[str | None]],
) -> Callable[..., Awaitable[str | None]]:
func_tool = FunctionTool(
name=name,
description=description,
parameters=parameters,
handler=func,
handler_module_path=getattr(func, "__module__", ""),
source="api",
)
get_registry().register(func_tool)
@wraps(func)
async def wrapper(*args: Any, **kwargs: Any) -> str | None:
return await func(*args, **kwargs)
return wrapper
return decorator