hanzo-mcp 0.6.10__tar.gz → 0.6.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/PKG-INFO +1 -1
- hanzo_mcp-0.6.12/hanzo_mcp/__init__.py +12 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/cli.py +67 -17
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/cli_enhanced.py +12 -9
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/config/settings.py +6 -2
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/dev_server.py +18 -13
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/server.py +12 -2
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/__init__.py +3 -1
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/bash_session_executor.py +7 -4
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/command_executor.py +6 -4
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/__init__.py +6 -2
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/ast_analyzer.py +12 -4
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/infinity_store.py +9 -3
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/project_manager.py +3 -1
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp.egg-info/PKG-INFO +1 -1
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp.egg-info/SOURCES.txt +6 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/pyproject.toml +1 -1
- hanzo_mcp-0.6.12/tests/test_litellm_warnings.py +72 -0
- hanzo_mcp-0.6.12/tests/test_manual.py +188 -0
- hanzo_mcp-0.6.12/tests/test_palette.py +35 -0
- hanzo_mcp-0.6.12/tests/test_simple.py +253 -0
- hanzo_mcp-0.6.12/tests/test_stdio_protocol.py +164 -0
- hanzo_mcp-0.6.12/tests/test_stdio_simple.py +91 -0
- hanzo_mcp-0.6.10/hanzo_mcp/__init__.py +0 -3
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/LICENSE +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/README.md +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/__main__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/config/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/config/tool_config.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/prompts/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/prompts/compact_conversation.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/prompts/create_release.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/prompts/project_system.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/prompts/project_todo_reminder.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/prompts/utils.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/agent/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/agent/agent.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/agent/agent_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/agent/prompt.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/agent/tool_adapter.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/base.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/batch_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/config_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/context.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/palette.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/palette_loader.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/permissions.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/stats.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/thinking_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/tool_disable.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/tool_enable.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/tool_list.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/common/validation.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/config/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/config/config_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/config/index_config.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/config/palette_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/database_manager.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/graph.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/graph_add.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/graph_query.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/graph_remove.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/graph_search.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/graph_stats.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/sql.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/sql_query.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/sql_search.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/database/sql_stats.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/editor/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/editor/neovim_command.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/editor/neovim_edit.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/editor/neovim_session.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/base.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/batch_search.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/content_replace.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/diff.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/directory_tree.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/edit.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/find.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/find_files.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/git_search.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/grep.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/multi_edit.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/read.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/symbols.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/symbols_unified.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/tree.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/unified_search.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/unix_aliases.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/watch.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/filesystem/write.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/jupyter/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/jupyter/base.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/jupyter/jupyter.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/jupyter/notebook_edit.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/jupyter/notebook_read.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/llm/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/llm/consensus_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/llm/llm_manage.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/llm/llm_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/llm/llm_unified.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/llm/provider_tools.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/mcp/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/mcp/mcp_add.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/mcp/mcp_remove.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/mcp/mcp_stats.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/mcp/mcp_unified.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/base.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/base_process.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/bash_session.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/bash_unified.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/logs.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/npx.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/npx_background.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/npx_unified.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/open.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/pkill.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/process_unified.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/processes.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/run_background.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/run_command.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/run_command_windows.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/session_manager.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/session_storage.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/uvx.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/uvx_background.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/shell/uvx_unified.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/todo/__init__.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/todo/base.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/todo/todo.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/todo/todo_read.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/todo/todo_write.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/git_ingester.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/index_tool.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/mock_infinity.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/vector.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/vector_index.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp/tools/vector/vector_search.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp.egg-info/dependency_links.txt +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp.egg-info/entry_points.txt +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp.egg-info/requires.txt +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/hanzo_mcp.egg-info/top_level.txt +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/setup.cfg +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/setup.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/tests/test_async_support.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/tests/test_cli.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/tests/test_git_ingestion.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/tests/test_new_tools.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/tests/test_search_quality.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/tests/test_unified_search.py +0 -0
- {hanzo_mcp-0.6.10 → hanzo_mcp-0.6.12}/tests/test_vector_store.py +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Hanzo MCP - Implementation of Hanzo capabilities using MCP."""
|
|
2
|
+
|
|
3
|
+
# Configure FastMCP logging globally for stdio transport
|
|
4
|
+
import os
|
|
5
|
+
if os.environ.get("HANZO_MCP_TRANSPORT") == "stdio":
|
|
6
|
+
try:
|
|
7
|
+
from fastmcp.utilities.logging import configure_logging
|
|
8
|
+
configure_logging(level="ERROR")
|
|
9
|
+
except ImportError:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
__version__ = "0.6.12"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
5
|
+
import logging
|
|
5
6
|
import os
|
|
6
7
|
import signal
|
|
7
8
|
import sys
|
|
@@ -13,13 +14,33 @@ from hanzo_mcp.server import HanzoMCPServer
|
|
|
13
14
|
|
|
14
15
|
def main() -> None:
|
|
15
16
|
"""Run the CLI for the Hanzo MCP server."""
|
|
16
|
-
# Set up signal handler to ensure clean exit
|
|
17
|
-
def signal_handler(signum, frame):
|
|
18
|
-
print("\nReceived interrupt signal, shutting down...")
|
|
19
|
-
sys.exit(0)
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
# Pre-parse arguments to check transport type early
|
|
19
|
+
import sys
|
|
20
|
+
early_parser = argparse.ArgumentParser(add_help=False)
|
|
21
|
+
early_parser.add_argument("--transport", choices=["stdio", "sse"], default="stdio")
|
|
22
|
+
early_args, _ = early_parser.parse_known_args()
|
|
23
|
+
|
|
24
|
+
# Configure logging VERY early based on transport
|
|
25
|
+
if early_args.transport == "stdio":
|
|
26
|
+
# Set environment variable for server to detect stdio mode
|
|
27
|
+
import os
|
|
28
|
+
os.environ["HANZO_MCP_TRANSPORT"] = "stdio"
|
|
29
|
+
|
|
30
|
+
# For stdio transport, disable ALL logging immediately
|
|
31
|
+
from fastmcp.utilities.logging import configure_logging
|
|
32
|
+
# Set to ERROR to suppress INFO/WARNING messages from FastMCP
|
|
33
|
+
configure_logging(level="ERROR")
|
|
34
|
+
|
|
35
|
+
# Also configure standard logging to ERROR level
|
|
36
|
+
logging.basicConfig(
|
|
37
|
+
level=logging.ERROR, # Only show errors
|
|
38
|
+
handlers=[] # No handlers for stdio to prevent protocol corruption
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Redirect stderr to devnull for stdio transport to prevent any output
|
|
42
|
+
import sys
|
|
43
|
+
sys.stderr = open(os.devnull, 'w')
|
|
23
44
|
|
|
24
45
|
parser = argparse.ArgumentParser(
|
|
25
46
|
description="MCP server implementing Hanzo AI capabilities"
|
|
@@ -212,6 +233,32 @@ def main() -> None:
|
|
|
212
233
|
)
|
|
213
234
|
return
|
|
214
235
|
|
|
236
|
+
# Get logger
|
|
237
|
+
logger = logging.getLogger(__name__)
|
|
238
|
+
|
|
239
|
+
# Set up signal handler to ensure clean exit
|
|
240
|
+
def signal_handler(signum, frame):
|
|
241
|
+
if transport != "stdio":
|
|
242
|
+
logger.info("\nReceived interrupt signal, shutting down...")
|
|
243
|
+
sys.exit(0)
|
|
244
|
+
|
|
245
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
246
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
247
|
+
|
|
248
|
+
# Configure logging based on transport (stdio already configured early)
|
|
249
|
+
if transport != "stdio":
|
|
250
|
+
# For SSE transport, logging is fine
|
|
251
|
+
log_level_map = {
|
|
252
|
+
"DEBUG": logging.DEBUG,
|
|
253
|
+
"INFO": logging.INFO,
|
|
254
|
+
"WARNING": logging.WARNING,
|
|
255
|
+
"ERROR": logging.ERROR
|
|
256
|
+
}
|
|
257
|
+
logging.basicConfig(
|
|
258
|
+
level=log_level_map.get(log_level, logging.INFO),
|
|
259
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
260
|
+
)
|
|
261
|
+
|
|
215
262
|
# If no allowed paths are specified, use the current directory
|
|
216
263
|
if not allowed_paths:
|
|
217
264
|
allowed_paths = [os.getcwd()]
|
|
@@ -263,10 +310,11 @@ def main() -> None:
|
|
|
263
310
|
# Transport will be automatically cast to Literal['stdio', 'sse'] by the server
|
|
264
311
|
server.run(transport=transport)
|
|
265
312
|
except KeyboardInterrupt:
|
|
266
|
-
|
|
313
|
+
if transport != "stdio":
|
|
314
|
+
logger.info("\nShutting down...")
|
|
267
315
|
sys.exit(0)
|
|
268
316
|
except Exception as e:
|
|
269
|
-
|
|
317
|
+
logger.error(f"Server error: {e}")
|
|
270
318
|
sys.exit(1)
|
|
271
319
|
|
|
272
320
|
|
|
@@ -342,27 +390,29 @@ def install_claude_desktop_config(
|
|
|
342
390
|
existing_config["mcpServers"][name] = config["mcpServers"][name]
|
|
343
391
|
config = existing_config
|
|
344
392
|
except Exception as e:
|
|
345
|
-
|
|
346
|
-
|
|
393
|
+
logger = logging.getLogger(__name__)
|
|
394
|
+
logger.error(f"Error reading existing config: {e}")
|
|
395
|
+
logger.info("Creating new config file.")
|
|
347
396
|
|
|
348
397
|
# Write the config file
|
|
349
398
|
with open(config_file, mode="w") as f:
|
|
350
399
|
json.dump(config, f, indent=2)
|
|
351
400
|
|
|
352
|
-
|
|
353
|
-
|
|
401
|
+
logger = logging.getLogger(__name__)
|
|
402
|
+
logger.info(f"Successfully installed {name} in Claude Desktop configuration.")
|
|
403
|
+
logger.info(f"Config file: {config_file}")
|
|
354
404
|
|
|
355
405
|
if allowed_paths:
|
|
356
|
-
|
|
406
|
+
logger.info("\nAllowed paths:")
|
|
357
407
|
for path in allowed_paths:
|
|
358
|
-
|
|
408
|
+
logger.info(f"- {path}")
|
|
359
409
|
else:
|
|
360
|
-
|
|
410
|
+
logger.info(f"\nDefault allowed path: {home}")
|
|
361
411
|
|
|
362
|
-
|
|
412
|
+
logger.info(
|
|
363
413
|
"\nYou can modify allowed paths in the config file directly."
|
|
364
414
|
)
|
|
365
|
-
|
|
415
|
+
logger.info("Restart Claude Desktop for changes to take effect.")
|
|
366
416
|
|
|
367
417
|
|
|
368
418
|
if __name__ == "__main__":
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
5
|
+
import logging
|
|
5
6
|
import os
|
|
6
7
|
import sys
|
|
7
8
|
from pathlib import Path
|
|
@@ -344,8 +345,9 @@ def apply_cli_overrides(args: argparse.Namespace) -> Dict[str, Any]:
|
|
|
344
345
|
|
|
345
346
|
def list_tools(settings: HanzoMCPSettings) -> None:
|
|
346
347
|
"""List all tools and their current status."""
|
|
347
|
-
|
|
348
|
-
|
|
348
|
+
logger = logging.getLogger(__name__)
|
|
349
|
+
logger.info("Hanzo MCP Tools Status:")
|
|
350
|
+
logger.info("=" * 50)
|
|
349
351
|
|
|
350
352
|
categories = {}
|
|
351
353
|
for tool_name, tool_config in TOOL_REGISTRY.items():
|
|
@@ -358,14 +360,14 @@ def list_tools(settings: HanzoMCPSettings) -> None:
|
|
|
358
360
|
categories[category].append((tool_name, status, tool_config.description))
|
|
359
361
|
|
|
360
362
|
for category, tools in categories.items():
|
|
361
|
-
|
|
362
|
-
|
|
363
|
+
logger.info(f"\n{category.upper()} TOOLS:")
|
|
364
|
+
logger.info("-" * 30)
|
|
363
365
|
for tool_name, status, description in tools:
|
|
364
|
-
|
|
366
|
+
logger.info(f" {status} {tool_name:<15} - {description}")
|
|
365
367
|
|
|
366
|
-
|
|
368
|
+
logger.info(f"\nTotal: {len(TOOL_REGISTRY)} tools")
|
|
367
369
|
enabled_count = len(settings.get_enabled_tools())
|
|
368
|
-
|
|
370
|
+
logger.info(f"Enabled: {enabled_count}, Disabled: {len(TOOL_REGISTRY) - enabled_count}")
|
|
369
371
|
|
|
370
372
|
|
|
371
373
|
def main() -> None:
|
|
@@ -385,14 +387,15 @@ def main() -> None:
|
|
|
385
387
|
settings = load_settings(project_dir=project_dir, config_overrides=config_overrides)
|
|
386
388
|
|
|
387
389
|
# Handle configuration saving
|
|
390
|
+
logger = logging.getLogger(__name__)
|
|
388
391
|
if hasattr(args, 'save_config') and args.save_config:
|
|
389
392
|
saved_path = save_settings(settings, global_config=True)
|
|
390
|
-
|
|
393
|
+
logger.info(f"Configuration saved to: {saved_path}")
|
|
391
394
|
return
|
|
392
395
|
|
|
393
396
|
if hasattr(args, 'save_project_config') and args.save_project_config:
|
|
394
397
|
saved_path = save_settings(settings, global_config=False)
|
|
395
|
-
|
|
398
|
+
logger.info(f"Project configuration saved to: {saved_path}")
|
|
396
399
|
return
|
|
397
400
|
|
|
398
401
|
# Handle installation
|
|
@@ -364,7 +364,9 @@ def load_settings(
|
|
|
364
364
|
global_config = json.load(f)
|
|
365
365
|
settings = _merge_config(settings, global_config)
|
|
366
366
|
except Exception as e:
|
|
367
|
-
|
|
367
|
+
import logging
|
|
368
|
+
logger = logging.getLogger(__name__)
|
|
369
|
+
logger.warning(f"Failed to load global config: {e}")
|
|
368
370
|
|
|
369
371
|
# Load project config
|
|
370
372
|
project_config_path = get_project_config_path(project_dir)
|
|
@@ -374,7 +376,9 @@ def load_settings(
|
|
|
374
376
|
project_config = json.load(f)
|
|
375
377
|
settings = _merge_config(settings, project_config)
|
|
376
378
|
except Exception as e:
|
|
377
|
-
|
|
379
|
+
import logging
|
|
380
|
+
logger = logging.getLogger(__name__)
|
|
381
|
+
logger.warning(f"Failed to load project config: {e}")
|
|
378
382
|
|
|
379
383
|
# Apply CLI overrides
|
|
380
384
|
if config_overrides:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Development server with hot reload for Hanzo MCP."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import logging
|
|
4
5
|
import os
|
|
5
6
|
import sys
|
|
6
7
|
import time
|
|
@@ -67,8 +68,9 @@ class MCPReloadHandler(FileSystemEventHandler):
|
|
|
67
68
|
|
|
68
69
|
self.last_reload = current_time
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
logger = logging.getLogger(__name__)
|
|
72
|
+
logger.info(f"\n🔄 File changed: {event.src_path}")
|
|
73
|
+
logger.info("🔄 Reloading MCP server...")
|
|
72
74
|
|
|
73
75
|
self.restart_callback()
|
|
74
76
|
|
|
@@ -133,9 +135,10 @@ class DevServer:
|
|
|
133
135
|
self.observer.schedule(handler, path, recursive=True)
|
|
134
136
|
|
|
135
137
|
self.observer.start()
|
|
136
|
-
|
|
138
|
+
logger = logging.getLogger(__name__)
|
|
139
|
+
logger.info(f"👀 Watching for changes in: {package_dir}")
|
|
137
140
|
if self.project_dir:
|
|
138
|
-
|
|
141
|
+
logger.info(f"👀 Also watching: {self.project_dir}")
|
|
139
142
|
|
|
140
143
|
def stop_file_watcher(self):
|
|
141
144
|
"""Stop the file watcher."""
|
|
@@ -146,18 +149,20 @@ class DevServer:
|
|
|
146
149
|
def restart_server(self):
|
|
147
150
|
"""Restart the MCP server."""
|
|
148
151
|
# Since MCP servers run in the same process, we need to handle this differently
|
|
149
|
-
# For now, we'll
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
# For now, we'll log a message indicating a restart is needed
|
|
153
|
+
logger = logging.getLogger(__name__)
|
|
154
|
+
logger.warning("\n⚠️ Server restart required. Please restart the MCP client to reload changes.")
|
|
155
|
+
logger.info("💡 Tip: In development, consider using the MCP test client for easier reloading.")
|
|
152
156
|
|
|
153
157
|
async def run_async(self, transport: str = "stdio"):
|
|
154
158
|
"""Run the development server asynchronously."""
|
|
155
159
|
self.running = True
|
|
156
160
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
logger = logging.getLogger(__name__)
|
|
162
|
+
logger.info(f"\n🚀 Starting Hanzo MCP in development mode...")
|
|
163
|
+
logger.info(f"🔧 Hot reload enabled - watching for file changes")
|
|
164
|
+
logger.info(f"📁 Project: {self.project_dir or 'current directory'}")
|
|
165
|
+
logger.info(f"🌐 Transport: {transport}\n")
|
|
161
166
|
|
|
162
167
|
# Start file watcher
|
|
163
168
|
self.start_file_watcher()
|
|
@@ -170,11 +175,11 @@ class DevServer:
|
|
|
170
175
|
server.run(transport=transport)
|
|
171
176
|
|
|
172
177
|
except KeyboardInterrupt:
|
|
173
|
-
|
|
178
|
+
logger.info("\n\n🛑 Shutting down development server...")
|
|
174
179
|
finally:
|
|
175
180
|
self.running = False
|
|
176
181
|
self.stop_file_watcher()
|
|
177
|
-
|
|
182
|
+
logger.info("👋 Development server stopped")
|
|
178
183
|
|
|
179
184
|
def run(self, transport: str = "stdio"):
|
|
180
185
|
"""Run the development server."""
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""MCP server implementing Hanzo capabilities."""
|
|
2
2
|
|
|
3
3
|
import atexit
|
|
4
|
+
import logging
|
|
4
5
|
import signal
|
|
5
6
|
import threading
|
|
6
7
|
import time
|
|
@@ -154,7 +155,10 @@ class HanzoMCPServer:
|
|
|
154
155
|
# Register signal handlers for graceful shutdown
|
|
155
156
|
def signal_handler(signum, frame):
|
|
156
157
|
import sys
|
|
157
|
-
|
|
158
|
+
# Only log if not stdio transport
|
|
159
|
+
if hasattr(self, '_transport') and self._transport != 'stdio':
|
|
160
|
+
logger = logging.getLogger(__name__)
|
|
161
|
+
logger.info("\nShutting down gracefully...")
|
|
158
162
|
self._cleanup_sessions()
|
|
159
163
|
self._shutdown_event.set()
|
|
160
164
|
sys.exit(0)
|
|
@@ -189,7 +193,10 @@ class HanzoMCPServer:
|
|
|
189
193
|
try:
|
|
190
194
|
cleared_count = SessionStorage.clear_all_sessions()
|
|
191
195
|
if cleared_count > 0:
|
|
192
|
-
|
|
196
|
+
# Only log if not stdio transport
|
|
197
|
+
if hasattr(self, '_transport') and self._transport != 'stdio':
|
|
198
|
+
logger = logging.getLogger(__name__)
|
|
199
|
+
logger.info(f"Cleaned up {cleared_count} tmux sessions on shutdown")
|
|
193
200
|
except Exception:
|
|
194
201
|
# Ignore cleanup errors during shutdown
|
|
195
202
|
pass
|
|
@@ -201,6 +208,9 @@ class HanzoMCPServer:
|
|
|
201
208
|
transport: The transport to use (stdio or sse)
|
|
202
209
|
allowed_paths: list of paths that the server is allowed to access
|
|
203
210
|
"""
|
|
211
|
+
# Store transport for later use
|
|
212
|
+
self._transport = transport
|
|
213
|
+
|
|
204
214
|
# Add allowed paths if provided
|
|
205
215
|
allowed_paths_list = allowed_paths or []
|
|
206
216
|
for path in allowed_paths_list:
|
|
@@ -124,7 +124,9 @@ def register_all_tools(
|
|
|
124
124
|
)
|
|
125
125
|
# Auto-detect projects from search paths
|
|
126
126
|
detected_projects = project_manager.detect_projects(search_paths)
|
|
127
|
-
|
|
127
|
+
import logging
|
|
128
|
+
logger = logging.getLogger(__name__)
|
|
129
|
+
logger.info(f"Detected {len(detected_projects)} projects with LLM.md files")
|
|
128
130
|
|
|
129
131
|
filesystem_tools = register_filesystem_tools(
|
|
130
132
|
mcp_server,
|
|
@@ -5,6 +5,7 @@ implementation with the new BashSession-based approach for better persistent exe
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import asyncio
|
|
8
|
+
import logging
|
|
8
9
|
import os
|
|
9
10
|
import shlex
|
|
10
11
|
import subprocess
|
|
@@ -64,16 +65,18 @@ class BashSessionExecutor:
|
|
|
64
65
|
if data is not None:
|
|
65
66
|
try:
|
|
66
67
|
import json
|
|
67
|
-
|
|
68
|
+
logger = logging.getLogger(__name__)
|
|
68
69
|
if isinstance(data, (dict, list)):
|
|
69
70
|
data_str = json.dumps(data)
|
|
70
71
|
else:
|
|
71
72
|
data_str = str(data)
|
|
72
|
-
|
|
73
|
+
logger.debug(f"{message}: {data_str}")
|
|
73
74
|
except Exception:
|
|
74
|
-
|
|
75
|
+
logger = logging.getLogger(__name__)
|
|
76
|
+
logger.debug(f"{message}: {data}")
|
|
75
77
|
else:
|
|
76
|
-
|
|
78
|
+
logger = logging.getLogger(__name__)
|
|
79
|
+
logger.debug(f"{message}")
|
|
77
80
|
|
|
78
81
|
def allow_command(self, command: str) -> None:
|
|
79
82
|
"""Allow a specific command that might otherwise be excluded.
|
|
@@ -183,16 +183,18 @@ class CommandExecutor:
|
|
|
183
183
|
if data is not None:
|
|
184
184
|
try:
|
|
185
185
|
import json
|
|
186
|
-
|
|
186
|
+
logger = logging.getLogger(__name__)
|
|
187
187
|
if isinstance(data, (dict, list)):
|
|
188
188
|
data_str = json.dumps(data)
|
|
189
189
|
else:
|
|
190
190
|
data_str = str(data)
|
|
191
|
-
|
|
191
|
+
logger.debug(f"{message}: {data_str}")
|
|
192
192
|
except Exception:
|
|
193
|
-
|
|
193
|
+
logger = logging.getLogger(__name__)
|
|
194
|
+
logger.debug(f"{message}: {data}")
|
|
194
195
|
else:
|
|
195
|
-
|
|
196
|
+
logger = logging.getLogger(__name__)
|
|
197
|
+
logger.debug(f"{message}")
|
|
196
198
|
|
|
197
199
|
def is_command_allowed(self, command: str) -> bool:
|
|
198
200
|
"""Check if a command is allowed based on exclusion lists.
|
|
@@ -62,7 +62,9 @@ try:
|
|
|
62
62
|
# Auto-detect projects from search paths for new manager
|
|
63
63
|
if search_paths:
|
|
64
64
|
detected_projects = project_manager.detect_projects(search_paths)
|
|
65
|
-
|
|
65
|
+
import logging
|
|
66
|
+
logger = logging.getLogger(__name__)
|
|
67
|
+
logger.info(f"Detected {len(detected_projects)} projects with LLM.md files")
|
|
66
68
|
|
|
67
69
|
# Register individual tools if enabled
|
|
68
70
|
if tool_enabled.get("index", True):
|
|
@@ -85,7 +87,9 @@ except ImportError:
|
|
|
85
87
|
|
|
86
88
|
def register_vector_tools(*args, **kwargs) -> list[BaseTool]:
|
|
87
89
|
"""Vector tools not available - missing dependencies."""
|
|
88
|
-
|
|
90
|
+
import logging
|
|
91
|
+
logger = logging.getLogger(__name__)
|
|
92
|
+
logger.warning("Vector tools not available. Install infinity-embedded: pip install infinity-embedded")
|
|
89
93
|
return []
|
|
90
94
|
|
|
91
95
|
|
|
@@ -94,7 +94,9 @@ class ASTAnalyzer:
|
|
|
94
94
|
# Python parser
|
|
95
95
|
self.parsers['python'] = tree_sitter.Language(tspython.language())
|
|
96
96
|
except Exception as e:
|
|
97
|
-
|
|
97
|
+
import logging
|
|
98
|
+
logger = logging.getLogger(__name__)
|
|
99
|
+
logger.warning(f"Could not initialize Python parser: {e}")
|
|
98
100
|
|
|
99
101
|
def analyze_file(self, file_path: str) -> Optional[FileAST]:
|
|
100
102
|
"""Analyze a file and extract AST information and symbols.
|
|
@@ -127,7 +129,9 @@ class ASTAnalyzer:
|
|
|
127
129
|
return self._analyze_generic_file(file_path, content, file_hash, language)
|
|
128
130
|
|
|
129
131
|
except Exception as e:
|
|
130
|
-
|
|
132
|
+
import logging
|
|
133
|
+
logger = logging.getLogger(__name__)
|
|
134
|
+
logger.error(f"Error analyzing file {file_path}: {e}")
|
|
131
135
|
return None
|
|
132
136
|
|
|
133
137
|
def _detect_language(self, path: Path) -> Optional[str]:
|
|
@@ -194,9 +198,13 @@ class ASTAnalyzer:
|
|
|
194
198
|
ast_nodes = self._extract_tree_sitter_nodes(ts_tree.root_node, content)
|
|
195
199
|
|
|
196
200
|
except SyntaxError as e:
|
|
197
|
-
|
|
201
|
+
import logging
|
|
202
|
+
logger = logging.getLogger(__name__)
|
|
203
|
+
logger.error(f"Syntax error in {file_path}: {e}")
|
|
198
204
|
except Exception as e:
|
|
199
|
-
|
|
205
|
+
import logging
|
|
206
|
+
logger = logging.getLogger(__name__)
|
|
207
|
+
logger.error(f"Error parsing Python file {file_path}: {e}")
|
|
200
208
|
|
|
201
209
|
return FileAST(
|
|
202
210
|
file_path=file_path,
|
|
@@ -528,7 +528,9 @@ class InfinityVectorStore:
|
|
|
528
528
|
return file_ast
|
|
529
529
|
|
|
530
530
|
except Exception as e:
|
|
531
|
-
|
|
531
|
+
import logging
|
|
532
|
+
logger = logging.getLogger(__name__)
|
|
533
|
+
logger.error(f"Error searching AST nodes: {e}")
|
|
532
534
|
return None
|
|
533
535
|
|
|
534
536
|
def get_file_references(self, file_path: str) -> List[Dict[str, Any]]:
|
|
@@ -555,7 +557,9 @@ class InfinityVectorStore:
|
|
|
555
557
|
return references
|
|
556
558
|
|
|
557
559
|
except Exception as e:
|
|
558
|
-
|
|
560
|
+
import logging
|
|
561
|
+
logger = logging.getLogger(__name__)
|
|
562
|
+
logger.error(f"Error getting file references: {e}")
|
|
559
563
|
return []
|
|
560
564
|
|
|
561
565
|
def search(
|
|
@@ -803,7 +807,9 @@ class InfinityVectorStore:
|
|
|
803
807
|
|
|
804
808
|
return True
|
|
805
809
|
except Exception as e:
|
|
806
|
-
|
|
810
|
+
import logging
|
|
811
|
+
logger = logging.getLogger(__name__)
|
|
812
|
+
logger.error(f"Error clearing vector store: {e}")
|
|
807
813
|
return False
|
|
808
814
|
|
|
809
815
|
async def index_document(
|
|
@@ -294,7 +294,9 @@ class ProjectVectorManager:
|
|
|
294
294
|
project_name = project_names[i]
|
|
295
295
|
if isinstance(result, Exception):
|
|
296
296
|
# Log error but continue
|
|
297
|
-
|
|
297
|
+
import logging
|
|
298
|
+
logger = logging.getLogger(__name__)
|
|
299
|
+
logger.error(f"Error searching project {project_name}: {result}")
|
|
298
300
|
combined_results[project_name] = []
|
|
299
301
|
else:
|
|
300
302
|
combined_results[project_name] = result
|
|
@@ -140,7 +140,13 @@ hanzo_mcp/tools/vector/vector_search.py
|
|
|
140
140
|
tests/test_async_support.py
|
|
141
141
|
tests/test_cli.py
|
|
142
142
|
tests/test_git_ingestion.py
|
|
143
|
+
tests/test_litellm_warnings.py
|
|
144
|
+
tests/test_manual.py
|
|
143
145
|
tests/test_new_tools.py
|
|
146
|
+
tests/test_palette.py
|
|
144
147
|
tests/test_search_quality.py
|
|
148
|
+
tests/test_simple.py
|
|
149
|
+
tests/test_stdio_protocol.py
|
|
150
|
+
tests/test_stdio_simple.py
|
|
145
151
|
tests/test_unified_search.py
|
|
146
152
|
tests/test_vector_store.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hanzo-mcp"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.12"
|
|
8
8
|
description = "The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Test that litellm deprecation warnings are properly suppressed."""
|
|
3
|
+
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_no_pydantic_warnings():
|
|
10
|
+
"""Test that running uvx hanzo-mcp doesn't show Pydantic deprecation warnings."""
|
|
11
|
+
# Run the command and capture stderr
|
|
12
|
+
result = subprocess.run(
|
|
13
|
+
[sys.executable, "-m", "hanzo_mcp.cli", "--help"],
|
|
14
|
+
capture_output=True,
|
|
15
|
+
text=True
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Check for deprecation warnings in stderr
|
|
19
|
+
assert "PydanticDeprecatedSince20" not in result.stderr, \
|
|
20
|
+
f"Pydantic deprecation warning found in stderr: {result.stderr}"
|
|
21
|
+
|
|
22
|
+
# Check that the command succeeded
|
|
23
|
+
assert result.returncode == 0, f"Command failed with return code {result.returncode}"
|
|
24
|
+
|
|
25
|
+
# Check that help text is shown
|
|
26
|
+
assert "MCP server implementing Hanzo AI capabilities" in result.stdout
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_agent_tool_no_warnings():
|
|
30
|
+
"""Test that importing agent tools doesn't produce warnings."""
|
|
31
|
+
# Create a test script that imports agent tools
|
|
32
|
+
test_script = '''
|
|
33
|
+
import warnings
|
|
34
|
+
import sys
|
|
35
|
+
|
|
36
|
+
# Capture warnings
|
|
37
|
+
with warnings.catch_warnings(record=True) as w:
|
|
38
|
+
warnings.simplefilter("always")
|
|
39
|
+
|
|
40
|
+
# Import agent tools (which imports litellm)
|
|
41
|
+
from hanzo_mcp.tools.agent import register_agent_tools
|
|
42
|
+
|
|
43
|
+
# Check for deprecation warnings
|
|
44
|
+
deprecation_warnings = [warning for warning in w if issubclass(warning.category, DeprecationWarning)]
|
|
45
|
+
|
|
46
|
+
if deprecation_warnings:
|
|
47
|
+
print("DEPRECATION WARNINGS FOUND:", file=sys.stderr)
|
|
48
|
+
for warning in deprecation_warnings:
|
|
49
|
+
print(f" {warning.category.__name__}: {warning.message}", file=sys.stderr)
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
else:
|
|
52
|
+
print("No deprecation warnings found")
|
|
53
|
+
sys.exit(0)
|
|
54
|
+
'''
|
|
55
|
+
|
|
56
|
+
# Run the test script
|
|
57
|
+
result = subprocess.run(
|
|
58
|
+
[sys.executable, "-c", test_script],
|
|
59
|
+
capture_output=True,
|
|
60
|
+
text=True,
|
|
61
|
+
env={**os.environ, "PYTHONPATH": os.path.dirname(os.path.dirname(__file__))}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Check that no warnings were found
|
|
65
|
+
assert result.returncode == 0, f"Deprecation warnings found: {result.stderr}"
|
|
66
|
+
assert "No deprecation warnings found" in result.stdout
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
test_no_pydantic_warnings()
|
|
71
|
+
test_agent_tool_no_warnings()
|
|
72
|
+
print("✅ All litellm warning tests passed!")
|