hanzo-mcp 0.6.10__py3-none-any.whl → 0.6.13__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 (78) hide show
  1. hanzo_mcp/__init__.py +11 -2
  2. hanzo_mcp/cli.py +69 -19
  3. hanzo_mcp/cli_enhanced.py +15 -12
  4. hanzo_mcp/cli_plugin.py +91 -0
  5. hanzo_mcp/config/__init__.py +1 -1
  6. hanzo_mcp/config/settings.py +75 -8
  7. hanzo_mcp/config/tool_config.py +2 -2
  8. hanzo_mcp/dev_server.py +20 -15
  9. hanzo_mcp/prompts/project_system.py +1 -1
  10. hanzo_mcp/server.py +18 -4
  11. hanzo_mcp/server_enhanced.py +69 -0
  12. hanzo_mcp/tools/__init__.py +78 -30
  13. hanzo_mcp/tools/agent/__init__.py +1 -1
  14. hanzo_mcp/tools/agent/agent_tool.py +2 -2
  15. hanzo_mcp/tools/common/__init__.py +15 -1
  16. hanzo_mcp/tools/common/base.py +4 -4
  17. hanzo_mcp/tools/common/batch_tool.py +1 -1
  18. hanzo_mcp/tools/common/config_tool.py +2 -2
  19. hanzo_mcp/tools/common/context.py +2 -2
  20. hanzo_mcp/tools/common/context_fix.py +26 -0
  21. hanzo_mcp/tools/common/critic_tool.py +196 -0
  22. hanzo_mcp/tools/common/decorators.py +208 -0
  23. hanzo_mcp/tools/common/enhanced_base.py +106 -0
  24. hanzo_mcp/tools/common/mode.py +116 -0
  25. hanzo_mcp/tools/common/mode_loader.py +105 -0
  26. hanzo_mcp/tools/common/permissions.py +1 -1
  27. hanzo_mcp/tools/common/personality.py +936 -0
  28. hanzo_mcp/tools/common/plugin_loader.py +287 -0
  29. hanzo_mcp/tools/common/stats.py +4 -4
  30. hanzo_mcp/tools/common/tool_list.py +1 -1
  31. hanzo_mcp/tools/common/validation.py +1 -1
  32. hanzo_mcp/tools/config/__init__.py +3 -1
  33. hanzo_mcp/tools/config/config_tool.py +1 -1
  34. hanzo_mcp/tools/config/mode_tool.py +209 -0
  35. hanzo_mcp/tools/database/__init__.py +1 -1
  36. hanzo_mcp/tools/editor/__init__.py +1 -1
  37. hanzo_mcp/tools/filesystem/__init__.py +19 -14
  38. hanzo_mcp/tools/filesystem/batch_search.py +3 -3
  39. hanzo_mcp/tools/filesystem/diff.py +2 -2
  40. hanzo_mcp/tools/filesystem/rules_tool.py +235 -0
  41. hanzo_mcp/tools/filesystem/{unified_search.py → search_tool.py} +12 -12
  42. hanzo_mcp/tools/filesystem/{symbols_unified.py → symbols_tool.py} +104 -5
  43. hanzo_mcp/tools/filesystem/watch.py +3 -2
  44. hanzo_mcp/tools/jupyter/__init__.py +2 -2
  45. hanzo_mcp/tools/jupyter/jupyter.py +1 -1
  46. hanzo_mcp/tools/llm/__init__.py +3 -3
  47. hanzo_mcp/tools/llm/llm_tool.py +648 -143
  48. hanzo_mcp/tools/mcp/__init__.py +2 -2
  49. hanzo_mcp/tools/mcp/{mcp_unified.py → mcp_tool.py} +3 -3
  50. hanzo_mcp/tools/shell/__init__.py +6 -6
  51. hanzo_mcp/tools/shell/base_process.py +4 -2
  52. hanzo_mcp/tools/shell/bash_session_executor.py +8 -5
  53. hanzo_mcp/tools/shell/{bash_unified.py → bash_tool.py} +1 -1
  54. hanzo_mcp/tools/shell/command_executor.py +8 -6
  55. hanzo_mcp/tools/shell/{npx_unified.py → npx_tool.py} +1 -1
  56. hanzo_mcp/tools/shell/open.py +2 -2
  57. hanzo_mcp/tools/shell/{process_unified.py → process_tool.py} +1 -1
  58. hanzo_mcp/tools/shell/run_command_windows.py +1 -1
  59. hanzo_mcp/tools/shell/uvx.py +47 -2
  60. hanzo_mcp/tools/shell/uvx_background.py +47 -2
  61. hanzo_mcp/tools/shell/{uvx_unified.py → uvx_tool.py} +1 -1
  62. hanzo_mcp/tools/todo/__init__.py +14 -19
  63. hanzo_mcp/tools/todo/todo.py +22 -1
  64. hanzo_mcp/tools/vector/__init__.py +7 -3
  65. hanzo_mcp/tools/vector/ast_analyzer.py +12 -4
  66. hanzo_mcp/tools/vector/infinity_store.py +11 -5
  67. hanzo_mcp/tools/vector/project_manager.py +4 -2
  68. hanzo_mcp-0.6.13.dist-info/METADATA +359 -0
  69. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/RECORD +73 -65
  70. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/entry_points.txt +1 -0
  71. hanzo_mcp/tools/common/palette.py +0 -344
  72. hanzo_mcp/tools/common/palette_loader.py +0 -108
  73. hanzo_mcp/tools/config/palette_tool.py +0 -179
  74. hanzo_mcp/tools/llm/llm_unified.py +0 -851
  75. hanzo_mcp-0.6.10.dist-info/METADATA +0 -339
  76. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/WHEEL +0 -0
  77. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/licenses/LICENSE +0 -0
  78. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/top_level.txt +0 -0
@@ -1,344 +0,0 @@
1
- """Tool palette system for organizing development tools."""
2
-
3
- from abc import ABC, abstractmethod
4
- from dataclasses import dataclass, field
5
- from typing import Dict, List, Optional, Set
6
-
7
- from hanzo_mcp.tools.common.base import BaseTool
8
-
9
-
10
- @dataclass
11
- class ToolPalette:
12
- """A collection of tools for a specific development environment."""
13
-
14
- name: str
15
- description: str
16
- author: Optional[str] = None
17
- tools: List[str] = field(default_factory=list)
18
- dependencies: List[str] = field(default_factory=list)
19
- environment: Dict[str, str] = field(default_factory=dict)
20
-
21
- def __post_init__(self):
22
- """Validate palette configuration."""
23
- if not self.name:
24
- raise ValueError("Palette name is required")
25
- if not self.tools:
26
- raise ValueError("Palette must include at least one tool")
27
-
28
-
29
- class PaletteRegistry:
30
- """Registry for tool palettes."""
31
-
32
- _palettes: Dict[str, ToolPalette] = {}
33
- _active_palette: Optional[str] = None
34
-
35
- @classmethod
36
- def register(cls, palette: ToolPalette) -> None:
37
- """Register a tool palette."""
38
- cls._palettes[palette.name] = palette
39
-
40
- @classmethod
41
- def get(cls, name: str) -> Optional[ToolPalette]:
42
- """Get a palette by name."""
43
- return cls._palettes.get(name)
44
-
45
- @classmethod
46
- def list(cls) -> List[ToolPalette]:
47
- """List all registered palettes."""
48
- return list(cls._palettes.values())
49
-
50
- @classmethod
51
- def set_active(cls, name: str) -> None:
52
- """Set the active palette."""
53
- if name not in cls._palettes:
54
- raise ValueError(f"Palette '{name}' not found")
55
- cls._active_palette = name
56
-
57
- @classmethod
58
- def get_active(cls) -> Optional[ToolPalette]:
59
- """Get the active palette."""
60
- if cls._active_palette:
61
- return cls._palettes.get(cls._active_palette)
62
- return None
63
-
64
- @classmethod
65
- def get_active_tools(cls) -> Set[str]:
66
- """Get the set of tools from the active palette."""
67
- palette = cls.get_active()
68
- if palette:
69
- return set(palette.tools)
70
- return set()
71
-
72
-
73
- # Pre-defined palettes for famous programmers and ecosystems
74
-
75
- # Python palette - Guido van Rossum style
76
- python_palette = ToolPalette(
77
- name="python",
78
- description="Python development tools following Guido's philosophy",
79
- author="Guido van Rossum",
80
- tools=[
81
- # Core tools
82
- "bash", "read", "write", "edit", "grep", "tree", "find",
83
- # Python specific
84
- "uvx", "process",
85
- # Python tooling commands via uvx
86
- "ruff", # Linting and formatting
87
- "black", # Code formatting
88
- "mypy", # Type checking
89
- "pytest", # Testing
90
- "poetry", # Dependency management
91
- "pip-tools", # Requirements management
92
- "jupyter", # Interactive notebooks
93
- "sphinx", # Documentation
94
- ],
95
- environment={
96
- "PYTHONDONTWRITEBYTECODE": "1",
97
- "PYTHONUNBUFFERED": "1",
98
- }
99
- )
100
-
101
- # Ruby palette - Yukihiro Matsumoto (Matz) style
102
- ruby_palette = ToolPalette(
103
- name="ruby",
104
- description="Ruby development tools for programmer happiness by Yukihiro Matsumoto",
105
- author="Yukihiro Matsumoto",
106
- tools=[
107
- # Core tools
108
- "bash", "read", "write", "edit", "grep", "tree", "find",
109
- # Ruby specific
110
- "process",
111
- # Ruby tooling - optimized for happiness
112
- "ruby", # Ruby interpreter
113
- "gem", # Package manager
114
- "bundler", # Dependency management
115
- "rake", # Task automation
116
- "irb", # Interactive Ruby
117
- "pry", # Enhanced REPL and debugging
118
- "rubocop", # Style guide enforcement
119
- "rspec", # Behavior-driven testing
120
- "minitest", # Lightweight testing
121
- "yard", # Documentation generator
122
- "rails", # Web application framework
123
- "sinatra", # Lightweight web framework
124
- "sidekiq", # Background processing
125
- "capistrano", # Deployment automation
126
- ],
127
- environment={
128
- "RUBYOPT": "-W:deprecated",
129
- "BUNDLE_JOBS": "4",
130
- "BUNDLE_RETRY": "3",
131
- }
132
- )
133
-
134
- # JavaScript/Node palette - Brendan Eich / Ryan Dahl style
135
- javascript_palette = ToolPalette(
136
- name="javascript",
137
- description="JavaScript/Node.js development tools by Brendan Eich / Ryan Dahl",
138
- author="Brendan Eich / Ryan Dahl",
139
- tools=[
140
- # Core tools
141
- "bash", "read", "write", "edit", "grep", "tree", "find",
142
- # JavaScript specific
143
- "npx", "process",
144
- # Package managers
145
- "npm", "yarn", "pnpm", "bun",
146
- # Core tooling via npx
147
- "node", # Node.js runtime
148
- "prettier", # Code formatting
149
- "eslint", # Linting and static analysis
150
- "typescript", # TypeScript compiler
151
- "jest", # Testing framework
152
- "vitest", # Fast testing with Vite
153
- "playwright", # End-to-end testing
154
- # Build tools
155
- "webpack", # Module bundler
156
- "vite", # Fast dev server and bundler
157
- "rollup", # Module bundler
158
- "esbuild", # Fast bundler
159
- # Frameworks and scaffolding
160
- "create-react-app", # React scaffolding
161
- "create-next-app", # Next.js scaffolding
162
- "nuxt", # Vue.js framework
163
- "svelte", # Component framework
164
- ],
165
- environment={
166
- "NODE_ENV": "development",
167
- "NPM_CONFIG_PROGRESS": "false",
168
- "FORCE_COLOR": "1",
169
- }
170
- )
171
-
172
- # Go palette - Rob Pike / Ken Thompson style
173
- go_palette = ToolPalette(
174
- name="go",
175
- description="Go development tools emphasizing simplicity by Rob Pike / Ken Thompson",
176
- author="Rob Pike / Ken Thompson",
177
- tools=[
178
- # Core tools
179
- "bash", "read", "write", "edit", "grep", "tree", "find",
180
- # Go specific
181
- "process",
182
- # Go tooling - emphasizing simplicity
183
- "go", # Compiler and standard tools (go build, go test, go mod)
184
- "gofmt", # Code formatting
185
- "goimports", # Import management
186
- "golangci-lint", # Modern linting (replaces golint)
187
- "godoc", # Documentation
188
- "delve", # Debugger (dlv)
189
- "go-outline", # Code outline
190
- "guru", # Code analysis
191
- "goreleaser", # Release automation
192
- ],
193
- environment={
194
- "GO111MODULE": "on",
195
- "GOPROXY": "https://proxy.golang.org,direct",
196
- "GOSUMDB": "sum.golang.org",
197
- "CGO_ENABLED": "0", # Rob Pike prefers pure Go when possible
198
- }
199
- )
200
-
201
- # Rust palette - Graydon Hoare style
202
- rust_palette = ToolPalette(
203
- name="rust",
204
- description="Rust development tools for systems programming by Graydon Hoare",
205
- author="Graydon Hoare",
206
- tools=[
207
- # Core tools
208
- "bash", "read", "write", "edit", "grep", "tree", "find",
209
- # Rust specific
210
- "process",
211
- # Rust tooling - all via cargo/rustup
212
- "cargo", # Build system and package manager
213
- "rustfmt", # Code formatting (cargo fmt)
214
- "clippy", # Linting (cargo clippy)
215
- "rustdoc", # Documentation (cargo doc)
216
- "rust-analyzer", # Language server
217
- "miri", # Interpreter for unsafe code checking
218
- "rustup", # Rust toolchain manager
219
- "sccache", # Shared compilation cache
220
- "wasm-pack", # WebAssembly workflow
221
- ],
222
- environment={
223
- "RUST_BACKTRACE": "1",
224
- "RUSTFLAGS": "-D warnings",
225
- "CARGO_INCREMENTAL": "1",
226
- }
227
- )
228
-
229
- # DevOps palette - Infrastructure and operations
230
- devops_palette = ToolPalette(
231
- name="devops",
232
- description="DevOps and infrastructure tools",
233
- tools=[
234
- # Core tools
235
- "bash", "read", "write", "edit", "grep", "tree", "find",
236
- "process", "open",
237
- # DevOps tooling
238
- "docker", # Containerization
239
- "kubectl", # Kubernetes
240
- "terraform", # Infrastructure as code
241
- "ansible", # Configuration management
242
- "helm", # Kubernetes package manager
243
- "prometheus", # Monitoring
244
- "grafana", # Visualization
245
- ],
246
- environment={
247
- "DOCKER_BUILDKIT": "1",
248
- }
249
- )
250
-
251
- # Full stack palette - Everything enabled
252
- fullstack_palette = ToolPalette(
253
- name="fullstack",
254
- description="All development tools enabled",
255
- tools=[
256
- # All filesystem tools
257
- "read", "write", "edit", "multi_edit", "grep", "tree", "find",
258
- "symbols", "git_search", "watch",
259
- # All shell tools
260
- "bash", "npx", "uvx", "process", "open",
261
- # All other tools
262
- "agent", "thinking", "llm", "mcp", "sql", "graph", "config",
263
- "todo", "jupyter", "vim",
264
- ]
265
- )
266
-
267
- # Minimal palette - Just the essentials
268
- minimal_palette = ToolPalette(
269
- name="minimal",
270
- description="Minimal set of essential tools",
271
- tools=[
272
- "read", "write", "edit", "bash", "grep", "tree"
273
- ]
274
- )
275
-
276
- # C/C++ palette - Dennis Ritchie / Bjarne Stroustrup style
277
- cpp_palette = ToolPalette(
278
- name="cpp",
279
- description="C/C++ development tools for systems programming by Dennis Ritchie / Bjarne Stroustrup",
280
- author="Dennis Ritchie / Bjarne Stroustrup",
281
- tools=[
282
- # Core tools
283
- "bash", "read", "write", "edit", "grep", "tree", "find",
284
- # C/C++ specific
285
- "process",
286
- # Compilers and build systems
287
- "gcc", "clang", "g++", "clang++",
288
- "make", "cmake", "ninja",
289
- # Debugging and analysis
290
- "gdb", "lldb", "valgrind",
291
- "clang-format", "clang-tidy",
292
- # Package management
293
- "conan", "vcpkg",
294
- # Documentation
295
- "doxygen",
296
- ],
297
- environment={
298
- "CC": "clang",
299
- "CXX": "clang++",
300
- "CFLAGS": "-Wall -Wextra",
301
- "CXXFLAGS": "-Wall -Wextra -std=c++20",
302
- }
303
- )
304
-
305
- # Data Science palette - Scientific computing
306
- datascience_palette = ToolPalette(
307
- name="datascience",
308
- description="Data science and machine learning tools",
309
- author="Scientific Computing Community",
310
- tools=[
311
- # Core tools
312
- "bash", "read", "write", "edit", "grep", "tree", "find",
313
- # Python for data science
314
- "uvx", "process", "jupyter",
315
- # Data science tooling via uvx/pip
316
- "pandas", "numpy", "scipy", "matplotlib", "seaborn",
317
- "scikit-learn", "tensorflow", "pytorch",
318
- "plotly", "bokeh", "streamlit",
319
- "dvc", "mlflow", "wandb",
320
- "black", "isort", "mypy",
321
- ],
322
- environment={
323
- "PYTHONDONTWRITEBYTECODE": "1",
324
- "PYTHONUNBUFFERED": "1",
325
- "JUPYTER_ENABLE_LAB": "yes",
326
- }
327
- )
328
-
329
- # Register all pre-defined palettes
330
- def register_default_palettes():
331
- """Register all default tool palettes."""
332
- for palette in [
333
- python_palette,
334
- ruby_palette,
335
- javascript_palette,
336
- go_palette,
337
- rust_palette,
338
- cpp_palette,
339
- datascience_palette,
340
- devops_palette,
341
- fullstack_palette,
342
- minimal_palette,
343
- ]:
344
- PaletteRegistry.register(palette)
@@ -1,108 +0,0 @@
1
- """Tool palette loader for dynamic tool configuration."""
2
-
3
- import os
4
- from typing import Dict, List, Optional, Set
5
-
6
- from hanzo_mcp.tools.common.palette import PaletteRegistry, register_default_palettes
7
-
8
-
9
- class PaletteLoader:
10
- """Loads and manages tool palettes for dynamic configuration."""
11
-
12
- @staticmethod
13
- def initialize_palettes() -> None:
14
- """Initialize the palette system with defaults."""
15
- register_default_palettes()
16
-
17
- # Check for environment variable to set default palette
18
- default_palette = os.environ.get("HANZO_MCP_PALETTE", "python")
19
- if PaletteRegistry.get(default_palette):
20
- PaletteRegistry.set_active(default_palette)
21
-
22
- @staticmethod
23
- def get_enabled_tools_from_palette(
24
- base_enabled_tools: Optional[Dict[str, bool]] = None,
25
- force_palette: Optional[str] = None
26
- ) -> Dict[str, bool]:
27
- """Get enabled tools configuration from active palette.
28
-
29
- Args:
30
- base_enabled_tools: Base configuration to merge with
31
- force_palette: Force a specific palette (overrides active)
32
-
33
- Returns:
34
- Dictionary of tool enable states
35
- """
36
- # Initialize if needed
37
- if not PaletteRegistry.list():
38
- PaletteLoader.initialize_palettes()
39
-
40
- # Get palette to use
41
- if force_palette:
42
- PaletteRegistry.set_active(force_palette)
43
-
44
- palette = PaletteRegistry.get_active()
45
- if not palette:
46
- # No active palette, return base config
47
- return base_enabled_tools or {}
48
-
49
- # Start with base configuration
50
- result = base_enabled_tools.copy() if base_enabled_tools else {}
51
-
52
- # Get all possible tools (this is a superset for safety)
53
- all_possible_tools = {
54
- # Filesystem tools
55
- "read", "write", "edit", "multi_edit", "grep", "tree", "find",
56
- "symbols", "git_search", "content_replace", "batch_search",
57
- "find_files", "unified_search", "watch",
58
- # Shell tools
59
- "bash", "npx", "uvx", "process", "open",
60
- # Database tools
61
- "sql", "graph",
62
- # Config tools
63
- "config", "palette",
64
- # LLM tools
65
- "llm", "agent", "thinking",
66
- # MCP tools
67
- "mcp",
68
- # Todo tools
69
- "todo",
70
- # Jupyter tools
71
- "jupyter",
72
- # Editor tools
73
- "vim",
74
- # Stats/system tools
75
- "stats", "tool_enable", "tool_disable", "tool_list",
76
- }
77
-
78
- # Disable all tools first (clean slate for palette)
79
- for tool in all_possible_tools:
80
- result[tool] = False
81
-
82
- # Enable tools from palette
83
- for tool in palette.tools:
84
- result[tool] = True
85
-
86
- # Always enable palette tool itself (meta)
87
- result["palette"] = True
88
-
89
- return result
90
-
91
- @staticmethod
92
- def get_environment_from_palette() -> Dict[str, str]:
93
- """Get environment variables from active palette.
94
-
95
- Returns:
96
- Dictionary of environment variables
97
- """
98
- palette = PaletteRegistry.get_active()
99
- if palette and palette.environment:
100
- return palette.environment.copy()
101
- return {}
102
-
103
- @staticmethod
104
- def apply_palette_environment() -> None:
105
- """Apply environment variables from active palette to process."""
106
- env_vars = PaletteLoader.get_environment_from_palette()
107
- for key, value in env_vars.items():
108
- os.environ[key] = value
@@ -1,179 +0,0 @@
1
- """Tool for managing development tool palettes."""
2
-
3
- from typing import Optional, override
4
-
5
- from mcp.server.fastmcp import Context as MCPContext
6
-
7
- from hanzo_mcp.tools.common.base import BaseTool
8
- from hanzo_mcp.tools.common.palette import PaletteRegistry, register_default_palettes
9
- from mcp.server import FastMCP
10
-
11
-
12
- class PaletteTool(BaseTool):
13
- """Tool for managing tool palettes."""
14
-
15
- name = "palette"
16
-
17
- def __init__(self):
18
- """Initialize the palette tool."""
19
- super().__init__()
20
- # Register default palettes on initialization
21
- register_default_palettes()
22
-
23
- @property
24
- @override
25
- def description(self) -> str:
26
- """Get the tool description."""
27
- return """Manage tool palettes. Actions: list (default), activate, show, current.
28
-
29
- Usage:
30
- palette
31
- palette --action list
32
- palette --action activate python
33
- palette --action show javascript
34
- palette --action current"""
35
-
36
- @override
37
- async def run(
38
- self,
39
- ctx: MCPContext,
40
- action: str = "list",
41
- name: Optional[str] = None,
42
- ) -> str:
43
- """Manage tool palettes.
44
-
45
- Args:
46
- ctx: MCP context
47
- action: Action to perform (list, activate, show, current)
48
- name: Palette name (for activate/show actions)
49
-
50
- Returns:
51
- Action result
52
- """
53
- if action == "list":
54
- palettes = PaletteRegistry.list()
55
- if not palettes:
56
- return "No palettes registered"
57
-
58
- output = ["Available tool palettes:"]
59
- active = PaletteRegistry.get_active()
60
-
61
- for palette in sorted(palettes, key=lambda p: p.name):
62
- marker = " (active)" if active and active.name == palette.name else ""
63
- author = f" by {palette.author}" if palette.author else ""
64
- output.append(f"\n{palette.name}{marker}:")
65
- output.append(f" {palette.description}{author}")
66
- output.append(f" Tools: {len(palette.tools)} enabled")
67
-
68
- return "\n".join(output)
69
-
70
- elif action == "activate":
71
- if not name:
72
- return "Error: Palette name required for activate action"
73
-
74
- try:
75
- PaletteRegistry.set_active(name)
76
- palette = PaletteRegistry.get(name)
77
-
78
- output = [f"Activated palette: {palette.name}"]
79
- if palette.author:
80
- output.append(f"Author: {palette.author}")
81
- output.append(f"Description: {palette.description}")
82
- output.append(f"\nEnabled tools ({len(palette.tools)}):")
83
-
84
- # Group tools by category
85
- core_tools = []
86
- package_tools = []
87
- other_tools = []
88
-
89
- for tool in sorted(palette.tools):
90
- if tool in ["read", "write", "edit", "grep", "tree", "find", "bash"]:
91
- core_tools.append(tool)
92
- elif tool in ["npx", "uvx", "pip", "cargo", "gem"]:
93
- package_tools.append(tool)
94
- else:
95
- other_tools.append(tool)
96
-
97
- if core_tools:
98
- output.append(f" Core: {', '.join(core_tools)}")
99
- if package_tools:
100
- output.append(f" Package managers: {', '.join(package_tools)}")
101
- if other_tools:
102
- output.append(f" Specialized: {', '.join(other_tools)}")
103
-
104
- if palette.environment:
105
- output.append("\nEnvironment variables:")
106
- for key, value in palette.environment.items():
107
- output.append(f" {key}={value}")
108
-
109
- output.append("\nNote: Restart MCP session for changes to take full effect")
110
-
111
- return "\n".join(output)
112
-
113
- except ValueError as e:
114
- return str(e)
115
-
116
- elif action == "show":
117
- if not name:
118
- return "Error: Palette name required for show action"
119
-
120
- palette = PaletteRegistry.get(name)
121
- if not palette:
122
- return f"Palette '{name}' not found"
123
-
124
- output = [f"Palette: {palette.name}"]
125
- if palette.author:
126
- output.append(f"Author: {palette.author}")
127
- output.append(f"Description: {palette.description}")
128
- output.append(f"\nTools ({len(palette.tools)}):")
129
-
130
- for tool in sorted(palette.tools):
131
- output.append(f" - {tool}")
132
-
133
- if palette.environment:
134
- output.append("\nEnvironment:")
135
- for key, value in palette.environment.items():
136
- output.append(f" {key}={value}")
137
-
138
- return "\n".join(output)
139
-
140
- elif action == "current":
141
- active = PaletteRegistry.get_active()
142
- if not active:
143
- return "No palette currently active\nUse 'palette --action activate <name>' to activate one"
144
-
145
- output = [f"Current palette: {active.name}"]
146
- if active.author:
147
- output.append(f"Author: {active.author}")
148
- output.append(f"Description: {active.description}")
149
- output.append(f"Enabled tools: {len(active.tools)}")
150
-
151
- return "\n".join(output)
152
-
153
- else:
154
- return f"Unknown action: {action}. Use 'list', 'activate', 'show', or 'current'"
155
-
156
- def register(self, server: FastMCP) -> None:
157
- """Register the tool with the MCP server."""
158
- tool_self = self
159
-
160
- @server.tool(name=self.name, description=self.description)
161
- async def palette_handler(
162
- ctx: MCPContext,
163
- action: str = "list",
164
- name: Optional[str] = None,
165
- ) -> str:
166
- """Handle palette tool calls."""
167
- return await tool_self.run(ctx, action=action, name=name)
168
-
169
- async def call(self, ctx: MCPContext, **params) -> str:
170
- """Call the tool with arguments."""
171
- return await self.run(
172
- ctx,
173
- action=params.get("action", "list"),
174
- name=params.get("name")
175
- )
176
-
177
-
178
- # Create tool instance
179
- palette_tool = PaletteTool()