claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__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 (263) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +70 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  12. claude_mpm/cli/__init__.py +0 -1
  13. claude_mpm/cli/__main__.py +4 -0
  14. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  15. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  16. claude_mpm/cli/commands/agents.py +175 -37
  17. claude_mpm/cli/commands/auto_configure.py +723 -236
  18. claude_mpm/cli/commands/config.py +88 -2
  19. claude_mpm/cli/commands/configure.py +1262 -157
  20. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  21. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  22. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  23. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  24. claude_mpm/cli/commands/postmortem.py +1 -1
  25. claude_mpm/cli/commands/profile.py +277 -0
  26. claude_mpm/cli/commands/skills.py +214 -189
  27. claude_mpm/cli/commands/summarize.py +413 -0
  28. claude_mpm/cli/executor.py +21 -3
  29. claude_mpm/cli/interactive/agent_wizard.py +85 -10
  30. claude_mpm/cli/parsers/agents_parser.py +54 -9
  31. claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
  32. claude_mpm/cli/parsers/base_parser.py +12 -0
  33. claude_mpm/cli/parsers/config_parser.py +153 -83
  34. claude_mpm/cli/parsers/profile_parser.py +148 -0
  35. claude_mpm/cli/parsers/skills_parser.py +3 -2
  36. claude_mpm/cli/startup.py +879 -149
  37. claude_mpm/commands/mpm-config.md +28 -0
  38. claude_mpm/commands/mpm-doctor.md +9 -22
  39. claude_mpm/commands/mpm-help.md +5 -287
  40. claude_mpm/commands/mpm-init.md +81 -507
  41. claude_mpm/commands/mpm-monitor.md +15 -402
  42. claude_mpm/commands/mpm-organize.md +120 -0
  43. claude_mpm/commands/mpm-postmortem.md +6 -108
  44. claude_mpm/commands/mpm-session-resume.md +12 -363
  45. claude_mpm/commands/mpm-status.md +5 -69
  46. claude_mpm/commands/mpm-ticket-view.md +52 -495
  47. claude_mpm/commands/mpm-version.md +5 -107
  48. claude_mpm/config/agent_sources.py +27 -0
  49. claude_mpm/core/config.py +2 -4
  50. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  51. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  52. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  53. claude_mpm/core/framework_loader.py +4 -2
  54. claude_mpm/core/logger.py +13 -0
  55. claude_mpm/core/optimized_startup.py +59 -0
  56. claude_mpm/core/output_style_manager.py +173 -43
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +3 -3
  59. claude_mpm/core/unified_agent_registry.py +134 -16
  60. claude_mpm/core/unified_config.py +22 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  74. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  75. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  76. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  77. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  78. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  88. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  89. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  90. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  91. claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
  92. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  97. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  98. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  99. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  100. claude_mpm/hooks/memory_integration_hook.py +46 -1
  101. claude_mpm/init.py +63 -19
  102. claude_mpm/models/agent_definition.py +7 -0
  103. claude_mpm/models/git_repository.py +3 -3
  104. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  105. claude_mpm/scripts/launch_monitor.py +93 -13
  106. claude_mpm/scripts/start_activity_logging.py +0 -0
  107. claude_mpm/services/agents/agent_builder.py +3 -3
  108. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  109. claude_mpm/services/agents/agent_review_service.py +280 -0
  110. claude_mpm/services/agents/cache_git_manager.py +6 -6
  111. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  112. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  113. claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
  114. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  115. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
  116. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
  117. claude_mpm/services/agents/git_source_manager.py +36 -2
  118. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  119. claude_mpm/services/agents/recommender.py +5 -3
  120. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  121. claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
  122. claude_mpm/services/agents/startup_sync.py +22 -2
  123. claude_mpm/services/agents/toolchain_detector.py +10 -6
  124. claude_mpm/services/analysis/__init__.py +11 -1
  125. claude_mpm/services/analysis/clone_detector.py +1030 -0
  126. claude_mpm/services/command_deployment_service.py +81 -10
  127. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  128. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  129. claude_mpm/services/event_bus/config.py +3 -1
  130. claude_mpm/services/git/git_operations_service.py +101 -16
  131. claude_mpm/services/monitor/daemon.py +9 -2
  132. claude_mpm/services/monitor/daemon_manager.py +39 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  134. claude_mpm/services/monitor/server.py +698 -22
  135. claude_mpm/services/pm_skills_deployer.py +676 -0
  136. claude_mpm/services/profile_manager.py +331 -0
  137. claude_mpm/services/project/project_organizer.py +4 -0
  138. claude_mpm/services/self_upgrade_service.py +120 -12
  139. claude_mpm/services/skills/__init__.py +3 -0
  140. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  141. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  142. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  143. claude_mpm/services/skills_deployer.py +126 -9
  144. claude_mpm/services/socketio/dashboard_server.py +1 -0
  145. claude_mpm/services/socketio/event_normalizer.py +51 -6
  146. claude_mpm/services/socketio/server/core.py +386 -108
  147. claude_mpm/services/version_control/git_operations.py +103 -0
  148. claude_mpm/skills/skill_manager.py +92 -3
  149. claude_mpm/utils/agent_dependency_loader.py +14 -2
  150. claude_mpm/utils/agent_filters.py +17 -44
  151. claude_mpm/utils/gitignore.py +3 -0
  152. claude_mpm/utils/migration.py +4 -4
  153. claude_mpm/utils/robust_installer.py +47 -3
  154. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
  155. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
  156. claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
  157. claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
  158. claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
  159. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  160. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  161. claude_mpm/agents/BASE_OPS.md +0 -219
  162. claude_mpm/agents/BASE_PM.md +0 -480
  163. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  164. claude_mpm/agents/BASE_QA.md +0 -167
  165. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  166. claude_mpm/agents/base_agent_loader.py +0 -601
  167. claude_mpm/cli/commands/agents_detect.py +0 -380
  168. claude_mpm/cli/commands/agents_recommend.py +0 -309
  169. claude_mpm/cli/ticket_cli.py +0 -35
  170. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  171. claude_mpm/commands/mpm-agents-detect.md +0 -177
  172. claude_mpm/commands/mpm-agents-list.md +0 -131
  173. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  174. claude_mpm/commands/mpm-config-view.md +0 -150
  175. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  176. claude_mpm/dashboard/analysis_runner.py +0 -455
  177. claude_mpm/dashboard/index.html +0 -13
  178. claude_mpm/dashboard/open_dashboard.py +0 -66
  179. claude_mpm/dashboard/static/css/activity.css +0 -1958
  180. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  181. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  182. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  183. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  184. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  185. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  186. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  187. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  188. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  189. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  190. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  191. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  192. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  193. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  194. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  195. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  196. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  197. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  198. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  199. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  200. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  201. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  202. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  203. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  204. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  205. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  206. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  207. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  208. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  209. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  210. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  211. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  212. claude_mpm/dashboard/templates/code_simple.html +0 -153
  213. claude_mpm/dashboard/templates/index.html +0 -606
  214. claude_mpm/dashboard/test_dashboard.html +0 -372
  215. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  216. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  217. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  218. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  219. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  226. claude_mpm/scripts/mcp_server.py +0 -75
  227. claude_mpm/scripts/mcp_wrapper.py +0 -39
  228. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  229. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  230. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  231. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  232. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  233. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  234. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  235. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  236. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  237. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  239. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  240. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  241. claude_mpm/services/mcp_gateway/main.py +0 -589
  242. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  243. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  244. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  245. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  246. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  247. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  248. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  249. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  250. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  251. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  252. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  253. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  254. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  255. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  256. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  257. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  258. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  259. claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
  260. claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
  261. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  262. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
  263. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.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