hanzo-mcp 0.7.7__py3-none-any.whl → 0.8.1__py3-none-any.whl
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/__init__.py +6 -0
- hanzo_mcp/__main__.py +1 -1
- hanzo_mcp/analytics/__init__.py +2 -2
- hanzo_mcp/analytics/posthog_analytics.py +76 -82
- hanzo_mcp/cli.py +31 -36
- hanzo_mcp/cli_enhanced.py +94 -72
- hanzo_mcp/cli_plugin.py +27 -17
- hanzo_mcp/config/__init__.py +2 -2
- hanzo_mcp/config/settings.py +112 -88
- hanzo_mcp/config/tool_config.py +32 -34
- hanzo_mcp/dev_server.py +66 -67
- hanzo_mcp/prompts/__init__.py +94 -12
- hanzo_mcp/prompts/enhanced_prompts.py +809 -0
- hanzo_mcp/prompts/example_custom_prompt.py +6 -5
- hanzo_mcp/prompts/project_todo_reminder.py +0 -1
- hanzo_mcp/prompts/tool_explorer.py +10 -7
- hanzo_mcp/server.py +17 -21
- hanzo_mcp/server_enhanced.py +15 -22
- hanzo_mcp/tools/__init__.py +56 -28
- hanzo_mcp/tools/agent/__init__.py +16 -19
- hanzo_mcp/tools/agent/agent.py +82 -65
- hanzo_mcp/tools/agent/agent_tool.py +152 -122
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
- hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
- hanzo_mcp/tools/agent/clarification_tool.py +11 -10
- hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
- hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
- hanzo_mcp/tools/agent/code_auth.py +102 -107
- hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
- hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
- hanzo_mcp/tools/agent/critic_tool.py +86 -73
- hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/iching_tool.py +404 -139
- hanzo_mcp/tools/agent/network_tool.py +89 -73
- hanzo_mcp/tools/agent/prompt.py +2 -1
- hanzo_mcp/tools/agent/review_tool.py +101 -98
- hanzo_mcp/tools/agent/swarm_alias.py +87 -0
- hanzo_mcp/tools/agent/swarm_tool.py +246 -161
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
- hanzo_mcp/tools/agent/tool_adapter.py +21 -11
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +3 -5
- hanzo_mcp/tools/common/batch_tool.py +46 -39
- hanzo_mcp/tools/common/config_tool.py +120 -84
- hanzo_mcp/tools/common/context.py +1 -5
- hanzo_mcp/tools/common/context_fix.py +5 -3
- hanzo_mcp/tools/common/critic_tool.py +4 -8
- hanzo_mcp/tools/common/decorators.py +58 -56
- hanzo_mcp/tools/common/enhanced_base.py +29 -32
- hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
- hanzo_mcp/tools/common/forgiving_edit.py +91 -87
- hanzo_mcp/tools/common/mode.py +15 -17
- hanzo_mcp/tools/common/mode_loader.py +27 -24
- hanzo_mcp/tools/common/paginated_base.py +61 -53
- hanzo_mcp/tools/common/paginated_response.py +72 -79
- hanzo_mcp/tools/common/pagination.py +50 -53
- hanzo_mcp/tools/common/permissions.py +4 -4
- hanzo_mcp/tools/common/personality.py +186 -138
- hanzo_mcp/tools/common/plugin_loader.py +54 -54
- hanzo_mcp/tools/common/stats.py +65 -47
- hanzo_mcp/tools/common/test_helpers.py +31 -0
- hanzo_mcp/tools/common/thinking_tool.py +4 -8
- hanzo_mcp/tools/common/tool_disable.py +17 -12
- hanzo_mcp/tools/common/tool_enable.py +13 -14
- hanzo_mcp/tools/common/tool_list.py +36 -28
- hanzo_mcp/tools/common/truncate.py +23 -23
- hanzo_mcp/tools/config/__init__.py +4 -4
- hanzo_mcp/tools/config/config_tool.py +42 -29
- hanzo_mcp/tools/config/index_config.py +37 -34
- hanzo_mcp/tools/config/mode_tool.py +175 -55
- hanzo_mcp/tools/database/__init__.py +15 -12
- hanzo_mcp/tools/database/database_manager.py +77 -75
- hanzo_mcp/tools/database/graph.py +137 -91
- hanzo_mcp/tools/database/graph_add.py +30 -18
- hanzo_mcp/tools/database/graph_query.py +178 -102
- hanzo_mcp/tools/database/graph_remove.py +33 -28
- hanzo_mcp/tools/database/graph_search.py +97 -75
- hanzo_mcp/tools/database/graph_stats.py +91 -59
- hanzo_mcp/tools/database/sql.py +107 -79
- hanzo_mcp/tools/database/sql_query.py +30 -24
- hanzo_mcp/tools/database/sql_search.py +29 -25
- hanzo_mcp/tools/database/sql_stats.py +47 -35
- hanzo_mcp/tools/editor/neovim_command.py +25 -28
- hanzo_mcp/tools/editor/neovim_edit.py +21 -23
- hanzo_mcp/tools/editor/neovim_session.py +60 -54
- hanzo_mcp/tools/filesystem/__init__.py +31 -30
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
- hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +316 -224
- hanzo_mcp/tools/filesystem/content_replace.py +4 -4
- hanzo_mcp/tools/filesystem/diff.py +71 -59
- hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
- hanzo_mcp/tools/filesystem/edit.py +4 -4
- hanzo_mcp/tools/filesystem/find.py +173 -80
- hanzo_mcp/tools/filesystem/find_files.py +73 -52
- hanzo_mcp/tools/filesystem/git_search.py +157 -104
- hanzo_mcp/tools/filesystem/grep.py +8 -8
- hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
- hanzo_mcp/tools/filesystem/read.py +12 -10
- hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
- hanzo_mcp/tools/filesystem/search_tool.py +263 -207
- hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
- hanzo_mcp/tools/filesystem/tree.py +35 -33
- hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
- hanzo_mcp/tools/filesystem/watch.py +37 -36
- hanzo_mcp/tools/filesystem/write.py +4 -8
- hanzo_mcp/tools/jupyter/__init__.py +4 -4
- hanzo_mcp/tools/jupyter/base.py +4 -5
- hanzo_mcp/tools/jupyter/jupyter.py +67 -47
- hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
- hanzo_mcp/tools/llm/__init__.py +5 -7
- hanzo_mcp/tools/llm/consensus_tool.py +72 -52
- hanzo_mcp/tools/llm/llm_manage.py +101 -60
- hanzo_mcp/tools/llm/llm_tool.py +226 -166
- hanzo_mcp/tools/llm/provider_tools.py +25 -26
- hanzo_mcp/tools/lsp/__init__.py +1 -1
- hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
- hanzo_mcp/tools/mcp/__init__.py +2 -3
- hanzo_mcp/tools/mcp/mcp_add.py +27 -25
- hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
- hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
- hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
- hanzo_mcp/tools/memory/__init__.py +39 -21
- hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
- hanzo_mcp/tools/memory/memory_tools.py +90 -108
- hanzo_mcp/tools/search/__init__.py +7 -2
- hanzo_mcp/tools/search/find_tool.py +297 -212
- hanzo_mcp/tools/search/unified_search.py +366 -314
- hanzo_mcp/tools/shell/__init__.py +8 -7
- hanzo_mcp/tools/shell/auto_background.py +56 -49
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +75 -75
- hanzo_mcp/tools/shell/bash_session.py +2 -2
- hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
- hanzo_mcp/tools/shell/bash_tool.py +24 -31
- hanzo_mcp/tools/shell/command_executor.py +12 -12
- hanzo_mcp/tools/shell/logs.py +43 -33
- hanzo_mcp/tools/shell/npx.py +13 -13
- hanzo_mcp/tools/shell/npx_background.py +24 -21
- hanzo_mcp/tools/shell/npx_tool.py +18 -22
- hanzo_mcp/tools/shell/open.py +19 -21
- hanzo_mcp/tools/shell/pkill.py +31 -26
- hanzo_mcp/tools/shell/process_tool.py +32 -32
- hanzo_mcp/tools/shell/processes.py +57 -58
- hanzo_mcp/tools/shell/run_background.py +24 -25
- hanzo_mcp/tools/shell/run_command.py +5 -5
- hanzo_mcp/tools/shell/run_command_windows.py +5 -5
- hanzo_mcp/tools/shell/session_storage.py +3 -3
- hanzo_mcp/tools/shell/streaming_command.py +141 -126
- hanzo_mcp/tools/shell/uvx.py +24 -25
- hanzo_mcp/tools/shell/uvx_background.py +35 -33
- hanzo_mcp/tools/shell/uvx_tool.py +18 -22
- hanzo_mcp/tools/todo/__init__.py +6 -2
- hanzo_mcp/tools/todo/todo.py +50 -37
- hanzo_mcp/tools/todo/todo_read.py +5 -8
- hanzo_mcp/tools/todo/todo_write.py +5 -7
- hanzo_mcp/tools/vector/__init__.py +40 -28
- hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
- hanzo_mcp/tools/vector/git_ingester.py +170 -179
- hanzo_mcp/tools/vector/index_tool.py +96 -44
- hanzo_mcp/tools/vector/infinity_store.py +283 -228
- hanzo_mcp/tools/vector/mock_infinity.py +39 -40
- hanzo_mcp/tools/vector/project_manager.py +88 -78
- hanzo_mcp/tools/vector/vector.py +59 -42
- hanzo_mcp/tools/vector/vector_index.py +30 -27
- hanzo_mcp/tools/vector/vector_search.py +64 -45
- hanzo_mcp/types.py +6 -4
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/METADATA +1 -1
- hanzo_mcp-0.8.1.dist-info/RECORD +185 -0
- hanzo_mcp-0.7.7.dist-info/RECORD +0 -182
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/top_level.txt +0 -0
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
This module provides the CriticTool for Claude to engage in critical analysis and code review.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from typing import Annotated, TypedDict,
|
|
6
|
+
from typing import Unpack, Annotated, TypedDict, final, override
|
|
7
7
|
|
|
8
|
-
from mcp.server.fastmcp import Context as MCPContext
|
|
9
|
-
from mcp.server import FastMCP
|
|
10
8
|
from pydantic import Field
|
|
9
|
+
from mcp.server import FastMCP
|
|
10
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
11
11
|
|
|
12
12
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
13
13
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
Analysis = Annotated[
|
|
17
16
|
str,
|
|
18
17
|
Field(
|
|
@@ -189,8 +188,5 @@ Continue with improvements based on this critical review."""
|
|
|
189
188
|
tool_self = self # Create a reference to self for use in the closure
|
|
190
189
|
|
|
191
190
|
@mcp_server.tool(name=self.name, description=self.description)
|
|
192
|
-
async def critic(
|
|
193
|
-
analysis: Analysis,
|
|
194
|
-
ctx: MCPContext
|
|
195
|
-
) -> str:
|
|
191
|
+
async def critic(analysis: Analysis, ctx: MCPContext) -> str:
|
|
196
192
|
return await tool_self.call(ctx, analysis=analysis)
|
|
@@ -4,47 +4,44 @@ This module provides decorators that handle common cross-cutting concerns
|
|
|
4
4
|
for MCP tools, such as context normalization and error handling.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import functools
|
|
8
7
|
import inspect
|
|
9
|
-
|
|
10
|
-
from
|
|
11
|
-
|
|
12
|
-
from mcp.server.fastmcp import Context as MCPContext
|
|
8
|
+
import functools
|
|
9
|
+
from typing import Any, TypeVar, Callable, cast
|
|
13
10
|
|
|
14
|
-
F = TypeVar(
|
|
11
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
15
12
|
|
|
16
13
|
|
|
17
14
|
class MockContext:
|
|
18
15
|
"""Mock context for when no real context is available.
|
|
19
|
-
|
|
16
|
+
|
|
20
17
|
This is used when tools are called externally through the MCP protocol
|
|
21
18
|
and the Context parameter is not properly serialized.
|
|
22
19
|
"""
|
|
23
|
-
|
|
20
|
+
|
|
24
21
|
def __init__(self):
|
|
25
22
|
self.request_id = "external-request"
|
|
26
23
|
self.client_id = "external-client"
|
|
27
|
-
|
|
24
|
+
|
|
28
25
|
async def info(self, message: str) -> None:
|
|
29
26
|
"""Mock info logging - no-op for external calls."""
|
|
30
27
|
pass
|
|
31
|
-
|
|
28
|
+
|
|
32
29
|
async def debug(self, message: str) -> None:
|
|
33
30
|
"""Mock debug logging - no-op for external calls."""
|
|
34
31
|
pass
|
|
35
|
-
|
|
32
|
+
|
|
36
33
|
async def warning(self, message: str) -> None:
|
|
37
34
|
"""Mock warning logging - no-op for external calls."""
|
|
38
35
|
pass
|
|
39
|
-
|
|
36
|
+
|
|
40
37
|
async def error(self, message: str) -> None:
|
|
41
38
|
"""Mock error logging - no-op for external calls."""
|
|
42
39
|
pass
|
|
43
|
-
|
|
40
|
+
|
|
44
41
|
async def report_progress(self, current: int, total: int) -> None:
|
|
45
42
|
"""Mock progress reporting - no-op for external calls."""
|
|
46
43
|
pass
|
|
47
|
-
|
|
44
|
+
|
|
48
45
|
async def read_resource(self, uri: str) -> Any:
|
|
49
46
|
"""Mock resource reading - returns empty result."""
|
|
50
47
|
return []
|
|
@@ -52,11 +49,11 @@ class MockContext:
|
|
|
52
49
|
|
|
53
50
|
def with_context_normalization(func: F) -> F:
|
|
54
51
|
"""Decorator that normalizes the context parameter for MCP tools.
|
|
55
|
-
|
|
52
|
+
|
|
56
53
|
This decorator intercepts the ctx parameter and ensures it's a valid
|
|
57
54
|
MCPContext object, even when called externally where it might be
|
|
58
55
|
passed as a string, dict, or None.
|
|
59
|
-
|
|
56
|
+
|
|
60
57
|
Usage:
|
|
61
58
|
@server.tool()
|
|
62
59
|
@with_context_normalization
|
|
@@ -64,138 +61,143 @@ def with_context_normalization(func: F) -> F:
|
|
|
64
61
|
# ctx is guaranteed to be a valid context object
|
|
65
62
|
await ctx.info("Processing...")
|
|
66
63
|
return "result"
|
|
67
|
-
|
|
64
|
+
|
|
68
65
|
Args:
|
|
69
66
|
func: The async function to decorate
|
|
70
|
-
|
|
67
|
+
|
|
71
68
|
Returns:
|
|
72
69
|
The decorated function with context normalization
|
|
73
70
|
"""
|
|
71
|
+
|
|
74
72
|
@functools.wraps(func)
|
|
75
73
|
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
76
74
|
# Get function signature to find ctx parameter
|
|
77
75
|
sig = inspect.signature(func)
|
|
78
76
|
params = list(sig.parameters.keys())
|
|
79
|
-
|
|
77
|
+
|
|
80
78
|
# Handle ctx in kwargs
|
|
81
|
-
if
|
|
82
|
-
ctx_value = kwargs[
|
|
79
|
+
if "ctx" in kwargs:
|
|
80
|
+
ctx_value = kwargs["ctx"]
|
|
83
81
|
if not _is_valid_context(ctx_value):
|
|
84
|
-
kwargs[
|
|
85
|
-
|
|
82
|
+
kwargs["ctx"] = MockContext()
|
|
83
|
+
|
|
86
84
|
# Handle ctx in args (positional)
|
|
87
|
-
elif
|
|
88
|
-
ctx_index = params.index(
|
|
85
|
+
elif "ctx" in params:
|
|
86
|
+
ctx_index = params.index("ctx")
|
|
89
87
|
if ctx_index < len(args):
|
|
90
88
|
ctx_value = args[ctx_index]
|
|
91
89
|
if not _is_valid_context(ctx_value):
|
|
92
90
|
args_list = list(args)
|
|
93
91
|
args_list[ctx_index] = MockContext()
|
|
94
92
|
args = tuple(args_list)
|
|
95
|
-
|
|
93
|
+
|
|
96
94
|
# Call the original function
|
|
97
95
|
return await func(*args, **kwargs)
|
|
98
|
-
|
|
96
|
+
|
|
99
97
|
return cast(F, wrapper)
|
|
100
98
|
|
|
101
99
|
|
|
102
100
|
def _is_valid_context(ctx: Any) -> bool:
|
|
103
101
|
"""Check if an object is a valid MCPContext.
|
|
104
|
-
|
|
102
|
+
|
|
105
103
|
Args:
|
|
106
104
|
ctx: The object to check
|
|
107
|
-
|
|
105
|
+
|
|
108
106
|
Returns:
|
|
109
107
|
True if ctx is a valid context object
|
|
110
108
|
"""
|
|
111
109
|
# Check for required context methods
|
|
112
110
|
return (
|
|
113
|
-
hasattr(ctx,
|
|
114
|
-
hasattr(ctx,
|
|
115
|
-
hasattr(ctx,
|
|
116
|
-
hasattr(ctx,
|
|
117
|
-
hasattr(ctx,
|
|
111
|
+
hasattr(ctx, "info")
|
|
112
|
+
and hasattr(ctx, "debug")
|
|
113
|
+
and hasattr(ctx, "warning")
|
|
114
|
+
and hasattr(ctx, "error")
|
|
115
|
+
and hasattr(ctx, "report_progress")
|
|
116
|
+
and
|
|
118
117
|
# Ensure they're callable
|
|
119
|
-
callable(getattr(ctx,
|
|
120
|
-
callable(getattr(ctx,
|
|
118
|
+
callable(getattr(ctx, "info", None))
|
|
119
|
+
and callable(getattr(ctx, "debug", None))
|
|
121
120
|
)
|
|
122
121
|
|
|
123
122
|
|
|
124
123
|
def mcp_tool(
|
|
125
|
-
server: Any,
|
|
126
|
-
name: str | None = None,
|
|
127
|
-
description: str | None = None
|
|
124
|
+
server: Any, name: str | None = None, description: str | None = None
|
|
128
125
|
) -> Callable[[F], F]:
|
|
129
126
|
"""Enhanced MCP tool decorator that includes context normalization.
|
|
130
|
-
|
|
127
|
+
|
|
131
128
|
This decorator combines the standard MCP tool registration with
|
|
132
129
|
automatic context normalization, providing a single-point solution
|
|
133
130
|
for all tools.
|
|
134
|
-
|
|
131
|
+
|
|
135
132
|
Usage:
|
|
136
133
|
@mcp_tool(server, name="my_tool", description="Does something")
|
|
137
134
|
async def my_tool(ctx: MCPContext, param: str) -> str:
|
|
138
135
|
await ctx.info("Processing...")
|
|
139
136
|
return "result"
|
|
140
|
-
|
|
137
|
+
|
|
141
138
|
Args:
|
|
142
139
|
server: The MCP server instance
|
|
143
140
|
name: Optional tool name (defaults to function name)
|
|
144
141
|
description: Optional tool description
|
|
145
|
-
|
|
142
|
+
|
|
146
143
|
Returns:
|
|
147
144
|
Decorator function
|
|
148
145
|
"""
|
|
146
|
+
|
|
149
147
|
def decorator(func: F) -> F:
|
|
150
148
|
# Apply context normalization first
|
|
151
149
|
normalized_func = with_context_normalization(func)
|
|
152
|
-
|
|
150
|
+
|
|
153
151
|
# Then apply the server's tool decorator
|
|
154
|
-
if hasattr(server,
|
|
152
|
+
if hasattr(server, "tool"):
|
|
155
153
|
# Use the server's tool decorator
|
|
156
154
|
server_decorator = server.tool(name=name, description=description)
|
|
157
155
|
return server_decorator(normalized_func)
|
|
158
156
|
else:
|
|
159
157
|
# Fallback if server doesn't have tool method
|
|
160
158
|
return normalized_func
|
|
161
|
-
|
|
159
|
+
|
|
162
160
|
return decorator
|
|
163
161
|
|
|
164
162
|
|
|
165
163
|
def create_tool_handler(server: Any, tool: Any) -> Callable[[], None]:
|
|
166
164
|
"""Create a standardized tool registration handler.
|
|
167
|
-
|
|
165
|
+
|
|
168
166
|
This function creates a registration method that automatically applies
|
|
169
167
|
context normalization to any tool handler registered with the server.
|
|
170
|
-
|
|
168
|
+
|
|
171
169
|
Usage:
|
|
172
170
|
class MyTool(BaseTool):
|
|
173
171
|
def register(self, mcp_server):
|
|
174
172
|
register = create_tool_handler(mcp_server, self)
|
|
175
173
|
register()
|
|
176
|
-
|
|
174
|
+
|
|
177
175
|
Args:
|
|
178
176
|
server: The MCP server instance
|
|
179
177
|
tool: The tool instance with name, description, and handler
|
|
180
|
-
|
|
178
|
+
|
|
181
179
|
Returns:
|
|
182
180
|
A function that registers the tool with context normalization
|
|
183
181
|
"""
|
|
182
|
+
|
|
184
183
|
def register_with_normalization():
|
|
185
184
|
# Get the original register method
|
|
186
185
|
original_register = tool.__class__.register
|
|
187
|
-
|
|
186
|
+
|
|
188
187
|
# Temporarily replace server.tool to wrap with normalization
|
|
189
188
|
original_tool_decorator = server.tool
|
|
190
|
-
|
|
189
|
+
|
|
191
190
|
def normalized_tool_decorator(name=None, description=None):
|
|
192
191
|
def decorator(func):
|
|
193
192
|
# Apply context normalization
|
|
194
193
|
normalized = with_context_normalization(func)
|
|
195
194
|
# Apply original decorator
|
|
196
|
-
return original_tool_decorator(name=name, description=description)(
|
|
195
|
+
return original_tool_decorator(name=name, description=description)(
|
|
196
|
+
normalized
|
|
197
|
+
)
|
|
198
|
+
|
|
197
199
|
return decorator
|
|
198
|
-
|
|
200
|
+
|
|
199
201
|
# Monkey-patch temporarily
|
|
200
202
|
server.tool = normalized_tool_decorator
|
|
201
203
|
try:
|
|
@@ -204,5 +206,5 @@ def create_tool_handler(server: Any, tool: Any) -> Callable[[], None]:
|
|
|
204
206
|
finally:
|
|
205
207
|
# Restore original
|
|
206
208
|
server.tool = original_tool_decorator
|
|
207
|
-
|
|
208
|
-
return register_with_normalization
|
|
209
|
+
|
|
210
|
+
return register_with_normalization
|
|
@@ -5,9 +5,9 @@ context normalization and other cross-cutting concerns, ensuring
|
|
|
5
5
|
consistent behavior across all tools.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from abc import ABC, abstractmethod
|
|
9
|
-
from typing import Any, get_type_hints, get_args, get_origin
|
|
10
8
|
import inspect
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import Any, get_type_hints
|
|
11
11
|
|
|
12
12
|
from mcp.server import FastMCP
|
|
13
13
|
from mcp.server.fastmcp import Context as MCPContext
|
|
@@ -18,40 +18,37 @@ from hanzo_mcp.tools.common.decorators import with_context_normalization
|
|
|
18
18
|
|
|
19
19
|
class EnhancedBaseTool(BaseTool, ABC):
|
|
20
20
|
"""Enhanced base class for MCP tools with automatic context normalization.
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
This base class automatically wraps the tool registration to include
|
|
23
23
|
context normalization, ensuring that all tools handle external calls
|
|
24
24
|
properly without requiring manual decoration or copy-pasted code.
|
|
25
25
|
"""
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
def register(self, mcp_server: FastMCP) -> None:
|
|
28
28
|
"""Register this tool with automatic context normalization.
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
This method automatically applies context normalization to the tool
|
|
31
31
|
handler, ensuring it works properly when called externally.
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
Args:
|
|
34
34
|
mcp_server: The FastMCP server instance
|
|
35
35
|
"""
|
|
36
36
|
# Get the tool method from the subclass
|
|
37
37
|
tool_method = self._create_tool_handler()
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
# Apply context normalization decorator
|
|
40
40
|
normalized_method = with_context_normalization(tool_method)
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
# Register with the server
|
|
43
|
-
mcp_server.tool(
|
|
44
|
-
|
|
45
|
-
description=self.description
|
|
46
|
-
)(normalized_method)
|
|
47
|
-
|
|
43
|
+
mcp_server.tool(name=self.name, description=self.description)(normalized_method)
|
|
44
|
+
|
|
48
45
|
@abstractmethod
|
|
49
46
|
def _create_tool_handler(self) -> Any:
|
|
50
47
|
"""Create the tool handler function.
|
|
51
|
-
|
|
48
|
+
|
|
52
49
|
Subclasses must implement this to return an async function
|
|
53
50
|
that will be registered as the tool handler.
|
|
54
|
-
|
|
51
|
+
|
|
55
52
|
Returns:
|
|
56
53
|
An async function that handles tool calls
|
|
57
54
|
"""
|
|
@@ -60,47 +57,47 @@ class EnhancedBaseTool(BaseTool, ABC):
|
|
|
60
57
|
|
|
61
58
|
class AutoRegisterTool(BaseTool, ABC):
|
|
62
59
|
"""Base class that automatically generates tool handlers from the call method.
|
|
63
|
-
|
|
60
|
+
|
|
64
61
|
This base class inspects the call method signature and automatically
|
|
65
62
|
creates a properly typed tool handler with context normalization.
|
|
66
63
|
"""
|
|
67
|
-
|
|
64
|
+
|
|
68
65
|
def register(self, mcp_server: FastMCP) -> None:
|
|
69
66
|
"""Register this tool with automatic handler generation.
|
|
70
|
-
|
|
67
|
+
|
|
71
68
|
This method inspects the call method signature and automatically
|
|
72
69
|
creates a tool handler with the correct parameters and types.
|
|
73
|
-
|
|
70
|
+
|
|
74
71
|
Args:
|
|
75
72
|
mcp_server: The FastMCP server instance
|
|
76
73
|
"""
|
|
77
74
|
# Get the call method signature
|
|
78
75
|
call_method = self.call
|
|
79
76
|
sig = inspect.signature(call_method)
|
|
80
|
-
|
|
77
|
+
|
|
81
78
|
# Get type hints for proper typing
|
|
82
79
|
hints = get_type_hints(call_method)
|
|
83
|
-
|
|
80
|
+
|
|
84
81
|
# Create a dynamic handler function
|
|
85
82
|
tool_self = self
|
|
86
|
-
|
|
83
|
+
|
|
87
84
|
# Build the handler dynamically based on the call signature
|
|
88
85
|
params = list(sig.parameters.items())
|
|
89
|
-
|
|
86
|
+
|
|
90
87
|
# Skip 'self' and 'ctx' parameters
|
|
91
|
-
tool_params = [
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
tool_params = [
|
|
89
|
+
(name, param) for name, param in params if name not in ("self", "ctx")
|
|
90
|
+
]
|
|
91
|
+
|
|
94
92
|
# Create the handler function dynamically
|
|
95
93
|
async def handler(ctx: MCPContext, **kwargs: Any) -> Any:
|
|
96
94
|
# Call the tool's call method with the context and parameters
|
|
97
95
|
return await tool_self.call(ctx, **kwargs)
|
|
98
|
-
|
|
96
|
+
|
|
99
97
|
# Apply context normalization
|
|
100
98
|
normalized_handler = with_context_normalization(handler)
|
|
101
|
-
|
|
99
|
+
|
|
102
100
|
# Register with the server
|
|
103
|
-
mcp_server.tool(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
)(normalized_handler)
|
|
101
|
+
mcp_server.tool(name=self.name, description=self.description)(
|
|
102
|
+
normalized_handler
|
|
103
|
+
)
|