hanzo-mcp 0.8.0__py3-none-any.whl → 0.8.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.

hanzo_mcp/bridge.py ADDED
@@ -0,0 +1,478 @@
1
+ """MCP Bridge for inter-Claude communication.
2
+
3
+ This module provides MCP server functionality that allows Claude instances
4
+ to communicate with each other, enabling peer-to-peer agent networks.
5
+ """
6
+
7
+ import argparse
8
+ import asyncio
9
+ import json
10
+ import logging
11
+ import os
12
+ import sys
13
+ from typing import Any, Dict, List, Optional
14
+ from dataclasses import dataclass, asdict
15
+
16
+ import mcp.server.fastmcp as mcp
17
+ from mcp import tool
18
+ from mcp.server.fastmcp import FastMCP
19
+ from mcp.server.models import InitializationOptions
20
+ from mcp.server.stdio import stdio_server
21
+ from mcp.types import TextContent, Tool, INTERNAL_ERROR
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ @dataclass
27
+ class BridgeConfig:
28
+ """Configuration for MCP bridge."""
29
+ target_port: int
30
+ instance_id: int
31
+ role: str
32
+ source_instance: Optional[int] = None
33
+ target_instance: Optional[int] = None
34
+
35
+
36
+ class ClaudeBridge(FastMCP):
37
+ """MCP Bridge server for Claude-to-Claude communication."""
38
+
39
+ def __init__(self, config: BridgeConfig):
40
+ """Initialize the bridge.
41
+
42
+ Args:
43
+ config: Bridge configuration
44
+ """
45
+ # Set server name based on target instance
46
+ super().__init__(f"claude_instance_{config.instance_id}")
47
+
48
+ self.config = config
49
+ self.conversation_history: List[Dict[str, Any]] = []
50
+ self.shared_context: Dict[str, Any] = {}
51
+
52
+ # Register tools
53
+ self._register_tools()
54
+
55
+ def _register_tools(self):
56
+ """Register MCP tools for inter-Claude communication."""
57
+
58
+ @self.tool()
59
+ async def chat_with_claude(message: str, context: Optional[str] = None) -> str:
60
+ """Chat with another Claude instance.
61
+
62
+ Args:
63
+ message: Message to send to the other Claude
64
+ context: Optional context to provide
65
+
66
+ Returns:
67
+ Response from the other Claude instance
68
+ """
69
+ logger.info(f"Bridge {self.config.instance_id}: Received chat request")
70
+
71
+ # Record in conversation history
72
+ self.conversation_history.append({
73
+ "from": self.config.source_instance,
74
+ "to": self.config.target_instance,
75
+ "message": message,
76
+ "context": context
77
+ })
78
+
79
+ # Simulate response (in production, this would make actual API call)
80
+ response = await self._forward_to_claude(message, context)
81
+
82
+ self.conversation_history.append({
83
+ "from": self.config.target_instance,
84
+ "to": self.config.source_instance,
85
+ "response": response
86
+ })
87
+
88
+ return response
89
+
90
+ @self.tool()
91
+ async def ask_claude_to_review(
92
+ code: str,
93
+ description: str,
94
+ focus_areas: Optional[List[str]] = None
95
+ ) -> Dict[str, Any]:
96
+ """Ask another Claude to review code.
97
+
98
+ Args:
99
+ code: Code to review
100
+ description: Description of what the code does
101
+ focus_areas: Specific areas to focus on (e.g., ["security", "performance"])
102
+
103
+ Returns:
104
+ Review feedback from the other Claude
105
+ """
106
+ logger.info(f"Bridge {self.config.instance_id}: Code review request")
107
+
108
+ review_prompt = self._build_review_prompt(code, description, focus_areas)
109
+ review = await self._forward_to_claude(review_prompt)
110
+
111
+ return {
112
+ "reviewer": f"claude_{self.config.instance_id}",
113
+ "role": self.config.role,
114
+ "feedback": review,
115
+ "focus_areas": focus_areas or ["general"]
116
+ }
117
+
118
+ @self.tool()
119
+ async def delegate_to_claude(
120
+ task: str,
121
+ requirements: List[str],
122
+ constraints: Optional[List[str]] = None
123
+ ) -> Dict[str, Any]:
124
+ """Delegate a task to another Claude instance.
125
+
126
+ Args:
127
+ task: Task description
128
+ requirements: List of requirements
129
+ constraints: Optional constraints
130
+
131
+ Returns:
132
+ Task completion result from the other Claude
133
+ """
134
+ logger.info(f"Bridge {self.config.instance_id}: Task delegation")
135
+
136
+ delegation_prompt = self._build_delegation_prompt(task, requirements, constraints)
137
+ result = await self._forward_to_claude(delegation_prompt)
138
+
139
+ return {
140
+ "delegated_to": f"claude_{self.config.instance_id}",
141
+ "role": self.config.role,
142
+ "task": task,
143
+ "result": result,
144
+ "status": "completed"
145
+ }
146
+
147
+ @self.tool()
148
+ async def get_claude_opinion(
149
+ question: str,
150
+ options: Optional[List[str]] = None,
151
+ criteria: Optional[List[str]] = None
152
+ ) -> Dict[str, Any]:
153
+ """Get another Claude's opinion on a decision.
154
+
155
+ Args:
156
+ question: The question or decision to get opinion on
157
+ options: Optional list of options to choose from
158
+ criteria: Optional evaluation criteria
159
+
160
+ Returns:
161
+ Opinion and reasoning from the other Claude
162
+ """
163
+ logger.info(f"Bridge {self.config.instance_id}: Opinion request")
164
+
165
+ opinion_prompt = self._build_opinion_prompt(question, options, criteria)
166
+ opinion = await self._forward_to_claude(opinion_prompt)
167
+
168
+ return {
169
+ "advisor": f"claude_{self.config.instance_id}",
170
+ "role": self.config.role,
171
+ "question": question,
172
+ "opinion": opinion,
173
+ "options_considered": options,
174
+ "criteria_used": criteria
175
+ }
176
+
177
+ @self.tool()
178
+ async def share_context_with_claude(
179
+ key: str,
180
+ value: Any,
181
+ description: Optional[str] = None
182
+ ) -> bool:
183
+ """Share context with another Claude instance.
184
+
185
+ Args:
186
+ key: Context key
187
+ value: Context value
188
+ description: Optional description of the context
189
+
190
+ Returns:
191
+ Success status
192
+ """
193
+ logger.info(f"Bridge {self.config.instance_id}: Sharing context '{key}'")
194
+
195
+ self.shared_context[key] = {
196
+ "value": value,
197
+ "description": description,
198
+ "shared_by": self.config.source_instance,
199
+ "shared_with": self.config.target_instance
200
+ }
201
+
202
+ return True
203
+
204
+ @self.tool()
205
+ async def get_shared_context(key: Optional[str] = None) -> Dict[str, Any]:
206
+ """Get shared context from Claude network.
207
+
208
+ Args:
209
+ key: Optional specific key to retrieve
210
+
211
+ Returns:
212
+ Shared context data
213
+ """
214
+ if key:
215
+ return self.shared_context.get(key, {})
216
+ return self.shared_context
217
+
218
+ @self.tool()
219
+ async def brainstorm_with_claude(
220
+ topic: str,
221
+ num_ideas: int = 5,
222
+ constraints: Optional[List[str]] = None
223
+ ) -> List[str]:
224
+ """Brainstorm ideas with another Claude.
225
+
226
+ Args:
227
+ topic: Topic to brainstorm about
228
+ num_ideas: Number of ideas to generate
229
+ constraints: Optional constraints
230
+
231
+ Returns:
232
+ List of brainstormed ideas
233
+ """
234
+ logger.info(f"Bridge {self.config.instance_id}: Brainstorming request")
235
+
236
+ brainstorm_prompt = f"""
237
+ Please brainstorm {num_ideas} ideas about: {topic}
238
+
239
+ {"Constraints: " + ", ".join(constraints) if constraints else ""}
240
+
241
+ Provide creative and practical ideas.
242
+ """
243
+
244
+ response = await self._forward_to_claude(brainstorm_prompt)
245
+
246
+ # Parse response into list (simplified)
247
+ ideas = response.split("\n")
248
+ ideas = [idea.strip() for idea in ideas if idea.strip()]
249
+
250
+ return ideas[:num_ideas]
251
+
252
+ @self.tool()
253
+ async def get_claude_status() -> Dict[str, Any]:
254
+ """Get status of the connected Claude instance.
255
+
256
+ Returns:
257
+ Status information
258
+ """
259
+ return {
260
+ "instance_id": self.config.instance_id,
261
+ "role": self.config.role,
262
+ "status": "available",
263
+ "conversation_count": len(self.conversation_history),
264
+ "shared_context_keys": list(self.shared_context.keys())
265
+ }
266
+
267
+ def _build_review_prompt(self, code: str, description: str,
268
+ focus_areas: Optional[List[str]]) -> str:
269
+ """Build a code review prompt."""
270
+ prompt = f"""
271
+ Please review the following code:
272
+
273
+ Description: {description}
274
+
275
+ Code:
276
+ ```
277
+ {code}
278
+ ```
279
+ """
280
+
281
+ if focus_areas:
282
+ prompt += f"\n\nPlease focus particularly on: {', '.join(focus_areas)}"
283
+
284
+ prompt += """
285
+
286
+ Provide constructive feedback on:
287
+ 1. Potential bugs or issues
288
+ 2. Code quality and best practices
289
+ 3. Performance considerations
290
+ 4. Security concerns
291
+ 5. Suggestions for improvement
292
+ """
293
+
294
+ return prompt
295
+
296
+ def _build_delegation_prompt(self, task: str, requirements: List[str],
297
+ constraints: Optional[List[str]]) -> str:
298
+ """Build a task delegation prompt."""
299
+ prompt = f"""
300
+ Please complete the following task:
301
+
302
+ Task: {task}
303
+
304
+ Requirements:
305
+ {chr(10).join(f"- {req}" for req in requirements)}
306
+ """
307
+
308
+ if constraints:
309
+ prompt += f"""
310
+
311
+ Constraints:
312
+ {chr(10).join(f"- {con}" for con in constraints)}
313
+ """
314
+
315
+ prompt += """
316
+
317
+ Provide a complete solution that meets all requirements.
318
+ """
319
+
320
+ return prompt
321
+
322
+ def _build_opinion_prompt(self, question: str, options: Optional[List[str]],
323
+ criteria: Optional[List[str]]) -> str:
324
+ """Build an opinion request prompt."""
325
+ prompt = f"""
326
+ I need your opinion on the following:
327
+
328
+ Question: {question}
329
+ """
330
+
331
+ if options:
332
+ prompt += f"""
333
+
334
+ Options to consider:
335
+ {chr(10).join(f"{i+1}. {opt}" for i, opt in enumerate(options))}
336
+ """
337
+
338
+ if criteria:
339
+ prompt += f"""
340
+
341
+ Please evaluate based on these criteria:
342
+ {chr(10).join(f"- {crit}" for crit in criteria)}
343
+ """
344
+
345
+ prompt += """
346
+
347
+ Provide your recommendation with clear reasoning.
348
+ """
349
+
350
+ return prompt
351
+
352
+ async def _forward_to_claude(self, prompt: str, context: Optional[str] = None) -> str:
353
+ """Forward a request to the target Claude instance.
354
+
355
+ In production, this would make an actual API call to the Claude instance.
356
+ For now, it returns a simulated response.
357
+ """
358
+ # Add context if provided
359
+ full_prompt = prompt
360
+ if context:
361
+ full_prompt = f"Context: {context}\n\n{prompt}"
362
+
363
+ # Log the forwarding
364
+ logger.info(f"Forwarding from instance {self.config.source_instance} to {self.config.target_instance}")
365
+ logger.debug(f"Prompt: {full_prompt[:200]}...")
366
+
367
+ # In production, this would:
368
+ # 1. Connect to the target Claude instance API
369
+ # 2. Send the prompt
370
+ # 3. Receive and return the response
371
+
372
+ # Simulated response based on role
373
+ if self.config.role.startswith("critic"):
374
+ return f"""
375
+ As {self.config.role}, I've analyzed your request:
376
+
377
+ Strengths:
378
+ - The approach is logical and well-structured
379
+ - Good attention to requirements
380
+
381
+ Areas for improvement:
382
+ - Consider edge cases more thoroughly
383
+ - Add more comprehensive error handling
384
+ - Optimize for performance in high-load scenarios
385
+
386
+ Recommendation: Proceed with suggested improvements.
387
+ """
388
+ else:
389
+ return f"""
390
+ Response from {self.config.role} (instance {self.config.instance_id}):
391
+
392
+ I've processed your request: "{prompt[:100]}..."
393
+
394
+ The task has been completed successfully with the following approach:
395
+ 1. Analyzed the requirements
396
+ 2. Implemented the solution
397
+ 3. Validated the results
398
+
399
+ The solution meets all specified criteria.
400
+ """
401
+
402
+
403
+ async def run_bridge_server(config: BridgeConfig):
404
+ """Run the MCP bridge server.
405
+
406
+ Args:
407
+ config: Bridge configuration
408
+ """
409
+ # Configure logging
410
+ logging.basicConfig(
411
+ level=logging.INFO,
412
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
413
+ )
414
+
415
+ logger.info(f"Starting MCP Bridge for Claude instance {config.instance_id}")
416
+ logger.info(f"Role: {config.role}")
417
+ logger.info(f"Target port: {config.target_port}")
418
+
419
+ # Create and run the bridge
420
+ bridge = ClaudeBridge(config)
421
+
422
+ # Run the stdio server
423
+ async with stdio_server() as (read_stream, write_stream):
424
+ await bridge.run(
425
+ read_stream=read_stream,
426
+ write_stream=write_stream,
427
+ InitializationOptions(
428
+ server_name=bridge.name,
429
+ server_version="1.0.0",
430
+ capabilities=bridge.get_capabilities()
431
+ )
432
+ )
433
+
434
+
435
+ def main():
436
+ """Main entry point for the bridge."""
437
+ parser = argparse.ArgumentParser(
438
+ description="MCP Bridge for Claude-to-Claude communication"
439
+ )
440
+ parser.add_argument(
441
+ "--target-port",
442
+ type=int,
443
+ required=True,
444
+ help="Port of the target Claude instance"
445
+ )
446
+ parser.add_argument(
447
+ "--instance-id",
448
+ type=int,
449
+ required=True,
450
+ help="ID of the target Claude instance"
451
+ )
452
+ parser.add_argument(
453
+ "--role",
454
+ type=str,
455
+ required=True,
456
+ help="Role of the target instance (primary, critic_1, etc.)"
457
+ )
458
+
459
+ args = parser.parse_args()
460
+
461
+ # Get source/target from environment
462
+ source_instance = int(os.environ.get("SOURCE_INSTANCE", "0"))
463
+ target_instance = int(os.environ.get("TARGET_INSTANCE", args.instance_id))
464
+
465
+ config = BridgeConfig(
466
+ target_port=args.target_port,
467
+ instance_id=args.instance_id,
468
+ role=args.role,
469
+ source_instance=source_instance,
470
+ target_instance=target_instance
471
+ )
472
+
473
+ # Run the bridge
474
+ asyncio.run(run_bridge_server(config))
475
+
476
+
477
+ if __name__ == "__main__":
478
+ main()
@@ -0,0 +1,179 @@
1
+ """Compute node detection and management for distributed processing."""
2
+
3
+ import os
4
+ import platform
5
+ import subprocess
6
+ from typing import Any, Dict, List
7
+
8
+
9
+ class ComputeNodeDetector:
10
+ """Detect available compute nodes (GPUs, WebGPU, CPUs) for distributed work."""
11
+
12
+ @staticmethod
13
+ def detect_local_gpus() -> List[Dict[str, Any]]:
14
+ """Detect local GPU devices."""
15
+ gpus = []
16
+
17
+ # Try NVIDIA GPUs
18
+ try:
19
+ result = subprocess.run(
20
+ ["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader"],
21
+ capture_output=True,
22
+ text=True,
23
+ timeout=2
24
+ )
25
+ if result.returncode == 0:
26
+ for line in result.stdout.strip().split('\n'):
27
+ if line:
28
+ name, memory = line.split(', ')
29
+ gpus.append({
30
+ "type": "cuda",
31
+ "name": name,
32
+ "memory": memory,
33
+ "id": f"cuda:{len(gpus)}"
34
+ })
35
+ except (FileNotFoundError, subprocess.TimeoutExpired):
36
+ pass
37
+
38
+ # Try Metal GPUs (macOS)
39
+ if platform.system() == "Darwin":
40
+ try:
41
+ # Check for Metal support
42
+ result = subprocess.run(
43
+ ["system_profiler", "SPDisplaysDataType"],
44
+ capture_output=True,
45
+ text=True,
46
+ timeout=2
47
+ )
48
+ if result.returncode == 0 and "Metal" in result.stdout:
49
+ # Parse GPU info from system_profiler
50
+ lines = result.stdout.split('\n')
51
+ for i, line in enumerate(lines):
52
+ if 'Chipset Model:' in line:
53
+ gpu_name = line.split(':')[1].strip()
54
+ gpus.append({
55
+ "type": "metal",
56
+ "name": gpu_name,
57
+ "memory": "Shared",
58
+ "id": f"metal:{len(gpus)}"
59
+ })
60
+ except (FileNotFoundError, subprocess.TimeoutExpired):
61
+ pass
62
+
63
+ return gpus
64
+
65
+ @staticmethod
66
+ def detect_webgpu_nodes() -> List[Dict[str, Any]]:
67
+ """Detect connected WebGPU nodes (from browsers)."""
68
+ webgpu_nodes = []
69
+
70
+ # Check for WebGPU connections (would need actual WebSocket/server to track)
71
+ # For now, check if a WebGPU server is running
72
+ webgpu_port = os.environ.get("HANZO_WEBGPU_PORT", "8765")
73
+ try:
74
+ import socket
75
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
76
+ result = sock.connect_ex(('localhost', int(webgpu_port)))
77
+ sock.close()
78
+ if result == 0:
79
+ webgpu_nodes.append({
80
+ "type": "webgpu",
81
+ "name": "Chrome WebGPU",
82
+ "memory": "Browser",
83
+ "id": "webgpu:0"
84
+ })
85
+ except Exception:
86
+ pass
87
+
88
+ return webgpu_nodes
89
+
90
+ @staticmethod
91
+ def detect_cpu_nodes() -> List[Dict[str, Any]]:
92
+ """Detect CPU compute nodes."""
93
+ import multiprocessing
94
+
95
+ return [{
96
+ "type": "cpu",
97
+ "name": f"{platform.processor() or 'CPU'}",
98
+ "cores": multiprocessing.cpu_count(),
99
+ "id": "cpu:0"
100
+ }]
101
+
102
+ @classmethod
103
+ def get_all_nodes(cls) -> List[Dict[str, Any]]:
104
+ """Get all available compute nodes."""
105
+ nodes = []
106
+
107
+ # Detect GPUs
108
+ gpus = cls.detect_local_gpus()
109
+ nodes.extend(gpus)
110
+
111
+ # Detect WebGPU connections
112
+ webgpu = cls.detect_webgpu_nodes()
113
+ nodes.extend(webgpu)
114
+
115
+ # If no GPUs/WebGPU, add CPU as compute node
116
+ if not nodes:
117
+ nodes.extend(cls.detect_cpu_nodes())
118
+
119
+ return nodes
120
+
121
+ @classmethod
122
+ def get_node_count(cls) -> int:
123
+ """Get total number of available compute nodes."""
124
+ return len(cls.get_all_nodes())
125
+
126
+ @classmethod
127
+ def get_node_summary(cls) -> str:
128
+ """Get a summary string of available nodes."""
129
+ nodes = cls.get_all_nodes()
130
+ if not nodes:
131
+ return "No compute nodes available"
132
+
133
+ count = len(nodes)
134
+ node_word = "node" if count == 1 else "nodes"
135
+
136
+ # Group by type
137
+ types = {}
138
+ for node in nodes:
139
+ node_type = node["type"]
140
+ if node_type not in types:
141
+ types[node_type] = 0
142
+ types[node_type] += 1
143
+
144
+ # Build summary
145
+ parts = []
146
+ for node_type, type_count in types.items():
147
+ if node_type == "cuda":
148
+ parts.append(f"{type_count} CUDA GPU{'s' if type_count > 1 else ''}")
149
+ elif node_type == "metal":
150
+ parts.append(f"{type_count} Metal GPU{'s' if type_count > 1 else ''}")
151
+ elif node_type == "webgpu":
152
+ parts.append(f"{type_count} WebGPU")
153
+ elif node_type == "cpu":
154
+ parts.append(f"{type_count} CPU")
155
+
156
+ type_str = ", ".join(parts)
157
+ return f"{count} {node_word} available ({type_str})"
158
+
159
+
160
+ def print_node_status():
161
+ """Print current node status."""
162
+ detector = ComputeNodeDetector()
163
+ nodes = detector.get_all_nodes()
164
+
165
+ print(f"\n🖥️ Compute Nodes: {len(nodes)}")
166
+ for node in nodes:
167
+ if node["type"] in ["cuda", "metal"]:
168
+ print(f" • {node['id']}: {node['name']} ({node['memory']})")
169
+ elif node["type"] == "webgpu":
170
+ print(f" • {node['id']}: {node['name']}")
171
+ elif node["type"] == "cpu":
172
+ print(f" • {node['id']}: {node['name']} ({node['cores']} cores)")
173
+ print()
174
+
175
+
176
+ if __name__ == "__main__":
177
+ # Test the detector
178
+ print_node_status()
179
+ print(ComputeNodeDetector.get_node_summary())
hanzo_mcp/dev_server.py CHANGED
@@ -172,6 +172,17 @@ class DevServer:
172
172
 
173
173
  logger = logging.getLogger(__name__)
174
174
  logger.info(f"\n🚀 Starting Hanzo AI in development mode...")
175
+
176
+ # Show compute nodes
177
+ try:
178
+ from hanzo_mcp.compute_nodes import ComputeNodeDetector
179
+ detector = ComputeNodeDetector()
180
+ summary = detector.get_node_summary()
181
+ logger.info(f"🖥️ {summary}")
182
+ except Exception:
183
+ # Silently ignore if compute node detection fails
184
+ pass
185
+
175
186
  logger.info(f"🔧 Hot reload enabled - watching for file changes")
176
187
  logger.info(f"📁 Project: {self.project_dir or 'current directory'}")
177
188
  logger.info(f"🌐 Transport: {transport}\n")
hanzo_mcp/server.py CHANGED
@@ -1,10 +1,11 @@
1
1
  """MCP server implementing Hanzo capabilities."""
2
2
 
3
+ import os
3
4
  import atexit
4
5
  import signal
5
6
  import logging
6
- import threading
7
7
  import warnings
8
+ import threading
8
9
  from typing import Literal, cast, final
9
10
 
10
11
  # Suppress litellm deprecation warnings about event loop
@@ -219,6 +220,18 @@ class HanzoMCPServer:
219
220
  for path in allowed_paths_list:
220
221
  self.permission_manager.add_allowed_path(path)
221
222
 
223
+ # Show compute nodes only in non-stdio mode (to avoid corrupting protocol)
224
+ if transport != "stdio" and not os.environ.get("HANZO_QUIET"):
225
+ try:
226
+ from hanzo_mcp.compute_nodes import ComputeNodeDetector
227
+ detector = ComputeNodeDetector()
228
+ summary = detector.get_node_summary()
229
+ logger = logging.getLogger(__name__)
230
+ logger.info(f"🖥️ {summary}")
231
+ except Exception:
232
+ # Silently ignore if compute node detection fails
233
+ pass
234
+
222
235
  # Set up cleanup handlers before running
223
236
  self._setup_cleanup_handlers()
224
237
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hanzo-mcp
3
- Version: 0.8.0
3
+ Version: 0.8.2
4
4
  Summary: The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others.
5
5
  Author-email: Hanzo Industries Inc <dev@hanzo.ai>
6
6
  License: MIT
@@ -24,8 +24,8 @@ Requires-Dist: grep-ast>=0.8.1
24
24
  Requires-Dist: bashlex>=0.18
25
25
  Requires-Dist: libtmux>=0.39.0
26
26
  Requires-Dist: nbformat>=5.10.4
27
- Requires-Dist: psutil>=6.1.1
28
- Requires-Dist: pydantic>=2.11.1
27
+ Requires-Dist: psutil>=6.0.0
28
+ Requires-Dist: pydantic>=2.9.2
29
29
  Requires-Dist: pydantic-settings>=2.7.0
30
30
  Requires-Dist: typing-extensions>=4.13.0
31
31
  Requires-Dist: watchdog>=6.0.0
@@ -1,10 +1,12 @@
1
1
  hanzo_mcp/__init__.py,sha256=wnFIxe852rFEPOs7DVn4fGJ_ui5m7CHXs1m6cKPvO04,541
2
2
  hanzo_mcp/__main__.py,sha256=EvDEygOjvP6S_CrZSL8E4qgGUcO_xfmzEIbsLEHPaK4,130
3
+ hanzo_mcp/bridge.py,sha256=WDPzpqmMXs7ZF4JDVuc8rAPKr_Zq7ky3RGOc_LU15UE,15605
3
4
  hanzo_mcp/cli.py,sha256=lGsPYN-Msa-5jcxUBATS0B24MSJyTqvfXPaIpjoZW_Q,13378
4
5
  hanzo_mcp/cli_enhanced.py,sha256=Nr4sXJ-dK9jkQ4BRDdwotXUwakL8CGme-FlYNPqCq0U,16022
5
6
  hanzo_mcp/cli_plugin.py,sha256=nQVn0sYP1eDaz61QbHwT6BBRPfYZb1eH1HJ1l5QkHsw,3281
6
- hanzo_mcp/dev_server.py,sha256=OUuA0se3uuChftJcRT0J7zW3rSDe361UyJK6VPBSuAg,7957
7
- hanzo_mcp/server.py,sha256=owdRTMn7VNevrNHB_eLX29CQs3xQVESFgqo2s8r4sPY,9823
7
+ hanzo_mcp/compute_nodes.py,sha256=URYyZMOz9_Qp3M9sNqpnP0w4Rw-TD9j_BC7RdwdGanE,6112
8
+ hanzo_mcp/dev_server.py,sha256=hoXKPSO8i-NCg1enC01uJsPVDyCeliWbeIptbIa_XDY,8332
9
+ hanzo_mcp/server.py,sha256=auBJ7UmqOZkAsN0ltwDGmupVYcQfdKh5Dr9-bZBB8w4,10401
8
10
  hanzo_mcp/server_enhanced.py,sha256=bBrObdysyda6Ggf-E3aL7UwktUNzYO_HG1V45Av5r-E,2003
9
11
  hanzo_mcp/types.py,sha256=4YjIJmM7byrsY4eN10pbhIUpFMQ-fZrpK6scgt-U9dU,648
10
12
  hanzo_mcp/analytics/__init__.py,sha256=ANyntTooBrpa_uvwE6KbYxB9uda610UT10pt2rrLiUU,213
@@ -178,8 +180,8 @@ hanzo_mcp/tools/vector/project_manager.py,sha256=b69kr84qoteFkx7feeC_XHsNteTyVEf
178
180
  hanzo_mcp/tools/vector/vector.py,sha256=IzJJlWAuhXNmm41NyXEyQsknLNugoRRciDTypYfof9w,9947
179
181
  hanzo_mcp/tools/vector/vector_index.py,sha256=EgxOveWt0R60WYcO-CLmEh8HbGXGmX4_xpqQXTtgQJQ,4188
180
182
  hanzo_mcp/tools/vector/vector_search.py,sha256=anavkfz_pPtfgVZUW4trj5q5GT6Naxw5ItjK9hUY9wU,9677
181
- hanzo_mcp-0.8.0.dist-info/METADATA,sha256=buOz8WrB1IEGdD0VkLwxLNRML6dN6pxlYgbi4wPvO1w,16472
182
- hanzo_mcp-0.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
183
- hanzo_mcp-0.8.0.dist-info/entry_points.txt,sha256=ML30pedHV5wjthfztzMMz3uYhNdR_6inzYY5pSqNME4,142
184
- hanzo_mcp-0.8.0.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
185
- hanzo_mcp-0.8.0.dist-info/RECORD,,
183
+ hanzo_mcp-0.8.2.dist-info/METADATA,sha256=4t9z7aantY2K6wxLNR_uATlm3CLkPpurz-5y_YAxxy4,16471
184
+ hanzo_mcp-0.8.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
185
+ hanzo_mcp-0.8.2.dist-info/entry_points.txt,sha256=ML30pedHV5wjthfztzMMz3uYhNdR_6inzYY5pSqNME4,142
186
+ hanzo_mcp-0.8.2.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
187
+ hanzo_mcp-0.8.2.dist-info/RECORD,,