claude-mpm 1.0.0__py3-none-any.whl → 1.1.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.
Files changed (38) hide show
  1. claude_mpm/_version.py +3 -2
  2. claude_mpm/agents/__init__.py +2 -2
  3. claude_mpm/agents/agent-template.yaml +83 -0
  4. claude_mpm/agents/agent_loader.py +66 -90
  5. claude_mpm/agents/base_agent_loader.py +10 -15
  6. claude_mpm/cli.py +41 -47
  7. claude_mpm/cli_enhancements.py +297 -0
  8. claude_mpm/core/factories.py +1 -46
  9. claude_mpm/core/service_registry.py +0 -8
  10. claude_mpm/core/simple_runner.py +43 -0
  11. claude_mpm/generators/__init__.py +5 -0
  12. claude_mpm/generators/agent_profile_generator.py +137 -0
  13. claude_mpm/hooks/README.md +75 -221
  14. claude_mpm/hooks/builtin/mpm_command_hook.py +125 -0
  15. claude_mpm/hooks/claude_hooks/__init__.py +5 -0
  16. claude_mpm/hooks/claude_hooks/hook_handler.py +399 -0
  17. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +47 -0
  18. claude_mpm/hooks/validation_hooks.py +181 -0
  19. claude_mpm/services/agent_management_service.py +4 -4
  20. claude_mpm/services/agent_profile_loader.py +1 -1
  21. claude_mpm/services/agent_registry.py +0 -1
  22. claude_mpm/services/base_agent_manager.py +3 -3
  23. claude_mpm/utils/error_handler.py +247 -0
  24. claude_mpm/validation/__init__.py +5 -0
  25. claude_mpm/validation/agent_validator.py +175 -0
  26. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/METADATA +44 -7
  27. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/RECORD +30 -26
  28. claude_mpm/config/hook_config.py +0 -42
  29. claude_mpm/hooks/hook_client.py +0 -264
  30. claude_mpm/hooks/hook_runner.py +0 -370
  31. claude_mpm/hooks/json_rpc_executor.py +0 -259
  32. claude_mpm/hooks/json_rpc_hook_client.py +0 -319
  33. claude_mpm/services/hook_service.py +0 -388
  34. claude_mpm/services/hook_service_manager.py +0 -223
  35. claude_mpm/services/json_rpc_hook_manager.py +0 -92
  36. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/WHEEL +0 -0
  37. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/entry_points.txt +0 -0
  38. {claude_mpm-1.0.0.dist-info → claude_mpm-1.1.0.dist-info}/top_level.txt +0 -0
@@ -1,42 +0,0 @@
1
- """Configuration for hook service integration."""
2
-
3
- import os
4
- from pathlib import Path
5
-
6
-
7
- class HookConfig:
8
- """Hook service configuration."""
9
-
10
- # Service settings
11
- DEFAULT_PORT = 8080
12
- PORT_RANGE_START = 8080
13
- PORT_RANGE_END = 8090
14
-
15
- # Timeouts
16
- SERVICE_START_TIMEOUT = 3.0 # seconds
17
- REQUEST_TIMEOUT = 30 # seconds
18
- HEALTH_CHECK_TIMEOUT = 2.0 # seconds
19
-
20
- # Paths
21
- HOOK_SERVICE_LOG_DIR = Path.home() / ".claude-mpm" / "logs"
22
- HOOK_SERVICE_PID_DIR = Path.home() / ".claude-mpm" / "run"
23
-
24
- # Enable/disable hooks by default
25
- HOOKS_ENABLED_BY_DEFAULT = True
26
-
27
- # Hook service endpoints
28
- HEALTH_ENDPOINT = "/health"
29
- SUBMIT_HOOK_ENDPOINT = "/hooks/submit"
30
- PRE_DELEGATION_HOOK_ENDPOINT = "/hooks/pre-delegation"
31
- POST_DELEGATION_HOOK_ENDPOINT = "/hooks/post-delegation"
32
- TICKET_EXTRACTION_HOOK_ENDPOINT = "/hooks/ticket-extraction"
33
-
34
- @classmethod
35
- def get_hook_service_url(cls, port: int) -> str:
36
- """Get the hook service URL for a given port."""
37
- return f"http://localhost:{port}"
38
-
39
- @classmethod
40
- def is_hooks_enabled(cls) -> bool:
41
- """Check if hooks are enabled via environment variable."""
42
- return os.environ.get("CLAUDE_MPM_HOOKS_ENABLED", str(cls.HOOKS_ENABLED_BY_DEFAULT)).lower() in ("true", "1", "yes")
@@ -1,264 +0,0 @@
1
- """Client for interacting with the hook service.
2
-
3
- DEPRECATED: This HTTP-based hook client is deprecated and will be removed in a future release.
4
- Please use the JSON-RPC implementation from claude_mpm.hooks.json_rpc_hook_client instead.
5
- See /docs/hook_system_migration_guide.md for migration instructions.
6
- """
7
-
8
- import json
9
- import logging
10
- import warnings
11
- from typing import Any, Dict, List, Optional
12
- from urllib.parse import urljoin
13
-
14
- import requests
15
- from requests.adapters import HTTPAdapter
16
- from requests.packages.urllib3.util.retry import Retry
17
-
18
- from claude_mpm.hooks.base_hook import HookType
19
- from claude_mpm.core.logger import get_logger
20
-
21
- logger = get_logger(__name__)
22
-
23
-
24
- class HookServiceClient:
25
- """Client for interacting with the centralized hook service.
26
-
27
- DEPRECATED: Use JSONRPCHookClient from claude_mpm.hooks.json_rpc_hook_client instead.
28
- """
29
-
30
- def __init__(self, base_url: str = "http://localhost:5001", timeout: int = 30):
31
- """Initialize hook service client.
32
-
33
- Args:
34
- base_url: Base URL of hook service
35
- timeout: Request timeout in seconds
36
- """
37
- warnings.warn(
38
- "HookServiceClient is deprecated and will be removed in a future release. "
39
- "Please use JSONRPCHookClient from claude_mpm.hooks.json_rpc_hook_client instead. "
40
- "See /docs/hook_system_migration_guide.md for migration instructions.",
41
- DeprecationWarning,
42
- stacklevel=2
43
- )
44
- self.base_url = base_url.rstrip('/')
45
- self.timeout = timeout
46
-
47
- # Setup session with retry logic
48
- self.session = requests.Session()
49
- retry_strategy = Retry(
50
- total=3,
51
- backoff_factor=1,
52
- status_forcelist=[429, 500, 502, 503, 504]
53
- )
54
- adapter = HTTPAdapter(max_retries=retry_strategy)
55
- self.session.mount("http://", adapter)
56
- self.session.mount("https://", adapter)
57
-
58
- def health_check(self) -> Dict[str, Any]:
59
- """Check health of hook service.
60
-
61
- Returns:
62
- Health status dictionary
63
- """
64
- try:
65
- response = self.session.get(
66
- urljoin(self.base_url, '/health'),
67
- timeout=self.timeout
68
- )
69
- response.raise_for_status()
70
- return response.json()
71
- except Exception as e:
72
- logger.error(f"Health check failed: {e}")
73
- return {
74
- 'status': 'unhealthy',
75
- 'error': str(e)
76
- }
77
-
78
- def list_hooks(self) -> Dict[str, List[Dict[str, Any]]]:
79
- """List all registered hooks.
80
-
81
- Returns:
82
- Dictionary mapping hook types to hook info
83
- """
84
- try:
85
- response = self.session.get(
86
- urljoin(self.base_url, '/hooks/list'),
87
- timeout=self.timeout
88
- )
89
- response.raise_for_status()
90
- data = response.json()
91
- return data.get('hooks', {})
92
- except Exception as e:
93
- logger.error(f"Failed to list hooks: {e}")
94
- return {}
95
-
96
- def execute_hook(self, hook_type: HookType, context_data: Dict[str, Any],
97
- metadata: Optional[Dict[str, Any]] = None,
98
- specific_hook: Optional[str] = None) -> List[Dict[str, Any]]:
99
- """Execute hooks of a given type.
100
-
101
- Args:
102
- hook_type: Type of hooks to execute
103
- context_data: Data to pass to hooks
104
- metadata: Optional metadata
105
- specific_hook: Optional specific hook name to execute
106
-
107
- Returns:
108
- List of execution results
109
- """
110
- try:
111
- payload = {
112
- 'hook_type': hook_type.value,
113
- 'context': context_data,
114
- 'metadata': metadata or {}
115
- }
116
-
117
- if specific_hook:
118
- payload['hook_name'] = specific_hook
119
-
120
- response = self.session.post(
121
- urljoin(self.base_url, '/hooks/execute'),
122
- json=payload,
123
- timeout=self.timeout
124
- )
125
- response.raise_for_status()
126
- data = response.json()
127
-
128
- if data.get('status') == 'success':
129
- return data.get('results', [])
130
- else:
131
- logger.error(f"Hook execution failed: {data.get('error')}")
132
- return []
133
-
134
- except Exception as e:
135
- logger.error(f"Failed to execute hooks: {e}")
136
- return []
137
-
138
- def execute_submit_hook(self, prompt: str, **kwargs) -> List[Dict[str, Any]]:
139
- """Execute submit hooks on a user prompt.
140
-
141
- Args:
142
- prompt: User prompt to process
143
- **kwargs: Additional context data
144
-
145
- Returns:
146
- List of execution results
147
- """
148
- context_data = {'prompt': prompt}
149
- context_data.update(kwargs)
150
- return self.execute_hook(HookType.SUBMIT, context_data)
151
-
152
- def execute_pre_delegation_hook(self, agent: str, context: Dict[str, Any],
153
- **kwargs) -> List[Dict[str, Any]]:
154
- """Execute pre-delegation hooks.
155
-
156
- Args:
157
- agent: Agent being delegated to
158
- context: Context being passed to agent
159
- **kwargs: Additional data
160
-
161
- Returns:
162
- List of execution results
163
- """
164
- context_data = {
165
- 'agent': agent,
166
- 'context': context
167
- }
168
- context_data.update(kwargs)
169
- return self.execute_hook(HookType.PRE_DELEGATION, context_data)
170
-
171
- def execute_post_delegation_hook(self, agent: str, result: Any,
172
- **kwargs) -> List[Dict[str, Any]]:
173
- """Execute post-delegation hooks.
174
-
175
- Args:
176
- agent: Agent that was delegated to
177
- result: Result from agent
178
- **kwargs: Additional data
179
-
180
- Returns:
181
- List of execution results
182
- """
183
- context_data = {
184
- 'agent': agent,
185
- 'result': result
186
- }
187
- context_data.update(kwargs)
188
- return self.execute_hook(HookType.POST_DELEGATION, context_data)
189
-
190
- def execute_ticket_extraction_hook(self, content: Any,
191
- **kwargs) -> List[Dict[str, Any]]:
192
- """Execute ticket extraction hooks.
193
-
194
- Args:
195
- content: Content to extract tickets from
196
- **kwargs: Additional data
197
-
198
- Returns:
199
- List of execution results
200
- """
201
- context_data = {'content': content}
202
- context_data.update(kwargs)
203
- return self.execute_hook(HookType.TICKET_EXTRACTION, context_data)
204
-
205
- def get_modified_data(self, results: List[Dict[str, Any]]) -> Dict[str, Any]:
206
- """Extract modified data from hook results.
207
-
208
- Args:
209
- results: Hook execution results
210
-
211
- Returns:
212
- Combined modified data from all hooks
213
- """
214
- modified_data = {}
215
-
216
- for result in results:
217
- if result.get('modified') and result.get('data'):
218
- modified_data.update(result['data'])
219
-
220
- return modified_data
221
-
222
- def get_extracted_tickets(self, results: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
223
- """Extract tickets from hook results.
224
-
225
- Args:
226
- results: Hook execution results
227
-
228
- Returns:
229
- List of extracted tickets
230
- """
231
- all_tickets = []
232
-
233
- for result in results:
234
- if result.get('success') and 'tickets' in result.get('data', {}):
235
- tickets = result['data']['tickets']
236
- if isinstance(tickets, list):
237
- all_tickets.extend(tickets)
238
-
239
- return all_tickets
240
-
241
-
242
- # Convenience function for creating a default client
243
- def get_hook_client(base_url: Optional[str] = None) -> 'JSONRPCHookClient':
244
- """Get a hook client instance.
245
-
246
- DEPRECATED: This function now returns a JSONRPCHookClient for compatibility.
247
- Import directly from claude_mpm.hooks.json_rpc_hook_client instead.
248
-
249
- Args:
250
- base_url: Ignored (kept for backward compatibility)
251
-
252
- Returns:
253
- JSONRPCHookClient instance
254
- """
255
- warnings.warn(
256
- "get_hook_client from hook_client module is deprecated. "
257
- "Import from claude_mpm.hooks.json_rpc_hook_client instead.",
258
- DeprecationWarning,
259
- stacklevel=2
260
- )
261
-
262
- # Import and return JSON-RPC client for compatibility
263
- from claude_mpm.hooks.json_rpc_hook_client import JSONRPCHookClient
264
- return JSONRPCHookClient()
@@ -1,370 +0,0 @@
1
- """Hook runner subprocess entry point for JSON-RPC execution."""
2
-
3
- import importlib.util
4
- import inspect
5
- import json
6
- import logging
7
- import sys
8
- import traceback
9
- from datetime import datetime
10
- from pathlib import Path
11
- from typing import Any, Dict, List, Optional, Type
12
-
13
- # Add parent directory to path for imports
14
- sys.path.insert(0, str(Path(__file__).parent.parent.parent))
15
-
16
- from claude_mpm.hooks.base_hook import (
17
- BaseHook, HookContext, HookResult, HookType,
18
- SubmitHook, PreDelegationHook, PostDelegationHook, TicketExtractionHook
19
- )
20
-
21
- # Configure logging to stderr so it doesn't interfere with stdout JSON-RPC
22
- logging.basicConfig(
23
- level=logging.INFO,
24
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
25
- stream=sys.stderr
26
- )
27
- logger = logging.getLogger(__name__)
28
-
29
-
30
- class HookLoader:
31
- """Loads and manages hook instances."""
32
-
33
- def __init__(self):
34
- """Initialize hook loader."""
35
- self._hooks: Dict[str, BaseHook] = {}
36
- self._hook_types: Dict[str, HookType] = {}
37
- self._load_builtin_hooks()
38
-
39
- def _load_builtin_hooks(self):
40
- """Load built-in hooks from hooks/builtin directory."""
41
- hooks_dir = Path(__file__).parent / 'builtin'
42
- if not hooks_dir.exists():
43
- logger.warning(f"Builtin hooks directory not found: {hooks_dir}")
44
- return
45
-
46
- for hook_file in hooks_dir.glob('*.py'):
47
- if hook_file.name.startswith('_'):
48
- continue
49
-
50
- try:
51
- # Load the module
52
- module_name = hook_file.stem
53
- spec = importlib.util.spec_from_file_location(module_name, hook_file)
54
- if spec and spec.loader:
55
- module = importlib.util.module_from_spec(spec)
56
- spec.loader.exec_module(module)
57
-
58
- # Find and instantiate hook classes
59
- for name, obj in inspect.getmembers(module):
60
- if (inspect.isclass(obj) and
61
- issubclass(obj, BaseHook) and
62
- obj not in [BaseHook, SubmitHook, PreDelegationHook,
63
- PostDelegationHook, TicketExtractionHook] and
64
- not name.startswith('_')):
65
- # Instantiate the hook
66
- hook_instance = obj()
67
- self._hooks[hook_instance.name] = hook_instance
68
-
69
- # Determine hook type
70
- if isinstance(hook_instance, SubmitHook):
71
- self._hook_types[hook_instance.name] = HookType.SUBMIT
72
- elif isinstance(hook_instance, PreDelegationHook):
73
- self._hook_types[hook_instance.name] = HookType.PRE_DELEGATION
74
- elif isinstance(hook_instance, PostDelegationHook):
75
- self._hook_types[hook_instance.name] = HookType.POST_DELEGATION
76
- elif isinstance(hook_instance, TicketExtractionHook):
77
- self._hook_types[hook_instance.name] = HookType.TICKET_EXTRACTION
78
- else:
79
- self._hook_types[hook_instance.name] = HookType.CUSTOM
80
-
81
- logger.info(f"Loaded hook '{hook_instance.name}' from {hook_file}")
82
-
83
- except Exception as e:
84
- logger.error(f"Failed to load hooks from {hook_file}: {e}")
85
- logger.error(traceback.format_exc())
86
-
87
- def get_hook(self, hook_name: str) -> Optional[BaseHook]:
88
- """Get a hook by name.
89
-
90
- Args:
91
- hook_name: Name of the hook
92
-
93
- Returns:
94
- Hook instance or None if not found
95
- """
96
- return self._hooks.get(hook_name)
97
-
98
- def get_hook_type(self, hook_name: str) -> Optional[HookType]:
99
- """Get the type of a hook by name.
100
-
101
- Args:
102
- hook_name: Name of the hook
103
-
104
- Returns:
105
- Hook type or None if not found
106
- """
107
- return self._hook_types.get(hook_name)
108
-
109
-
110
- class JSONRPCHookRunner:
111
- """Runs hooks in response to JSON-RPC requests."""
112
-
113
- def __init__(self):
114
- """Initialize hook runner."""
115
- self.loader = HookLoader()
116
-
117
- def handle_request(self, request: Dict[str, Any]) -> Dict[str, Any]:
118
- """Handle a single JSON-RPC request.
119
-
120
- Args:
121
- request: JSON-RPC request object
122
-
123
- Returns:
124
- JSON-RPC response object
125
- """
126
- # Validate request
127
- if not isinstance(request, dict):
128
- return self._error_response(
129
- -32600, "Invalid Request", None,
130
- "Request must be an object"
131
- )
132
-
133
- if request.get("jsonrpc") != "2.0":
134
- return self._error_response(
135
- -32600, "Invalid Request",
136
- request.get("id"),
137
- "Must specify jsonrpc: '2.0'"
138
- )
139
-
140
- request_id = request.get("id")
141
- method = request.get("method")
142
- params = request.get("params", {})
143
-
144
- if not method:
145
- return self._error_response(
146
- -32600, "Invalid Request", request_id,
147
- "Missing method"
148
- )
149
-
150
- # Route to method handler
151
- if method == "execute_hook":
152
- return self._execute_hook(params, request_id)
153
- else:
154
- return self._error_response(
155
- -32601, "Method not found", request_id,
156
- f"Unknown method: {method}"
157
- )
158
-
159
- def _execute_hook(self, params: Dict[str, Any], request_id: Any) -> Dict[str, Any]:
160
- """Execute a hook based on parameters.
161
-
162
- Args:
163
- params: Hook execution parameters
164
- request_id: JSON-RPC request ID
165
-
166
- Returns:
167
- JSON-RPC response
168
- """
169
- try:
170
- # Extract parameters
171
- hook_name = params.get("hook_name")
172
- hook_type_str = params.get("hook_type")
173
- context_data = params.get("context_data", {})
174
- metadata = params.get("metadata", {})
175
-
176
- if not hook_name:
177
- return self._error_response(
178
- -32602, "Invalid params", request_id,
179
- "Missing hook_name parameter"
180
- )
181
-
182
- # Get hook instance
183
- hook = self.loader.get_hook(hook_name)
184
- if not hook:
185
- return self._error_response(
186
- -32602, "Invalid params", request_id,
187
- f"Hook '{hook_name}' not found"
188
- )
189
-
190
- # Create hook context
191
- try:
192
- hook_type = HookType(hook_type_str) if hook_type_str else self.loader.get_hook_type(hook_name)
193
- except ValueError:
194
- return self._error_response(
195
- -32602, "Invalid params", request_id,
196
- f"Invalid hook type: {hook_type_str}"
197
- )
198
-
199
- context = HookContext(
200
- hook_type=hook_type,
201
- data=context_data,
202
- metadata=metadata,
203
- timestamp=datetime.now()
204
- )
205
-
206
- # Validate hook can run
207
- if not hook.validate(context):
208
- return self._success_response({
209
- "hook_name": hook_name,
210
- "success": False,
211
- "error": "Hook validation failed",
212
- "skipped": True
213
- }, request_id)
214
-
215
- # Execute hook
216
- import time
217
- start_time = time.time()
218
-
219
- try:
220
- result = hook.execute(context)
221
- execution_time = (time.time() - start_time) * 1000 # ms
222
-
223
- # Convert HookResult to dict
224
- return self._success_response({
225
- "hook_name": hook_name,
226
- "success": result.success,
227
- "data": result.data,
228
- "error": result.error,
229
- "modified": result.modified,
230
- "metadata": result.metadata,
231
- "execution_time_ms": execution_time
232
- }, request_id)
233
-
234
- except Exception as e:
235
- logger.error(f"Hook execution error: {e}")
236
- logger.error(traceback.format_exc())
237
- execution_time = (time.time() - start_time) * 1000
238
-
239
- return self._success_response({
240
- "hook_name": hook_name,
241
- "success": False,
242
- "error": str(e),
243
- "execution_time_ms": execution_time
244
- }, request_id)
245
-
246
- except Exception as e:
247
- logger.error(f"Unexpected error in _execute_hook: {e}")
248
- logger.error(traceback.format_exc())
249
- return self._error_response(
250
- -32603, "Internal error", request_id,
251
- str(e)
252
- )
253
-
254
- def _success_response(self, result: Any, request_id: Any) -> Dict[str, Any]:
255
- """Create a successful JSON-RPC response.
256
-
257
- Args:
258
- result: Result data
259
- request_id: Request ID
260
-
261
- Returns:
262
- JSON-RPC response object
263
- """
264
- return {
265
- "jsonrpc": "2.0",
266
- "result": result,
267
- "id": request_id
268
- }
269
-
270
- def _error_response(self, code: int, message: str,
271
- request_id: Any, data: Optional[Any] = None) -> Dict[str, Any]:
272
- """Create an error JSON-RPC response.
273
-
274
- Args:
275
- code: Error code
276
- message: Error message
277
- request_id: Request ID
278
- data: Optional error data
279
-
280
- Returns:
281
- JSON-RPC error response object
282
- """
283
- error = {
284
- "code": code,
285
- "message": message
286
- }
287
- if data is not None:
288
- error["data"] = data
289
-
290
- return {
291
- "jsonrpc": "2.0",
292
- "error": error,
293
- "id": request_id
294
- }
295
-
296
- def handle_batch(self, requests: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
297
- """Handle a batch of JSON-RPC requests.
298
-
299
- Args:
300
- requests: List of JSON-RPC requests
301
-
302
- Returns:
303
- List of JSON-RPC responses
304
- """
305
- if not isinstance(requests, list) or not requests:
306
- return [self._error_response(
307
- -32600, "Invalid Request", None,
308
- "Batch must be a non-empty array"
309
- )]
310
-
311
- responses = []
312
- for request in requests:
313
- response = self.handle_request(request)
314
- responses.append(response)
315
-
316
- return responses
317
-
318
-
319
- def main():
320
- """Main entry point for hook runner subprocess."""
321
- runner = JSONRPCHookRunner()
322
-
323
- try:
324
- # Check for batch mode
325
- batch_mode = "--batch" in sys.argv
326
-
327
- # Read JSON-RPC request from stdin
328
- input_data = sys.stdin.read()
329
-
330
- try:
331
- if batch_mode:
332
- requests = json.loads(input_data)
333
- responses = runner.handle_batch(requests)
334
- print(json.dumps(responses))
335
- else:
336
- request = json.loads(input_data)
337
- response = runner.handle_request(request)
338
- print(json.dumps(response))
339
-
340
- except json.JSONDecodeError as e:
341
- error_response = {
342
- "jsonrpc": "2.0",
343
- "error": {
344
- "code": -32700,
345
- "message": "Parse error",
346
- "data": str(e)
347
- },
348
- "id": None
349
- }
350
- print(json.dumps(error_response))
351
- sys.exit(1)
352
-
353
- except Exception as e:
354
- logger.error(f"Fatal error in hook runner: {e}")
355
- logger.error(traceback.format_exc())
356
- error_response = {
357
- "jsonrpc": "2.0",
358
- "error": {
359
- "code": -32603,
360
- "message": "Internal error",
361
- "data": str(e)
362
- },
363
- "id": None
364
- }
365
- print(json.dumps(error_response))
366
- sys.exit(1)
367
-
368
-
369
- if __name__ == "__main__":
370
- main()