claude-mpm 5.1.9__py3-none-any.whl → 5.4.22__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 claude-mpm might be problematic. Click here for more details.

Files changed (176) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +290 -34
  5. claude_mpm/agents/agent_loader.py +13 -44
  6. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  7. claude_mpm/cli/__main__.py +4 -0
  8. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  9. claude_mpm/cli/commands/agent_state_manager.py +8 -17
  10. claude_mpm/cli/commands/agents.py +0 -31
  11. claude_mpm/cli/commands/auto_configure.py +210 -25
  12. claude_mpm/cli/commands/config.py +88 -2
  13. claude_mpm/cli/commands/configure.py +1097 -158
  14. claude_mpm/cli/commands/configure_agent_display.py +15 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +160 -46
  16. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  18. claude_mpm/cli/commands/skills.py +214 -189
  19. claude_mpm/cli/commands/summarize.py +413 -0
  20. claude_mpm/cli/executor.py +11 -3
  21. claude_mpm/cli/parsers/agents_parser.py +0 -9
  22. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  23. claude_mpm/cli/parsers/base_parser.py +5 -0
  24. claude_mpm/cli/parsers/config_parser.py +153 -83
  25. claude_mpm/cli/parsers/skills_parser.py +3 -2
  26. claude_mpm/cli/startup.py +550 -94
  27. claude_mpm/commands/mpm-config.md +265 -0
  28. claude_mpm/commands/mpm-help.md +14 -95
  29. claude_mpm/commands/mpm-organize.md +500 -0
  30. claude_mpm/config/agent_sources.py +27 -0
  31. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  32. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  33. claude_mpm/core/framework_loader.py +4 -2
  34. claude_mpm/core/logger.py +13 -0
  35. claude_mpm/core/socketio_pool.py +3 -3
  36. claude_mpm/core/unified_agent_registry.py +5 -15
  37. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  38. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  39. claude_mpm/hooks/claude_hooks/hook_handler.py +6 -0
  40. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  41. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  42. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  43. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  44. claude_mpm/hooks/memory_integration_hook.py +46 -1
  45. claude_mpm/init.py +0 -19
  46. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  47. claude_mpm/scripts/launch_monitor.py +93 -13
  48. claude_mpm/scripts/start_activity_logging.py +0 -0
  49. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  50. claude_mpm/services/agents/agent_review_service.py +280 -0
  51. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  52. claude_mpm/services/agents/deployment/agent_template_builder.py +4 -2
  53. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +78 -9
  54. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +335 -53
  55. claude_mpm/services/agents/git_source_manager.py +34 -0
  56. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  57. claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
  58. claude_mpm/services/agents/toolchain_detector.py +10 -6
  59. claude_mpm/services/analysis/__init__.py +11 -1
  60. claude_mpm/services/analysis/clone_detector.py +1030 -0
  61. claude_mpm/services/command_deployment_service.py +81 -10
  62. claude_mpm/services/event_bus/config.py +3 -1
  63. claude_mpm/services/git/git_operations_service.py +93 -8
  64. claude_mpm/services/monitor/daemon.py +9 -2
  65. claude_mpm/services/monitor/daemon_manager.py +39 -3
  66. claude_mpm/services/monitor/server.py +225 -19
  67. claude_mpm/services/self_upgrade_service.py +120 -12
  68. claude_mpm/services/skills/__init__.py +3 -0
  69. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  70. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  71. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  72. claude_mpm/services/skills_deployer.py +126 -9
  73. claude_mpm/services/socketio/event_normalizer.py +15 -1
  74. claude_mpm/services/socketio/server/core.py +160 -21
  75. claude_mpm/services/version_control/git_operations.py +103 -0
  76. claude_mpm/utils/agent_filters.py +17 -44
  77. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/METADATA +47 -84
  78. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/RECORD +82 -161
  79. claude_mpm-5.4.22.dist-info/entry_points.txt +5 -0
  80. claude_mpm-5.4.22.dist-info/licenses/LICENSE +94 -0
  81. claude_mpm-5.4.22.dist-info/licenses/LICENSE-FAQ.md +153 -0
  82. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  83. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  84. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  85. claude_mpm/agents/BASE_OPS.md +0 -219
  86. claude_mpm/agents/BASE_PM.md +0 -480
  87. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  88. claude_mpm/agents/BASE_QA.md +0 -167
  89. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  90. claude_mpm/agents/base_agent.json +0 -31
  91. claude_mpm/agents/base_agent_loader.py +0 -601
  92. claude_mpm/cli/commands/agents_detect.py +0 -380
  93. claude_mpm/cli/commands/agents_recommend.py +0 -309
  94. claude_mpm/cli/ticket_cli.py +0 -35
  95. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  96. claude_mpm/commands/mpm-agents-detect.md +0 -177
  97. claude_mpm/commands/mpm-agents-list.md +0 -131
  98. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  99. claude_mpm/commands/mpm-config-view.md +0 -150
  100. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  101. claude_mpm/dashboard/analysis_runner.py +0 -455
  102. claude_mpm/dashboard/index.html +0 -13
  103. claude_mpm/dashboard/open_dashboard.py +0 -66
  104. claude_mpm/dashboard/static/css/activity.css +0 -1958
  105. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  106. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  107. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  108. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  109. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  110. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  111. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  112. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  113. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  114. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  115. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  116. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  117. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  118. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  119. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  120. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  121. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  122. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  123. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  124. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  125. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  126. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  127. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  128. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  129. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  130. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  131. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  132. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  133. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  134. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  135. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  136. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  137. claude_mpm/dashboard/templates/code_simple.html +0 -153
  138. claude_mpm/dashboard/templates/index.html +0 -606
  139. claude_mpm/dashboard/test_dashboard.html +0 -372
  140. claude_mpm/scripts/mcp_server.py +0 -75
  141. claude_mpm/scripts/mcp_wrapper.py +0 -39
  142. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  143. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  144. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  145. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  146. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  147. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  148. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  149. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  150. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  151. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  152. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  153. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  154. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  155. claude_mpm/services/mcp_gateway/main.py +0 -589
  156. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  157. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  158. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  159. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  160. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  161. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  162. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  163. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  164. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  165. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  166. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  167. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  168. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  169. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  170. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  171. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  172. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  173. claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
  174. claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
  175. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/WHEEL +0 -0
  176. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.22.dist-info}/top_level.txt +0 -0
@@ -1,372 +0,0 @@
1
- """
2
- STDIO Communication Handler for MCP
3
- ====================================
4
-
5
- Handles stdio-based communication for the MCP server.
6
- Manages JSON-RPC message exchange over stdin/stdout.
7
-
8
- Part of ISS-0035: MCP Server Implementation - Core Server and Tool Registry
9
- """
10
-
11
- import asyncio
12
- import json
13
- import sys
14
- from typing import TYPE_CHECKING, Any, Dict, Optional
15
-
16
- from claude_mpm.services.mcp_gateway.core.base import BaseMCPService
17
- from claude_mpm.services.mcp_gateway.core.interfaces import IMCPCommunication
18
-
19
- if TYPE_CHECKING:
20
- from asyncio import StreamReader, StreamWriter
21
-
22
-
23
- class StdioHandler(BaseMCPService, IMCPCommunication):
24
- """
25
- STDIO-based communication handler for MCP.
26
-
27
- WHY: The MCP protocol uses stdio (stdin/stdout) for communication between
28
- Claude Code and MCP servers. This handler manages the low-level
29
- message exchange, ensuring proper JSON-RPC formatting and error handling.
30
-
31
- DESIGN DECISIONS:
32
- - Use asyncio streams for non-blocking I/O
33
- - Implement message framing with Content-Length headers (LSP-style)
34
- - Handle both notification and request/response patterns
35
- - Provide robust error recovery and logging
36
- """
37
-
38
- def __init__(self):
39
- """Initialize the STDIO handler."""
40
- super().__init__("StdioHandler")
41
-
42
- # Async streams
43
- self._reader: Optional[StreamReader] = None
44
- self._writer: Optional[StreamWriter] = None
45
-
46
- # Connection state
47
- self._connected = False
48
-
49
- # Message buffer for partial reads
50
- self._buffer = b""
51
-
52
- # Metrics
53
- self._metrics = {
54
- "messages_sent": 0,
55
- "messages_received": 0,
56
- "errors": 0,
57
- "bytes_sent": 0,
58
- "bytes_received": 0,
59
- }
60
-
61
- async def _do_initialize(self) -> bool:
62
- """
63
- Initialize the STDIO handler.
64
-
65
- Returns:
66
- True if initialization successful
67
- """
68
- try:
69
- self.log_info("Initializing STDIO handler")
70
-
71
- # Create async streams for stdin/stdout
72
- loop = asyncio.get_event_loop()
73
-
74
- # For stdin
75
- self._reader = asyncio.StreamReader()
76
- stdin_protocol = asyncio.StreamReaderProtocol(self._reader)
77
- await loop.connect_read_pipe(lambda: stdin_protocol, sys.stdin)
78
-
79
- # For stdout (we'll write directly to sys.stdout)
80
- # Note: stdout doesn't need async handling for writes
81
-
82
- self._connected = True
83
- self.log_info("STDIO handler initialized")
84
- return True
85
-
86
- except Exception as e:
87
- self.log_error(f"Failed to initialize STDIO handler: {e}")
88
- return False
89
-
90
- async def _do_shutdown(self) -> None:
91
- """Shutdown the STDIO handler."""
92
- self.log_info("Shutting down STDIO handler")
93
-
94
- self._connected = False
95
-
96
- # Close streams if needed
97
- if self._reader:
98
- self._reader = None
99
-
100
- self.log_info("STDIO handler shutdown complete")
101
-
102
- async def send_message(self, message: Dict[str, Any]) -> None:
103
- """
104
- Send a message to the MCP client via stdout.
105
-
106
- Uses Content-Length header for message framing (LSP-style).
107
-
108
- Args:
109
- message: Message to send
110
- """
111
- try:
112
- if not self._connected:
113
- raise RuntimeError("STDIO handler not connected")
114
-
115
- # Convert message to JSON
116
- json_str = json.dumps(message, separators=(",", ":"))
117
- json_bytes = json_str.encode("utf-8")
118
-
119
- # Create Content-Length header
120
- content_length = len(json_bytes)
121
- header = f"Content-Length: {content_length}\r\n\r\n"
122
- header_bytes = header.encode("ascii")
123
-
124
- # Write header and content to stdout
125
- sys.stdout.buffer.write(header_bytes)
126
- sys.stdout.buffer.write(json_bytes)
127
- sys.stdout.buffer.flush()
128
-
129
- # Update metrics
130
- self._metrics["messages_sent"] += 1
131
- self._metrics["bytes_sent"] += len(header_bytes) + len(json_bytes)
132
-
133
- self.log_debug(
134
- f"Sent message: {message.get('method', message.get('id', 'unknown'))}"
135
- )
136
-
137
- except Exception as e:
138
- self.log_error(f"Error sending message: {e}")
139
- self._metrics["errors"] += 1
140
- raise
141
-
142
- async def receive_message(self) -> Optional[Dict[str, Any]]:
143
- """
144
- Receive a message from the MCP client via stdin.
145
-
146
- Handles Content-Length based message framing.
147
-
148
- Returns:
149
- Received message or None if no message available
150
- """
151
- try:
152
- if not self._connected or not self._reader:
153
- return None
154
-
155
- # Read header to get content length
156
- headers = {}
157
- while True:
158
- line_bytes = await self._reader.readline()
159
- if not line_bytes:
160
- # EOF reached
161
- self._connected = False
162
- return None
163
-
164
- line = line_bytes.decode("utf-8").rstrip("\r\n")
165
-
166
- if not line:
167
- # Empty line indicates end of headers
168
- break
169
-
170
- # Parse header
171
- if ":" in line:
172
- key, value = line.split(":", 1)
173
- headers[key.strip()] = value.strip()
174
-
175
- # Get content length
176
- content_length = headers.get("Content-Length")
177
- if not content_length:
178
- self.log_warning("No Content-Length header found")
179
- return None
180
-
181
- content_length = int(content_length)
182
-
183
- # Read content
184
- content_bytes = await self._reader.readexactly(content_length)
185
-
186
- # Parse JSON
187
- message = json.loads(content_bytes.decode("utf-8"))
188
-
189
- # Update metrics
190
- self._metrics["messages_received"] += 1
191
- self._metrics["bytes_received"] += len(line_bytes) + content_length
192
-
193
- self.log_debug(
194
- f"Received message: {message.get('method', message.get('id', 'unknown'))}"
195
- )
196
-
197
- return message
198
-
199
- except asyncio.IncompleteReadError:
200
- self.log_warning("Incomplete read - client may have disconnected")
201
- self._connected = False
202
- return None
203
- except json.JSONDecodeError as e:
204
- self.log_error(f"Invalid JSON received: {e}")
205
- self._metrics["errors"] += 1
206
- return None
207
- except Exception as e:
208
- self.log_error(f"Error receiving message: {e}")
209
- self._metrics["errors"] += 1
210
- return None
211
-
212
- async def send_response(self, request_id: str, result: Any) -> None:
213
- """
214
- Send a response to a request.
215
-
216
- Args:
217
- request_id: ID of the request being responded to
218
- result: Result data
219
- """
220
- response = {"jsonrpc": "2.0", "id": request_id, "result": result}
221
- await self.send_message(response)
222
-
223
- async def send_error(self, request_id: str, error: str, code: int = -1) -> None:
224
- """
225
- Send an error response.
226
-
227
- Args:
228
- request_id: ID of the request that caused the error
229
- error: Error message
230
- code: Error code (default -1 for generic error)
231
- """
232
- response = {
233
- "jsonrpc": "2.0",
234
- "id": request_id,
235
- "error": {"code": code, "message": error},
236
- }
237
- await self.send_message(response)
238
-
239
- async def send_notification(
240
- self, method: str, params: Optional[Dict[str, Any]] = None
241
- ) -> None:
242
- """
243
- Send a notification (no response expected).
244
-
245
- Args:
246
- method: Notification method
247
- params: Optional parameters
248
- """
249
- notification = {"jsonrpc": "2.0", "method": method}
250
- if params:
251
- notification["params"] = params
252
-
253
- await self.send_message(notification)
254
-
255
- def is_connected(self) -> bool:
256
- """
257
- Check if communication channel is connected.
258
-
259
- Returns:
260
- True if connected
261
- """
262
- return self._connected
263
-
264
- def get_metrics(self) -> Dict[str, Any]:
265
- """
266
- Get communication metrics.
267
-
268
- Returns:
269
- Metrics dictionary
270
- """
271
- return self._metrics.copy()
272
-
273
-
274
- class AlternativeStdioHandler(StdioHandler):
275
- """
276
- Alternative STDIO handler using direct sys.stdin/stdout.
277
-
278
- This implementation doesn't use asyncio streams but instead
279
- reads directly from sys.stdin in a blocking manner, which
280
- can be simpler for some use cases.
281
-
282
- WHY: Some MCP implementations may work better with simpler
283
- blocking I/O, especially when running as a subprocess.
284
- """
285
-
286
- async def _do_initialize(self) -> bool:
287
- """
288
- Initialize the alternative STDIO handler.
289
-
290
- Returns:
291
- True if initialization successful
292
- """
293
- try:
294
- self.log_info("Initializing alternative STDIO handler")
295
- self._connected = True
296
- self.log_info("Alternative STDIO handler initialized")
297
- return True
298
-
299
- except Exception as e:
300
- self.log_error(f"Failed to initialize alternative STDIO handler: {e}")
301
- return False
302
-
303
- async def receive_message(self) -> Optional[Dict[str, Any]]:
304
- """
305
- Receive a message using blocking I/O with asyncio executor.
306
-
307
- Returns:
308
- Received message or None if no message available
309
- """
310
- try:
311
- if not self._connected:
312
- return None
313
-
314
- # Run blocking I/O in executor
315
- loop = asyncio.get_event_loop()
316
- message = await loop.run_in_executor(None, self._blocking_receive)
317
-
318
- if message:
319
- self._metrics["messages_received"] += 1
320
- self.log_debug(
321
- f"Received message: {message.get('method', message.get('id', 'unknown'))}"
322
- )
323
-
324
- return message
325
-
326
- except Exception as e:
327
- self.log_error(f"Error receiving message: {e}")
328
- self._metrics["errors"] += 1
329
- return None
330
-
331
- def _blocking_receive(self) -> Optional[Dict[str, Any]]:
332
- """
333
- Blocking receive implementation.
334
-
335
- Returns:
336
- Received message or None
337
- """
338
- try:
339
- # Read headers
340
- headers = {}
341
- while True:
342
- line = sys.stdin.readline()
343
- if not line:
344
- # EOF
345
- self._connected = False
346
- return None
347
-
348
- line = line.rstrip("\r\n")
349
- if not line:
350
- # End of headers
351
- break
352
-
353
- if ":" in line:
354
- key, value = line.split(":", 1)
355
- headers[key.strip()] = value.strip()
356
-
357
- # Get content length
358
- content_length = headers.get("Content-Length")
359
- if not content_length:
360
- return None
361
-
362
- content_length = int(content_length)
363
-
364
- # Read content
365
- content = sys.stdin.read(content_length)
366
-
367
- # Parse JSON
368
- return json.loads(content)
369
-
370
- except Exception as e:
371
- self.log_error(f"Error in blocking receive: {e}")
372
- return None