Ctrl K
ComunidadGuías…

butler/mcp/server_stdio.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Expose a subset of Butler tools as an MCP Server (stdio, dev use)."""

from __future__ import annotations

import json
import logging
from typing import Any, cast

logger = logging.getLogger(__name__)

_EXPOSED = (
    "read_file",
    "list_directory",
    "search_files",
    "session_todos_list",
)


def run_stdio_server() -> int:
    try:
        from mcp.server.fastmcp import FastMCP
    except ImportError:
        logger.error("Install MCP SDK: pip install butler-system[mcp]")
        return 1

    mcp = FastMCP("butler")

    @mcp.tool(name="read_file", description="Read a file from the active Butler workspace")  # type: ignore[untyped-decorator]
    def mcp_read_file(path: str, offset: int = 1, limit: int = 500) -> str:
        return _dispatch_builtin("read_file", {"path": path, "offset": offset, "limit": limit})

    @mcp.tool(name="list_directory", description="List directory entries")  # type: ignore[untyped-decorator]
    def mcp_list_directory(path: str = ".") -> str:
        return _dispatch_builtin("list_directory", {"path": path})

    @mcp.tool(name="search_files", description="Ripgrep search in workspace")  # type: ignore[untyped-decorator]
    def mcp_search_files(pattern: str, path: str = ".", glob: str = "") -> str:
        args: dict[str, Any] = {"pattern": pattern, "path": path}
        if glob:
            args["glob"] = glob
        return _dispatch_builtin("search_files", args)

    @mcp.tool(name="session_todos_list", description="List session-scoped todos")  # type: ignore[untyped-decorator]
    def mcp_session_todos_list() -> str:
        return _dispatch_builtin("session_todos_list", {})

    mcp.run(transport="stdio")
    return 0


def _dispatch_builtin(name: str, args: dict[str, Any]) -> str:
    if name not in _EXPOSED:
        return json.dumps({"ok": False, "error": f"tool not exposed: {name}"})
    from butler.tools.registry import dispatch_tool

    return cast(str, dispatch_tool(name, args))