mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
feat: universal auto-timeout for all MCP tools with CLI support
- Add @auto_timeout decorator to all 102 Python MCP tools - Implement human-readable timeout parsing (2min, 5m, 120s, etc) - Add CLI options: --timeout, --search-timeout, --find-timeout, --ast-timeout - Support environment variable configuration (HANZO_MCP_TOOL_TIMEOUT) - Automatic backgrounding of long-running operations (default: 2 minutes) - Complete process management with IDs, logs, and kill capability - Bump version to 0.8.17 Breaking: Tools now automatically background after configured timeout
This commit is contained in:
@@ -15,6 +15,19 @@ import argparse
|
||||
from typing import Any, cast
|
||||
from pathlib import Path
|
||||
|
||||
# Import timeout parser (deferred to avoid early imports)
|
||||
def _parse_timeout_arg(timeout_str: str) -> float:
|
||||
"""Parse timeout argument with human-readable format support."""
|
||||
try:
|
||||
from hanzo_mcp.tools.common.timeout_parser import parse_timeout
|
||||
return parse_timeout(timeout_str)
|
||||
except ImportError:
|
||||
# Fallback if parser not available
|
||||
try:
|
||||
return float(timeout_str)
|
||||
except ValueError:
|
||||
raise ValueError(f"Invalid timeout format: '{timeout_str}'")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run the CLI for the Hanzo AI server."""
|
||||
@@ -150,9 +163,37 @@ def main() -> None:
|
||||
_ = parser.add_argument(
|
||||
"--command-timeout",
|
||||
dest="command_timeout",
|
||||
type=float,
|
||||
default=120.0,
|
||||
help="Default timeout for command execution in seconds (default: 120.0)",
|
||||
type=str,
|
||||
default="120s",
|
||||
help="Default timeout for command execution (default: 120s). Supports: 2min, 5m, 120s, 30sec, 1.5h",
|
||||
)
|
||||
|
||||
_ = parser.add_argument(
|
||||
"--timeout", "-t",
|
||||
dest="tool_timeout",
|
||||
type=str,
|
||||
help="Default timeout for MCP tool operations (default: 2min). Supports: 2min, 5m, 120s, 30sec, 1.5h",
|
||||
)
|
||||
|
||||
_ = parser.add_argument(
|
||||
"--search-timeout",
|
||||
dest="search_timeout",
|
||||
type=str,
|
||||
help="Timeout specifically for search operations. Supports: 2min, 5m, 120s, 30sec, 1.5h",
|
||||
)
|
||||
|
||||
_ = parser.add_argument(
|
||||
"--find-timeout",
|
||||
dest="find_timeout",
|
||||
type=str,
|
||||
help="Timeout specifically for find operations. Supports: 2min, 5m, 120s, 30sec, 1.5h",
|
||||
)
|
||||
|
||||
_ = parser.add_argument(
|
||||
"--ast-timeout",
|
||||
dest="ast_timeout",
|
||||
type=str,
|
||||
help="Timeout specifically for AST operations. Supports: 2min, 5m, 120s, 30sec, 1.5h",
|
||||
)
|
||||
|
||||
_ = parser.add_argument(
|
||||
@@ -222,6 +263,26 @@ def main() -> None:
|
||||
pass
|
||||
sys.stdout = original_stdout
|
||||
|
||||
# Parse timeout arguments with human-readable format support
|
||||
command_timeout = _parse_timeout_arg(str(args.command_timeout))
|
||||
|
||||
# Set timeout environment variables from CLI args
|
||||
if hasattr(args, 'tool_timeout') and args.tool_timeout:
|
||||
tool_timeout = _parse_timeout_arg(args.tool_timeout)
|
||||
os.environ["HANZO_MCP_TOOL_TIMEOUT"] = str(tool_timeout)
|
||||
|
||||
if hasattr(args, 'search_timeout') and args.search_timeout:
|
||||
search_timeout = _parse_timeout_arg(args.search_timeout)
|
||||
os.environ["HANZO_MCP_SEARCH_TIMEOUT"] = str(search_timeout)
|
||||
|
||||
if hasattr(args, 'find_timeout') and args.find_timeout:
|
||||
find_timeout = _parse_timeout_arg(args.find_timeout)
|
||||
os.environ["HANZO_MCP_FIND_TIMEOUT"] = str(find_timeout)
|
||||
|
||||
if hasattr(args, 'ast_timeout') and args.ast_timeout:
|
||||
ast_timeout = _parse_timeout_arg(args.ast_timeout)
|
||||
os.environ["HANZO_MCP_AST_TIMEOUT"] = str(ast_timeout)
|
||||
|
||||
# Cast args attributes to appropriate types to avoid 'Any' warnings
|
||||
name: str = cast(str, args.name)
|
||||
install: bool = cast(bool, args.install)
|
||||
@@ -234,7 +295,6 @@ def main() -> None:
|
||||
agent_max_iterations: int = cast(int, args.agent_max_iterations)
|
||||
agent_max_tool_uses: int = cast(int, args.agent_max_tool_uses)
|
||||
enable_agent_tool: bool = cast(bool, args.enable_agent_tool)
|
||||
command_timeout: float = cast(float, args.command_timeout)
|
||||
disable_write_tools: bool = cast(bool, args.disable_write_tools)
|
||||
disable_search_tools: bool = cast(bool, args.disable_search_tools)
|
||||
host: str = cast(str, args.host)
|
||||
|
||||
@@ -6,6 +6,8 @@ enabling concurrent execution of multiple operations and specialized processing.
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
|
||||
|
||||
# Import unified CLI tools (single source of truth)
|
||||
@@ -78,6 +80,9 @@ def register_agent_tools(
|
||||
def __init__(self, agent_tool: AgentTool):
|
||||
self._agent = agent_tool
|
||||
|
||||
@auto_timeout("__init__")
|
||||
|
||||
|
||||
async def call(self, ctx, **params): # type: ignore[override]
|
||||
# Default to 5 agents unless explicitly provided
|
||||
params = dict(params)
|
||||
|
||||
@@ -30,6 +30,8 @@ from pydantic import Field
|
||||
from openai.types.chat import ChatCompletionMessageParam
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.jupyter import get_read_only_jupyter_tools
|
||||
from hanzo_mcp.tools.filesystem import get_read_only_filesystem_tools
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -212,6 +214,9 @@ Modes:
|
||||
- rpc: Long-running agent for multiple calls (A2A support)"""
|
||||
|
||||
@override
|
||||
@auto_timeout("agent")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -20,6 +20,8 @@ from typing import (
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Import hanzo-agents SDK
|
||||
try:
|
||||
from hanzo_agents import (
|
||||
@@ -338,6 +340,7 @@ Usage notes:
|
||||
self.available_tools.append(BatchTool({t.name: t for t in self.available_tools}))
|
||||
|
||||
@override
|
||||
@auto_timeout("agent")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -19,6 +19,8 @@ with warnings.catch_warnings():
|
||||
import litellm
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from openai.types.chat import ChatCompletionToolParam, ChatCompletionMessageParam
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
@@ -177,6 +179,9 @@ Usage notes:
|
||||
self.review_protocol = ReviewProtocol()
|
||||
|
||||
@override
|
||||
@auto_timeout("agent_tool_v1_deprecated")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional, override
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
|
||||
|
||||
@@ -42,6 +43,7 @@ request_clarification(
|
||||
options=["github.com/luxfi/node/common", "github.com/project/common"]
|
||||
)"""
|
||||
|
||||
@auto_timeout("clarification")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
"""Claude Desktop authentication management.
|
||||
|
||||
This module provides tools to automate Claude Desktop login/logout,
|
||||
@@ -448,6 +450,9 @@ claude_auth ensure_agent swarm_agent_1"""
|
||||
"""Initialize the auth tool."""
|
||||
self.auth = ClaudeDesktopAuth()
|
||||
|
||||
@auto_timeout("claude_desktop_auth")
|
||||
|
||||
|
||||
async def call(self, ctx, action: str = "status", **kwargs) -> str:
|
||||
"""Execute auth action."""
|
||||
tool_ctx = create_tool_context(ctx)
|
||||
|
||||
@@ -13,6 +13,8 @@ from typing import List, Optional
|
||||
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -166,6 +168,9 @@ class CLIAgentBase(BaseTool):
|
||||
await tool_ctx.error(f"{self.provider_name} error: {str(e)}")
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
@auto_timeout("cli_agent_base")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, prompts: str, **kwargs) -> str:
|
||||
"""Execute the CLI agent.
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -186,6 +188,9 @@ class ClaudeCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute Claude CLI for AI assistance using Anthropic's models"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt: str = params.get("prompt", "")
|
||||
model: Optional[str] = params.get("model") or self.default_model
|
||||
@@ -236,6 +241,9 @@ class CodexCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute OpenAI Codex/GPT-4 CLI for code generation and AI assistance"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt: str = params.get("prompt", "")
|
||||
model: Optional[str] = params.get("model") or self.default_model
|
||||
@@ -274,6 +282,9 @@ class GeminiCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute Google Gemini CLI for multimodal AI assistance"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt: str = params.get("prompt", "")
|
||||
model: Optional[str] = params.get("model") or self.default_model
|
||||
@@ -312,6 +323,9 @@ class GrokCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute xAI Grok CLI for real-time AI assistance"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt: str = params.get("prompt", "")
|
||||
model: Optional[str] = params.get("model") or self.default_model
|
||||
@@ -350,6 +364,9 @@ class OpenHandsCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute OpenHands (OpenDevin) for autonomous coding assistance"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt = params.get("prompt", "")
|
||||
model = params.get("model") or self.default_model
|
||||
@@ -400,6 +417,9 @@ class HanzoDevCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute Hanzo Dev for AI-powered code editing and development"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt = params.get("prompt", "")
|
||||
model = params.get("model") or self.default_model
|
||||
@@ -438,6 +458,9 @@ class ClineCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute Cline for autonomous coding with Claude"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt = params.get("prompt", "")
|
||||
working_dir: str = params.get("working_dir") or os.getcwd()
|
||||
@@ -473,6 +496,9 @@ class AiderCLITool(BaseCLITool):
|
||||
def description(self) -> str:
|
||||
return "Execute Aider for AI pair programming"
|
||||
|
||||
@auto_timeout("cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
prompt = params.get("prompt", "")
|
||||
model = params.get("model") or self.default_model
|
||||
|
||||
@@ -6,6 +6,8 @@ This tool manages API keys and accounts for Claude Code and other AI coding tool
|
||||
from typing import Unpack, Optional, TypedDict, final, override
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -67,6 +69,9 @@ Providers: claude, openai, azure, deepseek, google, groq"""
|
||||
self.auth_manager = CodeAuthManager()
|
||||
|
||||
@override
|
||||
@auto_timeout("code_auth")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import List, Optional, override
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
|
||||
|
||||
@@ -57,6 +58,7 @@ critic(
|
||||
specific_concerns="Are the imports in the correct format and location?"
|
||||
)"""
|
||||
|
||||
@auto_timeout("critic")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,8 @@ from enum import Enum
|
||||
from typing import List, override
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -599,6 +601,9 @@ Use this when you need:
|
||||
super().__init__()
|
||||
self.oracle = IChing()
|
||||
|
||||
@auto_timeout("iching")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, challenge: str) -> str:
|
||||
"""Cast I Ching and provide guidance."""
|
||||
# Cast hexagram
|
||||
|
||||
@@ -18,6 +18,8 @@ from typing import (
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -104,6 +106,9 @@ class NetworkTool(BaseTool):
|
||||
return self._cluster
|
||||
|
||||
@override
|
||||
@auto_timeout("network")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params: Unpack[NetworkToolParams]) -> str:
|
||||
"""Execute a task on the agent network.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import List, Optional, override
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
|
||||
|
||||
@@ -57,6 +58,7 @@ review(
|
||||
context="This will be used to automatically fix missing imports in Go files"
|
||||
)"""
|
||||
|
||||
@auto_timeout("review")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
"""Swarm tool as an alias to Network tool for backward compatibility.
|
||||
|
||||
This module makes swarm an alias to the network tool, as network is the
|
||||
@@ -63,6 +65,9 @@ For new code, prefer using 'network' directly."""
|
||||
# Just pass through to NetworkTool
|
||||
super().__init__(permission_manager=permission_manager, default_mode=default_mode, **kwargs)
|
||||
|
||||
@auto_timeout("swarm_alias")
|
||||
|
||||
|
||||
async def call(self, **kwargs) -> str:
|
||||
"""Execute swarm via network tool.
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ from typing import (
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Import hanzo-agents SDK with fallback
|
||||
try:
|
||||
from hanzo_agents import (
|
||||
@@ -508,6 +510,7 @@ Models can be specified as:
|
||||
self.available_tools.append(BatchTool({t.name: t for t in self.available_tools}))
|
||||
|
||||
@override
|
||||
@auto_timeout("swarm")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -18,6 +18,8 @@ from typing import (
|
||||
)
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -203,6 +205,9 @@ Models can be specified as:
|
||||
self.agent_max_tool_uses = agent_max_tool_uses
|
||||
|
||||
@override
|
||||
@auto_timeout("swarm_tool_v1_deprecated")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -11,6 +11,8 @@ from typing import Any, Dict, List, Optional
|
||||
from pathlib import Path
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context
|
||||
|
||||
from ..common.base import BaseTool
|
||||
@@ -142,6 +144,9 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
||||
command.append(prompt)
|
||||
return command
|
||||
|
||||
@auto_timeout("unified_cli_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
||||
"""Execute the CLI tool via MCP interface.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from typing import Any, Callable, Optional, Tuple
|
||||
from collections.abc import Awaitable
|
||||
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
from hanzo_mcp.tools.shell.base_process import ProcessManager
|
||||
from .timeout_parser import parse_timeout, format_timeout
|
||||
|
||||
|
||||
class MCPToolTimeoutManager:
|
||||
@@ -27,19 +27,28 @@ class MCPToolTimeoutManager:
|
||||
# Environment variable to configure timeout
|
||||
TIMEOUT_ENV_VAR = "HANZO_MCP_TOOL_TIMEOUT"
|
||||
|
||||
def __init__(self, process_manager: Optional[ProcessManager] = None):
|
||||
def __init__(self, process_manager: Optional[Any] = None):
|
||||
"""Initialize the timeout manager.
|
||||
|
||||
|
||||
Args:
|
||||
process_manager: Process manager for tracking background operations
|
||||
"""
|
||||
self.process_manager = process_manager or ProcessManager()
|
||||
if process_manager is None:
|
||||
# Lazy import to avoid circular imports
|
||||
try:
|
||||
from hanzo_mcp.tools.shell.base_process import ProcessManager
|
||||
self.process_manager = ProcessManager()
|
||||
except ImportError:
|
||||
# If ProcessManager is not available, disable backgrounding
|
||||
self.process_manager = None
|
||||
else:
|
||||
self.process_manager = process_manager
|
||||
|
||||
# Get timeout from environment or use default
|
||||
env_timeout = os.getenv(self.TIMEOUT_ENV_VAR)
|
||||
if env_timeout:
|
||||
try:
|
||||
self.timeout = float(env_timeout)
|
||||
self.timeout = parse_timeout(env_timeout)
|
||||
except ValueError:
|
||||
self.timeout = self.DEFAULT_TIMEOUT
|
||||
else:
|
||||
@@ -59,7 +68,7 @@ class MCPToolTimeoutManager:
|
||||
tool_timeout = os.getenv(env_var)
|
||||
if tool_timeout:
|
||||
try:
|
||||
return float(tool_timeout)
|
||||
return parse_timeout(tool_timeout)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -161,8 +170,9 @@ def with_auto_timeout(tool_name: str, timeout_manager: Optional[MCPToolTimeoutMa
|
||||
)
|
||||
|
||||
# Return backgrounding message
|
||||
timeout_formatted = format_timeout(tool_timeout)
|
||||
return (
|
||||
f"Operation automatically backgrounded after {tool_timeout:.1f}s\\n"
|
||||
f"Operation automatically backgrounded after {timeout_formatted}\\n"
|
||||
f"Process ID: {process_id}\\n"
|
||||
f"Log file: {log_file}\\n\\n"
|
||||
f"Use 'process --action logs --id {process_id}' to view results\\n"
|
||||
|
||||
@@ -11,6 +11,8 @@ from typing import Any, Callable, final
|
||||
from collections.abc import Awaitable
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.validation import (
|
||||
@@ -88,6 +90,8 @@ class BaseTool(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@auto_timeout("base")
|
||||
|
||||
async def call(self, ctx: MCPContext, **params: Any) -> Any:
|
||||
"""Execute the tool with the given parameters.
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ from typing import Any, Unpack, Annotated, TypedDict, final, override
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -154,6 +156,9 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
||||
self.tools = tools
|
||||
|
||||
@override
|
||||
@auto_timeout("batch")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import Any, Dict, Unpack, Optional, TypedDict, final
|
||||
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.config.settings import (
|
||||
ProjectConfig,
|
||||
MCPServerConfig,
|
||||
@@ -62,6 +64,9 @@ Perfect for AI-driven configuration where users can say things like:
|
||||
|
||||
Automatically detects projects based on LLM.md files and manages .hanzo/ directories."""
|
||||
|
||||
@auto_timeout("config")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -131,6 +133,9 @@ Recommendations:
|
||||
pass
|
||||
|
||||
@override
|
||||
@auto_timeout("critic")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from typing import Any, Dict, Union
|
||||
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool, handle_connection_errors
|
||||
from hanzo_mcp.tools.common.pagination import CursorManager
|
||||
from hanzo_mcp.tools.common.paginated_response import paginate_if_needed
|
||||
@@ -44,6 +46,8 @@ class PaginatedBaseTool(BaseTool):
|
||||
pass
|
||||
|
||||
@handle_connection_errors
|
||||
@auto_timeout("paginated_base")
|
||||
|
||||
async def call(self, ctx: MCPContext, **params: Any) -> Union[str, Dict[str, Any]]:
|
||||
"""Execute the tool with automatic pagination support.
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ from datetime import datetime
|
||||
import psutil
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
@@ -57,6 +59,9 @@ Example:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -93,6 +95,9 @@ Feature Implementation Planning
|
||||
pass
|
||||
|
||||
@override
|
||||
@auto_timeout("thinking")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
"""Human-readable timeout parsing utilities."""
|
||||
|
||||
import re
|
||||
from typing import Union
|
||||
|
||||
|
||||
def parse_timeout(timeout_str: Union[str, int, float]) -> float:
|
||||
"""Parse timeout from human-readable string or numeric value.
|
||||
|
||||
Supports formats like:
|
||||
- "2min", "5m", "120s", "30sec", "1.5h", "0.5hr"
|
||||
- 120 (seconds as number)
|
||||
- "120" (seconds as string)
|
||||
|
||||
Args:
|
||||
timeout_str: Timeout value as string or number
|
||||
|
||||
Returns:
|
||||
Timeout in seconds as float
|
||||
|
||||
Raises:
|
||||
ValueError: If format is not recognized
|
||||
"""
|
||||
if isinstance(timeout_str, (int, float)):
|
||||
return float(timeout_str)
|
||||
|
||||
if isinstance(timeout_str, str):
|
||||
# Handle pure numeric strings
|
||||
try:
|
||||
return float(timeout_str)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Handle human-readable formats
|
||||
timeout_str = timeout_str.lower().strip()
|
||||
|
||||
# Regex patterns for different time units
|
||||
patterns = [
|
||||
# Hours: 1h, 1.5hr, 2hour, 3hours
|
||||
(r'^(\d*\.?\d+)\s*h(?:r|our|ours)?$', 3600),
|
||||
# Minutes: 2m, 5min, 10mins, 1.5minute
|
||||
(r'^(\d*\.?\d+)\s*m(?:in|ins|inute|inutes)?$', 60),
|
||||
# Seconds: 30s, 120sec, 45secs, 60second, 90seconds
|
||||
(r'^(\d*\.?\d+)\s*s(?:ec|ecs|econd|econds)?$', 1),
|
||||
]
|
||||
|
||||
for pattern, multiplier in patterns:
|
||||
match = re.match(pattern, timeout_str)
|
||||
if match:
|
||||
value = float(match.group(1))
|
||||
return value * multiplier
|
||||
|
||||
# If no pattern matches, raise error
|
||||
raise ValueError(
|
||||
f"Invalid timeout format: '{timeout_str}'. "
|
||||
f"Supported formats: 2min, 5m, 120s, 30sec, 1.5h, 0.5hr, or numeric seconds."
|
||||
)
|
||||
|
||||
raise ValueError(f"Unsupported timeout type: {type(timeout_str)}")
|
||||
|
||||
|
||||
def format_timeout(seconds: float) -> str:
|
||||
"""Format timeout seconds into human-readable string.
|
||||
|
||||
Args:
|
||||
seconds: Timeout in seconds
|
||||
|
||||
Returns:
|
||||
Human-readable string like "2m", "90s", "1.5h"
|
||||
"""
|
||||
if seconds >= 3600: # >= 1 hour
|
||||
hours = seconds / 3600
|
||||
if hours.is_integer():
|
||||
return f"{int(hours)}h"
|
||||
else:
|
||||
return f"{hours:.1f}h"
|
||||
elif seconds >= 60: # >= 1 minute
|
||||
minutes = seconds / 60
|
||||
if minutes.is_integer():
|
||||
return f"{int(minutes)}m"
|
||||
else:
|
||||
return f"{minutes:.1f}m"
|
||||
else: # < 1 minute
|
||||
if seconds.is_integer():
|
||||
return f"{int(seconds)}s"
|
||||
else:
|
||||
return f"{seconds:.1f}s"
|
||||
|
||||
|
||||
# Test the parser
|
||||
if __name__ == "__main__":
|
||||
test_cases = [
|
||||
"2min", "5m", "120s", "30sec", "1.5h", "0.5hr",
|
||||
"90", 120, 3600.0, "1hour", "2hours", "30seconds"
|
||||
]
|
||||
|
||||
for case in test_cases:
|
||||
try:
|
||||
result = parse_timeout(case)
|
||||
formatted = format_timeout(result)
|
||||
print(f"{case} -> {result}s ({formatted})")
|
||||
except ValueError as e:
|
||||
print(f"{case} -> ERROR: {e}")
|
||||
@@ -5,6 +5,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
|
||||
@@ -72,6 +74,9 @@ Use 'tool_enable' to re-enable disabled tools.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("tool_disable")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
|
||||
@@ -119,6 +121,9 @@ Use 'tool_list' to see all available tools and their status.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("tool_enable")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
|
||||
@@ -158,6 +160,9 @@ Use 'tool_enable' and 'tool_disable' to change tool status.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("tool_list")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.config import load_settings, save_settings
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -104,6 +106,9 @@ config --action list
|
||||
config --action toggle index.scope --path ./project"""
|
||||
|
||||
@override
|
||||
@auto_timeout("config")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
from typing import Optional, override
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -310,6 +312,9 @@ mode --action current"""
|
||||
"""Handle mode tool calls."""
|
||||
return await tool_self.run(ctx, action=action, name=name)
|
||||
|
||||
@auto_timeout("mode")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(ctx, action=params.get("action", "list"), name=params.get("name"))
|
||||
|
||||
@@ -15,6 +15,8 @@ from typing import (
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
@@ -146,6 +148,9 @@ graph --action search --pattern "John" --node-type User
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("graph")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,6 +6,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -137,6 +139,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_add")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -15,6 +15,8 @@ from collections import deque
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -147,6 +149,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_query")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -116,6 +118,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_remove")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -117,6 +119,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from collections import defaultdict
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -97,6 +99,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -15,6 +15,8 @@ from typing import (
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
@@ -100,6 +102,9 @@ sql --action stats --table users
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("sql")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -89,6 +90,7 @@ Examples:
|
||||
Note: Use sql_search for text search operations."""
|
||||
|
||||
@override
|
||||
@auto_timeout("sql_query")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,6 +6,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -107,6 +109,9 @@ Examples:
|
||||
Use sql_query for complex queries with joins, conditions, etc."""
|
||||
|
||||
@override
|
||||
@auto_timeout("sql_search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,6 +6,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -75,6 +77,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("sql_stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from typing import List, Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -128,6 +130,9 @@ Note: Requires Neovim to be installed.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("neovim_command")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -125,6 +127,9 @@ Note: Requires Neovim to be installed and available in PATH.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("neovim_edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -11,6 +11,8 @@ from datetime import datetime
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
|
||||
@@ -114,6 +116,9 @@ Note: Requires Neovim to be installed.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("neovim_session")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -21,6 +21,8 @@ from typing_extensions import Unpack, Annotated, TypedDict, final, override
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -539,6 +541,9 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
||||
return all_results
|
||||
|
||||
@override
|
||||
@auto_timeout("batch_search")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params: Unpack[BatchSearchParams]) -> str:
|
||||
"""Execute batch search with multiple queries in parallel."""
|
||||
import time
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -101,6 +103,9 @@ Can be run in dry-run mode to preview changes without applying them.
|
||||
Only works within allowed directories."""
|
||||
|
||||
@override
|
||||
@auto_timeout("content_replace")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import override
|
||||
from pathlib import Path
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -200,6 +202,9 @@ diff a.json b.json --ignore-whitespace"""
|
||||
show_line_numbers=show_line_numbers,
|
||||
)
|
||||
|
||||
@auto_timeout("diff")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -8,6 +8,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.truncate import truncate_response
|
||||
@@ -85,6 +87,9 @@ indented list for readability. By default, common development directories like
|
||||
requested. Only works within allowed directories."""
|
||||
|
||||
@override
|
||||
@auto_timeout("directory_tree")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -19,6 +19,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -116,6 +118,9 @@ Use the cursor field to continue from where the previous request left off.
|
||||
Returns nextCursor if more entries are available."""
|
||||
|
||||
@override
|
||||
@auto_timeout("directory_tree_paginated")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -88,6 +90,9 @@ Usage:
|
||||
- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required."""
|
||||
|
||||
@override
|
||||
@auto_timeout("edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -23,6 +23,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
|
||||
# Parameter types
|
||||
@@ -147,6 +149,9 @@ Fast, intuitive file content search."""
|
||||
return self._available_backends
|
||||
|
||||
@override
|
||||
@auto_timeout("find")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,6 +6,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -138,6 +140,9 @@ For database search, use 'sql_search' or 'vector_search'.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("find_files")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -153,6 +155,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("git_search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -14,6 +14,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.context import ToolContext
|
||||
@@ -353,6 +355,9 @@ When you are doing an open ended search that may require multiple rounds of glob
|
||||
return f"Error searching file contents: {str(e)}"
|
||||
|
||||
@override
|
||||
@auto_timeout("grep")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -132,6 +134,9 @@ If you want to create a new file, use:
|
||||
- Subsequent edits: normal edit operations on the created content"""
|
||||
|
||||
@override
|
||||
@auto_timeout("multi_edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.truncate import truncate_response
|
||||
@@ -91,6 +93,9 @@ Usage:
|
||||
- When reading multiple files, you MUST use the batch tool to read them all at once"""
|
||||
|
||||
@override
|
||||
@auto_timeout("read")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -74,6 +76,9 @@ The tool returns the contents of all found configuration files to help
|
||||
understand project-specific requirements and preferences."""
|
||||
|
||||
@override
|
||||
@auto_timeout("rules")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -27,6 +27,8 @@ from dataclasses import dataclass
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -528,6 +530,9 @@ This is the recommended search tool for comprehensive results."""
|
||||
return results
|
||||
|
||||
@override
|
||||
@auto_timeout("search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -23,6 +23,8 @@ from pydantic import Field
|
||||
from grep_ast.grep_ast import TreeContext
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
|
||||
# Parameter types
|
||||
@@ -126,6 +128,9 @@ symbols --action list --path ./src --symbol-type class
|
||||
Finds code structures (functions, classes, methods) with full context."""
|
||||
|
||||
@override
|
||||
@auto_timeout("symbols")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
|
||||
# Parameter types
|
||||
@@ -95,6 +97,9 @@ tree --dirs-only
|
||||
tree --pattern "*.py" --show-size"""
|
||||
|
||||
@override
|
||||
@auto_timeout("tree")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -41,6 +43,9 @@ class WatchTool(BaseTool):
|
||||
duration=duration,
|
||||
)
|
||||
|
||||
@auto_timeout("watch")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -8,6 +8,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
@@ -72,6 +74,9 @@ Usage:
|
||||
- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User."""
|
||||
|
||||
@override
|
||||
@auto_timeout("write")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -16,6 +16,8 @@ import nbformat
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.jupyter.base import JupyterBaseTool
|
||||
|
||||
# Parameter types
|
||||
@@ -111,6 +113,9 @@ jupyter --action create "new.ipynb"
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("jupyter")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.jupyter.base import JupyterBaseTool
|
||||
@@ -96,6 +98,9 @@ class NoteBookEditTool(JupyterBaseTool):
|
||||
return "Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file) with new source. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path. The cell_number is 0-indexed. Use edit_mode=insert to add a new cell at the index specified by cell_number. Use edit_mode=delete to delete the cell at the index specified by cell_number."
|
||||
|
||||
@override
|
||||
@auto_timeout("notebook_edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.jupyter.base import JupyterBaseTool
|
||||
@@ -56,6 +58,9 @@ class NotebookReadTool(JupyterBaseTool):
|
||||
return "Reads a Jupyter notebook (.ipynb file) and returns all of the cells with their outputs. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path."
|
||||
|
||||
@override
|
||||
@auto_timeout("notebook_read")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -15,6 +15,7 @@ from typing import (
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.llm.llm_tool import LLMTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context, ToolContext
|
||||
@@ -155,6 +156,7 @@ The tool will:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("consensus")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.llm.llm_tool import LLMTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
@@ -110,6 +112,9 @@ Providers are automatically detected based on environment variables:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("llm_manage")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -21,6 +21,7 @@ from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Check if litellm is available
|
||||
try:
|
||||
@@ -263,6 +264,7 @@ llm --action models --provider openai
|
||||
Available: {", ".join(available) if available else "None"}"""
|
||||
|
||||
@override
|
||||
@auto_timeout("llm")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -19,6 +19,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
|
||||
@@ -263,6 +265,9 @@ llm --action models --provider openai
|
||||
Available: {", ".join(available) if available else "None"}"""
|
||||
|
||||
@override
|
||||
@auto_timeout("llm_unified")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import Dict, Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.llm.llm_tool import LLMTool
|
||||
|
||||
@@ -98,6 +100,9 @@ class BaseProviderTool(BaseTool):
|
||||
return model
|
||||
|
||||
@override
|
||||
@auto_timeout("provider_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
"""Language Server Protocol (LSP) tool for code intelligence.
|
||||
|
||||
This tool provides on-demand LSP configuration and installation for various
|
||||
@@ -472,6 +474,9 @@ class LSPTool(BaseTool):
|
||||
|
||||
return MCPResourceDocument(data=result)
|
||||
|
||||
@auto_timeout("lsp")
|
||||
|
||||
|
||||
async def call(self, **kwargs) -> str:
|
||||
"""Tool interface for MCP - converts result to JSON string."""
|
||||
result = await self.run(**kwargs)
|
||||
|
||||
@@ -8,6 +8,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
|
||||
@@ -131,6 +133,9 @@ Use 'mcp_stats' to see all added servers and their status.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp_add")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,6 +5,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
@@ -64,6 +66,9 @@ Use 'mcp_stats' to see all servers before removing.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp_remove")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -4,6 +4,8 @@ from typing import Unpack, TypedDict, final, override
|
||||
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
@@ -47,6 +49,9 @@ Example:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp_stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -20,6 +20,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
|
||||
@@ -270,6 +272,9 @@ mcp --action enable --name github
|
||||
Status: {enabled} enabled, {running} running"""
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ supporting hierarchical organization (session, project, global).
|
||||
from typing import Any, Dict, List, Optional, final, override
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -69,6 +71,9 @@ recall_facts(queries=["company policies"], scope="global", limit=5)
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -190,6 +195,9 @@ store_facts(facts=["Company founded in 2020"], scope="global", kb_name="company_
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -286,6 +294,9 @@ summarize_to_memory(content="Company guidelines...", topic="Guidelines", scope="
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -390,6 +401,9 @@ manage_knowledge_bases(action="delete", kb_name="old_docs")
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ The hanzo-memory package provides embedded database and vector search capabiliti
|
||||
from typing import Dict, List, Optional, final, override
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -69,6 +71,9 @@ recall_memories(queries=["coding standards"], scope="global")
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -155,6 +160,9 @@ create_memories(statements=["User prefers dark mode", "User works in Python"])
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, statements: List[str]) -> str:
|
||||
"""Create new memories.
|
||||
|
||||
@@ -219,6 +227,9 @@ update_memories(updates=[
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, updates: List[Dict[str, str]]) -> str:
|
||||
"""Update memories.
|
||||
|
||||
@@ -283,6 +294,9 @@ delete_memories(ids=["mem_1", "mem_2"])
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, ids: List[str]) -> str:
|
||||
"""Delete memories.
|
||||
|
||||
@@ -345,6 +359,9 @@ manage_memories(
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,7 @@ from pathlib import Path
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.shell.base_process import BaseScriptTool
|
||||
|
||||
|
||||
@@ -30,6 +31,7 @@ class BashTool(BaseScriptTool):
|
||||
) -> str:
|
||||
return await tool_self.run(ctx, command=command, cwd=cwd, env=env, timeout=timeout)
|
||||
|
||||
@auto_timeout("bash")
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -6,6 +6,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -105,6 +107,9 @@ Use run_command with 'tail -f' for continuous monitoring.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("logs")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -103,6 +105,9 @@ For long-running servers, use npx_background instead.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("npx")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -120,6 +122,9 @@ Use 'processes' to list running processes and 'pkill' to stop them.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("npx")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -4,6 +4,8 @@ from typing import Optional, override
|
||||
from pathlib import Path
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.shell.base_process import BaseBinaryTool
|
||||
@@ -88,6 +90,9 @@ npx json-server db.json # Auto-backgrounds if needed"""
|
||||
) -> str:
|
||||
return await tool_self.run(ctx, package=package, args=args, cwd=cwd, yes=yes)
|
||||
|
||||
@auto_timeout("npx")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -8,6 +8,8 @@ from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -26,6 +28,9 @@ class OpenTool(BaseTool):
|
||||
async def open(path: str, ctx: MCPContext) -> str:
|
||||
return await tool_self.run(ctx, path)
|
||||
|
||||
@auto_timeout("open")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(ctx, params["path"])
|
||||
|
||||
@@ -7,6 +7,8 @@ import psutil
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -106,6 +108,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("pkill")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -4,6 +4,8 @@ import signal
|
||||
from typing import Optional, override
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
@@ -132,6 +134,9 @@ process --action logs --id bash_ghi789 --lines 50"""
|
||||
) -> str:
|
||||
return await tool_self.run(ctx, action=action, id=id, signal_type=signal_type, lines=lines)
|
||||
|
||||
@auto_timeout("process")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -7,6 +7,8 @@ import psutil
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -90,6 +92,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("processes")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -10,6 +10,8 @@ from datetime import datetime
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -181,6 +183,9 @@ Examples:
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("run")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,6 +9,7 @@ from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.shell.base import ShellBaseTool
|
||||
from hanzo_mcp.tools.common.base import handle_connection_errors
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
@@ -269,6 +270,7 @@ Important:
|
||||
return tool_ctx
|
||||
|
||||
@override
|
||||
@auto_timeout("run_command")
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from typing import Any, Annotated, final, override
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.shell.base import ShellBaseTool
|
||||
@@ -194,6 +196,9 @@ Important:
|
||||
return tool_ctx
|
||||
|
||||
@override
|
||||
@auto_timeout("run_command_windows")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params: Any) -> str:
|
||||
"""Execute the tool with the given parameters.
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
"""Streaming command execution with disk-based logging and session management."""
|
||||
|
||||
import os
|
||||
@@ -173,6 +175,9 @@ class StreamingCommandTool(BaseProcessTool):
|
||||
|
||||
return None
|
||||
|
||||
@auto_timeout("streaming_command")
|
||||
|
||||
|
||||
async def call(self, ctx: Any, **kwargs) -> Dict[str, Any]:
|
||||
"""MCP tool entry point.
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -100,6 +102,9 @@ For long-running servers, use uvx_background instead.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("uvx")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -119,6 +121,9 @@ Use 'processes' to list running processes and 'pkill' to stop them.
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("uvx")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -4,6 +4,8 @@ from typing import Optional, override
|
||||
from pathlib import Path
|
||||
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.shell.base_process import BaseBinaryTool
|
||||
@@ -88,6 +90,9 @@ uvx jupyter lab --port 8888 # Auto-backgrounds if needed"""
|
||||
) -> str:
|
||||
return await tool_self.run(ctx, package=package, args=args, cwd=cwd, python=python)
|
||||
|
||||
@auto_timeout("uvx")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -9,6 +9,7 @@ from pathlib import Path
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.shell.base_process import BaseScriptTool
|
||||
|
||||
|
||||
@@ -31,6 +32,7 @@ class ZshTool(BaseScriptTool):
|
||||
) -> str:
|
||||
return await tool_self.run(ctx, command=command, cwd=cwd, env=env, timeout=timeout)
|
||||
|
||||
@auto_timeout("zsh")
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
@@ -171,6 +173,7 @@ class ShellTool(BaseScriptTool):
|
||||
) -> str:
|
||||
return await tool_self.run(ctx, command=command, cwd=cwd, env=env, timeout=timeout)
|
||||
|
||||
@auto_timeout("shell")
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -16,6 +16,8 @@ from datetime import datetime
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.todo.base import TodoBaseTool
|
||||
|
||||
# Parameter types
|
||||
@@ -104,6 +106,9 @@ todo --filter in_progress
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("todo")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.todo.base import TodoStorage, TodoBaseTool
|
||||
@@ -66,6 +68,9 @@ Usage:
|
||||
- If no todos exist yet for the session, an empty list will be returned"""
|
||||
|
||||
@override
|
||||
@auto_timeout("todo_read")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ from typing import Unpack, Literal, Annotated, TypedDict, final, override
|
||||
|
||||
from pydantic import Field
|
||||
from mcp.server import FastMCP
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.todo.base import TodoStorage, TodoBaseTool
|
||||
@@ -254,6 +256,9 @@ The assistant did not use the todo list because this is a single command executi
|
||||
When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully."""
|
||||
|
||||
@override
|
||||
@auto_timeout("todo_write")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,6 +8,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
@@ -105,6 +107,9 @@ Usage:
|
||||
- index --force: Force re-indexing of all files"""
|
||||
|
||||
@override
|
||||
@auto_timeout("index")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -15,6 +15,8 @@ from pathlib import Path
|
||||
from pydantic import Field
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
|
||||
@@ -131,6 +133,9 @@ vector --action clear --path ./old_code
|
||||
"""
|
||||
|
||||
@override
|
||||
@auto_timeout("vector")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user