mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-26 11:49:36 +00:00
style: Fix import sorting and formatting in hanzo-mcp
Run ruff check --fix and ruff format on pkg/hanzo-mcp
This commit is contained in:
@@ -198,18 +198,12 @@ async def run_agent_grinding_demo():
|
||||
|
||||
# Show the key benefits
|
||||
print("\n=== Key Benefits of Agent Grinding ===")
|
||||
print(
|
||||
"1. Parallel Processing: Multiple agents work on different files simultaneously"
|
||||
)
|
||||
print(
|
||||
"2. Full Context: Each agent has all tools - can read, search, understand before editing"
|
||||
)
|
||||
print("1. Parallel Processing: Multiple agents work on different files simultaneously")
|
||||
print("2. Full Context: Each agent has all tools - can read, search, understand before editing")
|
||||
print("3. Smart Filtering: Agents skip files that don't need changes")
|
||||
print("4. Precise Edits: Agents make surgical changes based on understanding")
|
||||
print("5. Scalable: Add more agents to handle larger codebases")
|
||||
print(
|
||||
"6. Flexible: Different patterns (pipeline, parallel, consensus) for different tasks"
|
||||
)
|
||||
print("6. Flexible: Different patterns (pipeline, parallel, consensus) for different tasks")
|
||||
|
||||
# Show example swarm command
|
||||
print("\n=== Example Swarm Command ===")
|
||||
@@ -231,9 +225,7 @@ swarm_tool(
|
||||
print("\nThe agents will:")
|
||||
print("- finder: Search and categorize all FIXMEs")
|
||||
print("- grinders: Work in parallel on different directories")
|
||||
print(
|
||||
"- Each grinder: Read file → Understand context → Fix if needed → Move to next"
|
||||
)
|
||||
print("- Each grinder: Read file → Understand context → Fix if needed → Move to next")
|
||||
print("- reviewer: Ensure all fixes are appropriate")
|
||||
|
||||
|
||||
|
||||
@@ -191,9 +191,7 @@ async def demonstrate_performance_gains():
|
||||
|
||||
speedup = sequential_total / parallel_total
|
||||
print(f"\nSPEEDUP: {speedup:.1f}x faster!")
|
||||
print(
|
||||
f"Time saved: {sequential_total - parallel_total}s ({(sequential_total - parallel_total) / 60:.1f} minutes)"
|
||||
)
|
||||
print(f"Time saved: {sequential_total - parallel_total}s ({(sequential_total - parallel_total) / 60:.1f} minutes)")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("KEY INSIGHTS:")
|
||||
|
||||
@@ -69,9 +69,7 @@ def show_usage_examples():
|
||||
|
||||
print("\n1. Claude Code CLI:")
|
||||
print(" claude_cli(prompts='Fix the type errors in main.py')")
|
||||
print(
|
||||
" claude_cli(prompts='Refactor this function', model='claude-3-opus-20240229')"
|
||||
)
|
||||
print(" claude_cli(prompts='Refactor this function', model='claude-3-opus-20240229')")
|
||||
|
||||
print("\n2. OpenAI Codex CLI:")
|
||||
print(" codex_cli(prompts='Generate unit tests for the Calculator class')")
|
||||
@@ -79,9 +77,7 @@ def show_usage_examples():
|
||||
|
||||
print("\n3. Google Gemini CLI:")
|
||||
print(" gemini_cli(prompts='Create a REST API with FastAPI')")
|
||||
print(
|
||||
" gemini_cli(prompts='Analyze security vulnerabilities', model='gemini-1.5-flash')"
|
||||
)
|
||||
print(" gemini_cli(prompts='Analyze security vulnerabilities', model='gemini-1.5-flash')")
|
||||
|
||||
print("\n4. xAI Grok CLI:")
|
||||
print(" grok_cli(prompts='Explain this regex pattern')")
|
||||
|
||||
@@ -133,16 +133,12 @@ Work with the file_agent to read code files.""",
|
||||
|
||||
# Example 2: Read and analyze
|
||||
print("\n📖 Example 2: Read and analyze a file")
|
||||
result = await network.run(
|
||||
prompt="Read the pyproject.toml file and tell me what this project is about"
|
||||
)
|
||||
result = await network.run(prompt="Read the pyproject.toml file and tell me what this project is about")
|
||||
print(f"Response: {result['final_output']}")
|
||||
|
||||
# Example 3: Multi-agent collaboration
|
||||
print("\n🤝 Example 3: Multi-agent collaboration")
|
||||
result = await network.run(
|
||||
prompt="Find all Python files that might contain the main entry point and analyze them"
|
||||
)
|
||||
result = await network.run(prompt="Find all Python files that might contain the main entry point and analyze them")
|
||||
print(f"Response: {result['final_output']}")
|
||||
print(f"Agents used: {result['iterations']}")
|
||||
|
||||
|
||||
@@ -107,9 +107,7 @@ IMPORTANT: Use multi_edit for efficiency - add all imports in one operation!
|
||||
ctx = type("Context", (), {})() # Mock context for example
|
||||
|
||||
start_time = asyncio.get_event_loop().time()
|
||||
result = await self.swarm.call(
|
||||
ctx, tasks=tasks, max_concurrency=max_concurrency
|
||||
)
|
||||
result = await self.swarm.call(ctx, tasks=tasks, max_concurrency=max_concurrency)
|
||||
end_time = asyncio.get_event_loop().time()
|
||||
|
||||
# Parse and display results
|
||||
@@ -140,9 +138,7 @@ IMPORTANT: Use multi_edit for efficiency - add all imports in one operation!
|
||||
print(f" Sequential estimate: {sequential_estimate:.1f}s")
|
||||
print(f" Parallel actual: {parallel_time:.1f}s")
|
||||
print(f" Speedup: {speedup:.1f}x")
|
||||
print(
|
||||
f" Efficiency: {(speedup / min(max_concurrency, len(tasks))) * 100:.1f}%"
|
||||
)
|
||||
print(f" Efficiency: {(speedup / min(max_concurrency, len(tasks))) * 100:.1f}%")
|
||||
|
||||
|
||||
async def main():
|
||||
|
||||
@@ -166,11 +166,7 @@ class TestModels(unittest.TestCase):
|
||||
],
|
||||
)
|
||||
|
||||
print(
|
||||
analysis_result[:1000] + "..."
|
||||
if len(analysis_result) > 1000
|
||||
else analysis_result
|
||||
)
|
||||
print(analysis_result[:1000] + "..." if len(analysis_result) > 1000 else analysis_result)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("PHASE 2: Parallel editing with swarm")
|
||||
@@ -225,9 +221,7 @@ class TestModels(unittest.TestCase):
|
||||
max_concurrent=2,
|
||||
)
|
||||
|
||||
print(
|
||||
review_result[:1500] + "..." if len(review_result) > 1500 else review_result
|
||||
)
|
||||
print(review_result[:1500] + "..." if len(review_result) > 1500 else review_result)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("PHASE 4: Final verification with batch")
|
||||
@@ -240,22 +234,16 @@ class TestModels(unittest.TestCase):
|
||||
invocations=[
|
||||
{
|
||||
"tool_name": "read",
|
||||
"input": {
|
||||
"file_path": os.path.join(test_dir, "src/models/user.py")
|
||||
},
|
||||
"input": {"file_path": os.path.join(test_dir, "src/models/user.py")},
|
||||
},
|
||||
{
|
||||
"tool_name": "agent",
|
||||
"input": {
|
||||
"prompts": f"Verify that all files in {test_dir}/src/models/ now have proper type hints"
|
||||
},
|
||||
"input": {"prompts": f"Verify that all files in {test_dir}/src/models/ now have proper type hints"},
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
print(
|
||||
verify_result[:1000] + "..." if len(verify_result) > 1000 else verify_result
|
||||
)
|
||||
print(verify_result[:1000] + "..." if len(verify_result) > 1000 else verify_result)
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("TEST COMPLETE!")
|
||||
|
||||
@@ -124,9 +124,7 @@ class HanzoMCPSwarmLauncher:
|
||||
|
||||
# Code Generator Agent
|
||||
def generate_code(spec: str) -> str:
|
||||
return (
|
||||
f"Generated code for: {spec}\ndef example():\n return 'Hello World'"
|
||||
)
|
||||
return f"Generated code for: {spec}\ndef example():\n return 'Hello World'"
|
||||
|
||||
generator_agent = create_local_agent(
|
||||
name="generator_agent",
|
||||
@@ -227,14 +225,10 @@ class HanzoMCPSwarmLauncher:
|
||||
print(f"\n🎯 Coordinating swarms for: {complex_task}")
|
||||
|
||||
# Phase 1: Analysis
|
||||
analysis_result = await self.launch_swarm(
|
||||
"analysis", f"Analyze the codebase for: {complex_task}"
|
||||
)
|
||||
analysis_result = await self.launch_swarm("analysis", f"Analyze the codebase for: {complex_task}")
|
||||
|
||||
# Phase 2: Development based on analysis
|
||||
dev_result = await self.launch_swarm(
|
||||
"development", f"Based on analysis, implement: {complex_task}"
|
||||
)
|
||||
dev_result = await self.launch_swarm("development", f"Based on analysis, implement: {complex_task}")
|
||||
|
||||
return {"analysis": analysis_result, "development": dev_result}
|
||||
|
||||
@@ -263,9 +257,7 @@ async def main():
|
||||
|
||||
# Demo 1: Simple swarm launch
|
||||
print("\n\n📋 Demo 1: Simple Analysis Swarm")
|
||||
await launcher.launch_swarm(
|
||||
"analysis", "Find all authentication-related code and analyze security"
|
||||
)
|
||||
await launcher.launch_swarm("analysis", "Find all authentication-related code and analyze security")
|
||||
|
||||
# Demo 2: Development swarm
|
||||
print("\n\n📋 Demo 2: Development Swarm")
|
||||
@@ -278,9 +270,7 @@ async def main():
|
||||
# Show final statistics
|
||||
print("\n\n📊 Session Statistics:")
|
||||
print(f"Total swarms launched: {len(launcher.networks)}")
|
||||
print(
|
||||
f"Total agents created: {sum(len(n.agents) for n in launcher.networks.values())}"
|
||||
)
|
||||
print(f"Total agents created: {sum(len(n.agents) for n in launcher.networks.values())}")
|
||||
print("Inference engine: hanzo/net (local)")
|
||||
print("External API calls: 0")
|
||||
print("Privacy: 100% on-device execution")
|
||||
|
||||
@@ -48,11 +48,7 @@ async def main():
|
||||
name="code_analyzer",
|
||||
description="Analyzes code using distributed inference",
|
||||
system="You are powered by hanzo/net distributed inference. Analyze code efficiently.",
|
||||
tools=[
|
||||
create_tool(
|
||||
name="analyze_code", description="Analyze code", handler=analyze_code
|
||||
)
|
||||
],
|
||||
tools=[create_tool(name="analyze_code", description="Analyze code", handler=analyze_code)],
|
||||
local_model="llama3.2", # Will use hanzo/net
|
||||
)
|
||||
|
||||
|
||||
@@ -60,9 +60,7 @@ async def main():
|
||||
search_agent = create_agent(
|
||||
name="search_agent",
|
||||
description="Searches through codebase",
|
||||
model=ModelConfig(
|
||||
provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.3
|
||||
),
|
||||
model=ModelConfig(provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.3),
|
||||
system="""You are a code search specialist. Use the search_code tool to find patterns in code.
|
||||
Be precise and helpful in locating code elements.""",
|
||||
tools=[
|
||||
@@ -78,9 +76,7 @@ Be precise and helpful in locating code elements.""",
|
||||
analyzer_agent = create_agent(
|
||||
name="analyzer",
|
||||
description="Analyzes code structure and quality",
|
||||
model=ModelConfig(
|
||||
provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.5
|
||||
),
|
||||
model=ModelConfig(provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.5),
|
||||
system="""You are a code analysis expert. Use your tools to analyze functions and provide insights.
|
||||
Focus on code quality, performance, and best practices.""",
|
||||
tools=[
|
||||
@@ -96,9 +92,7 @@ Focus on code quality, performance, and best practices.""",
|
||||
test_agent = create_agent(
|
||||
name="test_generator",
|
||||
description="Generates unit tests",
|
||||
model=ModelConfig(
|
||||
provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.7
|
||||
),
|
||||
model=ModelConfig(provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.7),
|
||||
system="""You are a test generation specialist. Create comprehensive unit tests for given code.
|
||||
Follow pytest conventions and ensure good coverage.""",
|
||||
tools=[
|
||||
@@ -114,9 +108,7 @@ Follow pytest conventions and ensure good coverage.""",
|
||||
teacher_agent = create_agent(
|
||||
name="teacher",
|
||||
description="Explains programming concepts",
|
||||
model=ModelConfig(
|
||||
provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.8
|
||||
),
|
||||
model=ModelConfig(provider=ModelProvider.LOCAL, model="llama3.2", temperature=0.8),
|
||||
system="""You are a programming teacher. Explain concepts clearly and provide examples.
|
||||
Make complex topics accessible to learners.""",
|
||||
tools=[
|
||||
@@ -158,23 +150,17 @@ Make complex topics accessible to learners.""",
|
||||
|
||||
# Test 2: Code Analysis
|
||||
print("\n📊 Test 2: Code Analysis")
|
||||
result = await network.run(
|
||||
prompt="Analyze the add function implementation", initial_agent=analyzer_agent
|
||||
)
|
||||
result = await network.run(prompt="Analyze the add function implementation", initial_agent=analyzer_agent)
|
||||
print(f"Result: {result['final_output']}")
|
||||
|
||||
# Test 3: Test Generation
|
||||
print("\n🧪 Test 3: Test Generation")
|
||||
result = await network.run(
|
||||
prompt="Generate tests for an add function", initial_agent=test_agent
|
||||
)
|
||||
result = await network.run(prompt="Generate tests for an add function", initial_agent=test_agent)
|
||||
print(f"Result: {result['final_output']}")
|
||||
|
||||
# Test 4: Concept Explanation
|
||||
print("\n📚 Test 4: Concept Explanation")
|
||||
result = await network.run(
|
||||
prompt="Explain what recursion is", initial_agent=teacher_agent
|
||||
)
|
||||
result = await network.run(prompt="Explain what recursion is", initial_agent=teacher_agent)
|
||||
print(f"Result: {result['final_output']}")
|
||||
|
||||
# Test 5: Multi-Agent Collaboration
|
||||
@@ -187,9 +173,7 @@ Make complex topics accessible to learners.""",
|
||||
|
||||
# Test 6: Complex Query
|
||||
print("\n🎯 Test 6: Complex Development Task")
|
||||
result = await network.run(
|
||||
prompt="I need help understanding and testing a binary search implementation"
|
||||
)
|
||||
result = await network.run(prompt="I need help understanding and testing a binary search implementation")
|
||||
print(f"Result: {result['final_output']}")
|
||||
|
||||
print("\n✅ Local agent demo complete!")
|
||||
|
||||
@@ -115,13 +115,9 @@ async def main():
|
||||
coder = create_agent(
|
||||
name="coder",
|
||||
description="Code writing assistant using MLX",
|
||||
model=ModelConfig(
|
||||
provider=ModelProvider.LOCAL, model="mlx", temperature=0.3, max_tokens=200
|
||||
),
|
||||
model=ModelConfig(provider=ModelProvider.LOCAL, model="mlx", temperature=0.3, max_tokens=200),
|
||||
system="You are a code writing assistant. Write clean, efficient code.",
|
||||
tools=[
|
||||
create_tool(name="write_code", description="Write code", handler=write_code)
|
||||
],
|
||||
tools=[create_tool(name="write_code", description="Write code", handler=write_code)],
|
||||
)
|
||||
|
||||
# Create network
|
||||
@@ -137,9 +133,7 @@ async def main():
|
||||
|
||||
# Test 1: Simple question
|
||||
print("\n💬 Test 1: Simple Question")
|
||||
result = await network.run(
|
||||
prompt="What is machine learning?", initial_agent=assistant
|
||||
)
|
||||
result = await network.run(prompt="What is machine learning?", initial_agent=assistant)
|
||||
print(f"Response: {result['final_output']}")
|
||||
|
||||
# Test 2: Summarization
|
||||
@@ -152,9 +146,7 @@ async def main():
|
||||
|
||||
# Test 3: Code generation
|
||||
print("\n💻 Test 3: Code Generation")
|
||||
result = await network.run(
|
||||
prompt="Write a Python function to calculate factorial", initial_agent=coder
|
||||
)
|
||||
result = await network.run(prompt="Write a Python function to calculate factorial", initial_agent=coder)
|
||||
print(f"Response: {result['final_output']}")
|
||||
|
||||
# Test 4: Multi-agent
|
||||
|
||||
@@ -113,9 +113,7 @@ def main():
|
||||
print("=" * 60)
|
||||
|
||||
# Check for API key
|
||||
has_key = bool(
|
||||
os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_API_KEY")
|
||||
)
|
||||
has_key = bool(os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_API_KEY"))
|
||||
if has_key:
|
||||
print("\n✅ Claude API key detected!")
|
||||
print(" In real usage, actual Claude agents would edit the files.")
|
||||
|
||||
@@ -38,9 +38,7 @@ async def main():
|
||||
|
||||
# Check LLM status
|
||||
ollama_status = await check_local_llm_status("ollama")
|
||||
print(
|
||||
f"\n📡 Ollama status: {'✅ Available' if ollama_status['available'] else '❌ Not available'}"
|
||||
)
|
||||
print(f"\n📡 Ollama status: {'✅ Available' if ollama_status['available'] else '❌ Not available'}")
|
||||
|
||||
# Create AI agents
|
||||
analyzer = create_local_agent(
|
||||
|
||||
@@ -80,17 +80,13 @@ async def main():
|
||||
|
||||
# Test with tool-aware prompt
|
||||
messages_with_tools = [
|
||||
Message(
|
||||
role="system", content="You are a helpful assistant with access to tools."
|
||||
),
|
||||
Message(role="system", content="You are a helpful assistant with access to tools."),
|
||||
Message(role="user", content="Calculate 100 divided by 4"),
|
||||
]
|
||||
|
||||
tools = [{"name": "calculate", "description": "Calculate mathematical expressions"}]
|
||||
|
||||
response = await provider.generate(
|
||||
messages_with_tools, model="llama3.2", tools=tools
|
||||
)
|
||||
response = await provider.generate(messages_with_tools, model="llama3.2", tools=tools)
|
||||
|
||||
print("\nTool-aware response:")
|
||||
print(f"Output: {response['output'][0]['content']}")
|
||||
@@ -100,9 +96,7 @@ async def main():
|
||||
print("✅ All inference done locally via hanzo/net")
|
||||
print("✅ No external API calls made")
|
||||
print("✅ Using distributed inference engine: dummy")
|
||||
print(
|
||||
"\nNote: In production, this would use MLX (Apple Silicon) or Tinygrad engines"
|
||||
)
|
||||
print("\nNote: In production, this would use MLX (Apple Silicon) or Tinygrad engines")
|
||||
print(" with actual model weights loaded locally.")
|
||||
|
||||
# Test 5: Concurrent inference
|
||||
@@ -111,9 +105,7 @@ async def main():
|
||||
async def run_inference(prompt: str, id: int):
|
||||
"""Run a single inference."""
|
||||
start = asyncio.get_event_loop().time()
|
||||
response = await provider.generate(
|
||||
[Message(role="user", content=prompt)], model="llama3.2"
|
||||
)
|
||||
response = await provider.generate([Message(role="user", content=prompt)], model="llama3.2")
|
||||
end = asyncio.get_event_loop().time()
|
||||
return {
|
||||
"id": id,
|
||||
|
||||
@@ -28,18 +28,14 @@ async def demo_unified_search():
|
||||
|
||||
if result.data and "results" in result.data:
|
||||
print(f" Found {len(result.data['results'])} results")
|
||||
print(
|
||||
f" Search types used: {', '.join(result.data['statistics']['search_types_used'])}"
|
||||
)
|
||||
print(f" Search types used: {', '.join(result.data['statistics']['search_types_used'])}")
|
||||
for r in result.data["results"][:3]:
|
||||
print(f" - {r['file']}:{r['line']} ({r['type']})")
|
||||
print()
|
||||
|
||||
# Demo 2: Natural language query
|
||||
print("2. Natural language query:")
|
||||
result = await search.run(
|
||||
pattern="how does pagination work in search results", max_results_per_type=3
|
||||
)
|
||||
result = await search.run(pattern="how does pagination work in search results", max_results_per_type=3)
|
||||
|
||||
if result.data:
|
||||
stats = result.data["statistics"]
|
||||
@@ -63,9 +59,7 @@ async def demo_unified_search():
|
||||
|
||||
# Demo 4: File search
|
||||
print("4. Find Python test files:")
|
||||
result = await search.run(
|
||||
pattern="test_*.py", search_files=True, max_results_per_type=5
|
||||
)
|
||||
result = await search.run(pattern="test_*.py", search_files=True, max_results_per_type=5)
|
||||
|
||||
if result.data:
|
||||
stats = result.data["statistics"]
|
||||
@@ -85,9 +79,7 @@ async def demo_unified_search():
|
||||
|
||||
if result.data:
|
||||
# Should find symbol definitions
|
||||
symbol_results = [
|
||||
r for r in result.data["results"] if r.get("context", {}).get("node_type")
|
||||
]
|
||||
symbol_results = [r for r in result.data["results"] if r.get("context", {}).get("node_type")]
|
||||
if symbol_results:
|
||||
print(f" Found {len(symbol_results)} symbol matches")
|
||||
else:
|
||||
@@ -101,9 +93,7 @@ async def demo_unified_search():
|
||||
if result.data and "pagination" in result.data:
|
||||
pag = result.data["pagination"]
|
||||
print(f" Page {pag['page']} of {pag['total_pages']}")
|
||||
print(
|
||||
f" Showing {len(result.data['results'])} of {pag['total_results']} total results"
|
||||
)
|
||||
print(f" Showing {len(result.data['results'])} of {pag['total_results']} total results")
|
||||
print(f" Has next page: {pag['has_next']}")
|
||||
print()
|
||||
|
||||
@@ -112,9 +102,7 @@ async def demo_unified_search():
|
||||
result = await search.run(pattern="import", include="*.py", max_results_per_type=5)
|
||||
|
||||
if result.data:
|
||||
print(
|
||||
f" Found {len(result.data['results'])} import statements in Python files"
|
||||
)
|
||||
print(f" Found {len(result.data['results'])} import statements in Python files")
|
||||
stats = result.data["statistics"]
|
||||
print(f" Search completed in {stats['time_ms'].get('text', 0)}ms")
|
||||
print()
|
||||
|
||||
@@ -23,9 +23,7 @@ class LocalAgentSwarm:
|
||||
|
||||
# Code Scanner Agent
|
||||
def scan_files(pattern: str) -> str:
|
||||
return (
|
||||
f"Scanned files matching '{pattern}': Found 15 Python files, 8 JS files"
|
||||
)
|
||||
return f"Scanned files matching '{pattern}': Found 15 Python files, 8 JS files"
|
||||
|
||||
self.agents["scanner"] = create_local_agent(
|
||||
name="scanner",
|
||||
@@ -43,9 +41,7 @@ class LocalAgentSwarm:
|
||||
|
||||
# Vulnerability Detector Agent
|
||||
def detect_vulnerabilities(code: str) -> str:
|
||||
return (
|
||||
"Found 2 potential issues: SQL injection risk, missing input validation"
|
||||
)
|
||||
return "Found 2 potential issues: SQL injection risk, missing input validation"
|
||||
|
||||
self.agents["detector"] = create_local_agent(
|
||||
name="detector",
|
||||
@@ -197,9 +193,7 @@ class LocalAgentSwarm:
|
||||
yes_count = sum(1 for v in votes.values() if "yes" in v.lower())
|
||||
no_count = len(votes) - yes_count
|
||||
|
||||
print(
|
||||
f"\n🗳️ Consensus: {'YES' if yes_count > no_count else 'NO'} ({yes_count} yes, {no_count} no)"
|
||||
)
|
||||
print(f"\n🗳️ Consensus: {'YES' if yes_count > no_count else 'NO'} ({yes_count} yes, {no_count} no)")
|
||||
|
||||
return votes
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import traceback
|
||||
from typing import Any, Dict, TypeVar, Callable, Optional
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
from importlib.metadata import version, PackageNotFoundError
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
|
||||
# Try to import PostHog, but make it optional
|
||||
try:
|
||||
@@ -183,11 +183,12 @@ class Analytics:
|
||||
def _get_package_version(self) -> str:
|
||||
"""Get the current package version."""
|
||||
try:
|
||||
return version('hanzo-mcp')
|
||||
return version("hanzo-mcp")
|
||||
except PackageNotFoundError:
|
||||
# Fallback to hardcoded version if package not installed
|
||||
try:
|
||||
from hanzo_mcp import __version__
|
||||
|
||||
return __version__
|
||||
except ImportError:
|
||||
return "0.8.14"
|
||||
|
||||
@@ -15,11 +15,13 @@ 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
|
||||
@@ -169,7 +171,8 @@ def main() -> None:
|
||||
)
|
||||
|
||||
_ = parser.add_argument(
|
||||
"--timeout", "-t",
|
||||
"--timeout",
|
||||
"-t",
|
||||
dest="tool_timeout",
|
||||
type=str,
|
||||
help="Default timeout for MCP tool operations (default: 2min). Supports: 2min, 5m, 120s, 30sec, 1.5h",
|
||||
@@ -288,19 +291,19 @@ def main() -> None:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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)
|
||||
|
||||
@@ -382,8 +385,9 @@ def main() -> None:
|
||||
# Ensure only one daemon runs per socket
|
||||
try:
|
||||
import fcntl
|
||||
|
||||
lock_file = f"{socket_path}.lock"
|
||||
with open(lock_file, 'w') as f:
|
||||
with open(lock_file, "w") as f:
|
||||
fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except (ImportError, IOError):
|
||||
# Fallback for systems without fcntl or if lock fails
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import os
|
||||
import atexit
|
||||
import signal
|
||||
import secrets
|
||||
import logging
|
||||
import secrets
|
||||
import warnings
|
||||
import threading
|
||||
from typing import Literal, cast, final
|
||||
@@ -83,7 +83,7 @@ class HanzoMCPServer:
|
||||
self.mcp = mcp_instance if mcp_instance is not None else EnhancedFastMCP(name)
|
||||
|
||||
# Initialize authentication token
|
||||
self.auth_token = auth_token or os.environ.get('HANZO_MCP_TOKEN')
|
||||
self.auth_token = auth_token or os.environ.get("HANZO_MCP_TOKEN")
|
||||
if not self.auth_token:
|
||||
# Generate a secure random token if none provided
|
||||
self.auth_token = secrets.token_urlsafe(32)
|
||||
|
||||
@@ -6,8 +6,6 @@ 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)
|
||||
@@ -29,6 +27,7 @@ from hanzo_mcp.tools.agent.cli_tools import (
|
||||
from hanzo_mcp.tools.agent.agent_tool import AgentTool
|
||||
from hanzo_mcp.tools.agent.network_tool import NetworkTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.agent.code_auth_tool import CodeAuthTool
|
||||
|
||||
|
||||
@@ -81,8 +80,6 @@ def register_agent_tools(
|
||||
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,8 +30,6 @@ 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
|
||||
@@ -46,6 +44,7 @@ from hanzo_mcp.tools.common.context import (
|
||||
)
|
||||
from hanzo_mcp.tools.common.batch_tool import BatchTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Parameter types
|
||||
Action = Annotated[
|
||||
@@ -215,8 +214,6 @@ Modes:
|
||||
|
||||
@override
|
||||
@auto_timeout("agent")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -19,8 +19,6 @@ 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
|
||||
|
||||
@@ -45,6 +43,7 @@ from hanzo_mcp.tools.agent.tool_adapter import (
|
||||
convert_tools_to_openai_functions,
|
||||
)
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.agent.clarification_tool import ClarificationTool
|
||||
from hanzo_mcp.tools.agent.clarification_protocol import (
|
||||
ClarificationType,
|
||||
@@ -180,8 +179,6 @@ Usage notes:
|
||||
|
||||
@override
|
||||
@auto_timeout("agent_tool_v1_deprecated")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,8 +5,8 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class ClarificationTool(BaseTool):
|
||||
|
||||
@@ -1,5 +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,
|
||||
@@ -451,8 +451,6 @@ claude_auth ensure_agent swarm_agent_1"""
|
||||
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,11 +13,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class CLIAgentBase(BaseTool):
|
||||
@@ -169,8 +168,6 @@ class CLIAgentBase(BaseTool):
|
||||
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,13 +13,12 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Parameter types for CLI tools
|
||||
Prompt = Annotated[
|
||||
@@ -189,8 +188,6 @@ class ClaudeCLITool(BaseCLITool):
|
||||
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
|
||||
@@ -242,8 +239,6 @@ class CodexCLITool(BaseCLITool):
|
||||
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
|
||||
@@ -283,8 +278,6 @@ class GeminiCLITool(BaseCLITool):
|
||||
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
|
||||
@@ -324,8 +317,6 @@ class GrokCLITool(BaseCLITool):
|
||||
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
|
||||
@@ -365,8 +356,6 @@ class OpenHandsCLITool(BaseCLITool):
|
||||
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
|
||||
@@ -418,8 +407,6 @@ class HanzoDevCLITool(BaseCLITool):
|
||||
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
|
||||
@@ -459,8 +446,6 @@ class ClineCLITool(BaseCLITool):
|
||||
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()
|
||||
@@ -497,8 +482,6 @@ class AiderCLITool(BaseCLITool):
|
||||
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,13 +6,12 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.agent.code_auth import CodeAuthManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class CodeAuthParams(TypedDict, total=False):
|
||||
@@ -70,8 +69,6 @@ Providers: claude, openai, azure, deepseek, google, groq"""
|
||||
|
||||
@override
|
||||
@auto_timeout("code_auth")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,8 +6,8 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class ReviewType(Enum):
|
||||
|
||||
@@ -5,11 +5,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class HanzoPrinciple(Enum):
|
||||
@@ -602,8 +601,6 @@ Use this when you need:
|
||||
self.oracle = IChing()
|
||||
|
||||
@auto_timeout("iching")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, challenge: str) -> str:
|
||||
"""Cast I Ching and provide guidance."""
|
||||
# Cast hexagram
|
||||
|
||||
@@ -18,12 +18,11 @@ 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
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Import hanzo cluster if available
|
||||
try:
|
||||
@@ -107,8 +106,6 @@ class NetworkTool(BaseTool):
|
||||
|
||||
@override
|
||||
@auto_timeout("network")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params: Unpack[NetworkToolParams]) -> str:
|
||||
"""Execute a task on the agent network.
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class ReviewFocus(Enum):
|
||||
|
||||
@@ -1,5 +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
|
||||
@@ -66,8 +66,6 @@ For new code, prefer using 'network' directly."""
|
||||
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.
|
||||
|
||||
|
||||
@@ -18,14 +18,13 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.agent.agent_tool import AgentTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class AgentNode(TypedDict):
|
||||
@@ -206,8 +205,6 @@ Models can be specified as:
|
||||
|
||||
@override
|
||||
@auto_timeout("swarm_tool_v1_deprecated")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -11,9 +11,9 @@ from typing import Any, Dict, List, Optional
|
||||
from pathlib import Path
|
||||
|
||||
from mcp.server import FastMCP
|
||||
from mcp.server.fastmcp import Context
|
||||
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from mcp.server.fastmcp import Context
|
||||
|
||||
from ..common.base import BaseTool
|
||||
from ...core.base_agent import CLIAgent, AgentConfig
|
||||
@@ -145,8 +145,6 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
||||
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.
|
||||
|
||||
|
||||
@@ -4,29 +4,30 @@ This module provides automatic timeout and backgrounding for any MCP tool operat
|
||||
that takes longer than the configured threshold (default: 2 minutes).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import functools
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
import os
|
||||
import asyncio
|
||||
import functools
|
||||
from typing import Any, Tuple, Callable, Optional
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Optional, Tuple
|
||||
from collections.abc import Awaitable
|
||||
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from .timeout_parser import parse_timeout, format_timeout
|
||||
|
||||
|
||||
class MCPToolTimeoutManager:
|
||||
"""Manager for MCP tool timeouts and backgrounding."""
|
||||
|
||||
|
||||
# Default timeout before auto-backgrounding (2 minutes)
|
||||
DEFAULT_TIMEOUT = 120.0
|
||||
|
||||
|
||||
# Environment variable to configure timeout
|
||||
TIMEOUT_ENV_VAR = "HANZO_MCP_TOOL_TIMEOUT"
|
||||
|
||||
|
||||
def __init__(self, process_manager: Optional[Any] = None):
|
||||
"""Initialize the timeout manager.
|
||||
|
||||
@@ -37,13 +38,14 @@ class MCPToolTimeoutManager:
|
||||
# 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:
|
||||
@@ -53,13 +55,13 @@ class MCPToolTimeoutManager:
|
||||
self.timeout = self.DEFAULT_TIMEOUT
|
||||
else:
|
||||
self.timeout = self.DEFAULT_TIMEOUT
|
||||
|
||||
|
||||
def _get_timeout_for_tool(self, tool_name: str) -> float:
|
||||
"""Get timeout setting for a specific tool.
|
||||
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool
|
||||
|
||||
|
||||
Returns:
|
||||
Timeout in seconds
|
||||
"""
|
||||
@@ -71,20 +73,14 @@ class MCPToolTimeoutManager:
|
||||
return parse_timeout(tool_timeout)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
return self.timeout
|
||||
|
||||
|
||||
async def _background_tool_execution(
|
||||
self,
|
||||
tool_func: Callable,
|
||||
tool_name: str,
|
||||
ctx: MCPContext,
|
||||
process_id: str,
|
||||
log_file: Path,
|
||||
**params: Any
|
||||
self, tool_func: Callable, tool_name: str, ctx: MCPContext, process_id: str, log_file: Path, **params: Any
|
||||
) -> None:
|
||||
"""Execute tool in background and log results.
|
||||
|
||||
|
||||
Args:
|
||||
tool_func: The tool function to execute
|
||||
tool_name: Name of the tool
|
||||
@@ -99,10 +95,10 @@ class MCPToolTimeoutManager:
|
||||
f.write(f"=== Background execution started for {tool_name} ===\\n")
|
||||
f.write(f"Parameters: {json.dumps(params, indent=2, default=str)}\\n")
|
||||
f.write(f"Started at: {time.strftime('%Y-%m-%d %H:%M:%S')}\\n\\n")
|
||||
|
||||
|
||||
# Execute the tool
|
||||
result = await tool_func(ctx, **params)
|
||||
|
||||
|
||||
# Log completion
|
||||
with open(log_file, "a") as f:
|
||||
f.write(f"\\n\\n=== Tool execution completed ===\\n")
|
||||
@@ -111,10 +107,10 @@ class MCPToolTimeoutManager:
|
||||
f.write("\\n=== RESULT ===\\n")
|
||||
f.write(str(result))
|
||||
f.write("\\n=== END RESULT ===\\n")
|
||||
|
||||
|
||||
# Mark as completed
|
||||
self.process_manager.mark_completed(process_id, 0)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
# Log error
|
||||
with open(log_file, "a") as f:
|
||||
@@ -122,7 +118,7 @@ class MCPToolTimeoutManager:
|
||||
f.write(f"Failed at: {time.strftime('%Y-%m-%d %H:%M:%S')}\\n")
|
||||
f.write(f"Error: {str(e)}\\n")
|
||||
f.write(f"Error type: {type(e).__name__}\\n")
|
||||
|
||||
|
||||
self.process_manager.mark_completed(process_id, 1)
|
||||
|
||||
|
||||
@@ -201,6 +197,7 @@ def with_auto_timeout(tool_name: str, timeout_manager: Optional[MCPToolTimeoutMa
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -210,7 +207,7 @@ _global_timeout_manager = None
|
||||
|
||||
def get_global_timeout_manager() -> MCPToolTimeoutManager:
|
||||
"""Get the global timeout manager instance.
|
||||
|
||||
|
||||
Returns:
|
||||
Global timeout manager
|
||||
"""
|
||||
@@ -222,7 +219,7 @@ def get_global_timeout_manager() -> MCPToolTimeoutManager:
|
||||
|
||||
def set_global_timeout(timeout_seconds: float) -> None:
|
||||
"""Set the global timeout for all MCP tools.
|
||||
|
||||
|
||||
Args:
|
||||
timeout_seconds: Timeout in seconds
|
||||
"""
|
||||
@@ -232,7 +229,7 @@ def set_global_timeout(timeout_seconds: float) -> None:
|
||||
|
||||
def set_tool_timeout(tool_name: str, timeout_seconds: float) -> None:
|
||||
"""Set timeout for a specific tool via environment variable.
|
||||
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool
|
||||
timeout_seconds: Timeout in seconds
|
||||
@@ -244,11 +241,11 @@ def set_tool_timeout(tool_name: str, timeout_seconds: float) -> None:
|
||||
# Convenience decorator using global manager
|
||||
def auto_timeout(tool_name: str):
|
||||
"""Convenience decorator using the global timeout manager.
|
||||
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool
|
||||
|
||||
|
||||
Returns:
|
||||
Decorator function
|
||||
"""
|
||||
return with_auto_timeout(tool_name, get_global_timeout_manager())
|
||||
return with_auto_timeout(tool_name, get_global_timeout_manager())
|
||||
|
||||
@@ -5,15 +5,13 @@ for all tools used in Hanzo AI. These abstractions help ensure consistent tool
|
||||
behavior and provide a foundation for tool registration and management.
|
||||
"""
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import functools
|
||||
from abc import ABC, abstractmethod
|
||||
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 (
|
||||
@@ -21,6 +19,7 @@ from hanzo_mcp.tools.common.validation import (
|
||||
validate_path_parameter,
|
||||
)
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.error_logger import log_tool_error, log_call_signature_error
|
||||
|
||||
|
||||
@@ -33,6 +32,7 @@ def with_error_logging(tool_name: str) -> Callable:
|
||||
Returns:
|
||||
Decorator function
|
||||
"""
|
||||
|
||||
def decorator(func: Callable[..., Awaitable[str]]) -> Callable[..., Awaitable[str]]:
|
||||
@functools.wraps(func)
|
||||
async def wrapper(*args: Any, **kwargs: Any) -> str:
|
||||
@@ -49,12 +49,7 @@ def with_error_logging(tool_name: str) -> Callable:
|
||||
log_call_signature_error(tool_name, expected, actual, e)
|
||||
|
||||
# Log the error
|
||||
log_tool_error(
|
||||
tool_name,
|
||||
e,
|
||||
params=kwargs,
|
||||
context=f"Call signature mismatch or type error"
|
||||
)
|
||||
log_tool_error(tool_name, e, params=kwargs, context=f"Call signature mismatch or type error")
|
||||
|
||||
# Return user-friendly error message
|
||||
return (
|
||||
@@ -74,6 +69,7 @@ def with_error_logging(tool_name: str) -> Callable:
|
||||
)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -146,7 +142,6 @@ class BaseTool(ABC):
|
||||
|
||||
@abstractmethod
|
||||
@auto_timeout("base")
|
||||
|
||||
async def call(self, ctx: MCPContext, **params: Any) -> Any:
|
||||
"""Execute the tool with the given parameters.
|
||||
|
||||
|
||||
@@ -9,13 +9,12 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.truncate import truncate_response
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.fastmcp_pagination import (
|
||||
CursorData,
|
||||
create_paginated_response,
|
||||
@@ -157,8 +156,6 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
||||
|
||||
@override
|
||||
@auto_timeout("batch")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,8 +5,6 @@ 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,
|
||||
@@ -16,6 +14,7 @@ from hanzo_mcp.config.settings import (
|
||||
)
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class ConfigToolParams(TypedDict, total=False):
|
||||
@@ -65,8 +64,6 @@ 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,12 +7,11 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Analysis = Annotated[
|
||||
str,
|
||||
@@ -134,8 +133,6 @@ Recommendations:
|
||||
|
||||
@override
|
||||
@auto_timeout("critic")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -4,13 +4,13 @@ This module provides comprehensive error logging for all MCP tool operations,
|
||||
writing errors to ~/.hanzo/mcp/logs/ for debugging and analysis.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class MCPErrorLogger:
|
||||
@@ -58,8 +58,7 @@ class MCPErrorLogger:
|
||||
|
||||
# Formatter
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
"%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
general_handler.setFormatter(formatter)
|
||||
@@ -106,16 +105,16 @@ class MCPErrorLogger:
|
||||
# Also log to standard logger
|
||||
self.logger.error(
|
||||
f"Tool '{tool_name}' error: {type(error).__name__}: {str(error)}",
|
||||
extra={"tool": tool_name, "params": error_data.get("params")}
|
||||
extra={"tool": tool_name, "params": error_data.get("params")},
|
||||
)
|
||||
|
||||
# Write detailed error to tool-specific file
|
||||
tool_log_file = self.log_dir / f"{tool_name}-errors.log"
|
||||
try:
|
||||
with open(tool_log_file, "a") as f:
|
||||
f.write(f"\n{'='*80}\n")
|
||||
f.write(f"\n{'=' * 80}\n")
|
||||
f.write(f"ERROR at {error_data['timestamp']}\n")
|
||||
f.write(f"{'='*80}\n")
|
||||
f.write(f"{'=' * 80}\n")
|
||||
f.write(f"Tool: {tool_name}\n")
|
||||
f.write(f"Error Type: {error_data['error_type']}\n")
|
||||
f.write(f"Error Message: {error_data['error_message']}\n")
|
||||
@@ -156,9 +155,9 @@ class MCPErrorLogger:
|
||||
sig_log_file = self.log_dir / "signature-errors.log"
|
||||
try:
|
||||
with open(sig_log_file, "a") as f:
|
||||
f.write(f"\n{'='*80}\n")
|
||||
f.write(f"\n{'=' * 80}\n")
|
||||
f.write(f"CALL SIGNATURE ERROR at {error_data['timestamp']}\n")
|
||||
f.write(f"{'='*80}\n")
|
||||
f.write(f"{'=' * 80}\n")
|
||||
f.write(f"Tool: {tool_name}\n")
|
||||
f.write(f"Expected: {expected_signature}\n")
|
||||
f.write(f"Actual: {actual_call}\n")
|
||||
@@ -169,9 +168,7 @@ class MCPErrorLogger:
|
||||
|
||||
# Also log as regular tool error
|
||||
self.log_tool_error(
|
||||
tool_name,
|
||||
error,
|
||||
context=f"Call signature mismatch - Expected: {expected_signature}, Got: {actual_call}"
|
||||
tool_name, error, context=f"Call signature mismatch - Expected: {expected_signature}, Got: {actual_call}"
|
||||
)
|
||||
|
||||
def _sanitize_params(self, params: dict[str, Any]) -> dict[str, Any]:
|
||||
@@ -185,8 +182,16 @@ class MCPErrorLogger:
|
||||
"""
|
||||
sanitized = {}
|
||||
sensitive_keys = {
|
||||
"password", "token", "key", "secret", "api_key", "auth",
|
||||
"credential", "private", "ssh_key", "passphrase"
|
||||
"password",
|
||||
"token",
|
||||
"key",
|
||||
"secret",
|
||||
"api_key",
|
||||
"auth",
|
||||
"credential",
|
||||
"private",
|
||||
"ssh_key",
|
||||
"passphrase",
|
||||
}
|
||||
|
||||
for key, value in params.items():
|
||||
@@ -214,7 +219,7 @@ class MCPErrorLogger:
|
||||
Returns:
|
||||
List of error dictionaries
|
||||
"""
|
||||
today = datetime.now().strftime('%Y-%m-%d')
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
json_log_file = self.log_dir / f"tool-errors-{today}.jsonl"
|
||||
|
||||
if not json_log_file.exists():
|
||||
|
||||
@@ -9,10 +9,9 @@ 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.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.common.paginated_response import paginate_if_needed
|
||||
|
||||
|
||||
@@ -47,7 +46,6 @@ class PaginatedBaseTool(BaseTool):
|
||||
|
||||
@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.
|
||||
|
||||
|
||||
@@ -123,11 +123,11 @@ class PermissionManager:
|
||||
# Security check: Reject paths with traversal attempts
|
||||
if ".." in str(path) or "~" in str(path):
|
||||
return False
|
||||
|
||||
|
||||
try:
|
||||
# Resolve the path (follows symlinks and makes absolute)
|
||||
resolved_path: Path = Path(path).resolve(strict=False)
|
||||
|
||||
|
||||
# Security check: Ensure resolved path doesn't escape allowed directories
|
||||
# by checking if it's actually under an allowed path after resolution
|
||||
original_path = Path(path)
|
||||
@@ -160,7 +160,7 @@ class PermissionManager:
|
||||
continue
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _is_subpath(self, child: Path, parent: Path) -> bool:
|
||||
"""Check if child is a subpath of parent."""
|
||||
try:
|
||||
|
||||
@@ -7,11 +7,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.shell.run_background import RunBackgroundTool
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
@@ -60,8 +59,6 @@ Example:
|
||||
|
||||
@override
|
||||
@auto_timeout("stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,12 +7,11 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Thought = Annotated[
|
||||
str,
|
||||
@@ -96,8 +95,6 @@ Feature Implementation Planning
|
||||
|
||||
@override
|
||||
@auto_timeout("thinking")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -37,11 +37,11 @@ def parse_timeout(timeout_str: Union[str, int, float]) -> float:
|
||||
# Regex patterns for different time units
|
||||
patterns = [
|
||||
# Hours: 1h, 1.5hr, 2hour, 3hours
|
||||
(r'^(\d*\.?\d+)\s*h(?:r|our|ours)?$', 3600),
|
||||
(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),
|
||||
(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),
|
||||
(r"^(\d*\.?\d+)\s*s(?:ec|ecs|econd|econds)?$", 1),
|
||||
]
|
||||
|
||||
for pattern, multiplier in patterns:
|
||||
@@ -89,10 +89,7 @@ def format_timeout(seconds: float) -> str:
|
||||
|
||||
# Test the parser
|
||||
if __name__ == "__main__":
|
||||
test_cases = [
|
||||
"2min", "5m", "120s", "30sec", "1.5h", "0.5hr",
|
||||
"90", 120, 3600.0, "1hour", "2hours", "30seconds"
|
||||
]
|
||||
test_cases = ["2min", "5m", "120s", "30sec", "1.5h", "0.5hr", "90", 120, 3600.0, "1hour", "2hours", "30seconds"]
|
||||
|
||||
for case in test_cases:
|
||||
try:
|
||||
@@ -100,4 +97,4 @@ if __name__ == "__main__":
|
||||
formatted = format_timeout(result)
|
||||
print(f"{case} -> {result}s ({formatted})")
|
||||
except ValueError as e:
|
||||
print(f"{case} -> ERROR: {e}")
|
||||
print(f"{case} -> ERROR: {e}")
|
||||
|
||||
@@ -5,11 +5,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
ToolName = Annotated[
|
||||
str,
|
||||
@@ -75,8 +74,6 @@ Use 'tool_enable' to re-enable disabled tools.
|
||||
|
||||
@override
|
||||
@auto_timeout("tool_disable")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,10 +7,9 @@ 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.auto_timeout import auto_timeout
|
||||
|
||||
ToolName = Annotated[
|
||||
str,
|
||||
@@ -122,8 +121,6 @@ Use 'tool_list' to see all available tools and their status.
|
||||
|
||||
@override
|
||||
@auto_timeout("tool_enable")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,11 +5,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
ShowDisabled = Annotated[
|
||||
bool,
|
||||
@@ -161,8 +160,6 @@ Use 'tool_enable' and 'tool_disable' to change tool status.
|
||||
|
||||
@override
|
||||
@auto_timeout("tool_list")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.config.index_config import IndexScope, IndexConfig
|
||||
|
||||
# Parameter types
|
||||
@@ -107,8 +106,6 @@ config --action toggle index.scope --path ./project"""
|
||||
|
||||
@override
|
||||
@auto_timeout("config")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
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
|
||||
from hanzo_mcp.tools.common.mode import ModeRegistry, register_default_modes
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class ModeTool(BaseTool):
|
||||
@@ -313,8 +312,6 @@ mode --action current"""
|
||||
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,10 +15,9 @@ 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.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
# Parameter types
|
||||
@@ -149,8 +148,6 @@ graph --action search --pattern "John" --node-type User
|
||||
|
||||
@override
|
||||
@auto_timeout("graph")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,11 +6,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
NodeId = Annotated[
|
||||
@@ -140,8 +139,6 @@ Examples:
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_add")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -15,11 +15,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
Query = Annotated[
|
||||
@@ -150,8 +149,6 @@ Examples:
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_query")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,11 +5,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
NodeId = Annotated[
|
||||
@@ -119,8 +118,6 @@ Examples:
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_remove")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,11 +7,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
Pattern = Annotated[
|
||||
@@ -120,8 +119,6 @@ Examples:
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,11 +7,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
ProjectPath = Annotated[
|
||||
@@ -100,8 +99,6 @@ Examples:
|
||||
|
||||
@override
|
||||
@auto_timeout("graph_stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -15,10 +15,9 @@ 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.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
# Parameter types
|
||||
@@ -103,8 +102,6 @@ sql --action stats --table users
|
||||
|
||||
@override
|
||||
@auto_timeout("sql")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,10 +6,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
Query = Annotated[
|
||||
|
||||
@@ -6,11 +6,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
SearchPattern = Annotated[
|
||||
@@ -110,8 +109,6 @@ Use sql_query for complex queries with joins, conditions, etc."""
|
||||
|
||||
@override
|
||||
@auto_timeout("sql_search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,11 +6,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
||||
|
||||
ProjectPath = Annotated[
|
||||
@@ -78,8 +77,6 @@ Examples:
|
||||
|
||||
@override
|
||||
@auto_timeout("sql_stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Command = Annotated[
|
||||
Optional[str],
|
||||
@@ -131,8 +130,6 @@ Note: Requires Neovim to be installed.
|
||||
|
||||
@override
|
||||
@auto_timeout("neovim_command")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,11 +8,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
FilePath = Annotated[
|
||||
str,
|
||||
@@ -128,8 +127,6 @@ Note: Requires Neovim to be installed and available in PATH.
|
||||
|
||||
@override
|
||||
@auto_timeout("neovim_edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -11,10 +11,9 @@ 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.auto_timeout import auto_timeout
|
||||
|
||||
Action = Annotated[
|
||||
str,
|
||||
@@ -117,8 +116,6 @@ Note: Requires Neovim to be installed.
|
||||
|
||||
@override
|
||||
@auto_timeout("neovim_session")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -21,13 +21,12 @@ 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
|
||||
from hanzo_mcp.tools.filesystem.grep import Grep
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.filesystem.ast_tool import ASTTool
|
||||
from hanzo_mcp.tools.vector.ast_analyzer import Symbol, ASTAnalyzer
|
||||
from hanzo_mcp.tools.vector.vector_search import VectorSearchTool
|
||||
@@ -542,8 +541,6 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
||||
|
||||
@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,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Pattern = Annotated[
|
||||
str,
|
||||
@@ -104,8 +103,6 @@ Only works within allowed directories."""
|
||||
|
||||
@override
|
||||
@auto_timeout("content_replace")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,12 +5,11 @@ 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
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class DiffTool(BaseTool):
|
||||
@@ -203,8 +202,6 @@ diff a.json b.json --ignore-whitespace"""
|
||||
)
|
||||
|
||||
@auto_timeout("diff")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -8,12 +8,11 @@ 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
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
DirectoryPath = Annotated[
|
||||
str,
|
||||
@@ -88,8 +87,6 @@ requested. Only works within allowed directories."""
|
||||
|
||||
@override
|
||||
@auto_timeout("directory_tree")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -19,8 +19,6 @@ 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
|
||||
@@ -28,6 +26,7 @@ from hanzo_mcp.tools.common.pagination import (
|
||||
CursorManager,
|
||||
paginate_list,
|
||||
)
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
DirectoryPath = Annotated[
|
||||
str,
|
||||
@@ -119,8 +118,6 @@ Returns nextCursor if more entries are available."""
|
||||
|
||||
@override
|
||||
@auto_timeout("directory_tree_paginated")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
FilePath = Annotated[
|
||||
str,
|
||||
@@ -91,8 +90,6 @@ Usage:
|
||||
|
||||
@override
|
||||
@auto_timeout("edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -23,9 +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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Parameter types
|
||||
Pattern = Annotated[
|
||||
@@ -150,8 +149,6 @@ Fast, intuitive file content search."""
|
||||
|
||||
@override
|
||||
@auto_timeout("find")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -6,11 +6,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
try:
|
||||
import ffind
|
||||
@@ -141,8 +140,6 @@ For database search, use 'sql_search' or 'vector_search'.
|
||||
|
||||
@override
|
||||
@auto_timeout("find_files")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,11 +8,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Pattern = Annotated[
|
||||
str,
|
||||
@@ -156,8 +155,6 @@ Examples:
|
||||
|
||||
@override
|
||||
@auto_timeout("git_search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -14,13 +14,12 @@ 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
|
||||
from hanzo_mcp.tools.common.truncate import truncate_response
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Pattern = Annotated[
|
||||
str,
|
||||
@@ -356,8 +355,6 @@ When you are doing an open ended search that may require multiple rounds of glob
|
||||
|
||||
@override
|
||||
@auto_timeout("grep")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
FilePath = Annotated[
|
||||
str,
|
||||
@@ -135,8 +134,6 @@ If you want to create a new file, use:
|
||||
|
||||
@override
|
||||
@auto_timeout("multi_edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -3,19 +3,18 @@
|
||||
This module provides the ReadTool for reading the contents of files.
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Unpack, Annotated, TypedDict, final, override
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
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 with_error_logging
|
||||
from hanzo_mcp.tools.common.truncate import truncate_response
|
||||
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
||||
from hanzo_mcp.tools.common.base import with_error_logging
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
FilePath = Annotated[
|
||||
str,
|
||||
|
||||
@@ -9,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
SearchPath = Annotated[
|
||||
str,
|
||||
@@ -77,8 +76,6 @@ understand project-specific requirements and preferences."""
|
||||
|
||||
@override
|
||||
@auto_timeout("rules")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -27,13 +27,12 @@ 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
|
||||
from hanzo_mcp.tools.filesystem.grep import Grep
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.vector.vector_search import VectorSearchTool
|
||||
from hanzo_mcp.tools.filesystem.git_search import GitSearchTool
|
||||
from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
|
||||
@@ -531,8 +530,6 @@ This is the recommended search tool for comprehensive results."""
|
||||
|
||||
@override
|
||||
@auto_timeout("search")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -23,9 +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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Parameter types
|
||||
Action = Annotated[
|
||||
@@ -129,8 +128,6 @@ Finds code structures (functions, classes, methods) with full context."""
|
||||
|
||||
@override
|
||||
@auto_timeout("symbols")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,9 +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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Parameter types
|
||||
TreePath = Annotated[
|
||||
@@ -98,8 +97,6 @@ tree --pattern "*.py" --show-size"""
|
||||
|
||||
@override
|
||||
@auto_timeout("tree")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,11 +7,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class WatchTool(BaseTool):
|
||||
@@ -44,8 +43,6 @@ class WatchTool(BaseTool):
|
||||
)
|
||||
|
||||
@auto_timeout("watch")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, **params) -> str:
|
||||
"""Call the tool with arguments."""
|
||||
return await self.run(
|
||||
|
||||
@@ -8,11 +8,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
FilePath = Annotated[
|
||||
str,
|
||||
@@ -75,8 +74,6 @@ Usage:
|
||||
|
||||
@override
|
||||
@auto_timeout("write")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -16,9 +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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Parameter types
|
||||
Action = Annotated[
|
||||
@@ -114,8 +113,6 @@ jupyter --action create "new.ipynb"
|
||||
|
||||
@override
|
||||
@auto_timeout("jupyter")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -325,9 +322,7 @@ jupyter --action create "new.ipynb"
|
||||
|
||||
# Create a notebook client with default kernel
|
||||
client = NotebookClient(
|
||||
nb,
|
||||
timeout=params.get('timeout', 600),
|
||||
kernel_name=params.get('kernel_name', 'python3')
|
||||
nb, timeout=params.get("timeout", 600), kernel_name=params.get("kernel_name", "python3")
|
||||
)
|
||||
|
||||
# Execute the notebook
|
||||
|
||||
@@ -9,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
NotebookPath = Annotated[
|
||||
str,
|
||||
@@ -99,8 +98,6 @@ class NoteBookEditTool(JupyterBaseTool):
|
||||
|
||||
@override
|
||||
@auto_timeout("notebook_edit")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -9,11 +9,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
NotebookPath = Annotated[
|
||||
str,
|
||||
@@ -59,8 +58,6 @@ class NotebookReadTool(JupyterBaseTool):
|
||||
|
||||
@override
|
||||
@auto_timeout("notebook_read")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -15,10 +15,10 @@ 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
|
||||
from hanzo_mcp.tools.common.context import ToolContext, create_tool_context
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Prompt = Annotated[
|
||||
str,
|
||||
|
||||
@@ -7,11 +7,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Action = Annotated[
|
||||
str,
|
||||
@@ -113,8 +112,6 @@ Providers are automatically detected based on environment variables:
|
||||
|
||||
@override
|
||||
@auto_timeout("llm_manage")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -19,10 +19,9 @@ 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.auto_timeout import auto_timeout
|
||||
|
||||
# Check if litellm is available
|
||||
try:
|
||||
@@ -266,8 +265,6 @@ Available: {", ".join(available) if available else "None"}"""
|
||||
|
||||
@override
|
||||
@auto_timeout("llm_unified")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,10 +5,9 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Prompt = Annotated[
|
||||
str,
|
||||
@@ -101,8 +100,6 @@ class BaseProviderTool(BaseTool):
|
||||
|
||||
@override
|
||||
@auto_timeout("provider_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -1,5 +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
|
||||
@@ -475,8 +475,6 @@ 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,10 +8,9 @@ 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.auto_timeout import auto_timeout
|
||||
|
||||
ServerCommand = Annotated[
|
||||
str,
|
||||
@@ -134,8 +133,6 @@ Use 'mcp_stats' to see all added servers and their status.
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp_add")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -5,11 +5,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
ServerName = Annotated[
|
||||
str,
|
||||
@@ -67,8 +66,6 @@ Use 'mcp_stats' to see all servers before removing.
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp_remove")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -112,8 +109,9 @@ Use 'mcp_stats' to see all servers before removing.
|
||||
process_id = server.get("process_id")
|
||||
if process_id:
|
||||
try:
|
||||
import signal
|
||||
import os
|
||||
import signal
|
||||
|
||||
os.kill(process_id, signal.SIGTERM)
|
||||
await tool_ctx.info(f"Stopped running server '{name}' (PID: {process_id})")
|
||||
except ProcessLookupError:
|
||||
|
||||
@@ -4,11 +4,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class McpStatsParams(TypedDict, total=False):
|
||||
@@ -50,8 +49,6 @@ Example:
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp_stats")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -20,10 +20,9 @@ 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.auto_timeout import auto_timeout
|
||||
|
||||
# Parameter types
|
||||
Action = Annotated[
|
||||
@@ -273,8 +272,6 @@ Status: {enabled} enabled, {running} running"""
|
||||
|
||||
@override
|
||||
@auto_timeout("mcp")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,12 +7,11 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Import from hanzo-memory package
|
||||
try:
|
||||
@@ -72,8 +71,6 @@ recall_facts(queries=["company policies"], scope="global", limit=5)
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -196,8 +193,6 @@ store_facts(facts=["Company founded in 2020"], scope="global", kb_name="company_
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -295,8 +290,6 @@ summarize_to_memory(content="Company guidelines...", topic="Guidelines", scope="
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -402,8 +395,6 @@ manage_knowledge_bases(action="delete", kb_name="old_docs")
|
||||
|
||||
@override
|
||||
@auto_timeout("knowledge_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,12 +7,11 @@ 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
|
||||
from hanzo_mcp.tools.common.context import create_tool_context
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
# Import from hanzo-memory package
|
||||
try:
|
||||
@@ -72,8 +71,6 @@ recall_memories(queries=["coding standards"], scope="global")
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
@@ -161,8 +158,6 @@ 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.
|
||||
|
||||
@@ -228,8 +223,6 @@ update_memories(updates=[
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, updates: List[Dict[str, str]]) -> str:
|
||||
"""Update memories.
|
||||
|
||||
@@ -295,8 +288,6 @@ delete_memories(ids=["mem_1", "mem_2"])
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(self, ctx: MCPContext, ids: List[str]) -> str:
|
||||
"""Delete memories.
|
||||
|
||||
@@ -360,8 +351,6 @@ manage_memories(
|
||||
|
||||
@override
|
||||
@auto_timeout("memory_tools")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -11,8 +11,8 @@ from typing import Any, final
|
||||
from mcp.server.fastmcp import Context as MCPContext
|
||||
|
||||
from hanzo_mcp.tools.common.base import BaseTool
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
from hanzo_mcp.tools.common.truncate import truncate_response
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
|
||||
|
||||
class BashCommandStatus(Enum):
|
||||
@@ -139,7 +139,7 @@ class CommandResult:
|
||||
return truncate_response(
|
||||
result,
|
||||
max_tokens=max_tokens,
|
||||
truncation_message="\n\n[Shell output truncated due to token limit. Use pagination, filtering, or limit parameters to reduce output size.]"
|
||||
truncation_message="\n\n[Shell output truncated due to token limit. Use pagination, filtering, or limit parameters to reduce output size.]",
|
||||
)
|
||||
|
||||
def to_agent_observation(self, max_tokens: int = 25000) -> str:
|
||||
@@ -164,7 +164,7 @@ class CommandResult:
|
||||
return truncate_response(
|
||||
content,
|
||||
max_tokens=max_tokens,
|
||||
truncation_message="\n\n[Shell output truncated due to token limit. Use pagination, filtering, or limit parameters to reduce output size.]"
|
||||
truncation_message="\n\n[Shell output truncated due to token limit. Use pagination, filtering, or limit parameters to reduce output size.]",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
|
||||
class BashTool(BaseScriptTool):
|
||||
|
||||
@@ -112,9 +112,9 @@ class CommandExecutor:
|
||||
# Use shlex.quote for proper escaping to prevent command injection
|
||||
escaped_command = shlex.quote(command)
|
||||
if use_login_shell:
|
||||
formatted_command = f'{user_shell} bash -l -c {escaped_command}'
|
||||
formatted_command = f"{user_shell} bash -l -c {escaped_command}"
|
||||
else:
|
||||
formatted_command = f'{user_shell} bash -c {escaped_command}'
|
||||
formatted_command = f"{user_shell} bash -c {escaped_command}"
|
||||
else:
|
||||
# # For simple commands without special characters
|
||||
# # Still respect login shell preference
|
||||
@@ -126,7 +126,7 @@ class CommandExecutor:
|
||||
elif shell_basename in ["powershell", "powershell.exe", "pwsh", "pwsh.exe"]:
|
||||
# Use proper escaping for PowerShell to prevent injection
|
||||
# PowerShell requires different escaping than POSIX shells
|
||||
escaped_command = command.replace('"', '`"').replace("'", "``'").replace('$', '`$')
|
||||
escaped_command = command.replace('"', '`"').replace("'", "``'").replace("$", "`$")
|
||||
formatted_command = f'"{user_shell}" -Command "{escaped_command}"'
|
||||
|
||||
else:
|
||||
|
||||
@@ -6,11 +6,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.shell.run_background import RunBackgroundTool
|
||||
|
||||
ProcessId = Annotated[
|
||||
@@ -108,8 +107,6 @@ Use run_command with 'tail -f' for continuous monitoring.
|
||||
|
||||
@override
|
||||
@auto_timeout("logs")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -7,11 +7,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
|
||||
Package = Annotated[
|
||||
str,
|
||||
@@ -106,8 +105,6 @@ For long-running servers, use npx_background instead.
|
||||
|
||||
@override
|
||||
@auto_timeout("npx")
|
||||
|
||||
|
||||
async def call(
|
||||
self,
|
||||
ctx: MCPContext,
|
||||
|
||||
@@ -8,11 +8,10 @@ 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
|
||||
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
||||
from hanzo_mcp.tools.shell.run_background import BackgroundProcess, RunBackgroundTool
|
||||
|
||||
Package = Annotated[
|
||||
@@ -123,8 +122,6 @@ Use 'processes' to list running processes and 'pkill' to stop them.
|
||||
|
||||
@override
|
||||
@auto_timeout("npx")
|
||||
|
||||
|
||||
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