hanzo-mcp 0.9.0__py3-none-any.whl → 0.9.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 +1 -1
- hanzo_mcp/analytics/posthog_analytics.py +14 -1
- hanzo_mcp/cli.py +108 -4
- hanzo_mcp/server.py +11 -0
- hanzo_mcp/tools/__init__.py +3 -16
- hanzo_mcp/tools/agent/__init__.py +5 -0
- hanzo_mcp/tools/agent/agent.py +5 -0
- hanzo_mcp/tools/agent/agent_tool.py +3 -17
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +623 -0
- hanzo_mcp/tools/agent/clarification_tool.py +7 -1
- hanzo_mcp/tools/agent/claude_desktop_auth.py +16 -6
- hanzo_mcp/tools/agent/cli_agent_base.py +5 -0
- hanzo_mcp/tools/agent/cli_tools.py +26 -0
- hanzo_mcp/tools/agent/code_auth_tool.py +5 -0
- hanzo_mcp/tools/agent/critic_tool.py +7 -1
- hanzo_mcp/tools/agent/iching_tool.py +5 -0
- hanzo_mcp/tools/agent/network_tool.py +5 -0
- hanzo_mcp/tools/agent/review_tool.py +7 -1
- hanzo_mcp/tools/agent/swarm_alias.py +5 -0
- hanzo_mcp/tools/agent/swarm_tool.py +701 -0
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +554 -0
- hanzo_mcp/tools/agent/unified_cli_tools.py +5 -0
- hanzo_mcp/tools/common/auto_timeout.py +234 -0
- hanzo_mcp/tools/common/base.py +4 -0
- hanzo_mcp/tools/common/batch_tool.py +5 -0
- hanzo_mcp/tools/common/config_tool.py +5 -0
- hanzo_mcp/tools/common/critic_tool.py +5 -0
- hanzo_mcp/tools/common/paginated_base.py +4 -0
- hanzo_mcp/tools/common/permissions.py +38 -12
- hanzo_mcp/tools/common/personality.py +673 -980
- hanzo_mcp/tools/common/stats.py +5 -0
- hanzo_mcp/tools/common/thinking_tool.py +5 -0
- hanzo_mcp/tools/common/timeout_parser.py +103 -0
- hanzo_mcp/tools/common/tool_disable.py +5 -0
- hanzo_mcp/tools/common/tool_enable.py +5 -0
- hanzo_mcp/tools/common/tool_list.py +5 -0
- hanzo_mcp/tools/config/config_tool.py +5 -0
- hanzo_mcp/tools/config/mode_tool.py +5 -0
- hanzo_mcp/tools/database/graph.py +5 -0
- hanzo_mcp/tools/database/graph_add.py +5 -0
- hanzo_mcp/tools/database/graph_query.py +5 -0
- hanzo_mcp/tools/database/graph_remove.py +5 -0
- hanzo_mcp/tools/database/graph_search.py +5 -0
- hanzo_mcp/tools/database/graph_stats.py +5 -0
- hanzo_mcp/tools/database/sql.py +5 -0
- hanzo_mcp/tools/database/sql_query.py +2 -0
- hanzo_mcp/tools/database/sql_search.py +5 -0
- hanzo_mcp/tools/database/sql_stats.py +5 -0
- hanzo_mcp/tools/editor/neovim_command.py +5 -0
- hanzo_mcp/tools/editor/neovim_edit.py +7 -2
- hanzo_mcp/tools/editor/neovim_session.py +5 -0
- hanzo_mcp/tools/filesystem/__init__.py +23 -26
- hanzo_mcp/tools/filesystem/ast_tool.py +2 -3
- hanzo_mcp/tools/filesystem/base.py +0 -16
- hanzo_mcp/tools/filesystem/batch_search.py +825 -0
- hanzo_mcp/tools/filesystem/content_replace.py +5 -3
- hanzo_mcp/tools/filesystem/diff.py +5 -0
- hanzo_mcp/tools/filesystem/directory_tree.py +34 -281
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +345 -0
- hanzo_mcp/tools/filesystem/edit.py +5 -4
- hanzo_mcp/tools/filesystem/find.py +177 -311
- hanzo_mcp/tools/filesystem/find_files.py +370 -0
- hanzo_mcp/tools/filesystem/git_search.py +5 -3
- hanzo_mcp/tools/filesystem/grep.py +454 -0
- hanzo_mcp/tools/filesystem/multi_edit.py +5 -4
- hanzo_mcp/tools/filesystem/read.py +11 -8
- hanzo_mcp/tools/filesystem/rules_tool.py +5 -3
- hanzo_mcp/tools/filesystem/search_tool.py +728 -0
- hanzo_mcp/tools/filesystem/symbols_tool.py +510 -0
- hanzo_mcp/tools/filesystem/tree.py +273 -0
- hanzo_mcp/tools/filesystem/watch.py +6 -1
- hanzo_mcp/tools/filesystem/write.py +12 -6
- hanzo_mcp/tools/jupyter/jupyter.py +30 -2
- hanzo_mcp/tools/jupyter/notebook_edit.py +298 -0
- hanzo_mcp/tools/jupyter/notebook_read.py +148 -0
- hanzo_mcp/tools/llm/consensus_tool.py +8 -6
- hanzo_mcp/tools/llm/llm_manage.py +5 -0
- hanzo_mcp/tools/llm/llm_tool.py +2 -0
- hanzo_mcp/tools/llm/llm_unified.py +5 -0
- hanzo_mcp/tools/llm/provider_tools.py +5 -0
- hanzo_mcp/tools/lsp/lsp_tool.py +475 -622
- hanzo_mcp/tools/mcp/mcp_add.py +7 -2
- hanzo_mcp/tools/mcp/mcp_remove.py +15 -2
- hanzo_mcp/tools/mcp/mcp_stats.py +5 -0
- hanzo_mcp/tools/mcp/mcp_tool.py +5 -0
- hanzo_mcp/tools/memory/knowledge_tools.py +14 -0
- hanzo_mcp/tools/memory/memory_tools.py +17 -0
- hanzo_mcp/tools/search/find_tool.py +5 -3
- hanzo_mcp/tools/search/unified_search.py +3 -1
- hanzo_mcp/tools/shell/__init__.py +2 -14
- hanzo_mcp/tools/shell/base_process.py +4 -2
- hanzo_mcp/tools/shell/bash_tool.py +2 -0
- hanzo_mcp/tools/shell/command_executor.py +7 -7
- hanzo_mcp/tools/shell/logs.py +5 -0
- hanzo_mcp/tools/shell/npx.py +5 -0
- hanzo_mcp/tools/shell/npx_background.py +5 -0
- hanzo_mcp/tools/shell/npx_tool.py +5 -0
- hanzo_mcp/tools/shell/open.py +5 -0
- hanzo_mcp/tools/shell/pkill.py +5 -0
- hanzo_mcp/tools/shell/process_tool.py +5 -0
- hanzo_mcp/tools/shell/processes.py +5 -0
- hanzo_mcp/tools/shell/run_background.py +5 -0
- hanzo_mcp/tools/shell/run_command.py +2 -0
- hanzo_mcp/tools/shell/run_command_windows.py +5 -0
- hanzo_mcp/tools/shell/streaming_command.py +5 -0
- hanzo_mcp/tools/shell/uvx.py +5 -0
- hanzo_mcp/tools/shell/uvx_background.py +5 -0
- hanzo_mcp/tools/shell/uvx_tool.py +5 -0
- hanzo_mcp/tools/shell/zsh_tool.py +3 -0
- hanzo_mcp/tools/todo/todo.py +5 -0
- hanzo_mcp/tools/todo/todo_read.py +142 -0
- hanzo_mcp/tools/todo/todo_write.py +367 -0
- hanzo_mcp/tools/vector/__init__.py +42 -95
- hanzo_mcp/tools/vector/index_tool.py +5 -0
- hanzo_mcp/tools/vector/vector.py +5 -0
- hanzo_mcp/tools/vector/vector_index.py +5 -0
- hanzo_mcp/tools/vector/vector_search.py +5 -0
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/METADATA +1 -1
- hanzo_mcp-0.9.1.dist-info/RECORD +195 -0
- hanzo_mcp/tools/common/path_utils.py +0 -34
- hanzo_mcp/tools/compiler/__init__.py +0 -8
- hanzo_mcp/tools/compiler/sandboxed_compiler.py +0 -681
- hanzo_mcp/tools/environment/__init__.py +0 -8
- hanzo_mcp/tools/environment/environment_detector.py +0 -594
- hanzo_mcp/tools/filesystem/search.py +0 -1160
- hanzo_mcp/tools/framework/__init__.py +0 -8
- hanzo_mcp/tools/framework/framework_modes.py +0 -714
- hanzo_mcp/tools/memory/conversation_memory.py +0 -636
- hanzo_mcp/tools/shell/run_tool.py +0 -56
- hanzo_mcp/tools/vector/node_tool.py +0 -538
- hanzo_mcp/tools/vector/unified_vector.py +0 -384
- hanzo_mcp-0.9.0.dist-info/RECORD +0 -191
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/top_level.txt +0 -0
|
@@ -1,594 +0,0 @@
|
|
|
1
|
-
"""Environment detection tool for automatic development environment configuration.
|
|
2
|
-
|
|
3
|
-
This tool automatically detects installed development tools, frameworks, and
|
|
4
|
-
languages in the user's environment and dynamically loads appropriate MCP tools.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
import sys
|
|
9
|
-
import json
|
|
10
|
-
import shutil
|
|
11
|
-
import subprocess
|
|
12
|
-
import logging
|
|
13
|
-
from typing import Dict, List, Any, Optional, Set
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
from dataclasses import dataclass, field
|
|
16
|
-
|
|
17
|
-
from hanzo_mcp.types import MCPResourceDocument
|
|
18
|
-
from hanzo_mcp.tools.common.base import BaseTool
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass
|
|
22
|
-
class ToolDetection:
|
|
23
|
-
"""Represents a detected development tool."""
|
|
24
|
-
name: str
|
|
25
|
-
version: Optional[str]
|
|
26
|
-
path: str
|
|
27
|
-
category: str # language, framework, database, etc.
|
|
28
|
-
mcp_tools: List[str] = field(default_factory=list) # Related MCP tools to enable
|
|
29
|
-
environment: Dict[str, str] = field(default_factory=dict) # Environment variables
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# Tool detection configurations
|
|
33
|
-
TOOL_DETECTIONS = {
|
|
34
|
-
# Programming Languages
|
|
35
|
-
"python": {
|
|
36
|
-
"check_cmd": ["python", "--version"],
|
|
37
|
-
"category": "language",
|
|
38
|
-
"mcp_tools": ["run_command", "uvx", "uvx_background"],
|
|
39
|
-
"parse_version": lambda out: out.split()[1] if out else None,
|
|
40
|
-
"frameworks": {
|
|
41
|
-
"django": {
|
|
42
|
-
"check": ["python", "-c", "import django; print(django.get_version())"],
|
|
43
|
-
"mcp_tools": ["django_manage", "django_shell"],
|
|
44
|
-
},
|
|
45
|
-
"fastapi": {
|
|
46
|
-
"check": ["python", "-c", "import fastapi; print(fastapi.__version__)"],
|
|
47
|
-
"mcp_tools": ["uvicorn", "fastapi_routes"],
|
|
48
|
-
},
|
|
49
|
-
"flask": {
|
|
50
|
-
"check": ["python", "-c", "import flask; print(flask.__version__)"],
|
|
51
|
-
"mcp_tools": ["flask_run"],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
"node": {
|
|
56
|
-
"check_cmd": ["node", "--version"],
|
|
57
|
-
"category": "language",
|
|
58
|
-
"mcp_tools": ["npx", "npx_background"],
|
|
59
|
-
"parse_version": lambda out: out.strip().lstrip("v") if out else None,
|
|
60
|
-
"frameworks": {
|
|
61
|
-
"next": {
|
|
62
|
-
"check": ["npm", "list", "next", "--json"],
|
|
63
|
-
"mcp_tools": ["next_dev", "next_build"],
|
|
64
|
-
},
|
|
65
|
-
"react": {
|
|
66
|
-
"check": ["npm", "list", "react", "--json"],
|
|
67
|
-
"mcp_tools": ["react_dev"],
|
|
68
|
-
},
|
|
69
|
-
"vue": {
|
|
70
|
-
"check": ["npm", "list", "vue", "--json"],
|
|
71
|
-
"mcp_tools": ["vue_serve"],
|
|
72
|
-
},
|
|
73
|
-
"angular": {
|
|
74
|
-
"check": ["ng", "version"],
|
|
75
|
-
"mcp_tools": ["ng_serve", "ng_build"],
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
"go": {
|
|
80
|
-
"check_cmd": ["go", "version"],
|
|
81
|
-
"category": "language",
|
|
82
|
-
"mcp_tools": ["go_build", "go_run", "go_test"],
|
|
83
|
-
"parse_version": lambda out: out.split()[2].lstrip("go") if out else None,
|
|
84
|
-
"frameworks": {
|
|
85
|
-
"gin": {
|
|
86
|
-
"check_file": "go.mod",
|
|
87
|
-
"check_content": "github.com/gin-gonic/gin",
|
|
88
|
-
"mcp_tools": ["gin_server"],
|
|
89
|
-
},
|
|
90
|
-
"echo": {
|
|
91
|
-
"check_file": "go.mod",
|
|
92
|
-
"check_content": "github.com/labstack/echo",
|
|
93
|
-
"mcp_tools": ["echo_server"],
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
"rust": {
|
|
98
|
-
"check_cmd": ["rustc", "--version"],
|
|
99
|
-
"category": "language",
|
|
100
|
-
"mcp_tools": ["cargo_build", "cargo_run", "cargo_test"],
|
|
101
|
-
"parse_version": lambda out: out.split()[1] if out else None,
|
|
102
|
-
"frameworks": {
|
|
103
|
-
"actix": {
|
|
104
|
-
"check_file": "Cargo.toml",
|
|
105
|
-
"check_content": 'actix-web',
|
|
106
|
-
"mcp_tools": ["actix_server"],
|
|
107
|
-
},
|
|
108
|
-
"rocket": {
|
|
109
|
-
"check_file": "Cargo.toml",
|
|
110
|
-
"check_content": 'rocket',
|
|
111
|
-
"mcp_tools": ["rocket_server"],
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
"java": {
|
|
116
|
-
"check_cmd": ["java", "--version"],
|
|
117
|
-
"category": "language",
|
|
118
|
-
"mcp_tools": ["javac", "java_run"],
|
|
119
|
-
"parse_version": lambda out: out.split()[1] if out else None,
|
|
120
|
-
"frameworks": {
|
|
121
|
-
"spring": {
|
|
122
|
-
"check_file": "pom.xml",
|
|
123
|
-
"check_content": "spring-boot",
|
|
124
|
-
"mcp_tools": ["spring_boot", "mvn"],
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
"cpp": {
|
|
129
|
-
"check_cmd": ["clang++", "--version"],
|
|
130
|
-
"category": "language",
|
|
131
|
-
"mcp_tools": ["cmake", "make", "clang_compile"],
|
|
132
|
-
"parse_version": lambda out: out.split()[2] if "version" in out else None,
|
|
133
|
-
},
|
|
134
|
-
"c": {
|
|
135
|
-
"check_cmd": ["clang", "--version"],
|
|
136
|
-
"category": "language",
|
|
137
|
-
"mcp_tools": ["cmake", "make", "clang_compile"],
|
|
138
|
-
"parse_version": lambda out: out.split()[2] if "version" in out else None,
|
|
139
|
-
},
|
|
140
|
-
"zig": {
|
|
141
|
-
"check_cmd": ["zig", "version"],
|
|
142
|
-
"category": "language",
|
|
143
|
-
"mcp_tools": ["zig_build", "zig_run", "zig_test"],
|
|
144
|
-
"parse_version": lambda out: out.strip() if out else None,
|
|
145
|
-
},
|
|
146
|
-
# Databases
|
|
147
|
-
"postgresql": {
|
|
148
|
-
"check_cmd": ["psql", "--version"],
|
|
149
|
-
"category": "database",
|
|
150
|
-
"mcp_tools": ["sql_query", "sql_search", "sql_stats"],
|
|
151
|
-
"parse_version": lambda out: out.split()[-1] if out else None,
|
|
152
|
-
},
|
|
153
|
-
"mysql": {
|
|
154
|
-
"check_cmd": ["mysql", "--version"],
|
|
155
|
-
"category": "database",
|
|
156
|
-
"mcp_tools": ["sql_query", "sql_search", "sql_stats"],
|
|
157
|
-
"parse_version": lambda out: out.split()[4].rstrip(",") if out else None,
|
|
158
|
-
},
|
|
159
|
-
"redis": {
|
|
160
|
-
"check_cmd": ["redis-cli", "--version"],
|
|
161
|
-
"category": "database",
|
|
162
|
-
"mcp_tools": ["redis_get", "redis_set", "redis_keys"],
|
|
163
|
-
"parse_version": lambda out: out.split()[2] if out else None,
|
|
164
|
-
},
|
|
165
|
-
"mongodb": {
|
|
166
|
-
"check_cmd": ["mongod", "--version"],
|
|
167
|
-
"category": "database",
|
|
168
|
-
"mcp_tools": ["mongo_query", "mongo_insert"],
|
|
169
|
-
"parse_version": lambda out: out.split()[2] if "version" in out else None,
|
|
170
|
-
},
|
|
171
|
-
# Container & Orchestration
|
|
172
|
-
"docker": {
|
|
173
|
-
"check_cmd": ["docker", "--version"],
|
|
174
|
-
"category": "container",
|
|
175
|
-
"mcp_tools": ["docker_build", "docker_run", "docker_ps"],
|
|
176
|
-
"parse_version": lambda out: out.split()[2].rstrip(",") if out else None,
|
|
177
|
-
},
|
|
178
|
-
"kubernetes": {
|
|
179
|
-
"check_cmd": ["kubectl", "version", "--client", "--short"],
|
|
180
|
-
"category": "orchestration",
|
|
181
|
-
"mcp_tools": ["kubectl_apply", "kubectl_get", "kubectl_logs"],
|
|
182
|
-
"parse_version": lambda out: out.split()[-1] if out else None,
|
|
183
|
-
},
|
|
184
|
-
# Version Control
|
|
185
|
-
"git": {
|
|
186
|
-
"check_cmd": ["git", "--version"],
|
|
187
|
-
"category": "vcs",
|
|
188
|
-
"mcp_tools": ["git_search", "git_log", "git_diff"],
|
|
189
|
-
"parse_version": lambda out: out.split()[-1] if out else None,
|
|
190
|
-
},
|
|
191
|
-
# Package Managers
|
|
192
|
-
"npm": {
|
|
193
|
-
"check_cmd": ["npm", "--version"],
|
|
194
|
-
"category": "package_manager",
|
|
195
|
-
"mcp_tools": ["npx", "npm_install"],
|
|
196
|
-
"parse_version": lambda out: out.strip() if out else None,
|
|
197
|
-
},
|
|
198
|
-
"yarn": {
|
|
199
|
-
"check_cmd": ["yarn", "--version"],
|
|
200
|
-
"category": "package_manager",
|
|
201
|
-
"mcp_tools": ["yarn_install", "yarn_run"],
|
|
202
|
-
"parse_version": lambda out: out.strip() if out else None,
|
|
203
|
-
},
|
|
204
|
-
"pnpm": {
|
|
205
|
-
"check_cmd": ["pnpm", "--version"],
|
|
206
|
-
"category": "package_manager",
|
|
207
|
-
"mcp_tools": ["pnpm_install", "pnpm_run"],
|
|
208
|
-
"parse_version": lambda out: out.strip() if out else None,
|
|
209
|
-
},
|
|
210
|
-
"pip": {
|
|
211
|
-
"check_cmd": ["pip", "--version"],
|
|
212
|
-
"category": "package_manager",
|
|
213
|
-
"mcp_tools": ["pip_install", "pip_freeze"],
|
|
214
|
-
"parse_version": lambda out: out.split()[1] if out else None,
|
|
215
|
-
},
|
|
216
|
-
"cargo": {
|
|
217
|
-
"check_cmd": ["cargo", "--version"],
|
|
218
|
-
"category": "package_manager",
|
|
219
|
-
"mcp_tools": ["cargo_build", "cargo_run"],
|
|
220
|
-
"parse_version": lambda out: out.split()[1] if out else None,
|
|
221
|
-
},
|
|
222
|
-
# Build Tools
|
|
223
|
-
"make": {
|
|
224
|
-
"check_cmd": ["make", "--version"],
|
|
225
|
-
"category": "build",
|
|
226
|
-
"mcp_tools": ["make_build", "make_clean"],
|
|
227
|
-
"parse_version": lambda out: out.split()[-1] if out else None,
|
|
228
|
-
},
|
|
229
|
-
"cmake": {
|
|
230
|
-
"check_cmd": ["cmake", "--version"],
|
|
231
|
-
"category": "build",
|
|
232
|
-
"mcp_tools": ["cmake_configure", "cmake_build"],
|
|
233
|
-
"parse_version": lambda out: out.split()[2] if out else None,
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
class EnvironmentDetector(BaseTool):
|
|
239
|
-
"""Tool for detecting and configuring development environment."""
|
|
240
|
-
|
|
241
|
-
name = "env_detect"
|
|
242
|
-
description = """Detect development environment and auto-configure tools.
|
|
243
|
-
|
|
244
|
-
Actions:
|
|
245
|
-
- detect: Detect all installed development tools
|
|
246
|
-
- check: Check specific tool availability
|
|
247
|
-
- suggest: Suggest tools to install for project
|
|
248
|
-
- configure: Auto-configure MCP tools based on detection
|
|
249
|
-
- export: Export environment configuration
|
|
250
|
-
|
|
251
|
-
This tool helps automatically configure your development environment
|
|
252
|
-
by detecting installed languages, frameworks, databases, and tools.
|
|
253
|
-
"""
|
|
254
|
-
|
|
255
|
-
def __init__(self):
|
|
256
|
-
super().__init__()
|
|
257
|
-
self.logger = logging.getLogger(__name__)
|
|
258
|
-
self.detected_tools: List[ToolDetection] = []
|
|
259
|
-
self.project_root = self._find_project_root()
|
|
260
|
-
|
|
261
|
-
def _find_project_root(self) -> Path:
|
|
262
|
-
"""Find project root directory."""
|
|
263
|
-
markers = [
|
|
264
|
-
".git", "package.json", "pyproject.toml", "Cargo.toml",
|
|
265
|
-
"go.mod", "pom.xml", "build.gradle", "CMakeLists.txt"
|
|
266
|
-
]
|
|
267
|
-
|
|
268
|
-
cwd = Path.cwd()
|
|
269
|
-
for parent in [cwd] + list(cwd.parents):
|
|
270
|
-
for marker in markers:
|
|
271
|
-
if (parent / marker).exists():
|
|
272
|
-
return parent
|
|
273
|
-
|
|
274
|
-
return cwd
|
|
275
|
-
|
|
276
|
-
def _run_command(self, cmd: List[str]) -> Optional[str]:
|
|
277
|
-
"""Run command and return output."""
|
|
278
|
-
try:
|
|
279
|
-
result = subprocess.run(
|
|
280
|
-
cmd,
|
|
281
|
-
capture_output=True,
|
|
282
|
-
text=True,
|
|
283
|
-
timeout=5,
|
|
284
|
-
check=False,
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
if result.returncode == 0:
|
|
288
|
-
return result.stdout.strip()
|
|
289
|
-
|
|
290
|
-
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
291
|
-
pass
|
|
292
|
-
|
|
293
|
-
return None
|
|
294
|
-
|
|
295
|
-
def _check_file_content(self, file_path: str, content: str) -> bool:
|
|
296
|
-
"""Check if file contains specific content."""
|
|
297
|
-
try:
|
|
298
|
-
file = self.project_root / file_path
|
|
299
|
-
if file.exists():
|
|
300
|
-
with open(file, "r") as f:
|
|
301
|
-
return content in f.read()
|
|
302
|
-
except Exception:
|
|
303
|
-
pass
|
|
304
|
-
|
|
305
|
-
return False
|
|
306
|
-
|
|
307
|
-
def _detect_tool(self, tool_name: str, config: Dict[str, Any]) -> Optional[ToolDetection]:
|
|
308
|
-
"""Detect a specific tool."""
|
|
309
|
-
# Check main tool
|
|
310
|
-
output = self._run_command(config["check_cmd"])
|
|
311
|
-
if not output:
|
|
312
|
-
return None
|
|
313
|
-
|
|
314
|
-
# Parse version
|
|
315
|
-
version = None
|
|
316
|
-
if "parse_version" in config:
|
|
317
|
-
try:
|
|
318
|
-
version = config["parse_version"](output)
|
|
319
|
-
except Exception:
|
|
320
|
-
pass
|
|
321
|
-
|
|
322
|
-
# Find executable path
|
|
323
|
-
executable = config["check_cmd"][0]
|
|
324
|
-
path = shutil.which(executable) or executable
|
|
325
|
-
|
|
326
|
-
# Create detection
|
|
327
|
-
detection = ToolDetection(
|
|
328
|
-
name=tool_name,
|
|
329
|
-
version=version,
|
|
330
|
-
path=path,
|
|
331
|
-
category=config["category"],
|
|
332
|
-
mcp_tools=config.get("mcp_tools", []),
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
# Check frameworks if applicable
|
|
336
|
-
if "frameworks" in config:
|
|
337
|
-
for fw_name, fw_config in config["frameworks"].items():
|
|
338
|
-
if self._check_framework(fw_config):
|
|
339
|
-
detection.mcp_tools.extend(fw_config.get("mcp_tools", []))
|
|
340
|
-
self.logger.info(f"Detected {fw_name} framework")
|
|
341
|
-
|
|
342
|
-
return detection
|
|
343
|
-
|
|
344
|
-
def _check_framework(self, config: Dict[str, Any]) -> bool:
|
|
345
|
-
"""Check if framework is present."""
|
|
346
|
-
if "check" in config:
|
|
347
|
-
output = self._run_command(config["check"])
|
|
348
|
-
return output is not None
|
|
349
|
-
|
|
350
|
-
if "check_file" in config and "check_content" in config:
|
|
351
|
-
return self._check_file_content(config["check_file"], config["check_content"])
|
|
352
|
-
|
|
353
|
-
return False
|
|
354
|
-
|
|
355
|
-
def detect_all(self) -> List[ToolDetection]:
|
|
356
|
-
"""Detect all available tools."""
|
|
357
|
-
self.detected_tools = []
|
|
358
|
-
|
|
359
|
-
for tool_name, config in TOOL_DETECTIONS.items():
|
|
360
|
-
detection = self._detect_tool(tool_name, config)
|
|
361
|
-
if detection:
|
|
362
|
-
self.detected_tools.append(detection)
|
|
363
|
-
self.logger.info(f"Detected {tool_name} v{detection.version}")
|
|
364
|
-
|
|
365
|
-
return self.detected_tools
|
|
366
|
-
|
|
367
|
-
def suggest_tools(self) -> Dict[str, List[str]]:
|
|
368
|
-
"""Suggest tools to install based on project."""
|
|
369
|
-
suggestions = {
|
|
370
|
-
"languages": [],
|
|
371
|
-
"frameworks": [],
|
|
372
|
-
"tools": [],
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
# Check project files
|
|
376
|
-
files = list(self.project_root.glob("*"))
|
|
377
|
-
file_names = [f.name for f in files]
|
|
378
|
-
|
|
379
|
-
# Language suggestions
|
|
380
|
-
if "package.json" in file_names and "node" not in [d.name for d in self.detected_tools]:
|
|
381
|
-
suggestions["languages"].append("node")
|
|
382
|
-
|
|
383
|
-
if "requirements.txt" in file_names and "python" not in [d.name for d in self.detected_tools]:
|
|
384
|
-
suggestions["languages"].append("python")
|
|
385
|
-
|
|
386
|
-
if "Cargo.toml" in file_names and "rust" not in [d.name for d in self.detected_tools]:
|
|
387
|
-
suggestions["languages"].append("rust")
|
|
388
|
-
|
|
389
|
-
if "go.mod" in file_names and "go" not in [d.name for d in self.detected_tools]:
|
|
390
|
-
suggestions["languages"].append("go")
|
|
391
|
-
|
|
392
|
-
# Tool suggestions
|
|
393
|
-
if any(d.category == "language" for d in self.detected_tools):
|
|
394
|
-
if "git" not in [d.name for d in self.detected_tools]:
|
|
395
|
-
suggestions["tools"].append("git")
|
|
396
|
-
|
|
397
|
-
if "docker" not in [d.name for d in self.detected_tools]:
|
|
398
|
-
suggestions["tools"].append("docker")
|
|
399
|
-
|
|
400
|
-
return suggestions
|
|
401
|
-
|
|
402
|
-
def get_enabled_mcp_tools(self) -> Set[str]:
|
|
403
|
-
"""Get list of MCP tools to enable based on detection."""
|
|
404
|
-
tools = set()
|
|
405
|
-
|
|
406
|
-
for detection in self.detected_tools:
|
|
407
|
-
tools.update(detection.mcp_tools)
|
|
408
|
-
|
|
409
|
-
# Always include essential tools
|
|
410
|
-
essential = [
|
|
411
|
-
"read", "write", "edit", "multi_edit",
|
|
412
|
-
"directory_tree", "grep", "find", "search",
|
|
413
|
-
"run_command", "bash", "ast", "lsp"
|
|
414
|
-
]
|
|
415
|
-
tools.update(essential)
|
|
416
|
-
|
|
417
|
-
return tools
|
|
418
|
-
|
|
419
|
-
def generate_configuration(self) -> Dict[str, Any]:
|
|
420
|
-
"""Generate MCP configuration based on detection."""
|
|
421
|
-
config = {
|
|
422
|
-
"detected_environment": {
|
|
423
|
-
"project_root": str(self.project_root),
|
|
424
|
-
"tools": [
|
|
425
|
-
{
|
|
426
|
-
"name": d.name,
|
|
427
|
-
"version": d.version,
|
|
428
|
-
"category": d.category,
|
|
429
|
-
"path": d.path,
|
|
430
|
-
}
|
|
431
|
-
for d in self.detected_tools
|
|
432
|
-
],
|
|
433
|
-
},
|
|
434
|
-
"enabled_mcp_tools": sorted(self.get_enabled_mcp_tools()),
|
|
435
|
-
"environment_variables": {},
|
|
436
|
-
"modes": [],
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
# Add environment variables
|
|
440
|
-
for detection in self.detected_tools:
|
|
441
|
-
config["environment_variables"].update(detection.environment)
|
|
442
|
-
|
|
443
|
-
# Determine modes to enable
|
|
444
|
-
categories = {d.category for d in self.detected_tools}
|
|
445
|
-
languages = {d.name for d in self.detected_tools if d.category == "language"}
|
|
446
|
-
|
|
447
|
-
if "python" in languages:
|
|
448
|
-
config["modes"].append("python")
|
|
449
|
-
if any("django" in t for d in self.detected_tools for t in d.mcp_tools):
|
|
450
|
-
config["modes"].append("django")
|
|
451
|
-
if any("fastapi" in t for d in self.detected_tools for t in d.mcp_tools):
|
|
452
|
-
config["modes"].append("fastapi")
|
|
453
|
-
|
|
454
|
-
if "node" in languages or "typescript" in languages:
|
|
455
|
-
config["modes"].append("javascript")
|
|
456
|
-
if any("next" in t for d in self.detected_tools for t in d.mcp_tools):
|
|
457
|
-
config["modes"].append("nextjs")
|
|
458
|
-
if any("react" in t for d in self.detected_tools for t in d.mcp_tools):
|
|
459
|
-
config["modes"].append("react")
|
|
460
|
-
|
|
461
|
-
if "rust" in languages:
|
|
462
|
-
config["modes"].append("rust")
|
|
463
|
-
|
|
464
|
-
if "go" in languages:
|
|
465
|
-
config["modes"].append("go")
|
|
466
|
-
|
|
467
|
-
return config
|
|
468
|
-
|
|
469
|
-
async def run(
|
|
470
|
-
self,
|
|
471
|
-
action: str = "detect",
|
|
472
|
-
tool: Optional[str] = None,
|
|
473
|
-
export_path: Optional[str] = None,
|
|
474
|
-
**kwargs,
|
|
475
|
-
) -> MCPResourceDocument:
|
|
476
|
-
"""Execute environment detection action."""
|
|
477
|
-
|
|
478
|
-
if action == "detect":
|
|
479
|
-
# Detect all tools
|
|
480
|
-
detections = self.detect_all()
|
|
481
|
-
|
|
482
|
-
return MCPResourceDocument(
|
|
483
|
-
data={
|
|
484
|
-
"detected_tools": [
|
|
485
|
-
{
|
|
486
|
-
"name": d.name,
|
|
487
|
-
"version": d.version,
|
|
488
|
-
"category": d.category,
|
|
489
|
-
"path": d.path,
|
|
490
|
-
"mcp_tools": d.mcp_tools,
|
|
491
|
-
}
|
|
492
|
-
for d in detections
|
|
493
|
-
],
|
|
494
|
-
"total": len(detections),
|
|
495
|
-
"categories": list(set(d.category for d in detections)),
|
|
496
|
-
}
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
elif action == "check":
|
|
500
|
-
# Check specific tool
|
|
501
|
-
if not tool:
|
|
502
|
-
return MCPResourceDocument(data={"error": "Tool name required for check action"})
|
|
503
|
-
|
|
504
|
-
if tool in TOOL_DETECTIONS:
|
|
505
|
-
detection = self._detect_tool(tool, TOOL_DETECTIONS[tool])
|
|
506
|
-
if detection:
|
|
507
|
-
return MCPResourceDocument(
|
|
508
|
-
data={
|
|
509
|
-
"available": True,
|
|
510
|
-
"name": detection.name,
|
|
511
|
-
"version": detection.version,
|
|
512
|
-
"path": detection.path,
|
|
513
|
-
"mcp_tools": detection.mcp_tools,
|
|
514
|
-
}
|
|
515
|
-
)
|
|
516
|
-
else:
|
|
517
|
-
return MCPResourceDocument(
|
|
518
|
-
data={
|
|
519
|
-
"available": False,
|
|
520
|
-
"name": tool,
|
|
521
|
-
"install_hint": f"Install {tool} to enable related MCP tools",
|
|
522
|
-
}
|
|
523
|
-
)
|
|
524
|
-
else:
|
|
525
|
-
return MCPResourceDocument(data={"error": f"Unknown tool: {tool}"})
|
|
526
|
-
|
|
527
|
-
elif action == "suggest":
|
|
528
|
-
# Suggest tools to install
|
|
529
|
-
if not self.detected_tools:
|
|
530
|
-
self.detect_all()
|
|
531
|
-
|
|
532
|
-
suggestions = self.suggest_tools()
|
|
533
|
-
|
|
534
|
-
return MCPResourceDocument(
|
|
535
|
-
data={
|
|
536
|
-
"suggestions": suggestions,
|
|
537
|
-
"detected": [d.name for d in self.detected_tools],
|
|
538
|
-
}
|
|
539
|
-
)
|
|
540
|
-
|
|
541
|
-
elif action == "configure":
|
|
542
|
-
# Generate and apply configuration
|
|
543
|
-
if not self.detected_tools:
|
|
544
|
-
self.detect_all()
|
|
545
|
-
|
|
546
|
-
config = self.generate_configuration()
|
|
547
|
-
|
|
548
|
-
# Save configuration if export path provided
|
|
549
|
-
if export_path:
|
|
550
|
-
export_file = Path(export_path)
|
|
551
|
-
export_file.write_text(json.dumps(config, indent=2))
|
|
552
|
-
config["exported_to"] = str(export_file)
|
|
553
|
-
|
|
554
|
-
return MCPResourceDocument(data=config)
|
|
555
|
-
|
|
556
|
-
elif action == "export":
|
|
557
|
-
# Export configuration
|
|
558
|
-
if not self.detected_tools:
|
|
559
|
-
self.detect_all()
|
|
560
|
-
|
|
561
|
-
config = self.generate_configuration()
|
|
562
|
-
|
|
563
|
-
if export_path:
|
|
564
|
-
export_file = Path(export_path)
|
|
565
|
-
export_file.write_text(json.dumps(config, indent=2))
|
|
566
|
-
|
|
567
|
-
return MCPResourceDocument(
|
|
568
|
-
data={
|
|
569
|
-
"exported": True,
|
|
570
|
-
"path": str(export_file),
|
|
571
|
-
"configuration": config,
|
|
572
|
-
}
|
|
573
|
-
)
|
|
574
|
-
else:
|
|
575
|
-
return MCPResourceDocument(data=config)
|
|
576
|
-
|
|
577
|
-
else:
|
|
578
|
-
return MCPResourceDocument(
|
|
579
|
-
data={
|
|
580
|
-
"error": f"Unknown action: {action}",
|
|
581
|
-
"valid_actions": ["detect", "check", "suggest", "configure", "export"],
|
|
582
|
-
}
|
|
583
|
-
)
|
|
584
|
-
|
|
585
|
-
async def call(self, **kwargs) -> str:
|
|
586
|
-
"""Tool interface for MCP."""
|
|
587
|
-
result = await self.run(**kwargs)
|
|
588
|
-
return result.to_json_string()
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
# Factory function
|
|
592
|
-
def create_environment_detector():
|
|
593
|
-
"""Create environment detection tool."""
|
|
594
|
-
return EnvironmentDetector()
|