hanzo-mcp 0.8.11__py3-none-any.whl → 0.9.0__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 -3
- hanzo_mcp/analytics/posthog_analytics.py +3 -9
- hanzo_mcp/bridge.py +9 -25
- hanzo_mcp/cli.py +6 -15
- hanzo_mcp/cli_enhanced.py +5 -14
- hanzo_mcp/cli_plugin.py +3 -9
- hanzo_mcp/config/settings.py +6 -20
- hanzo_mcp/config/tool_config.py +1 -3
- hanzo_mcp/core/base_agent.py +88 -88
- hanzo_mcp/core/model_registry.py +238 -210
- hanzo_mcp/dev_server.py +5 -15
- hanzo_mcp/prompts/__init__.py +2 -6
- hanzo_mcp/prompts/project_todo_reminder.py +3 -9
- hanzo_mcp/prompts/tool_explorer.py +1 -3
- hanzo_mcp/prompts/utils.py +7 -21
- hanzo_mcp/server.py +2 -6
- hanzo_mcp/tools/__init__.py +26 -27
- hanzo_mcp/tools/agent/__init__.py +2 -1
- hanzo_mcp/tools/agent/agent.py +10 -30
- hanzo_mcp/tools/agent/agent_tool.py +22 -15
- hanzo_mcp/tools/agent/claude_desktop_auth.py +3 -9
- hanzo_mcp/tools/agent/cli_agent_base.py +7 -24
- hanzo_mcp/tools/agent/cli_tools.py +75 -74
- hanzo_mcp/tools/agent/code_auth.py +1 -3
- hanzo_mcp/tools/agent/code_auth_tool.py +2 -6
- hanzo_mcp/tools/agent/critic_tool.py +8 -24
- hanzo_mcp/tools/agent/iching_tool.py +12 -36
- hanzo_mcp/tools/agent/network_tool.py +7 -18
- hanzo_mcp/tools/agent/prompt.py +1 -5
- hanzo_mcp/tools/agent/review_tool.py +10 -25
- hanzo_mcp/tools/agent/swarm_alias.py +1 -3
- hanzo_mcp/tools/agent/unified_cli_tools.py +38 -38
- hanzo_mcp/tools/common/batch_tool.py +15 -45
- hanzo_mcp/tools/common/config_tool.py +9 -28
- hanzo_mcp/tools/common/context.py +1 -3
- hanzo_mcp/tools/common/critic_tool.py +1 -3
- hanzo_mcp/tools/common/decorators.py +2 -6
- hanzo_mcp/tools/common/enhanced_base.py +2 -6
- hanzo_mcp/tools/common/fastmcp_pagination.py +4 -12
- hanzo_mcp/tools/common/forgiving_edit.py +9 -28
- hanzo_mcp/tools/common/mode.py +1 -5
- hanzo_mcp/tools/common/paginated_base.py +3 -11
- hanzo_mcp/tools/common/paginated_response.py +10 -30
- hanzo_mcp/tools/common/pagination.py +3 -9
- hanzo_mcp/tools/common/path_utils.py +34 -0
- hanzo_mcp/tools/common/permissions.py +14 -13
- hanzo_mcp/tools/common/personality.py +983 -701
- hanzo_mcp/tools/common/plugin_loader.py +3 -15
- hanzo_mcp/tools/common/stats.py +6 -18
- hanzo_mcp/tools/common/thinking_tool.py +1 -3
- hanzo_mcp/tools/common/tool_disable.py +2 -6
- hanzo_mcp/tools/common/tool_list.py +2 -6
- hanzo_mcp/tools/common/validation.py +1 -3
- hanzo_mcp/tools/compiler/__init__.py +8 -0
- hanzo_mcp/tools/compiler/sandboxed_compiler.py +681 -0
- hanzo_mcp/tools/config/config_tool.py +7 -13
- hanzo_mcp/tools/config/index_config.py +1 -3
- hanzo_mcp/tools/config/mode_tool.py +5 -15
- hanzo_mcp/tools/database/database_manager.py +3 -9
- hanzo_mcp/tools/database/graph.py +1 -3
- hanzo_mcp/tools/database/graph_add.py +3 -9
- hanzo_mcp/tools/database/graph_query.py +11 -34
- hanzo_mcp/tools/database/graph_remove.py +3 -9
- hanzo_mcp/tools/database/graph_search.py +6 -20
- hanzo_mcp/tools/database/graph_stats.py +11 -33
- hanzo_mcp/tools/database/sql.py +4 -12
- hanzo_mcp/tools/database/sql_query.py +6 -10
- hanzo_mcp/tools/database/sql_search.py +2 -6
- hanzo_mcp/tools/database/sql_stats.py +5 -15
- hanzo_mcp/tools/editor/neovim_command.py +1 -3
- hanzo_mcp/tools/editor/neovim_session.py +7 -13
- hanzo_mcp/tools/environment/__init__.py +8 -0
- hanzo_mcp/tools/environment/environment_detector.py +594 -0
- hanzo_mcp/tools/filesystem/__init__.py +28 -26
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +14 -43
- hanzo_mcp/tools/filesystem/ast_tool.py +3 -0
- hanzo_mcp/tools/filesystem/base.py +20 -12
- hanzo_mcp/tools/filesystem/content_replace.py +7 -12
- hanzo_mcp/tools/filesystem/diff.py +2 -10
- hanzo_mcp/tools/filesystem/directory_tree.py +285 -51
- hanzo_mcp/tools/filesystem/edit.py +10 -18
- hanzo_mcp/tools/filesystem/find.py +312 -179
- hanzo_mcp/tools/filesystem/git_search.py +12 -24
- hanzo_mcp/tools/filesystem/multi_edit.py +10 -18
- hanzo_mcp/tools/filesystem/read.py +14 -30
- hanzo_mcp/tools/filesystem/rules_tool.py +9 -17
- hanzo_mcp/tools/filesystem/search.py +1160 -0
- hanzo_mcp/tools/filesystem/watch.py +2 -4
- hanzo_mcp/tools/filesystem/write.py +7 -10
- hanzo_mcp/tools/framework/__init__.py +8 -0
- hanzo_mcp/tools/framework/framework_modes.py +714 -0
- hanzo_mcp/tools/jupyter/base.py +6 -20
- hanzo_mcp/tools/jupyter/jupyter.py +4 -12
- hanzo_mcp/tools/llm/consensus_tool.py +8 -24
- hanzo_mcp/tools/llm/llm_manage.py +2 -6
- hanzo_mcp/tools/llm/llm_tool.py +17 -58
- hanzo_mcp/tools/llm/llm_unified.py +18 -59
- hanzo_mcp/tools/llm/provider_tools.py +1 -3
- hanzo_mcp/tools/lsp/lsp_tool.py +621 -481
- hanzo_mcp/tools/mcp/mcp_add.py +1 -3
- hanzo_mcp/tools/mcp/mcp_stats.py +1 -3
- hanzo_mcp/tools/mcp/mcp_tool.py +9 -23
- hanzo_mcp/tools/memory/__init__.py +10 -27
- hanzo_mcp/tools/memory/conversation_memory.py +636 -0
- hanzo_mcp/tools/memory/knowledge_tools.py +7 -25
- hanzo_mcp/tools/memory/memory_tools.py +6 -18
- hanzo_mcp/tools/search/find_tool.py +12 -34
- hanzo_mcp/tools/search/unified_search.py +24 -78
- hanzo_mcp/tools/shell/__init__.py +16 -4
- hanzo_mcp/tools/shell/auto_background.py +2 -6
- hanzo_mcp/tools/shell/base.py +1 -5
- hanzo_mcp/tools/shell/base_process.py +5 -7
- hanzo_mcp/tools/shell/bash_session.py +7 -24
- hanzo_mcp/tools/shell/bash_session_executor.py +5 -15
- hanzo_mcp/tools/shell/bash_tool.py +3 -7
- hanzo_mcp/tools/shell/command_executor.py +26 -79
- hanzo_mcp/tools/shell/logs.py +4 -16
- hanzo_mcp/tools/shell/npx.py +2 -8
- hanzo_mcp/tools/shell/npx_tool.py +1 -3
- hanzo_mcp/tools/shell/pkill.py +4 -12
- hanzo_mcp/tools/shell/process_tool.py +2 -8
- hanzo_mcp/tools/shell/processes.py +5 -17
- hanzo_mcp/tools/shell/run_background.py +1 -3
- hanzo_mcp/tools/shell/run_command.py +1 -3
- hanzo_mcp/tools/shell/run_command_windows.py +1 -3
- hanzo_mcp/tools/shell/run_tool.py +56 -0
- hanzo_mcp/tools/shell/session_manager.py +2 -6
- hanzo_mcp/tools/shell/session_storage.py +2 -6
- hanzo_mcp/tools/shell/streaming_command.py +7 -23
- hanzo_mcp/tools/shell/uvx.py +4 -14
- hanzo_mcp/tools/shell/uvx_background.py +2 -6
- hanzo_mcp/tools/shell/uvx_tool.py +1 -3
- hanzo_mcp/tools/shell/zsh_tool.py +12 -20
- hanzo_mcp/tools/todo/todo.py +1 -3
- hanzo_mcp/tools/vector/__init__.py +97 -50
- hanzo_mcp/tools/vector/ast_analyzer.py +6 -20
- hanzo_mcp/tools/vector/git_ingester.py +10 -30
- hanzo_mcp/tools/vector/index_tool.py +3 -9
- hanzo_mcp/tools/vector/infinity_store.py +7 -27
- hanzo_mcp/tools/vector/mock_infinity.py +1 -3
- hanzo_mcp/tools/vector/node_tool.py +538 -0
- hanzo_mcp/tools/vector/project_manager.py +4 -12
- hanzo_mcp/tools/vector/unified_vector.py +384 -0
- hanzo_mcp/tools/vector/vector.py +2 -6
- hanzo_mcp/tools/vector/vector_index.py +8 -8
- hanzo_mcp/tools/vector/vector_search.py +7 -21
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/METADATA +2 -2
- hanzo_mcp-0.9.0.dist-info/RECORD +191 -0
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +0 -645
- hanzo_mcp/tools/agent/swarm_tool.py +0 -718
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +0 -577
- hanzo_mcp/tools/filesystem/batch_search.py +0 -900
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +0 -350
- hanzo_mcp/tools/filesystem/find_files.py +0 -369
- hanzo_mcp/tools/filesystem/grep.py +0 -467
- hanzo_mcp/tools/filesystem/search_tool.py +0 -767
- hanzo_mcp/tools/filesystem/symbols_tool.py +0 -515
- hanzo_mcp/tools/filesystem/tree.py +0 -270
- hanzo_mcp/tools/jupyter/notebook_edit.py +0 -317
- hanzo_mcp/tools/jupyter/notebook_read.py +0 -147
- hanzo_mcp/tools/todo/todo_read.py +0 -143
- hanzo_mcp/tools/todo/todo_write.py +0 -374
- hanzo_mcp-0.8.11.dist-info/RECORD +0 -193
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
"""Framework-specific modes for dynamic tool loading.
|
|
2
|
+
|
|
3
|
+
This module provides mode management for different development frameworks,
|
|
4
|
+
allowing dynamic loading of framework-specific tools and configurations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Dict, List, Any, Optional, Set, Callable
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from hanzo_mcp.types import MCPResourceDocument
|
|
14
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class FrameworkMode:
|
|
19
|
+
"""Represents a framework-specific mode."""
|
|
20
|
+
name: str
|
|
21
|
+
description: str
|
|
22
|
+
category: str # language, web, mobile, data, etc.
|
|
23
|
+
tools: List[str] # MCP tools to enable
|
|
24
|
+
aliases: List[str] = field(default_factory=list)
|
|
25
|
+
environment: Dict[str, str] = field(default_factory=dict)
|
|
26
|
+
snippets: Dict[str, str] = field(default_factory=dict) # Code snippets
|
|
27
|
+
commands: Dict[str, List[str]] = field(default_factory=dict) # Common commands
|
|
28
|
+
dependencies: List[str] = field(default_factory=list) # Required modes
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Framework mode definitions
|
|
32
|
+
FRAMEWORK_MODES = {
|
|
33
|
+
# Python Frameworks
|
|
34
|
+
"django": FrameworkMode(
|
|
35
|
+
name="django",
|
|
36
|
+
description="Django web framework mode",
|
|
37
|
+
category="web",
|
|
38
|
+
tools=[
|
|
39
|
+
"run_command", "python", "pip_install",
|
|
40
|
+
"django_manage", "django_shell", "django_migrate",
|
|
41
|
+
"django_makemigrations", "django_test", "django_runserver"
|
|
42
|
+
],
|
|
43
|
+
aliases=["dj"],
|
|
44
|
+
environment={
|
|
45
|
+
"DJANGO_SETTINGS_MODULE": "settings",
|
|
46
|
+
"PYTHONPATH": ".",
|
|
47
|
+
},
|
|
48
|
+
snippets={
|
|
49
|
+
"model": """from django.db import models
|
|
50
|
+
|
|
51
|
+
class {ModelName}(models.Model):
|
|
52
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
53
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
54
|
+
|
|
55
|
+
class Meta:
|
|
56
|
+
ordering = ['-created_at']""",
|
|
57
|
+
"view": """from django.shortcuts import render
|
|
58
|
+
from django.views import View
|
|
59
|
+
|
|
60
|
+
class {ViewName}(View):
|
|
61
|
+
def get(self, request):
|
|
62
|
+
return render(request, 'template.html', {})""",
|
|
63
|
+
},
|
|
64
|
+
commands={
|
|
65
|
+
"serve": ["python", "manage.py", "runserver"],
|
|
66
|
+
"migrate": ["python", "manage.py", "migrate"],
|
|
67
|
+
"test": ["python", "manage.py", "test"],
|
|
68
|
+
},
|
|
69
|
+
dependencies=["python"],
|
|
70
|
+
),
|
|
71
|
+
|
|
72
|
+
"fastapi": FrameworkMode(
|
|
73
|
+
name="fastapi",
|
|
74
|
+
description="FastAPI web framework mode",
|
|
75
|
+
category="web",
|
|
76
|
+
tools=[
|
|
77
|
+
"run_command", "python", "uvicorn",
|
|
78
|
+
"fastapi_routes", "fastapi_swagger", "fastapi_test"
|
|
79
|
+
],
|
|
80
|
+
aliases=["fa"],
|
|
81
|
+
environment={
|
|
82
|
+
"PYTHONPATH": ".",
|
|
83
|
+
},
|
|
84
|
+
snippets={
|
|
85
|
+
"endpoint": """from fastapi import FastAPI, HTTPException
|
|
86
|
+
from pydantic import BaseModel
|
|
87
|
+
|
|
88
|
+
app = FastAPI()
|
|
89
|
+
|
|
90
|
+
class {ModelName}(BaseModel):
|
|
91
|
+
field: str
|
|
92
|
+
|
|
93
|
+
@app.post("/{endpoint}")
|
|
94
|
+
async def {function_name}(data: {ModelName}):
|
|
95
|
+
return {"message": "success", "data": data}""",
|
|
96
|
+
},
|
|
97
|
+
commands={
|
|
98
|
+
"serve": ["uvicorn", "main:app", "--reload"],
|
|
99
|
+
"test": ["pytest", "-v"],
|
|
100
|
+
},
|
|
101
|
+
dependencies=["python"],
|
|
102
|
+
),
|
|
103
|
+
|
|
104
|
+
"flask": FrameworkMode(
|
|
105
|
+
name="flask",
|
|
106
|
+
description="Flask web framework mode",
|
|
107
|
+
category="web",
|
|
108
|
+
tools=[
|
|
109
|
+
"run_command", "python", "flask_run",
|
|
110
|
+
"flask_shell", "flask_routes"
|
|
111
|
+
],
|
|
112
|
+
aliases=["fl"],
|
|
113
|
+
environment={
|
|
114
|
+
"FLASK_APP": "app.py",
|
|
115
|
+
"FLASK_ENV": "development",
|
|
116
|
+
},
|
|
117
|
+
snippets={
|
|
118
|
+
"route": """from flask import Flask, request, jsonify
|
|
119
|
+
|
|
120
|
+
app = Flask(__name__)
|
|
121
|
+
|
|
122
|
+
@app.route('/{endpoint}', methods=['GET', 'POST'])
|
|
123
|
+
def {function_name}():
|
|
124
|
+
if request.method == 'POST':
|
|
125
|
+
data = request.json
|
|
126
|
+
return jsonify({"message": "success", "data": data})
|
|
127
|
+
return jsonify({"message": "GET request"})""",
|
|
128
|
+
},
|
|
129
|
+
commands={
|
|
130
|
+
"serve": ["flask", "run"],
|
|
131
|
+
"shell": ["flask", "shell"],
|
|
132
|
+
},
|
|
133
|
+
dependencies=["python"],
|
|
134
|
+
),
|
|
135
|
+
|
|
136
|
+
# JavaScript/TypeScript Frameworks
|
|
137
|
+
"nextjs": FrameworkMode(
|
|
138
|
+
name="nextjs",
|
|
139
|
+
description="Next.js React framework mode",
|
|
140
|
+
category="web",
|
|
141
|
+
tools=[
|
|
142
|
+
"npx", "npm_install", "next_dev", "next_build",
|
|
143
|
+
"next_start", "next_lint", "react_component"
|
|
144
|
+
],
|
|
145
|
+
aliases=["next"],
|
|
146
|
+
environment={
|
|
147
|
+
"NODE_ENV": "development",
|
|
148
|
+
},
|
|
149
|
+
snippets={
|
|
150
|
+
"page": """export default function {PageName}() {
|
|
151
|
+
return (
|
|
152
|
+
<div>
|
|
153
|
+
<h1>{PageName}</h1>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}""",
|
|
157
|
+
"api": """export default function handler(req, res) {
|
|
158
|
+
if (req.method === 'POST') {
|
|
159
|
+
const data = req.body;
|
|
160
|
+
res.status(200).json({ message: 'Success', data });
|
|
161
|
+
} else {
|
|
162
|
+
res.status(200).json({ message: 'GET request' });
|
|
163
|
+
}
|
|
164
|
+
}""",
|
|
165
|
+
},
|
|
166
|
+
commands={
|
|
167
|
+
"dev": ["npm", "run", "dev"],
|
|
168
|
+
"build": ["npm", "run", "build"],
|
|
169
|
+
"start": ["npm", "run", "start"],
|
|
170
|
+
},
|
|
171
|
+
dependencies=["javascript", "react"],
|
|
172
|
+
),
|
|
173
|
+
|
|
174
|
+
"react": FrameworkMode(
|
|
175
|
+
name="react",
|
|
176
|
+
description="React framework mode",
|
|
177
|
+
category="web",
|
|
178
|
+
tools=[
|
|
179
|
+
"npx", "npm_install", "react_component",
|
|
180
|
+
"react_hook", "react_context"
|
|
181
|
+
],
|
|
182
|
+
aliases=["r"],
|
|
183
|
+
snippets={
|
|
184
|
+
"component": """import React from 'react';
|
|
185
|
+
|
|
186
|
+
export const {ComponentName} = () => {
|
|
187
|
+
return (
|
|
188
|
+
<div>
|
|
189
|
+
<h1>{ComponentName}</h1>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
};""",
|
|
193
|
+
"hook": """import { useState, useEffect } from 'react';
|
|
194
|
+
|
|
195
|
+
export const use{HookName} = () => {
|
|
196
|
+
const [data, setData] = useState(null);
|
|
197
|
+
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
// Effect logic
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
return { data };
|
|
203
|
+
};""",
|
|
204
|
+
},
|
|
205
|
+
commands={
|
|
206
|
+
"start": ["npm", "start"],
|
|
207
|
+
"build": ["npm", "run", "build"],
|
|
208
|
+
"test": ["npm", "test"],
|
|
209
|
+
},
|
|
210
|
+
dependencies=["javascript"],
|
|
211
|
+
),
|
|
212
|
+
|
|
213
|
+
"vue": FrameworkMode(
|
|
214
|
+
name="vue",
|
|
215
|
+
description="Vue.js framework mode",
|
|
216
|
+
category="web",
|
|
217
|
+
tools=[
|
|
218
|
+
"npx", "npm_install", "vue_component",
|
|
219
|
+
"vue_store", "vue_router"
|
|
220
|
+
],
|
|
221
|
+
aliases=["v"],
|
|
222
|
+
snippets={
|
|
223
|
+
"component": """<template>
|
|
224
|
+
<div>
|
|
225
|
+
<h1>{{ title }}</h1>
|
|
226
|
+
</div>
|
|
227
|
+
</template>
|
|
228
|
+
|
|
229
|
+
<script>
|
|
230
|
+
export default {
|
|
231
|
+
name: '{ComponentName}',
|
|
232
|
+
data() {
|
|
233
|
+
return {
|
|
234
|
+
title: '{ComponentName}'
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
</script>""",
|
|
239
|
+
},
|
|
240
|
+
commands={
|
|
241
|
+
"serve": ["npm", "run", "serve"],
|
|
242
|
+
"build": ["npm", "run", "build"],
|
|
243
|
+
},
|
|
244
|
+
dependencies=["javascript"],
|
|
245
|
+
),
|
|
246
|
+
|
|
247
|
+
# Rust Frameworks
|
|
248
|
+
"actix": FrameworkMode(
|
|
249
|
+
name="actix",
|
|
250
|
+
description="Actix web framework mode",
|
|
251
|
+
category="web",
|
|
252
|
+
tools=[
|
|
253
|
+
"cargo_build", "cargo_run", "cargo_test",
|
|
254
|
+
"actix_handler", "actix_middleware"
|
|
255
|
+
],
|
|
256
|
+
aliases=["ax"],
|
|
257
|
+
snippets={
|
|
258
|
+
"handler": """use actix_web::{web, HttpResponse, Result};
|
|
259
|
+
use serde::{Deserialize, Serialize};
|
|
260
|
+
|
|
261
|
+
#[derive(Serialize, Deserialize)]
|
|
262
|
+
struct {ModelName} {
|
|
263
|
+
field: String,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
pub async fn {handler_name}(data: web::Json<{ModelName}>) -> Result<HttpResponse> {
|
|
267
|
+
Ok(HttpResponse::Ok().json(&data.into_inner()))
|
|
268
|
+
}""",
|
|
269
|
+
},
|
|
270
|
+
commands={
|
|
271
|
+
"run": ["cargo", "run"],
|
|
272
|
+
"build": ["cargo", "build", "--release"],
|
|
273
|
+
"test": ["cargo", "test"],
|
|
274
|
+
},
|
|
275
|
+
dependencies=["rust"],
|
|
276
|
+
),
|
|
277
|
+
|
|
278
|
+
# Go Frameworks
|
|
279
|
+
"gin": FrameworkMode(
|
|
280
|
+
name="gin",
|
|
281
|
+
description="Gin web framework mode",
|
|
282
|
+
category="web",
|
|
283
|
+
tools=[
|
|
284
|
+
"go_build", "go_run", "go_test",
|
|
285
|
+
"gin_handler", "gin_middleware"
|
|
286
|
+
],
|
|
287
|
+
aliases=["g"],
|
|
288
|
+
snippets={
|
|
289
|
+
"handler": """func {HandlerName}(c *gin.Context) {
|
|
290
|
+
var data struct {
|
|
291
|
+
Field string `json:"field"`
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if err := c.ShouldBindJSON(&data); err != nil {
|
|
295
|
+
c.JSON(400, gin.H{"error": err.Error()})
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
c.JSON(200, gin.H{
|
|
300
|
+
"message": "success",
|
|
301
|
+
"data": data,
|
|
302
|
+
})
|
|
303
|
+
}""",
|
|
304
|
+
},
|
|
305
|
+
commands={
|
|
306
|
+
"run": ["go", "run", "."],
|
|
307
|
+
"build": ["go", "build"],
|
|
308
|
+
"test": ["go", "test", "./..."],
|
|
309
|
+
},
|
|
310
|
+
dependencies=["go"],
|
|
311
|
+
),
|
|
312
|
+
|
|
313
|
+
# Language Modes (Base modes)
|
|
314
|
+
"python": FrameworkMode(
|
|
315
|
+
name="python",
|
|
316
|
+
description="Python language mode",
|
|
317
|
+
category="language",
|
|
318
|
+
tools=[
|
|
319
|
+
"run_command", "python", "pip", "pytest",
|
|
320
|
+
"black", "ruff", "mypy", "uvx"
|
|
321
|
+
],
|
|
322
|
+
environment={
|
|
323
|
+
"PYTHONPATH": ".",
|
|
324
|
+
},
|
|
325
|
+
commands={
|
|
326
|
+
"run": ["python"],
|
|
327
|
+
"test": ["pytest"],
|
|
328
|
+
"format": ["black", "."],
|
|
329
|
+
"lint": ["ruff", "check", "."],
|
|
330
|
+
},
|
|
331
|
+
),
|
|
332
|
+
|
|
333
|
+
"javascript": FrameworkMode(
|
|
334
|
+
name="javascript",
|
|
335
|
+
description="JavaScript/TypeScript language mode",
|
|
336
|
+
category="language",
|
|
337
|
+
tools=[
|
|
338
|
+
"npx", "npm", "node", "tsc",
|
|
339
|
+
"eslint", "prettier", "jest"
|
|
340
|
+
],
|
|
341
|
+
commands={
|
|
342
|
+
"run": ["node"],
|
|
343
|
+
"test": ["npm", "test"],
|
|
344
|
+
"format": ["prettier", "--write", "."],
|
|
345
|
+
"lint": ["eslint", "."],
|
|
346
|
+
},
|
|
347
|
+
),
|
|
348
|
+
|
|
349
|
+
"rust": FrameworkMode(
|
|
350
|
+
name="rust",
|
|
351
|
+
description="Rust language mode",
|
|
352
|
+
category="language",
|
|
353
|
+
tools=[
|
|
354
|
+
"cargo", "rustc", "rustfmt", "clippy"
|
|
355
|
+
],
|
|
356
|
+
commands={
|
|
357
|
+
"build": ["cargo", "build"],
|
|
358
|
+
"run": ["cargo", "run"],
|
|
359
|
+
"test": ["cargo", "test"],
|
|
360
|
+
"format": ["cargo", "fmt"],
|
|
361
|
+
"lint": ["cargo", "clippy"],
|
|
362
|
+
},
|
|
363
|
+
),
|
|
364
|
+
|
|
365
|
+
"go": FrameworkMode(
|
|
366
|
+
name="go",
|
|
367
|
+
description="Go language mode",
|
|
368
|
+
category="language",
|
|
369
|
+
tools=[
|
|
370
|
+
"go", "gofmt", "golint", "gotest"
|
|
371
|
+
],
|
|
372
|
+
commands={
|
|
373
|
+
"build": ["go", "build"],
|
|
374
|
+
"run": ["go", "run", "."],
|
|
375
|
+
"test": ["go", "test", "./..."],
|
|
376
|
+
"format": ["go", "fmt", "./..."],
|
|
377
|
+
},
|
|
378
|
+
),
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class FrameworkModeManager(BaseTool):
|
|
383
|
+
"""Manager for framework-specific modes."""
|
|
384
|
+
|
|
385
|
+
name = "framework_mode"
|
|
386
|
+
description = """Manage framework-specific development modes.
|
|
387
|
+
|
|
388
|
+
Actions:
|
|
389
|
+
- enable: Enable a framework mode
|
|
390
|
+
- disable: Disable a framework mode
|
|
391
|
+
- list: List available modes
|
|
392
|
+
- current: Show currently active modes
|
|
393
|
+
- info: Get information about a mode
|
|
394
|
+
- snippet: Get code snippet for framework
|
|
395
|
+
- command: Get command for framework task
|
|
396
|
+
|
|
397
|
+
Modes automatically load framework-specific tools and configurations.
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
def __init__(self):
|
|
401
|
+
super().__init__()
|
|
402
|
+
self.logger = logging.getLogger(__name__)
|
|
403
|
+
self.active_modes: Set[str] = set()
|
|
404
|
+
self.mode_stack: List[str] = [] # For mode history
|
|
405
|
+
|
|
406
|
+
def _resolve_dependencies(self, mode_name: str) -> List[str]:
|
|
407
|
+
"""Resolve mode dependencies."""
|
|
408
|
+
if mode_name not in FRAMEWORK_MODES:
|
|
409
|
+
return []
|
|
410
|
+
|
|
411
|
+
mode = FRAMEWORK_MODES[mode_name]
|
|
412
|
+
dependencies = []
|
|
413
|
+
|
|
414
|
+
# Add dependencies recursively
|
|
415
|
+
for dep in mode.dependencies:
|
|
416
|
+
dependencies.extend(self._resolve_dependencies(dep))
|
|
417
|
+
dependencies.append(dep)
|
|
418
|
+
|
|
419
|
+
return dependencies
|
|
420
|
+
|
|
421
|
+
def enable_mode(self, mode_name: str) -> Dict[str, Any]:
|
|
422
|
+
"""Enable a framework mode."""
|
|
423
|
+
# Check if mode exists
|
|
424
|
+
if mode_name not in FRAMEWORK_MODES:
|
|
425
|
+
# Check aliases
|
|
426
|
+
for name, mode in FRAMEWORK_MODES.items():
|
|
427
|
+
if mode_name in mode.aliases:
|
|
428
|
+
mode_name = name
|
|
429
|
+
break
|
|
430
|
+
else:
|
|
431
|
+
return {
|
|
432
|
+
"success": False,
|
|
433
|
+
"error": f"Unknown mode: {mode_name}",
|
|
434
|
+
"available_modes": list(FRAMEWORK_MODES.keys()),
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
mode = FRAMEWORK_MODES[mode_name]
|
|
438
|
+
|
|
439
|
+
# Resolve dependencies
|
|
440
|
+
dependencies = self._resolve_dependencies(mode_name)
|
|
441
|
+
|
|
442
|
+
# Enable dependencies first
|
|
443
|
+
for dep in dependencies:
|
|
444
|
+
if dep not in self.active_modes:
|
|
445
|
+
self.active_modes.add(dep)
|
|
446
|
+
self.logger.info(f"Enabled dependency mode: {dep}")
|
|
447
|
+
|
|
448
|
+
# Enable the mode
|
|
449
|
+
self.active_modes.add(mode_name)
|
|
450
|
+
self.mode_stack.append(mode_name)
|
|
451
|
+
|
|
452
|
+
# Get all tools to enable
|
|
453
|
+
tools = set(mode.tools)
|
|
454
|
+
for dep in dependencies:
|
|
455
|
+
tools.update(FRAMEWORK_MODES[dep].tools)
|
|
456
|
+
|
|
457
|
+
return {
|
|
458
|
+
"success": True,
|
|
459
|
+
"mode": mode_name,
|
|
460
|
+
"description": mode.description,
|
|
461
|
+
"category": mode.category,
|
|
462
|
+
"enabled_tools": sorted(tools),
|
|
463
|
+
"dependencies_enabled": dependencies,
|
|
464
|
+
"environment": mode.environment,
|
|
465
|
+
"active_modes": sorted(self.active_modes),
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
def disable_mode(self, mode_name: str) -> Dict[str, Any]:
|
|
469
|
+
"""Disable a framework mode."""
|
|
470
|
+
if mode_name not in self.active_modes:
|
|
471
|
+
return {
|
|
472
|
+
"success": False,
|
|
473
|
+
"error": f"Mode not active: {mode_name}",
|
|
474
|
+
"active_modes": sorted(self.active_modes),
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
# Remove from active modes
|
|
478
|
+
self.active_modes.discard(mode_name)
|
|
479
|
+
|
|
480
|
+
# Remove from stack
|
|
481
|
+
if mode_name in self.mode_stack:
|
|
482
|
+
self.mode_stack.remove(mode_name)
|
|
483
|
+
|
|
484
|
+
# Check if any active modes depend on this
|
|
485
|
+
dependent_modes = []
|
|
486
|
+
for active in self.active_modes.copy():
|
|
487
|
+
mode = FRAMEWORK_MODES[active]
|
|
488
|
+
if mode_name in mode.dependencies:
|
|
489
|
+
dependent_modes.append(active)
|
|
490
|
+
self.active_modes.discard(active)
|
|
491
|
+
|
|
492
|
+
return {
|
|
493
|
+
"success": True,
|
|
494
|
+
"mode": mode_name,
|
|
495
|
+
"disabled": True,
|
|
496
|
+
"dependent_modes_disabled": dependent_modes,
|
|
497
|
+
"active_modes": sorted(self.active_modes),
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
def get_snippet(self, mode_name: str, snippet_name: str) -> Optional[str]:
|
|
501
|
+
"""Get code snippet for framework."""
|
|
502
|
+
if mode_name not in FRAMEWORK_MODES:
|
|
503
|
+
return None
|
|
504
|
+
|
|
505
|
+
mode = FRAMEWORK_MODES[mode_name]
|
|
506
|
+
return mode.snippets.get(snippet_name)
|
|
507
|
+
|
|
508
|
+
def get_command(self, mode_name: str, command_name: str) -> Optional[List[str]]:
|
|
509
|
+
"""Get command for framework task."""
|
|
510
|
+
if mode_name not in FRAMEWORK_MODES:
|
|
511
|
+
return None
|
|
512
|
+
|
|
513
|
+
mode = FRAMEWORK_MODES[mode_name]
|
|
514
|
+
return mode.commands.get(command_name)
|
|
515
|
+
|
|
516
|
+
def list_modes(self, category: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
517
|
+
"""List available modes."""
|
|
518
|
+
modes = []
|
|
519
|
+
|
|
520
|
+
for name, mode in FRAMEWORK_MODES.items():
|
|
521
|
+
if category and mode.category != category:
|
|
522
|
+
continue
|
|
523
|
+
|
|
524
|
+
modes.append({
|
|
525
|
+
"name": name,
|
|
526
|
+
"description": mode.description,
|
|
527
|
+
"category": mode.category,
|
|
528
|
+
"aliases": mode.aliases,
|
|
529
|
+
"active": name in self.active_modes,
|
|
530
|
+
"tools_count": len(mode.tools),
|
|
531
|
+
"has_snippets": bool(mode.snippets),
|
|
532
|
+
"has_commands": bool(mode.commands),
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
return modes
|
|
536
|
+
|
|
537
|
+
def get_mode_info(self, mode_name: str) -> Optional[Dict[str, Any]]:
|
|
538
|
+
"""Get detailed information about a mode."""
|
|
539
|
+
if mode_name not in FRAMEWORK_MODES:
|
|
540
|
+
return None
|
|
541
|
+
|
|
542
|
+
mode = FRAMEWORK_MODES[mode_name]
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
"name": mode.name,
|
|
546
|
+
"description": mode.description,
|
|
547
|
+
"category": mode.category,
|
|
548
|
+
"aliases": mode.aliases,
|
|
549
|
+
"active": mode.name in self.active_modes,
|
|
550
|
+
"tools": mode.tools,
|
|
551
|
+
"environment": mode.environment,
|
|
552
|
+
"snippets": list(mode.snippets.keys()),
|
|
553
|
+
"commands": list(mode.commands.keys()),
|
|
554
|
+
"dependencies": mode.dependencies,
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
def export_configuration(self) -> Dict[str, Any]:
|
|
558
|
+
"""Export current mode configuration."""
|
|
559
|
+
config = {
|
|
560
|
+
"active_modes": sorted(self.active_modes),
|
|
561
|
+
"mode_stack": self.mode_stack,
|
|
562
|
+
"enabled_tools": set(),
|
|
563
|
+
"environment": {},
|
|
564
|
+
"commands": {},
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
# Collect all enabled tools and environment
|
|
568
|
+
for mode_name in self.active_modes:
|
|
569
|
+
mode = FRAMEWORK_MODES[mode_name]
|
|
570
|
+
config["enabled_tools"].update(mode.tools)
|
|
571
|
+
config["environment"].update(mode.environment)
|
|
572
|
+
|
|
573
|
+
# Add commands with mode prefix
|
|
574
|
+
for cmd_name, cmd in mode.commands.items():
|
|
575
|
+
config["commands"][f"{mode_name}:{cmd_name}"] = cmd
|
|
576
|
+
|
|
577
|
+
config["enabled_tools"] = sorted(config["enabled_tools"])
|
|
578
|
+
|
|
579
|
+
return config
|
|
580
|
+
|
|
581
|
+
async def run(
|
|
582
|
+
self,
|
|
583
|
+
action: str,
|
|
584
|
+
mode: Optional[str] = None,
|
|
585
|
+
snippet: Optional[str] = None,
|
|
586
|
+
command: Optional[str] = None,
|
|
587
|
+
category: Optional[str] = None,
|
|
588
|
+
**kwargs,
|
|
589
|
+
) -> MCPResourceDocument:
|
|
590
|
+
"""Execute framework mode action."""
|
|
591
|
+
|
|
592
|
+
if action == "enable":
|
|
593
|
+
if not mode:
|
|
594
|
+
return MCPResourceDocument(data={"error": "Mode name required"})
|
|
595
|
+
|
|
596
|
+
result = self.enable_mode(mode)
|
|
597
|
+
return MCPResourceDocument(data=result)
|
|
598
|
+
|
|
599
|
+
elif action == "disable":
|
|
600
|
+
if not mode:
|
|
601
|
+
return MCPResourceDocument(data={"error": "Mode name required"})
|
|
602
|
+
|
|
603
|
+
result = self.disable_mode(mode)
|
|
604
|
+
return MCPResourceDocument(data=result)
|
|
605
|
+
|
|
606
|
+
elif action == "list":
|
|
607
|
+
modes = self.list_modes(category)
|
|
608
|
+
|
|
609
|
+
return MCPResourceDocument(
|
|
610
|
+
data={
|
|
611
|
+
"modes": modes,
|
|
612
|
+
"total": len(modes),
|
|
613
|
+
"active_count": len(self.active_modes),
|
|
614
|
+
"categories": list(set(m["category"] for m in modes)),
|
|
615
|
+
}
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
elif action == "current":
|
|
619
|
+
config = self.export_configuration()
|
|
620
|
+
|
|
621
|
+
return MCPResourceDocument(data=config)
|
|
622
|
+
|
|
623
|
+
elif action == "info":
|
|
624
|
+
if not mode:
|
|
625
|
+
return MCPResourceDocument(data={"error": "Mode name required"})
|
|
626
|
+
|
|
627
|
+
info = self.get_mode_info(mode)
|
|
628
|
+
|
|
629
|
+
if info:
|
|
630
|
+
return MCPResourceDocument(data=info)
|
|
631
|
+
else:
|
|
632
|
+
return MCPResourceDocument(
|
|
633
|
+
data={
|
|
634
|
+
"error": f"Unknown mode: {mode}",
|
|
635
|
+
"available_modes": list(FRAMEWORK_MODES.keys()),
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
elif action == "snippet":
|
|
640
|
+
if not mode or not snippet:
|
|
641
|
+
return MCPResourceDocument(
|
|
642
|
+
data={"error": "Mode and snippet name required"}
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
code = self.get_snippet(mode, snippet)
|
|
646
|
+
|
|
647
|
+
if code:
|
|
648
|
+
return MCPResourceDocument(
|
|
649
|
+
data={
|
|
650
|
+
"mode": mode,
|
|
651
|
+
"snippet": snippet,
|
|
652
|
+
"code": code,
|
|
653
|
+
}
|
|
654
|
+
)
|
|
655
|
+
else:
|
|
656
|
+
mode_obj = FRAMEWORK_MODES.get(mode)
|
|
657
|
+
available = list(mode_obj.snippets.keys()) if mode_obj else []
|
|
658
|
+
|
|
659
|
+
return MCPResourceDocument(
|
|
660
|
+
data={
|
|
661
|
+
"error": f"Snippet '{snippet}' not found for mode '{mode}'",
|
|
662
|
+
"available_snippets": available,
|
|
663
|
+
}
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
elif action == "command":
|
|
667
|
+
if not mode or not command:
|
|
668
|
+
return MCPResourceDocument(
|
|
669
|
+
data={"error": "Mode and command name required"}
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
cmd = self.get_command(mode, command)
|
|
673
|
+
|
|
674
|
+
if cmd:
|
|
675
|
+
return MCPResourceDocument(
|
|
676
|
+
data={
|
|
677
|
+
"mode": mode,
|
|
678
|
+
"command": command,
|
|
679
|
+
"cmd": cmd,
|
|
680
|
+
"executable": " ".join(cmd),
|
|
681
|
+
}
|
|
682
|
+
)
|
|
683
|
+
else:
|
|
684
|
+
mode_obj = FRAMEWORK_MODES.get(mode)
|
|
685
|
+
available = list(mode_obj.commands.keys()) if mode_obj else []
|
|
686
|
+
|
|
687
|
+
return MCPResourceDocument(
|
|
688
|
+
data={
|
|
689
|
+
"error": f"Command '{command}' not found for mode '{mode}'",
|
|
690
|
+
"available_commands": available,
|
|
691
|
+
}
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
else:
|
|
695
|
+
return MCPResourceDocument(
|
|
696
|
+
data={
|
|
697
|
+
"error": f"Unknown action: {action}",
|
|
698
|
+
"valid_actions": [
|
|
699
|
+
"enable", "disable", "list", "current",
|
|
700
|
+
"info", "snippet", "command"
|
|
701
|
+
],
|
|
702
|
+
}
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
async def call(self, **kwargs) -> str:
|
|
706
|
+
"""Tool interface for MCP."""
|
|
707
|
+
result = await self.run(**kwargs)
|
|
708
|
+
return result.to_json_string()
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
# Factory function
|
|
712
|
+
def create_framework_mode_manager():
|
|
713
|
+
"""Create framework mode manager."""
|
|
714
|
+
return FrameworkModeManager()
|