claude-mpm 3.9.7__py3-none-any.whl → 3.9.9__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.
Files changed (54) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/base_agent.json +1 -1
  3. claude_mpm/agents/templates/ticketing.json +1 -1
  4. claude_mpm/cli/__init__.py +3 -1
  5. claude_mpm/cli/commands/__init__.py +3 -1
  6. claude_mpm/cli/commands/cleanup.py +21 -1
  7. claude_mpm/cli/commands/mcp.py +821 -0
  8. claude_mpm/cli/parser.py +148 -1
  9. claude_mpm/config/memory_guardian_config.py +325 -0
  10. claude_mpm/constants.py +13 -0
  11. claude_mpm/hooks/claude_hooks/hook_handler.py +76 -19
  12. claude_mpm/models/state_models.py +433 -0
  13. claude_mpm/services/__init__.py +28 -0
  14. claude_mpm/services/communication/__init__.py +2 -2
  15. claude_mpm/services/communication/socketio.py +18 -16
  16. claude_mpm/services/infrastructure/__init__.py +4 -1
  17. claude_mpm/services/infrastructure/logging.py +3 -3
  18. claude_mpm/services/infrastructure/memory_guardian.py +770 -0
  19. claude_mpm/services/mcp_gateway/__init__.py +138 -0
  20. claude_mpm/services/mcp_gateway/config/__init__.py +17 -0
  21. claude_mpm/services/mcp_gateway/config/config_loader.py +232 -0
  22. claude_mpm/services/mcp_gateway/config/config_schema.py +234 -0
  23. claude_mpm/services/mcp_gateway/config/configuration.py +371 -0
  24. claude_mpm/services/mcp_gateway/core/__init__.py +51 -0
  25. claude_mpm/services/mcp_gateway/core/base.py +315 -0
  26. claude_mpm/services/mcp_gateway/core/exceptions.py +239 -0
  27. claude_mpm/services/mcp_gateway/core/interfaces.py +476 -0
  28. claude_mpm/services/mcp_gateway/main.py +326 -0
  29. claude_mpm/services/mcp_gateway/registry/__init__.py +12 -0
  30. claude_mpm/services/mcp_gateway/registry/service_registry.py +397 -0
  31. claude_mpm/services/mcp_gateway/registry/tool_registry.py +477 -0
  32. claude_mpm/services/mcp_gateway/server/__init__.py +15 -0
  33. claude_mpm/services/mcp_gateway/server/mcp_server.py +430 -0
  34. claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +444 -0
  35. claude_mpm/services/mcp_gateway/server/stdio_handler.py +373 -0
  36. claude_mpm/services/mcp_gateway/tools/__init__.py +22 -0
  37. claude_mpm/services/mcp_gateway/tools/base_adapter.py +497 -0
  38. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +729 -0
  39. claude_mpm/services/mcp_gateway/tools/hello_world.py +551 -0
  40. claude_mpm/utils/file_utils.py +293 -0
  41. claude_mpm/utils/platform_memory.py +524 -0
  42. claude_mpm/utils/subprocess_utils.py +305 -0
  43. {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.9.dist-info}/METADATA +4 -1
  44. {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.9.dist-info}/RECORD +49 -26
  45. claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
  46. claude_mpm/agents/templates/.claude-mpm/memories/engineer_agent.md +0 -39
  47. claude_mpm/agents/templates/.claude-mpm/memories/qa_agent.md +0 -38
  48. claude_mpm/agents/templates/.claude-mpm/memories/research_agent.md +0 -39
  49. claude_mpm/agents/templates/.claude-mpm/memories/version_control_agent.md +0 -38
  50. /claude_mpm/agents/templates/{research_memory_efficient.json → backup/research_memory_efficient.json} +0 -0
  51. {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.9.dist-info}/WHEEL +0 -0
  52. {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.9.dist-info}/entry_points.txt +0 -0
  53. {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.9.dist-info}/licenses/LICENSE +0 -0
  54. {claude_mpm-3.9.7.dist-info → claude_mpm-3.9.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,497 @@
1
+ """
2
+ Base Tool Adapter and Example Implementations
3
+ ==============================================
4
+
5
+ Base class for MCP tool adapters and example implementations.
6
+
7
+ Part of ISS-0035: MCP Server Implementation - Core Server and Tool Registry
8
+ """
9
+
10
+ from abc import ABC
11
+ from typing import Any, Dict, Optional
12
+ import json
13
+ import traceback
14
+ from datetime import datetime
15
+
16
+ from claude_mpm.services.mcp_gateway.core.interfaces import (
17
+ IMCPToolAdapter,
18
+ MCPToolDefinition,
19
+ MCPToolInvocation,
20
+ MCPToolResult,
21
+ )
22
+ from claude_mpm.services.mcp_gateway.core.base import BaseMCPService
23
+
24
+
25
+ class BaseToolAdapter(BaseMCPService, IMCPToolAdapter, ABC):
26
+ """
27
+ Base class for MCP tool adapters.
28
+
29
+ WHY: This base class provides common functionality for all tool adapters,
30
+ including parameter validation, error handling, and metrics tracking.
31
+ Concrete tool implementations should extend this class.
32
+
33
+ DESIGN DECISIONS:
34
+ - Provide default parameter validation using JSON Schema
35
+ - Include standard error handling and logging
36
+ - Track execution metrics for monitoring
37
+ - Support both sync and async tool implementations
38
+ """
39
+
40
+ def __init__(self, tool_definition: MCPToolDefinition):
41
+ """
42
+ Initialize the base tool adapter.
43
+
44
+ Args:
45
+ tool_definition: Tool definition with schema and metadata
46
+ """
47
+ super().__init__(f"Tool-{tool_definition.name}")
48
+ self._definition = tool_definition
49
+ self._initialized = False
50
+
51
+ # Metrics
52
+ self._metrics = {
53
+ "invocations": 0,
54
+ "successes": 0,
55
+ "failures": 0,
56
+ "total_execution_time": 0.0,
57
+ "average_execution_time": 0.0,
58
+ "last_invocation": None,
59
+ "last_error": None
60
+ }
61
+
62
+ def get_definition(self) -> MCPToolDefinition:
63
+ """
64
+ Get the tool definition.
65
+
66
+ Returns:
67
+ Tool definition with schema and metadata
68
+ """
69
+ return self._definition
70
+
71
+ def validate_parameters(self, parameters: Dict[str, Any]) -> bool:
72
+ """
73
+ Validate tool parameters against schema.
74
+
75
+ Default implementation performs basic JSON Schema validation.
76
+ Override for custom validation logic.
77
+
78
+ Args:
79
+ parameters: Parameters to validate
80
+
81
+ Returns:
82
+ True if parameters are valid
83
+ """
84
+ try:
85
+ # Get required parameters from schema
86
+ schema = self._definition.input_schema
87
+ required = schema.get("required", [])
88
+ properties = schema.get("properties", {})
89
+
90
+ # Check required parameters
91
+ for param in required:
92
+ if param not in parameters:
93
+ self.log_error(f"Missing required parameter: {param}")
94
+ return False
95
+
96
+ # Check parameter types (basic validation)
97
+ for param_name, param_value in parameters.items():
98
+ if param_name in properties:
99
+ expected_type = properties[param_name].get("type")
100
+ if expected_type:
101
+ if not self._validate_type(param_value, expected_type):
102
+ self.log_error(f"Invalid type for parameter {param_name}: expected {expected_type}")
103
+ return False
104
+
105
+ return True
106
+
107
+ except Exception as e:
108
+ self.log_error(f"Error validating parameters: {e}")
109
+ return False
110
+
111
+ def _validate_type(self, value: Any, expected_type: str) -> bool:
112
+ """
113
+ Validate a value against an expected JSON Schema type.
114
+
115
+ Args:
116
+ value: Value to validate
117
+ expected_type: Expected type (string, number, boolean, array, object)
118
+
119
+ Returns:
120
+ True if type matches
121
+ """
122
+ type_map = {
123
+ "string": str,
124
+ "number": (int, float),
125
+ "boolean": bool,
126
+ "array": list,
127
+ "object": dict,
128
+ "null": type(None)
129
+ }
130
+
131
+ expected_python_type = type_map.get(expected_type)
132
+ if expected_python_type:
133
+ return isinstance(value, expected_python_type)
134
+
135
+ return True # Unknown type, allow it
136
+
137
+ async def initialize(self) -> bool:
138
+ """
139
+ Initialize the tool adapter.
140
+
141
+ Default implementation marks as initialized.
142
+ Override for custom initialization logic.
143
+
144
+ Returns:
145
+ True if initialization successful
146
+ """
147
+ try:
148
+ self.log_info(f"Initializing tool: {self._definition.name}")
149
+ self._initialized = True
150
+ return True
151
+
152
+ except Exception as e:
153
+ self.log_error(f"Failed to initialize tool: {e}")
154
+ return False
155
+
156
+ async def shutdown(self) -> None:
157
+ """
158
+ Shutdown the tool adapter and clean up resources.
159
+
160
+ Default implementation marks as not initialized.
161
+ Override for custom cleanup logic.
162
+ """
163
+ try:
164
+ self.log_info(f"Shutting down tool: {self._definition.name}")
165
+ self._initialized = False
166
+
167
+ except Exception as e:
168
+ self.log_error(f"Error during tool shutdown: {e}")
169
+
170
+ def get_metrics(self) -> Dict[str, Any]:
171
+ """
172
+ Get tool metrics.
173
+
174
+ Returns:
175
+ Metrics dictionary
176
+ """
177
+ return self._metrics.copy()
178
+
179
+ def _update_metrics(self, success: bool, execution_time: float) -> None:
180
+ """
181
+ Update tool metrics after invocation.
182
+
183
+ Args:
184
+ success: Whether invocation was successful
185
+ execution_time: Execution time in seconds
186
+ """
187
+ self._metrics["invocations"] += 1
188
+
189
+ if success:
190
+ self._metrics["successes"] += 1
191
+ else:
192
+ self._metrics["failures"] += 1
193
+
194
+ self._metrics["total_execution_time"] += execution_time
195
+ self._metrics["average_execution_time"] = (
196
+ self._metrics["total_execution_time"] / self._metrics["invocations"]
197
+ )
198
+ self._metrics["last_invocation"] = datetime.now().isoformat()
199
+
200
+
201
+ class EchoToolAdapter(BaseToolAdapter):
202
+ """
203
+ Example tool adapter that echoes input back.
204
+
205
+ This is a simple example showing how to implement a concrete tool adapter.
206
+ """
207
+
208
+ def __init__(self):
209
+ """Initialize the echo tool."""
210
+ definition = MCPToolDefinition(
211
+ name="echo",
212
+ description="Echoes the input message back to the user",
213
+ input_schema={
214
+ "type": "object",
215
+ "properties": {
216
+ "message": {
217
+ "type": "string",
218
+ "description": "The message to echo"
219
+ },
220
+ "uppercase": {
221
+ "type": "boolean",
222
+ "description": "Whether to convert to uppercase",
223
+ "default": False
224
+ }
225
+ },
226
+ "required": ["message"]
227
+ }
228
+ )
229
+ super().__init__(definition)
230
+
231
+ async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
232
+ """
233
+ Invoke the echo tool.
234
+
235
+ Args:
236
+ invocation: Tool invocation request
237
+
238
+ Returns:
239
+ Tool execution result with echoed message
240
+ """
241
+ start_time = datetime.now()
242
+
243
+ try:
244
+ # Get parameters
245
+ message = invocation.parameters.get("message", "")
246
+ uppercase = invocation.parameters.get("uppercase", False)
247
+
248
+ # Process message
249
+ result = message.upper() if uppercase else message
250
+
251
+ # Calculate execution time
252
+ execution_time = (datetime.now() - start_time).total_seconds()
253
+
254
+ # Update metrics
255
+ self._update_metrics(True, execution_time)
256
+
257
+ return MCPToolResult(
258
+ success=True,
259
+ data=result,
260
+ execution_time=execution_time,
261
+ metadata={"tool": "echo", "length": len(result)}
262
+ )
263
+
264
+ except Exception as e:
265
+ execution_time = (datetime.now() - start_time).total_seconds()
266
+ self._update_metrics(False, execution_time)
267
+ self._metrics["last_error"] = str(e)
268
+
269
+ return MCPToolResult(
270
+ success=False,
271
+ error=f"Echo tool failed: {str(e)}",
272
+ execution_time=execution_time
273
+ )
274
+
275
+
276
+ class CalculatorToolAdapter(BaseToolAdapter):
277
+ """
278
+ Example calculator tool adapter.
279
+
280
+ Demonstrates a more complex tool with multiple operations.
281
+ """
282
+
283
+ def __init__(self):
284
+ """Initialize the calculator tool."""
285
+ definition = MCPToolDefinition(
286
+ name="calculator",
287
+ description="Performs basic mathematical calculations",
288
+ input_schema={
289
+ "type": "object",
290
+ "properties": {
291
+ "operation": {
292
+ "type": "string",
293
+ "enum": ["add", "subtract", "multiply", "divide"],
294
+ "description": "The mathematical operation to perform"
295
+ },
296
+ "a": {
297
+ "type": "number",
298
+ "description": "First operand"
299
+ },
300
+ "b": {
301
+ "type": "number",
302
+ "description": "Second operand"
303
+ }
304
+ },
305
+ "required": ["operation", "a", "b"]
306
+ },
307
+ output_schema={
308
+ "type": "object",
309
+ "properties": {
310
+ "result": {
311
+ "type": "number",
312
+ "description": "The calculation result"
313
+ },
314
+ "expression": {
315
+ "type": "string",
316
+ "description": "The mathematical expression"
317
+ }
318
+ }
319
+ }
320
+ )
321
+ super().__init__(definition)
322
+
323
+ async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
324
+ """
325
+ Invoke the calculator tool.
326
+
327
+ Args:
328
+ invocation: Tool invocation request
329
+
330
+ Returns:
331
+ Tool execution result with calculation
332
+ """
333
+ start_time = datetime.now()
334
+
335
+ try:
336
+ # Get parameters
337
+ operation = invocation.parameters["operation"]
338
+ a = invocation.parameters["a"]
339
+ b = invocation.parameters["b"]
340
+
341
+ # Perform calculation
342
+ if operation == "add":
343
+ result = a + b
344
+ expression = f"{a} + {b} = {result}"
345
+ elif operation == "subtract":
346
+ result = a - b
347
+ expression = f"{a} - {b} = {result}"
348
+ elif operation == "multiply":
349
+ result = a * b
350
+ expression = f"{a} * {b} = {result}"
351
+ elif operation == "divide":
352
+ if b == 0:
353
+ raise ValueError("Division by zero")
354
+ result = a / b
355
+ expression = f"{a} / {b} = {result}"
356
+ else:
357
+ raise ValueError(f"Unknown operation: {operation}")
358
+
359
+ # Calculate execution time
360
+ execution_time = (datetime.now() - start_time).total_seconds()
361
+
362
+ # Update metrics
363
+ self._update_metrics(True, execution_time)
364
+
365
+ return MCPToolResult(
366
+ success=True,
367
+ data={
368
+ "result": result,
369
+ "expression": expression
370
+ },
371
+ execution_time=execution_time,
372
+ metadata={"tool": "calculator", "operation": operation}
373
+ )
374
+
375
+ except Exception as e:
376
+ execution_time = (datetime.now() - start_time).total_seconds()
377
+ self._update_metrics(False, execution_time)
378
+ self._metrics["last_error"] = str(e)
379
+
380
+ return MCPToolResult(
381
+ success=False,
382
+ error=f"Calculator tool failed: {str(e)}",
383
+ execution_time=execution_time
384
+ )
385
+
386
+
387
+ class SystemInfoToolAdapter(BaseToolAdapter):
388
+ """
389
+ Example system information tool adapter.
390
+
391
+ Demonstrates async operations and system interaction.
392
+ """
393
+
394
+ def __init__(self):
395
+ """Initialize the system info tool."""
396
+ definition = MCPToolDefinition(
397
+ name="system_info",
398
+ description="Provides system information",
399
+ input_schema={
400
+ "type": "object",
401
+ "properties": {
402
+ "info_type": {
403
+ "type": "string",
404
+ "enum": ["platform", "memory", "cpu", "time"],
405
+ "description": "Type of system information to retrieve"
406
+ }
407
+ },
408
+ "required": ["info_type"]
409
+ }
410
+ )
411
+ super().__init__(definition)
412
+
413
+ async def invoke(self, invocation: MCPToolInvocation) -> MCPToolResult:
414
+ """
415
+ Invoke the system info tool.
416
+
417
+ Args:
418
+ invocation: Tool invocation request
419
+
420
+ Returns:
421
+ Tool execution result with system information
422
+ """
423
+ start_time = datetime.now()
424
+
425
+ try:
426
+ import platform
427
+ import psutil
428
+ from datetime import datetime
429
+
430
+ info_type = invocation.parameters["info_type"]
431
+
432
+ if info_type == "platform":
433
+ result = {
434
+ "system": platform.system(),
435
+ "release": platform.release(),
436
+ "version": platform.version(),
437
+ "machine": platform.machine(),
438
+ "processor": platform.processor(),
439
+ "python_version": platform.python_version()
440
+ }
441
+ elif info_type == "memory":
442
+ mem = psutil.virtual_memory()
443
+ result = {
444
+ "total": mem.total,
445
+ "available": mem.available,
446
+ "percent": mem.percent,
447
+ "used": mem.used,
448
+ "free": mem.free
449
+ }
450
+ elif info_type == "cpu":
451
+ result = {
452
+ "count": psutil.cpu_count(),
453
+ "percent": psutil.cpu_percent(interval=0.1),
454
+ "freq": psutil.cpu_freq()._asdict() if psutil.cpu_freq() else None
455
+ }
456
+ elif info_type == "time":
457
+ result = {
458
+ "current": datetime.now().isoformat(),
459
+ "timestamp": datetime.now().timestamp(),
460
+ "timezone": str(datetime.now().astimezone().tzinfo)
461
+ }
462
+ else:
463
+ raise ValueError(f"Unknown info type: {info_type}")
464
+
465
+ # Calculate execution time
466
+ execution_time = (datetime.now() - start_time).total_seconds()
467
+
468
+ # Update metrics
469
+ self._update_metrics(True, execution_time)
470
+
471
+ return MCPToolResult(
472
+ success=True,
473
+ data=result,
474
+ execution_time=execution_time,
475
+ metadata={"tool": "system_info", "info_type": info_type}
476
+ )
477
+
478
+ except ImportError as e:
479
+ # Handle missing psutil dependency gracefully
480
+ execution_time = (datetime.now() - start_time).total_seconds()
481
+ self._update_metrics(False, execution_time)
482
+
483
+ return MCPToolResult(
484
+ success=False,
485
+ error=f"System info tool requires psutil: {str(e)}",
486
+ execution_time=execution_time
487
+ )
488
+ except Exception as e:
489
+ execution_time = (datetime.now() - start_time).total_seconds()
490
+ self._update_metrics(False, execution_time)
491
+ self._metrics["last_error"] = str(e)
492
+
493
+ return MCPToolResult(
494
+ success=False,
495
+ error=f"System info tool failed: {str(e)}",
496
+ execution_time=execution_time
497
+ )