hanzo-mcp 0.8.2__py3-none-any.whl → 0.8.4__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 CHANGED
@@ -1,11 +1,24 @@
1
1
  """Hanzo AI - Implementation of Hanzo capabilities using MCP."""
2
2
 
3
+ # Polyfill typing.override for Python < 3.12
4
+ try: # pragma: no cover
5
+ from typing import override as _override # type: ignore
6
+ except Exception: # pragma: no cover
7
+ import typing as _typing
8
+
9
+ def override(obj): # type: ignore
10
+ return obj
11
+
12
+ _typing.override = override # type: ignore[attr-defined]
13
+
3
14
  # Configure FastMCP logging globally for stdio transport
4
15
  import os
5
16
  import warnings
6
17
 
7
18
  # Suppress litellm deprecation warnings about event loop
8
- warnings.filterwarnings("ignore", message="There is no current event loop", category=DeprecationWarning)
19
+ warnings.filterwarnings(
20
+ "ignore", message="There is no current event loop", category=DeprecationWarning
21
+ )
9
22
 
10
23
  if os.environ.get("HANZO_MCP_TRANSPORT") == "stdio":
11
24
  try:
@@ -15,4 +28,4 @@ if os.environ.get("HANZO_MCP_TRANSPORT") == "stdio":
15
28
  except ImportError:
16
29
  pass
17
30
 
18
- __version__ = "0.7.7"
31
+ __version__ = "0.8.4"
hanzo_mcp/bridge.py CHANGED
@@ -4,21 +4,21 @@ This module provides MCP server functionality that allows Claude instances
4
4
  to communicate with each other, enabling peer-to-peer agent networks.
5
5
  """
6
6
 
7
- import argparse
8
- import asyncio
9
- import json
10
- import logging
11
7
  import os
12
8
  import sys
9
+ import json
10
+ import asyncio
11
+ import logging
12
+ import argparse
13
13
  from typing import Any, Dict, List, Optional
14
- from dataclasses import dataclass, asdict
14
+ from dataclasses import asdict, dataclass
15
15
 
16
16
  import mcp.server.fastmcp as mcp
17
17
  from mcp import tool
18
- from mcp.server.fastmcp import FastMCP
19
- from mcp.server.models import InitializationOptions
18
+ from mcp.types import INTERNAL_ERROR, Tool, TextContent
20
19
  from mcp.server.stdio import stdio_server
21
- from mcp.types import TextContent, Tool, INTERNAL_ERROR
20
+ from mcp.server.models import InitializationOptions
21
+ from mcp.server.fastmcp import FastMCP
22
22
 
23
23
  logger = logging.getLogger(__name__)
24
24
 
@@ -26,213 +26,212 @@ logger = logging.getLogger(__name__)
26
26
  @dataclass
27
27
  class BridgeConfig:
28
28
  """Configuration for MCP bridge."""
29
+
29
30
  target_port: int
30
31
  instance_id: int
31
32
  role: str
32
33
  source_instance: Optional[int] = None
33
34
  target_instance: Optional[int] = None
34
-
35
-
35
+
36
+
36
37
  class ClaudeBridge(FastMCP):
37
38
  """MCP Bridge server for Claude-to-Claude communication."""
38
-
39
+
39
40
  def __init__(self, config: BridgeConfig):
40
41
  """Initialize the bridge.
41
-
42
+
42
43
  Args:
43
44
  config: Bridge configuration
44
45
  """
45
46
  # Set server name based on target instance
46
47
  super().__init__(f"claude_instance_{config.instance_id}")
47
-
48
+
48
49
  self.config = config
49
50
  self.conversation_history: List[Dict[str, Any]] = []
50
51
  self.shared_context: Dict[str, Any] = {}
51
-
52
+
52
53
  # Register tools
53
54
  self._register_tools()
54
-
55
+
55
56
  def _register_tools(self):
56
57
  """Register MCP tools for inter-Claude communication."""
57
-
58
+
58
59
  @self.tool()
59
60
  async def chat_with_claude(message: str, context: Optional[str] = None) -> str:
60
61
  """Chat with another Claude instance.
61
-
62
+
62
63
  Args:
63
64
  message: Message to send to the other Claude
64
65
  context: Optional context to provide
65
-
66
+
66
67
  Returns:
67
68
  Response from the other Claude instance
68
69
  """
69
70
  logger.info(f"Bridge {self.config.instance_id}: Received chat request")
70
-
71
+
71
72
  # 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
-
73
+ self.conversation_history.append(
74
+ {
75
+ "from": self.config.source_instance,
76
+ "to": self.config.target_instance,
77
+ "message": message,
78
+ "context": context,
79
+ }
80
+ )
81
+
79
82
  # Simulate response (in production, this would make actual API call)
80
83
  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
-
84
+
85
+ self.conversation_history.append(
86
+ {
87
+ "from": self.config.target_instance,
88
+ "to": self.config.source_instance,
89
+ "response": response,
90
+ }
91
+ )
92
+
88
93
  return response
89
-
94
+
90
95
  @self.tool()
91
96
  async def ask_claude_to_review(
92
- code: str,
93
- description: str,
94
- focus_areas: Optional[List[str]] = None
97
+ code: str, description: str, focus_areas: Optional[List[str]] = None
95
98
  ) -> Dict[str, Any]:
96
99
  """Ask another Claude to review code.
97
-
100
+
98
101
  Args:
99
102
  code: Code to review
100
103
  description: Description of what the code does
101
104
  focus_areas: Specific areas to focus on (e.g., ["security", "performance"])
102
-
105
+
103
106
  Returns:
104
107
  Review feedback from the other Claude
105
108
  """
106
109
  logger.info(f"Bridge {self.config.instance_id}: Code review request")
107
-
110
+
108
111
  review_prompt = self._build_review_prompt(code, description, focus_areas)
109
112
  review = await self._forward_to_claude(review_prompt)
110
-
113
+
111
114
  return {
112
115
  "reviewer": f"claude_{self.config.instance_id}",
113
116
  "role": self.config.role,
114
117
  "feedback": review,
115
- "focus_areas": focus_areas or ["general"]
118
+ "focus_areas": focus_areas or ["general"],
116
119
  }
117
-
120
+
118
121
  @self.tool()
119
122
  async def delegate_to_claude(
120
- task: str,
121
- requirements: List[str],
122
- constraints: Optional[List[str]] = None
123
+ task: str, requirements: List[str], constraints: Optional[List[str]] = None
123
124
  ) -> Dict[str, Any]:
124
125
  """Delegate a task to another Claude instance.
125
-
126
+
126
127
  Args:
127
128
  task: Task description
128
129
  requirements: List of requirements
129
130
  constraints: Optional constraints
130
-
131
+
131
132
  Returns:
132
133
  Task completion result from the other Claude
133
134
  """
134
135
  logger.info(f"Bridge {self.config.instance_id}: Task delegation")
135
-
136
- delegation_prompt = self._build_delegation_prompt(task, requirements, constraints)
136
+
137
+ delegation_prompt = self._build_delegation_prompt(
138
+ task, requirements, constraints
139
+ )
137
140
  result = await self._forward_to_claude(delegation_prompt)
138
-
141
+
139
142
  return {
140
143
  "delegated_to": f"claude_{self.config.instance_id}",
141
144
  "role": self.config.role,
142
145
  "task": task,
143
146
  "result": result,
144
- "status": "completed"
147
+ "status": "completed",
145
148
  }
146
-
149
+
147
150
  @self.tool()
148
151
  async def get_claude_opinion(
149
152
  question: str,
150
153
  options: Optional[List[str]] = None,
151
- criteria: Optional[List[str]] = None
154
+ criteria: Optional[List[str]] = None,
152
155
  ) -> Dict[str, Any]:
153
156
  """Get another Claude's opinion on a decision.
154
-
157
+
155
158
  Args:
156
159
  question: The question or decision to get opinion on
157
160
  options: Optional list of options to choose from
158
161
  criteria: Optional evaluation criteria
159
-
162
+
160
163
  Returns:
161
164
  Opinion and reasoning from the other Claude
162
165
  """
163
166
  logger.info(f"Bridge {self.config.instance_id}: Opinion request")
164
-
167
+
165
168
  opinion_prompt = self._build_opinion_prompt(question, options, criteria)
166
169
  opinion = await self._forward_to_claude(opinion_prompt)
167
-
170
+
168
171
  return {
169
172
  "advisor": f"claude_{self.config.instance_id}",
170
173
  "role": self.config.role,
171
174
  "question": question,
172
175
  "opinion": opinion,
173
176
  "options_considered": options,
174
- "criteria_used": criteria
177
+ "criteria_used": criteria,
175
178
  }
176
-
179
+
177
180
  @self.tool()
178
181
  async def share_context_with_claude(
179
- key: str,
180
- value: Any,
181
- description: Optional[str] = None
182
+ key: str, value: Any, description: Optional[str] = None
182
183
  ) -> bool:
183
184
  """Share context with another Claude instance.
184
-
185
+
185
186
  Args:
186
187
  key: Context key
187
188
  value: Context value
188
189
  description: Optional description of the context
189
-
190
+
190
191
  Returns:
191
192
  Success status
192
193
  """
193
194
  logger.info(f"Bridge {self.config.instance_id}: Sharing context '{key}'")
194
-
195
+
195
196
  self.shared_context[key] = {
196
197
  "value": value,
197
198
  "description": description,
198
199
  "shared_by": self.config.source_instance,
199
- "shared_with": self.config.target_instance
200
+ "shared_with": self.config.target_instance,
200
201
  }
201
-
202
+
202
203
  return True
203
-
204
+
204
205
  @self.tool()
205
206
  async def get_shared_context(key: Optional[str] = None) -> Dict[str, Any]:
206
207
  """Get shared context from Claude network.
207
-
208
+
208
209
  Args:
209
210
  key: Optional specific key to retrieve
210
-
211
+
211
212
  Returns:
212
213
  Shared context data
213
214
  """
214
215
  if key:
215
216
  return self.shared_context.get(key, {})
216
217
  return self.shared_context
217
-
218
+
218
219
  @self.tool()
219
220
  async def brainstorm_with_claude(
220
- topic: str,
221
- num_ideas: int = 5,
222
- constraints: Optional[List[str]] = None
221
+ topic: str, num_ideas: int = 5, constraints: Optional[List[str]] = None
223
222
  ) -> List[str]:
224
223
  """Brainstorm ideas with another Claude.
225
-
224
+
226
225
  Args:
227
226
  topic: Topic to brainstorm about
228
227
  num_ideas: Number of ideas to generate
229
228
  constraints: Optional constraints
230
-
229
+
231
230
  Returns:
232
231
  List of brainstormed ideas
233
232
  """
234
233
  logger.info(f"Bridge {self.config.instance_id}: Brainstorming request")
235
-
234
+
236
235
  brainstorm_prompt = f"""
237
236
  Please brainstorm {num_ideas} ideas about: {topic}
238
237
 
@@ -240,19 +239,19 @@ class ClaudeBridge(FastMCP):
240
239
 
241
240
  Provide creative and practical ideas.
242
241
  """
243
-
242
+
244
243
  response = await self._forward_to_claude(brainstorm_prompt)
245
-
244
+
246
245
  # Parse response into list (simplified)
247
246
  ideas = response.split("\n")
248
247
  ideas = [idea.strip() for idea in ideas if idea.strip()]
249
-
248
+
250
249
  return ideas[:num_ideas]
251
-
250
+
252
251
  @self.tool()
253
252
  async def get_claude_status() -> Dict[str, Any]:
254
253
  """Get status of the connected Claude instance.
255
-
254
+
256
255
  Returns:
257
256
  Status information
258
257
  """
@@ -261,11 +260,12 @@ class ClaudeBridge(FastMCP):
261
260
  "role": self.config.role,
262
261
  "status": "available",
263
262
  "conversation_count": len(self.conversation_history),
264
- "shared_context_keys": list(self.shared_context.keys())
263
+ "shared_context_keys": list(self.shared_context.keys()),
265
264
  }
266
-
267
- def _build_review_prompt(self, code: str, description: str,
268
- focus_areas: Optional[List[str]]) -> str:
265
+
266
+ def _build_review_prompt(
267
+ self, code: str, description: str, focus_areas: Optional[List[str]]
268
+ ) -> str:
269
269
  """Build a code review prompt."""
270
270
  prompt = f"""
271
271
  Please review the following code:
@@ -277,10 +277,10 @@ class ClaudeBridge(FastMCP):
277
277
  {code}
278
278
  ```
279
279
  """
280
-
280
+
281
281
  if focus_areas:
282
282
  prompt += f"\n\nPlease focus particularly on: {', '.join(focus_areas)}"
283
-
283
+
284
284
  prompt += """
285
285
 
286
286
  Provide constructive feedback on:
@@ -290,11 +290,12 @@ class ClaudeBridge(FastMCP):
290
290
  4. Security concerns
291
291
  5. Suggestions for improvement
292
292
  """
293
-
293
+
294
294
  return prompt
295
-
296
- def _build_delegation_prompt(self, task: str, requirements: List[str],
297
- constraints: Optional[List[str]]) -> str:
295
+
296
+ def _build_delegation_prompt(
297
+ self, task: str, requirements: List[str], constraints: Optional[List[str]]
298
+ ) -> str:
298
299
  """Build a task delegation prompt."""
299
300
  prompt = f"""
300
301
  Please complete the following task:
@@ -304,54 +305,57 @@ class ClaudeBridge(FastMCP):
304
305
  Requirements:
305
306
  {chr(10).join(f"- {req}" for req in requirements)}
306
307
  """
307
-
308
+
308
309
  if constraints:
309
310
  prompt += f"""
310
311
 
311
312
  Constraints:
312
313
  {chr(10).join(f"- {con}" for con in constraints)}
313
314
  """
314
-
315
+
315
316
  prompt += """
316
317
 
317
318
  Provide a complete solution that meets all requirements.
318
319
  """
319
-
320
+
320
321
  return prompt
321
-
322
- def _build_opinion_prompt(self, question: str, options: Optional[List[str]],
323
- criteria: Optional[List[str]]) -> str:
322
+
323
+ def _build_opinion_prompt(
324
+ self, question: str, options: Optional[List[str]], criteria: Optional[List[str]]
325
+ ) -> str:
324
326
  """Build an opinion request prompt."""
325
327
  prompt = f"""
326
328
  I need your opinion on the following:
327
329
 
328
330
  Question: {question}
329
331
  """
330
-
332
+
331
333
  if options:
332
334
  prompt += f"""
333
335
 
334
336
  Options to consider:
335
337
  {chr(10).join(f"{i+1}. {opt}" for i, opt in enumerate(options))}
336
338
  """
337
-
339
+
338
340
  if criteria:
339
341
  prompt += f"""
340
342
 
341
343
  Please evaluate based on these criteria:
342
344
  {chr(10).join(f"- {crit}" for crit in criteria)}
343
345
  """
344
-
346
+
345
347
  prompt += """
346
348
 
347
349
  Provide your recommendation with clear reasoning.
348
350
  """
349
-
351
+
350
352
  return prompt
351
-
352
- async def _forward_to_claude(self, prompt: str, context: Optional[str] = None) -> str:
353
+
354
+ async def _forward_to_claude(
355
+ self, prompt: str, context: Optional[str] = None
356
+ ) -> str:
353
357
  """Forward a request to the target Claude instance.
354
-
358
+
355
359
  In production, this would make an actual API call to the Claude instance.
356
360
  For now, it returns a simulated response.
357
361
  """
@@ -359,16 +363,18 @@ class ClaudeBridge(FastMCP):
359
363
  full_prompt = prompt
360
364
  if context:
361
365
  full_prompt = f"Context: {context}\n\n{prompt}"
362
-
366
+
363
367
  # Log the forwarding
364
- logger.info(f"Forwarding from instance {self.config.source_instance} to {self.config.target_instance}")
368
+ logger.info(
369
+ f"Forwarding from instance {self.config.source_instance} to {self.config.target_instance}"
370
+ )
365
371
  logger.debug(f"Prompt: {full_prompt[:200]}...")
366
-
372
+
367
373
  # In production, this would:
368
374
  # 1. Connect to the target Claude instance API
369
375
  # 2. Send the prompt
370
376
  # 3. Receive and return the response
371
-
377
+
372
378
  # Simulated response based on role
373
379
  if self.config.role.startswith("critic"):
374
380
  return f"""
@@ -402,33 +408,33 @@ class ClaudeBridge(FastMCP):
402
408
 
403
409
  async def run_bridge_server(config: BridgeConfig):
404
410
  """Run the MCP bridge server.
405
-
411
+
406
412
  Args:
407
413
  config: Bridge configuration
408
414
  """
409
415
  # Configure logging
410
416
  logging.basicConfig(
411
417
  level=logging.INFO,
412
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
418
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
413
419
  )
414
-
420
+
415
421
  logger.info(f"Starting MCP Bridge for Claude instance {config.instance_id}")
416
422
  logger.info(f"Role: {config.role}")
417
423
  logger.info(f"Target port: {config.target_port}")
418
-
424
+
419
425
  # Create and run the bridge
420
426
  bridge = ClaudeBridge(config)
421
-
427
+
422
428
  # Run the stdio server
423
429
  async with stdio_server() as (read_stream, write_stream):
424
430
  await bridge.run(
425
431
  read_stream=read_stream,
426
432
  write_stream=write_stream,
427
- InitializationOptions(
433
+ initialization_options=InitializationOptions(
428
434
  server_name=bridge.name,
429
435
  server_version="1.0.0",
430
- capabilities=bridge.get_capabilities()
431
- )
436
+ capabilities=bridge.get_capabilities(),
437
+ ),
432
438
  )
433
439
 
434
440
 
@@ -441,38 +447,38 @@ def main():
441
447
  "--target-port",
442
448
  type=int,
443
449
  required=True,
444
- help="Port of the target Claude instance"
450
+ help="Port of the target Claude instance",
445
451
  )
446
452
  parser.add_argument(
447
453
  "--instance-id",
448
454
  type=int,
449
455
  required=True,
450
- help="ID of the target Claude instance"
456
+ help="ID of the target Claude instance",
451
457
  )
452
458
  parser.add_argument(
453
459
  "--role",
454
460
  type=str,
455
461
  required=True,
456
- help="Role of the target instance (primary, critic_1, etc.)"
462
+ help="Role of the target instance (primary, critic_1, etc.)",
457
463
  )
458
-
464
+
459
465
  args = parser.parse_args()
460
-
466
+
461
467
  # Get source/target from environment
462
468
  source_instance = int(os.environ.get("SOURCE_INSTANCE", "0"))
463
469
  target_instance = int(os.environ.get("TARGET_INSTANCE", args.instance_id))
464
-
470
+
465
471
  config = BridgeConfig(
466
472
  target_port=args.target_port,
467
473
  instance_id=args.instance_id,
468
474
  role=args.role,
469
475
  source_instance=source_instance,
470
- target_instance=target_instance
476
+ target_instance=target_instance,
471
477
  )
472
-
478
+
473
479
  # Run the bridge
474
480
  asyncio.run(run_bridge_server(config))
475
481
 
476
482
 
477
483
  if __name__ == "__main__":
478
- main()
484
+ main()