hanzo-mcp 0.9.0__py3-none-any.whl → 0.9.2__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.

Files changed (135) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/analytics/posthog_analytics.py +14 -1
  3. hanzo_mcp/cli.py +108 -4
  4. hanzo_mcp/server.py +11 -0
  5. hanzo_mcp/tools/__init__.py +3 -16
  6. hanzo_mcp/tools/agent/__init__.py +5 -0
  7. hanzo_mcp/tools/agent/agent.py +5 -0
  8. hanzo_mcp/tools/agent/agent_tool.py +3 -17
  9. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +623 -0
  10. hanzo_mcp/tools/agent/clarification_tool.py +7 -1
  11. hanzo_mcp/tools/agent/claude_desktop_auth.py +16 -6
  12. hanzo_mcp/tools/agent/cli_agent_base.py +5 -0
  13. hanzo_mcp/tools/agent/cli_tools.py +26 -0
  14. hanzo_mcp/tools/agent/code_auth_tool.py +5 -0
  15. hanzo_mcp/tools/agent/critic_tool.py +7 -1
  16. hanzo_mcp/tools/agent/iching_tool.py +5 -0
  17. hanzo_mcp/tools/agent/network_tool.py +5 -0
  18. hanzo_mcp/tools/agent/review_tool.py +7 -1
  19. hanzo_mcp/tools/agent/swarm_alias.py +5 -0
  20. hanzo_mcp/tools/agent/swarm_tool.py +701 -0
  21. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +554 -0
  22. hanzo_mcp/tools/agent/unified_cli_tools.py +5 -0
  23. hanzo_mcp/tools/common/auto_timeout.py +254 -0
  24. hanzo_mcp/tools/common/base.py +4 -0
  25. hanzo_mcp/tools/common/batch_tool.py +5 -0
  26. hanzo_mcp/tools/common/config_tool.py +5 -0
  27. hanzo_mcp/tools/common/critic_tool.py +5 -0
  28. hanzo_mcp/tools/common/paginated_base.py +4 -0
  29. hanzo_mcp/tools/common/permissions.py +38 -12
  30. hanzo_mcp/tools/common/personality.py +673 -980
  31. hanzo_mcp/tools/common/stats.py +5 -0
  32. hanzo_mcp/tools/common/thinking_tool.py +5 -0
  33. hanzo_mcp/tools/common/timeout_parser.py +103 -0
  34. hanzo_mcp/tools/common/tool_disable.py +5 -0
  35. hanzo_mcp/tools/common/tool_enable.py +5 -0
  36. hanzo_mcp/tools/common/tool_list.py +5 -0
  37. hanzo_mcp/tools/config/config_tool.py +5 -0
  38. hanzo_mcp/tools/config/mode_tool.py +5 -0
  39. hanzo_mcp/tools/database/graph.py +5 -0
  40. hanzo_mcp/tools/database/graph_add.py +5 -0
  41. hanzo_mcp/tools/database/graph_query.py +5 -0
  42. hanzo_mcp/tools/database/graph_remove.py +5 -0
  43. hanzo_mcp/tools/database/graph_search.py +5 -0
  44. hanzo_mcp/tools/database/graph_stats.py +5 -0
  45. hanzo_mcp/tools/database/sql.py +5 -0
  46. hanzo_mcp/tools/database/sql_query.py +2 -0
  47. hanzo_mcp/tools/database/sql_search.py +5 -0
  48. hanzo_mcp/tools/database/sql_stats.py +5 -0
  49. hanzo_mcp/tools/editor/neovim_command.py +5 -0
  50. hanzo_mcp/tools/editor/neovim_edit.py +7 -2
  51. hanzo_mcp/tools/editor/neovim_session.py +5 -0
  52. hanzo_mcp/tools/filesystem/__init__.py +23 -26
  53. hanzo_mcp/tools/filesystem/ast_tool.py +3 -4
  54. hanzo_mcp/tools/filesystem/base.py +2 -18
  55. hanzo_mcp/tools/filesystem/batch_search.py +825 -0
  56. hanzo_mcp/tools/filesystem/content_replace.py +5 -3
  57. hanzo_mcp/tools/filesystem/diff.py +5 -0
  58. hanzo_mcp/tools/filesystem/directory_tree.py +34 -281
  59. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +345 -0
  60. hanzo_mcp/tools/filesystem/edit.py +6 -5
  61. hanzo_mcp/tools/filesystem/find.py +177 -311
  62. hanzo_mcp/tools/filesystem/find_files.py +370 -0
  63. hanzo_mcp/tools/filesystem/git_search.py +5 -3
  64. hanzo_mcp/tools/filesystem/grep.py +454 -0
  65. hanzo_mcp/tools/filesystem/multi_edit.py +6 -5
  66. hanzo_mcp/tools/filesystem/read.py +10 -9
  67. hanzo_mcp/tools/filesystem/rules_tool.py +6 -4
  68. hanzo_mcp/tools/filesystem/search_tool.py +728 -0
  69. hanzo_mcp/tools/filesystem/symbols_tool.py +510 -0
  70. hanzo_mcp/tools/filesystem/tree.py +273 -0
  71. hanzo_mcp/tools/filesystem/watch.py +6 -1
  72. hanzo_mcp/tools/filesystem/write.py +13 -7
  73. hanzo_mcp/tools/jupyter/jupyter.py +30 -2
  74. hanzo_mcp/tools/jupyter/notebook_edit.py +298 -0
  75. hanzo_mcp/tools/jupyter/notebook_read.py +148 -0
  76. hanzo_mcp/tools/llm/consensus_tool.py +8 -6
  77. hanzo_mcp/tools/llm/llm_manage.py +5 -0
  78. hanzo_mcp/tools/llm/llm_tool.py +2 -0
  79. hanzo_mcp/tools/llm/llm_unified.py +5 -0
  80. hanzo_mcp/tools/llm/provider_tools.py +5 -0
  81. hanzo_mcp/tools/lsp/lsp_tool.py +475 -622
  82. hanzo_mcp/tools/mcp/mcp_add.py +7 -2
  83. hanzo_mcp/tools/mcp/mcp_remove.py +15 -2
  84. hanzo_mcp/tools/mcp/mcp_stats.py +5 -0
  85. hanzo_mcp/tools/mcp/mcp_tool.py +5 -0
  86. hanzo_mcp/tools/memory/knowledge_tools.py +14 -0
  87. hanzo_mcp/tools/memory/memory_tools.py +17 -0
  88. hanzo_mcp/tools/search/find_tool.py +5 -3
  89. hanzo_mcp/tools/search/unified_search.py +3 -1
  90. hanzo_mcp/tools/shell/__init__.py +2 -14
  91. hanzo_mcp/tools/shell/base_process.py +4 -2
  92. hanzo_mcp/tools/shell/bash_tool.py +2 -0
  93. hanzo_mcp/tools/shell/command_executor.py +7 -7
  94. hanzo_mcp/tools/shell/logs.py +5 -0
  95. hanzo_mcp/tools/shell/npx.py +5 -0
  96. hanzo_mcp/tools/shell/npx_background.py +5 -0
  97. hanzo_mcp/tools/shell/npx_tool.py +5 -0
  98. hanzo_mcp/tools/shell/open.py +5 -0
  99. hanzo_mcp/tools/shell/pkill.py +5 -0
  100. hanzo_mcp/tools/shell/process_tool.py +5 -0
  101. hanzo_mcp/tools/shell/processes.py +5 -0
  102. hanzo_mcp/tools/shell/run_background.py +5 -0
  103. hanzo_mcp/tools/shell/run_command.py +2 -0
  104. hanzo_mcp/tools/shell/run_command_windows.py +5 -0
  105. hanzo_mcp/tools/shell/streaming_command.py +5 -0
  106. hanzo_mcp/tools/shell/uvx.py +5 -0
  107. hanzo_mcp/tools/shell/uvx_background.py +5 -0
  108. hanzo_mcp/tools/shell/uvx_tool.py +5 -0
  109. hanzo_mcp/tools/shell/zsh_tool.py +3 -0
  110. hanzo_mcp/tools/todo/todo.py +5 -0
  111. hanzo_mcp/tools/todo/todo_read.py +142 -0
  112. hanzo_mcp/tools/todo/todo_write.py +367 -0
  113. hanzo_mcp/tools/vector/__init__.py +42 -95
  114. hanzo_mcp/tools/vector/index_tool.py +5 -0
  115. hanzo_mcp/tools/vector/vector.py +5 -0
  116. hanzo_mcp/tools/vector/vector_index.py +5 -0
  117. hanzo_mcp/tools/vector/vector_search.py +5 -0
  118. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/METADATA +1 -1
  119. hanzo_mcp-0.9.2.dist-info/RECORD +195 -0
  120. hanzo_mcp/tools/common/path_utils.py +0 -34
  121. hanzo_mcp/tools/compiler/__init__.py +0 -8
  122. hanzo_mcp/tools/compiler/sandboxed_compiler.py +0 -681
  123. hanzo_mcp/tools/environment/__init__.py +0 -8
  124. hanzo_mcp/tools/environment/environment_detector.py +0 -594
  125. hanzo_mcp/tools/filesystem/search.py +0 -1160
  126. hanzo_mcp/tools/framework/__init__.py +0 -8
  127. hanzo_mcp/tools/framework/framework_modes.py +0 -714
  128. hanzo_mcp/tools/memory/conversation_memory.py +0 -636
  129. hanzo_mcp/tools/shell/run_tool.py +0 -56
  130. hanzo_mcp/tools/vector/node_tool.py +0 -538
  131. hanzo_mcp/tools/vector/unified_vector.py +0 -384
  132. hanzo_mcp-0.9.0.dist-info/RECORD +0 -191
  133. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/WHEEL +0 -0
  134. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/entry_points.txt +0 -0
  135. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.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()