claude-mpm 4.1.6__py3-none-any.whl → 4.24.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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (866) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__init__.py +20 -5
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  5. claude_mpm/agents/BASE_DOCUMENTATION.md +53 -0
  6. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  7. claude_mpm/agents/BASE_OPS.md +219 -0
  8. claude_mpm/agents/BASE_PM.md +431 -214
  9. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
  10. claude_mpm/agents/BASE_QA.md +167 -0
  11. claude_mpm/agents/BASE_RESEARCH.md +53 -0
  12. claude_mpm/agents/MEMORY.md +3 -0
  13. claude_mpm/agents/OUTPUT_STYLE.md +335 -0
  14. claude_mpm/agents/PM_INSTRUCTIONS.md +1159 -0
  15. claude_mpm/agents/WORKFLOW.md +355 -187
  16. claude_mpm/agents/agent_loader.py +40 -10
  17. claude_mpm/agents/agent_loader_integration.py +3 -2
  18. claude_mpm/agents/agents_metadata.py +57 -0
  19. claude_mpm/agents/async_agent_loader.py +3 -3
  20. claude_mpm/agents/base_agent_loader.py +11 -9
  21. claude_mpm/agents/frontmatter_validator.py +291 -251
  22. claude_mpm/agents/system_agent_config.py +3 -2
  23. claude_mpm/agents/templates/.claude-mpm/memories/README.md +17 -0
  24. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +3 -0
  25. claude_mpm/agents/templates/README.md +465 -0
  26. claude_mpm/agents/templates/agent-manager.json +267 -18
  27. claude_mpm/agents/templates/agentic-coder-optimizer.json +248 -0
  28. claude_mpm/agents/templates/api_qa.json +16 -4
  29. claude_mpm/agents/templates/circuit_breakers.md +638 -0
  30. claude_mpm/agents/templates/clerk-ops.json +235 -0
  31. claude_mpm/agents/templates/code_analyzer.json +25 -9
  32. claude_mpm/agents/templates/content-agent.json +358 -0
  33. claude_mpm/agents/templates/dart_engineer.json +307 -0
  34. claude_mpm/agents/templates/data_engineer.json +87 -14
  35. claude_mpm/agents/templates/documentation.json +76 -13
  36. claude_mpm/agents/templates/engineer.json +44 -10
  37. claude_mpm/agents/templates/gcp_ops_agent.json +253 -0
  38. claude_mpm/agents/templates/git_file_tracking.md +584 -0
  39. claude_mpm/agents/templates/golang_engineer.json +270 -0
  40. claude_mpm/agents/templates/imagemagick.json +5 -2
  41. claude_mpm/agents/templates/java_engineer.json +346 -0
  42. claude_mpm/agents/templates/javascript_engineer_agent.json +380 -0
  43. claude_mpm/agents/templates/local_ops_agent.json +1840 -0
  44. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +39 -0
  45. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md +400 -0
  46. claude_mpm/agents/templates/memory_manager.json +6 -3
  47. claude_mpm/agents/templates/nextjs_engineer.json +285 -0
  48. claude_mpm/agents/templates/ops.json +27 -8
  49. claude_mpm/agents/templates/php-engineer.json +287 -0
  50. claude_mpm/agents/templates/pm_examples.md +474 -0
  51. claude_mpm/agents/templates/pm_red_flags.md +262 -0
  52. claude_mpm/agents/templates/product_owner.json +338 -0
  53. claude_mpm/agents/templates/project_organizer.json +19 -5
  54. claude_mpm/agents/templates/prompt-engineer.json +737 -0
  55. claude_mpm/agents/templates/python_engineer.json +387 -0
  56. claude_mpm/agents/templates/qa.json +26 -6
  57. claude_mpm/agents/templates/react_engineer.json +239 -0
  58. claude_mpm/agents/templates/refactoring_engineer.json +15 -5
  59. claude_mpm/agents/templates/research.json +47 -22
  60. claude_mpm/agents/templates/response_format.md +583 -0
  61. claude_mpm/agents/templates/ruby-engineer.json +280 -0
  62. claude_mpm/agents/templates/rust_engineer.json +275 -0
  63. claude_mpm/agents/templates/security.json +59 -10
  64. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  65. claude_mpm/agents/templates/tauri_engineer.json +274 -0
  66. claude_mpm/agents/templates/ticketing.json +16 -7
  67. claude_mpm/agents/templates/typescript_engineer.json +285 -0
  68. claude_mpm/agents/templates/validation_templates.md +312 -0
  69. claude_mpm/agents/templates/vercel_ops_agent.json +164 -33
  70. claude_mpm/agents/templates/version_control.json +16 -4
  71. claude_mpm/agents/templates/web_qa.json +243 -21
  72. claude_mpm/agents/templates/web_ui.json +18 -5
  73. claude_mpm/cli/__init__.py +38 -363
  74. claude_mpm/cli/commands/__init__.py +8 -0
  75. claude_mpm/cli/commands/agent_manager.py +675 -20
  76. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  77. claude_mpm/cli/commands/agents.py +722 -150
  78. claude_mpm/cli/commands/agents_detect.py +380 -0
  79. claude_mpm/cli/commands/agents_recommend.py +309 -0
  80. claude_mpm/cli/commands/aggregate.py +10 -6
  81. claude_mpm/cli/commands/analyze.py +553 -0
  82. claude_mpm/cli/commands/analyze_code.py +528 -0
  83. claude_mpm/cli/commands/auto_configure.py +570 -0
  84. claude_mpm/cli/commands/cleanup.py +12 -12
  85. claude_mpm/cli/commands/config.py +47 -13
  86. claude_mpm/cli/commands/configure.py +488 -884
  87. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  88. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  89. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  90. claude_mpm/cli/commands/configure_models.py +18 -0
  91. claude_mpm/cli/commands/configure_navigation.py +167 -0
  92. claude_mpm/cli/commands/configure_paths.py +104 -0
  93. claude_mpm/cli/commands/configure_persistence.py +254 -0
  94. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  95. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  96. claude_mpm/cli/commands/configure_validators.py +73 -0
  97. claude_mpm/cli/commands/dashboard.py +286 -0
  98. claude_mpm/cli/commands/debug.py +1386 -0
  99. claude_mpm/cli/commands/doctor.py +43 -7
  100. claude_mpm/cli/commands/info.py +3 -4
  101. claude_mpm/cli/commands/local_deploy.py +537 -0
  102. claude_mpm/cli/commands/mcp.py +17 -10
  103. claude_mpm/cli/commands/mcp_command_router.py +11 -0
  104. claude_mpm/cli/commands/mcp_config.py +154 -0
  105. claude_mpm/cli/commands/mcp_external_commands.py +249 -0
  106. claude_mpm/cli/commands/mcp_install_commands.py +101 -32
  107. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  108. claude_mpm/cli/commands/mcp_setup_external.py +868 -0
  109. claude_mpm/cli/commands/memory.py +55 -21
  110. claude_mpm/cli/commands/monitor.py +168 -617
  111. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  112. claude_mpm/cli/commands/mpm_init/core.py +525 -0
  113. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  114. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  115. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  116. claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
  117. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  118. claude_mpm/cli/commands/mpm_init_handler.py +195 -0
  119. claude_mpm/cli/commands/run.py +169 -42
  120. claude_mpm/cli/commands/search.py +458 -0
  121. claude_mpm/cli/commands/skills.py +488 -0
  122. claude_mpm/cli/commands/uninstall.py +176 -0
  123. claude_mpm/cli/commands/upgrade.py +152 -0
  124. claude_mpm/cli/commands/verify.py +119 -0
  125. claude_mpm/cli/executor.py +204 -0
  126. claude_mpm/cli/helpers.py +105 -0
  127. claude_mpm/cli/interactive/__init__.py +21 -0
  128. claude_mpm/cli/interactive/agent_wizard.py +962 -0
  129. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  130. claude_mpm/cli/parser.py +79 -2
  131. claude_mpm/cli/parsers/__init__.py +7 -1
  132. claude_mpm/cli/parsers/agent_manager_parser.py +161 -1
  133. claude_mpm/cli/parsers/agents_parser.py +116 -0
  134. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  135. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  136. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  137. claude_mpm/cli/parsers/base_parser.py +187 -3
  138. claude_mpm/cli/parsers/configure_parser.py +34 -15
  139. claude_mpm/cli/parsers/dashboard_parser.py +113 -0
  140. claude_mpm/cli/parsers/debug_parser.py +319 -0
  141. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  142. claude_mpm/cli/parsers/mcp_parser.py +15 -0
  143. claude_mpm/cli/parsers/monitor_parser.py +12 -2
  144. claude_mpm/cli/parsers/mpm_init_parser.py +311 -0
  145. claude_mpm/cli/parsers/run_parser.py +5 -0
  146. claude_mpm/cli/parsers/search_parser.py +245 -0
  147. claude_mpm/cli/parsers/skills_parser.py +137 -0
  148. claude_mpm/cli/shared/argument_patterns.py +20 -13
  149. claude_mpm/cli/shared/base_command.py +2 -2
  150. claude_mpm/cli/shared/output_formatters.py +28 -19
  151. claude_mpm/cli/startup.py +562 -0
  152. claude_mpm/cli/startup_logging.py +179 -13
  153. claude_mpm/cli/utils.py +53 -2
  154. claude_mpm/commands/__init__.py +14 -0
  155. claude_mpm/commands/mpm-agents-detect.md +168 -0
  156. claude_mpm/commands/mpm-agents-recommend.md +214 -0
  157. claude_mpm/commands/mpm-agents.md +122 -0
  158. claude_mpm/commands/mpm-auto-configure.md +269 -0
  159. claude_mpm/commands/mpm-config.md +141 -0
  160. claude_mpm/commands/mpm-doctor.md +24 -0
  161. claude_mpm/commands/mpm-help.md +290 -0
  162. claude_mpm/commands/mpm-init.md +521 -0
  163. claude_mpm/commands/mpm-monitor.md +409 -0
  164. claude_mpm/commands/mpm-organize.md +295 -0
  165. claude_mpm/commands/mpm-resume.md +372 -0
  166. claude_mpm/commands/mpm-status.md +75 -0
  167. claude_mpm/commands/mpm-tickets.md +151 -0
  168. claude_mpm/commands/mpm-version.md +113 -0
  169. claude_mpm/commands/mpm.md +21 -0
  170. claude_mpm/config/agent_config.py +4 -4
  171. claude_mpm/config/experimental_features.py +7 -7
  172. claude_mpm/config/model_config.py +428 -0
  173. claude_mpm/config/paths.py +3 -2
  174. claude_mpm/config/socketio_config.py +36 -7
  175. claude_mpm/constants.py +27 -1
  176. claude_mpm/core/__init__.py +53 -17
  177. claude_mpm/core/agent_name_normalizer.py +3 -2
  178. claude_mpm/core/agent_registry.py +2 -2
  179. claude_mpm/core/agent_session_manager.py +10 -10
  180. claude_mpm/core/api_validator.py +330 -0
  181. claude_mpm/core/base_service.py +33 -23
  182. claude_mpm/core/cache.py +9 -9
  183. claude_mpm/core/claude_runner.py +19 -8
  184. claude_mpm/core/config.py +103 -8
  185. claude_mpm/core/config_aliases.py +7 -6
  186. claude_mpm/core/constants.py +65 -0
  187. claude_mpm/core/container.py +11 -5
  188. claude_mpm/core/enums.py +452 -0
  189. claude_mpm/core/error_handler.py +623 -0
  190. claude_mpm/core/factories.py +1 -1
  191. claude_mpm/core/file_utils.py +764 -0
  192. claude_mpm/core/framework/__init__.py +38 -0
  193. claude_mpm/core/framework/formatters/__init__.py +11 -0
  194. claude_mpm/core/framework/formatters/capability_generator.py +367 -0
  195. claude_mpm/core/framework/formatters/content_formatter.py +288 -0
  196. claude_mpm/core/framework/formatters/context_generator.py +185 -0
  197. claude_mpm/core/framework/loaders/__init__.py +13 -0
  198. claude_mpm/core/framework/loaders/agent_loader.py +210 -0
  199. claude_mpm/core/framework/loaders/file_loader.py +223 -0
  200. claude_mpm/core/framework/loaders/instruction_loader.py +161 -0
  201. claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
  202. claude_mpm/core/framework/processors/__init__.py +11 -0
  203. claude_mpm/core/framework/processors/memory_processor.py +230 -0
  204. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  205. claude_mpm/core/framework/processors/template_processor.py +244 -0
  206. claude_mpm/core/framework_loader.py +323 -1491
  207. claude_mpm/core/hook_manager.py +8 -6
  208. claude_mpm/core/injectable_service.py +11 -8
  209. claude_mpm/core/instruction_reinforcement_hook.py +267 -0
  210. claude_mpm/core/interactive_session.py +55 -8
  211. claude_mpm/core/interfaces.py +56 -1
  212. claude_mpm/core/lazy.py +3 -3
  213. claude_mpm/core/log_manager.py +100 -28
  214. claude_mpm/core/logger.py +19 -14
  215. claude_mpm/core/logging_config.py +6 -2
  216. claude_mpm/core/logging_utils.py +520 -0
  217. claude_mpm/core/oneshot_session.py +51 -7
  218. claude_mpm/core/optimized_agent_loader.py +9 -9
  219. claude_mpm/core/optimized_startup.py +1 -1
  220. claude_mpm/core/output_style_manager.py +12 -192
  221. claude_mpm/core/pm_hook_interceptor.py +118 -15
  222. claude_mpm/core/service_registry.py +7 -3
  223. claude_mpm/core/session_manager.py +14 -12
  224. claude_mpm/core/shared/config_loader.py +1 -1
  225. claude_mpm/core/socketio_pool.py +15 -15
  226. claude_mpm/core/tool_access_control.py +3 -2
  227. claude_mpm/core/types.py +4 -11
  228. claude_mpm/core/typing_utils.py +7 -6
  229. claude_mpm/core/unified_agent_registry.py +116 -12
  230. claude_mpm/core/unified_config.py +6 -6
  231. claude_mpm/core/unified_paths.py +23 -20
  232. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +1 -0
  233. claude_mpm/dashboard/__init__.py +12 -0
  234. claude_mpm/dashboard/analysis_runner.py +455 -0
  235. claude_mpm/dashboard/api/simple_directory.py +261 -0
  236. claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +188 -0
  237. claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +156 -0
  238. claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +38 -0
  239. claude_mpm/dashboard/react/components/shared/FilterBar.module.css +92 -0
  240. claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +248 -0
  241. claude_mpm/dashboard/static/archive/activity_dashboard_test.html +61 -0
  242. claude_mpm/dashboard/static/archive/test_activity_connection.html +179 -0
  243. claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +68 -0
  244. claude_mpm/dashboard/static/archive/test_dashboard.html +409 -0
  245. claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +519 -0
  246. claude_mpm/dashboard/static/archive/test_dashboard_verification.html +181 -0
  247. claude_mpm/dashboard/static/archive/test_file_data.html +315 -0
  248. claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +243 -0
  249. claude_mpm/dashboard/static/archive/test_file_tree_fix.html +234 -0
  250. claude_mpm/dashboard/static/archive/test_file_tree_rename.html +117 -0
  251. claude_mpm/dashboard/static/archive/test_file_tree_tab.html +115 -0
  252. claude_mpm/dashboard/static/archive/test_file_viewer.html +224 -0
  253. claude_mpm/dashboard/static/archive/test_final_activity.html +220 -0
  254. claude_mpm/dashboard/static/archive/test_tab_fix.html +139 -0
  255. claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +1 -0
  256. claude_mpm/dashboard/static/built/components/activity-tree.js +2 -0
  257. claude_mpm/dashboard/static/built/components/agent-hierarchy.js +777 -0
  258. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  259. claude_mpm/dashboard/static/built/components/build-tracker.js +333 -0
  260. claude_mpm/dashboard/static/built/components/code-simple.js +857 -0
  261. claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +353 -0
  262. claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +235 -0
  263. claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +409 -0
  264. claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +435 -0
  265. claude_mpm/dashboard/static/built/components/code-tree.js +2 -0
  266. claude_mpm/dashboard/static/built/components/code-viewer.js +2 -0
  267. claude_mpm/dashboard/static/built/components/connection-debug.js +654 -0
  268. claude_mpm/dashboard/static/built/components/diff-viewer.js +891 -0
  269. claude_mpm/dashboard/static/built/components/event-processor.js +1 -1
  270. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  271. claude_mpm/dashboard/static/built/components/export-manager.js +1 -1
  272. claude_mpm/dashboard/static/built/components/file-change-tracker.js +443 -0
  273. claude_mpm/dashboard/static/built/components/file-change-viewer.js +690 -0
  274. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  275. claude_mpm/dashboard/static/built/components/file-viewer.js +2 -0
  276. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  277. claude_mpm/dashboard/static/built/components/nav-bar.js +145 -0
  278. claude_mpm/dashboard/static/built/components/page-structure.js +429 -0
  279. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  280. claude_mpm/dashboard/static/built/components/unified-data-viewer.js +2 -0
  281. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  282. claude_mpm/dashboard/static/built/connection-manager.js +536 -0
  283. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  284. claude_mpm/dashboard/static/built/extension-error-handler.js +164 -0
  285. claude_mpm/dashboard/static/built/react/events.js +30 -0
  286. claude_mpm/dashboard/static/built/shared/dom-helpers.js +396 -0
  287. claude_mpm/dashboard/static/built/shared/event-bus.js +330 -0
  288. claude_mpm/dashboard/static/built/shared/event-filter-service.js +540 -0
  289. claude_mpm/dashboard/static/built/shared/logger.js +385 -0
  290. claude_mpm/dashboard/static/built/shared/page-structure.js +249 -0
  291. claude_mpm/dashboard/static/built/shared/tooltip-service.js +253 -0
  292. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  293. claude_mpm/dashboard/static/built/tab-isolation-fix.js +185 -0
  294. claude_mpm/dashboard/static/css/activity.css +1958 -0
  295. claude_mpm/dashboard/static/css/dashboard.css +1413 -72
  296. claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +1 -0
  297. claude_mpm/dashboard/static/dist/components/activity-tree.js +2 -0
  298. claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
  299. claude_mpm/dashboard/static/dist/components/code-tree.js +2 -0
  300. claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
  301. claude_mpm/dashboard/static/dist/components/event-processor.js +1 -1
  302. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  303. claude_mpm/dashboard/static/dist/components/export-manager.js +1 -1
  304. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
  305. claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
  306. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  307. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  308. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +2 -0
  309. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  310. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  311. claude_mpm/dashboard/static/dist/react/events.js +30 -0
  312. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  313. claude_mpm/dashboard/static/events.html +607 -0
  314. claude_mpm/dashboard/static/index.html +635 -0
  315. claude_mpm/dashboard/static/js/components/activity-tree.js +1871 -0
  316. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +4 -1
  317. claude_mpm/dashboard/static/js/components/agent-inference.js +3 -0
  318. claude_mpm/dashboard/static/js/components/build-tracker.js +23 -13
  319. claude_mpm/dashboard/static/js/components/code-simple.js +857 -0
  320. claude_mpm/dashboard/static/js/components/diff-viewer.js +891 -0
  321. claude_mpm/dashboard/static/js/components/event-processor.js +3 -107
  322. claude_mpm/dashboard/static/js/components/event-viewer.js +98 -11
  323. claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
  324. claude_mpm/dashboard/static/js/components/file-change-tracker.js +443 -0
  325. claude_mpm/dashboard/static/js/components/file-change-viewer.js +690 -0
  326. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +30 -10
  327. claude_mpm/dashboard/static/js/components/file-viewer.js +580 -0
  328. claude_mpm/dashboard/static/js/components/module-viewer.js +68 -205
  329. claude_mpm/dashboard/static/js/components/session-manager.js +46 -10
  330. claude_mpm/dashboard/static/js/components/socket-manager.js +16 -0
  331. claude_mpm/dashboard/static/js/components/ui-state-manager.js +359 -40
  332. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +1824 -0
  333. claude_mpm/dashboard/static/js/components/working-directory.js +61 -10
  334. claude_mpm/dashboard/static/js/connection-manager.js +1 -1
  335. claude_mpm/dashboard/static/js/dashboard.js +523 -622
  336. claude_mpm/dashboard/static/js/shared/dom-helpers.js +396 -0
  337. claude_mpm/dashboard/static/js/shared/event-bus.js +330 -0
  338. claude_mpm/dashboard/static/js/shared/logger.js +385 -0
  339. claude_mpm/dashboard/static/js/shared/tooltip-service.js +253 -0
  340. claude_mpm/dashboard/static/js/socket-client.js +549 -62
  341. claude_mpm/dashboard/static/js/stores/dashboard-store.js +562 -0
  342. claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
  343. claude_mpm/dashboard/static/legacy/activity.html +736 -0
  344. claude_mpm/dashboard/static/legacy/agents.html +786 -0
  345. claude_mpm/dashboard/static/legacy/files.html +747 -0
  346. claude_mpm/dashboard/static/legacy/tools.html +831 -0
  347. claude_mpm/dashboard/static/monitors.html +431 -0
  348. claude_mpm/dashboard/static/production/events.html +659 -0
  349. claude_mpm/dashboard/static/production/main.html +698 -0
  350. claude_mpm/dashboard/static/production/monitors.html +483 -0
  351. claude_mpm/dashboard/static/socket.io.min.js +7 -0
  352. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
  353. claude_mpm/dashboard/static/test-archive/dashboard.html +635 -0
  354. claude_mpm/dashboard/static/test-archive/debug-events.html +147 -0
  355. claude_mpm/dashboard/static/test-archive/test-navigation.html +256 -0
  356. claude_mpm/dashboard/static/test-archive/test-react-exports.html +180 -0
  357. claude_mpm/dashboard/static/test-archive/test_debug.html +25 -0
  358. claude_mpm/dashboard/templates/code_simple.html +153 -0
  359. claude_mpm/dashboard/templates/index.html +267 -9
  360. claude_mpm/experimental/__init__.py +10 -0
  361. claude_mpm/experimental/cli_enhancements.py +4 -2
  362. claude_mpm/generators/agent_profile_generator.py +5 -3
  363. claude_mpm/hooks/__init__.py +37 -1
  364. claude_mpm/hooks/base_hook.py +5 -4
  365. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  366. claude_mpm/hooks/claude_hooks/event_handlers.py +21 -18
  367. claude_mpm/hooks/claude_hooks/hook_handler.py +209 -25
  368. claude_mpm/hooks/claude_hooks/installer.py +783 -0
  369. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  370. claude_mpm/hooks/claude_hooks/response_tracking.py +57 -17
  371. claude_mpm/hooks/claude_hooks/services/connection_manager.py +64 -49
  372. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +140 -76
  373. claude_mpm/hooks/claude_hooks/services/state_manager.py +11 -9
  374. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  375. claude_mpm/hooks/failure_learning/__init__.py +60 -0
  376. claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
  377. claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
  378. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
  379. claude_mpm/hooks/instruction_reinforcement.py +301 -0
  380. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  381. claude_mpm/hooks/kuzu_memory_hook.py +386 -0
  382. claude_mpm/hooks/kuzu_response_hook.py +183 -0
  383. claude_mpm/hooks/memory_integration_hook.py +1 -1
  384. claude_mpm/hooks/session_resume_hook.py +121 -0
  385. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  386. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  387. claude_mpm/hooks/tool_call_interceptor.py +8 -5
  388. claude_mpm/hooks/validation_hooks.py +3 -3
  389. claude_mpm/init.py +23 -4
  390. claude_mpm/models/agent_session.py +8 -6
  391. claude_mpm/models/resume_log.py +340 -0
  392. claude_mpm/schemas/__init__.py +12 -0
  393. claude_mpm/scripts/claude-hook-handler.sh +187 -0
  394. claude_mpm/scripts/launch_monitor.py +85 -0
  395. claude_mpm/scripts/mcp_server.py +3 -5
  396. claude_mpm/scripts/mpm_doctor.py +3 -2
  397. claude_mpm/scripts/socketio_daemon.py +156 -396
  398. claude_mpm/services/__init__.py +144 -160
  399. claude_mpm/services/agents/__init__.py +18 -5
  400. claude_mpm/services/agents/agent_builder.py +13 -11
  401. claude_mpm/services/agents/auto_config_manager.py +796 -0
  402. claude_mpm/services/agents/deployment/agent_config_provider.py +127 -27
  403. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  404. claude_mpm/services/agents/deployment/agent_deployment.py +38 -15
  405. claude_mpm/services/agents/deployment/agent_discovery_service.py +125 -7
  406. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +5 -5
  407. claude_mpm/services/agents/deployment/agent_format_converter.py +56 -12
  408. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +4 -2
  409. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  410. claude_mpm/services/agents/deployment/agent_record_service.py +5 -6
  411. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  412. claude_mpm/services/agents/deployment/agent_template_builder.py +722 -37
  413. claude_mpm/services/agents/deployment/agent_validator.py +31 -7
  414. claude_mpm/services/agents/deployment/agent_version_manager.py +9 -1
  415. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  416. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  417. claude_mpm/services/agents/deployment/deployment_config_loader.py +131 -7
  418. claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
  419. claude_mpm/services/agents/deployment/deployment_wrapper.py +58 -0
  420. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  421. claude_mpm/services/agents/deployment/local_template_deployment.py +360 -0
  422. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +134 -38
  423. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +8 -7
  424. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  425. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  426. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +7 -5
  427. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  428. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  429. claude_mpm/services/agents/deployment/system_instructions_deployer.py +9 -6
  430. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  431. claude_mpm/services/agents/deployment/validation/template_validator.py +64 -44
  432. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  433. claude_mpm/services/agents/loading/agent_profile_loader.py +10 -9
  434. claude_mpm/services/agents/loading/base_agent_manager.py +16 -6
  435. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -2
  436. claude_mpm/services/agents/local_template_manager.py +744 -0
  437. claude_mpm/services/agents/management/agent_capabilities_generator.py +3 -2
  438. claude_mpm/services/agents/management/agent_management_service.py +5 -5
  439. claude_mpm/services/agents/memory/agent_memory_manager.py +32 -29
  440. claude_mpm/services/agents/memory/content_manager.py +17 -9
  441. claude_mpm/services/agents/memory/memory_categorization_service.py +4 -2
  442. claude_mpm/services/agents/memory/memory_file_service.py +32 -6
  443. claude_mpm/services/agents/memory/memory_format_service.py +7 -7
  444. claude_mpm/services/agents/memory/memory_limits_service.py +4 -2
  445. claude_mpm/services/agents/memory/template_generator.py +3 -3
  446. claude_mpm/services/agents/observers.py +547 -0
  447. claude_mpm/services/agents/recommender.py +615 -0
  448. claude_mpm/services/agents/registry/deployed_agent_discovery.py +3 -3
  449. claude_mpm/services/agents/registry/modification_tracker.py +30 -19
  450. claude_mpm/services/async_session_logger.py +141 -98
  451. claude_mpm/services/claude_session_logger.py +82 -74
  452. claude_mpm/services/cli/agent_cleanup_service.py +6 -5
  453. claude_mpm/services/cli/agent_dependency_service.py +1 -1
  454. claude_mpm/services/cli/agent_listing_service.py +5 -5
  455. claude_mpm/services/cli/agent_validation_service.py +6 -5
  456. claude_mpm/services/cli/memory_crud_service.py +12 -7
  457. claude_mpm/services/cli/memory_output_formatter.py +2 -2
  458. claude_mpm/services/cli/resume_service.py +617 -0
  459. claude_mpm/services/cli/session_manager.py +104 -13
  460. claude_mpm/services/cli/session_pause_manager.py +504 -0
  461. claude_mpm/services/cli/session_resume_helper.py +372 -0
  462. claude_mpm/services/cli/startup_checker.py +13 -21
  463. claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
  464. claude_mpm/services/command_deployment_service.py +17 -9
  465. claude_mpm/services/command_handler_service.py +11 -5
  466. claude_mpm/services/core/__init__.py +33 -1
  467. claude_mpm/services/core/base.py +26 -11
  468. claude_mpm/services/core/cache_manager.py +1 -3
  469. claude_mpm/services/core/interfaces/__init__.py +90 -3
  470. claude_mpm/services/core/interfaces/agent.py +184 -0
  471. claude_mpm/services/core/interfaces/health.py +172 -0
  472. claude_mpm/services/core/interfaces/model.py +281 -0
  473. claude_mpm/services/core/interfaces/process.py +372 -0
  474. claude_mpm/services/core/interfaces/project.py +121 -0
  475. claude_mpm/services/core/interfaces/restart.py +307 -0
  476. claude_mpm/services/core/interfaces/stability.py +260 -0
  477. claude_mpm/services/core/interfaces.py +56 -1
  478. claude_mpm/services/core/memory_manager.py +92 -47
  479. claude_mpm/services/core/models/__init__.py +79 -0
  480. claude_mpm/services/core/models/agent_config.py +384 -0
  481. claude_mpm/services/core/models/health.py +162 -0
  482. claude_mpm/services/core/models/process.py +239 -0
  483. claude_mpm/services/core/models/restart.py +302 -0
  484. claude_mpm/services/core/models/stability.py +264 -0
  485. claude_mpm/services/core/models/toolchain.py +306 -0
  486. claude_mpm/services/core/path_resolver.py +37 -18
  487. claude_mpm/services/core/service_container.py +2 -2
  488. claude_mpm/services/diagnostics/__init__.py +2 -2
  489. claude_mpm/services/diagnostics/checks/__init__.py +4 -2
  490. claude_mpm/services/diagnostics/checks/agent_check.py +30 -32
  491. claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
  492. claude_mpm/services/diagnostics/checks/common_issues_check.py +28 -27
  493. claude_mpm/services/diagnostics/checks/configuration_check.py +26 -25
  494. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  495. claude_mpm/services/diagnostics/checks/installation_check.py +165 -60
  496. claude_mpm/services/diagnostics/checks/instructions_check.py +22 -24
  497. claude_mpm/services/diagnostics/checks/mcp_check.py +57 -43
  498. claude_mpm/services/diagnostics/checks/mcp_services_check.py +1066 -0
  499. claude_mpm/services/diagnostics/checks/monitor_check.py +24 -23
  500. claude_mpm/services/diagnostics/checks/startup_log_check.py +14 -11
  501. claude_mpm/services/diagnostics/diagnostic_runner.py +22 -13
  502. claude_mpm/services/diagnostics/doctor_reporter.py +275 -47
  503. claude_mpm/services/diagnostics/models.py +37 -21
  504. claude_mpm/services/event_aggregator.py +5 -3
  505. claude_mpm/services/event_bus/direct_relay.py +152 -13
  506. claude_mpm/services/event_bus/event_bus.py +51 -9
  507. claude_mpm/services/event_bus/relay.py +33 -14
  508. claude_mpm/services/events/consumers/dead_letter.py +7 -5
  509. claude_mpm/services/events/core.py +5 -6
  510. claude_mpm/services/events/producers/hook.py +6 -6
  511. claude_mpm/services/events/producers/system.py +8 -8
  512. claude_mpm/services/exceptions.py +5 -5
  513. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  514. claude_mpm/services/framework_claude_md_generator/content_assembler.py +5 -5
  515. claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
  516. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  517. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  518. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  519. claude_mpm/services/hook_installer_service.py +506 -0
  520. claude_mpm/services/hook_service.py +5 -6
  521. claude_mpm/services/infrastructure/context_preservation.py +13 -11
  522. claude_mpm/services/infrastructure/daemon_manager.py +9 -9
  523. claude_mpm/services/infrastructure/logging.py +2 -2
  524. claude_mpm/services/infrastructure/monitoring/__init__.py +12 -12
  525. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  526. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  527. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  528. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  529. claude_mpm/services/infrastructure/monitoring/resources.py +8 -7
  530. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  531. claude_mpm/services/infrastructure/monitoring.py +12 -12
  532. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  533. claude_mpm/services/local_ops/__init__.py +165 -0
  534. claude_mpm/services/local_ops/crash_detector.py +257 -0
  535. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  536. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  537. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  538. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  539. claude_mpm/services/local_ops/health_manager.py +430 -0
  540. claude_mpm/services/local_ops/log_monitor.py +396 -0
  541. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  542. claude_mpm/services/local_ops/process_manager.py +595 -0
  543. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  544. claude_mpm/services/local_ops/restart_manager.py +401 -0
  545. claude_mpm/services/local_ops/restart_policy.py +387 -0
  546. claude_mpm/services/local_ops/state_manager.py +372 -0
  547. claude_mpm/services/local_ops/unified_manager.py +600 -0
  548. claude_mpm/services/mcp_config_manager.py +1612 -0
  549. claude_mpm/services/mcp_gateway/__init__.py +97 -93
  550. claude_mpm/services/mcp_gateway/auto_configure.py +43 -38
  551. claude_mpm/services/mcp_gateway/config/config_loader.py +3 -3
  552. claude_mpm/services/mcp_gateway/config/configuration.py +23 -4
  553. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  554. claude_mpm/services/mcp_gateway/core/base.py +20 -33
  555. claude_mpm/services/mcp_gateway/core/process_pool.py +585 -31
  556. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  557. claude_mpm/services/mcp_gateway/core/startup_verification.py +3 -3
  558. claude_mpm/services/mcp_gateway/main.py +90 -15
  559. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  560. claude_mpm/services/mcp_gateway/registry/tool_registry.py +12 -9
  561. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +4 -4
  562. claude_mpm/services/mcp_gateway/server/stdio_server.py +9 -15
  563. claude_mpm/services/mcp_gateway/tools/__init__.py +14 -2
  564. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  565. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +10 -9
  566. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +654 -0
  567. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +36 -34
  568. claude_mpm/services/mcp_gateway/tools/hello_world.py +8 -8
  569. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +551 -0
  570. claude_mpm/services/mcp_gateway/utils/__init__.py +14 -0
  571. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +160 -0
  572. claude_mpm/services/mcp_gateway/utils/update_preferences.py +170 -0
  573. claude_mpm/services/mcp_service_verifier.py +729 -0
  574. claude_mpm/services/memory/builder.py +9 -8
  575. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  576. claude_mpm/services/memory/cache/simple_cache.py +2 -2
  577. claude_mpm/services/memory/failure_tracker.py +578 -0
  578. claude_mpm/services/memory/indexed_memory.py +8 -8
  579. claude_mpm/services/memory/optimizer.py +8 -9
  580. claude_mpm/services/memory/router.py +3 -3
  581. claude_mpm/services/memory_hook_service.py +165 -4
  582. claude_mpm/services/model/__init__.py +147 -0
  583. claude_mpm/services/model/base_provider.py +365 -0
  584. claude_mpm/services/model/claude_provider.py +412 -0
  585. claude_mpm/services/model/model_router.py +453 -0
  586. claude_mpm/services/model/ollama_provider.py +415 -0
  587. claude_mpm/services/monitor/__init__.py +20 -0
  588. claude_mpm/services/monitor/daemon.py +671 -0
  589. claude_mpm/services/monitor/daemon_manager.py +963 -0
  590. claude_mpm/services/monitor/event_emitter.py +350 -0
  591. claude_mpm/services/monitor/handlers/__init__.py +21 -0
  592. claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
  593. claude_mpm/services/monitor/handlers/dashboard.py +299 -0
  594. claude_mpm/services/monitor/handlers/file.py +264 -0
  595. claude_mpm/services/monitor/handlers/hooks.py +512 -0
  596. claude_mpm/services/monitor/management/__init__.py +18 -0
  597. claude_mpm/services/monitor/management/health.py +124 -0
  598. claude_mpm/services/monitor/management/lifecycle.py +724 -0
  599. claude_mpm/services/monitor/server.py +817 -0
  600. claude_mpm/services/monitor_build_service.py +2 -2
  601. claude_mpm/services/native_agent_converter.py +356 -0
  602. claude_mpm/services/orphan_detection.py +786 -0
  603. claude_mpm/services/port_manager.py +2 -2
  604. claude_mpm/services/project/__init__.py +23 -0
  605. claude_mpm/services/project/analyzer.py +3 -3
  606. claude_mpm/services/project/architecture_analyzer.py +6 -6
  607. claude_mpm/services/project/archive_manager.py +1045 -0
  608. claude_mpm/services/project/dependency_analyzer.py +8 -8
  609. claude_mpm/services/project/detection_strategies.py +719 -0
  610. claude_mpm/services/project/documentation_manager.py +553 -0
  611. claude_mpm/services/project/enhanced_analyzer.py +572 -0
  612. claude_mpm/services/project/language_analyzer.py +3 -3
  613. claude_mpm/services/project/metrics_collector.py +7 -10
  614. claude_mpm/services/project/project_organizer.py +1005 -0
  615. claude_mpm/services/project/registry.py +13 -7
  616. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  617. claude_mpm/services/project_port_allocator.py +596 -0
  618. claude_mpm/services/response_tracker.py +21 -10
  619. claude_mpm/services/runner_configuration_service.py +1 -0
  620. claude_mpm/services/self_upgrade_service.py +500 -0
  621. claude_mpm/services/session_management_service.py +7 -5
  622. claude_mpm/services/session_manager.py +380 -0
  623. claude_mpm/services/shared/__init__.py +2 -1
  624. claude_mpm/services/shared/async_service_base.py +16 -27
  625. claude_mpm/services/shared/config_service_base.py +17 -14
  626. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  627. claude_mpm/services/shared/service_factory.py +8 -5
  628. claude_mpm/services/socketio/client_proxy.py +60 -5
  629. claude_mpm/services/socketio/dashboard_server.py +361 -0
  630. claude_mpm/services/socketio/event_normalizer.py +74 -6
  631. claude_mpm/services/socketio/handlers/__init__.py +5 -0
  632. claude_mpm/services/socketio/handlers/base.py +2 -2
  633. claude_mpm/services/socketio/handlers/code_analysis.py +682 -0
  634. claude_mpm/services/socketio/handlers/connection.py +21 -40
  635. claude_mpm/services/socketio/handlers/connection_handler.py +16 -28
  636. claude_mpm/services/socketio/handlers/file.py +46 -10
  637. claude_mpm/services/socketio/handlers/git.py +8 -8
  638. claude_mpm/services/socketio/handlers/hook.py +29 -17
  639. claude_mpm/services/socketio/handlers/registry.py +4 -0
  640. claude_mpm/services/socketio/monitor_client.py +364 -0
  641. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  642. claude_mpm/services/socketio/server/connection_manager.py +131 -68
  643. claude_mpm/services/socketio/server/core.py +275 -22
  644. claude_mpm/services/socketio/server/eventbus_integration.py +20 -14
  645. claude_mpm/services/socketio/server/main.py +99 -29
  646. claude_mpm/services/socketio_client_manager.py +4 -4
  647. claude_mpm/services/subprocess_launcher_service.py +19 -15
  648. claude_mpm/services/system_instructions_service.py +2 -2
  649. claude_mpm/services/ticket_services/formatter_service.py +1 -1
  650. claude_mpm/services/ticket_services/validation_service.py +5 -5
  651. claude_mpm/services/unified/__init__.py +65 -0
  652. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  653. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
  654. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
  655. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +903 -0
  656. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +746 -0
  657. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
  658. claude_mpm/services/unified/config_strategies/__init__.py +175 -0
  659. claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
  660. claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
  661. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
  662. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
  663. claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
  664. claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
  665. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  666. claude_mpm/services/unified/deployment_strategies/base.py +553 -0
  667. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
  668. claude_mpm/services/unified/deployment_strategies/local.py +607 -0
  669. claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
  670. claude_mpm/services/unified/deployment_strategies/vercel.py +475 -0
  671. claude_mpm/services/unified/interfaces.py +475 -0
  672. claude_mpm/services/unified/migration.py +509 -0
  673. claude_mpm/services/unified/strategies.py +534 -0
  674. claude_mpm/services/unified/unified_analyzer.py +542 -0
  675. claude_mpm/services/unified/unified_config.py +691 -0
  676. claude_mpm/services/unified/unified_deployment.py +470 -0
  677. claude_mpm/services/utility_service.py +6 -3
  678. claude_mpm/services/version_control/branch_strategy.py +2 -2
  679. claude_mpm/services/version_control/conflict_resolution.py +8 -4
  680. claude_mpm/services/version_control/git_operations.py +26 -24
  681. claude_mpm/services/version_control/semantic_versioning.py +14 -14
  682. claude_mpm/services/version_control/version_parser.py +14 -11
  683. claude_mpm/services/version_service.py +104 -1
  684. claude_mpm/services/visualization/__init__.py +19 -0
  685. claude_mpm/services/visualization/mermaid_generator.py +938 -0
  686. claude_mpm/skills/__init__.py +42 -0
  687. claude_mpm/skills/agent_skills_injector.py +324 -0
  688. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  689. claude_mpm/skills/bundled/__init__.py +6 -0
  690. claude_mpm/skills/bundled/api-documentation.md +393 -0
  691. claude_mpm/skills/bundled/async-testing.md +571 -0
  692. claude_mpm/skills/bundled/code-review.md +143 -0
  693. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  694. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  695. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  696. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  697. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  698. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  699. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  700. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  701. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  702. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  703. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  704. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  705. claude_mpm/skills/bundled/database-migration.md +199 -0
  706. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  707. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  708. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  709. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  710. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  711. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  712. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  713. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  714. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  715. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  716. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  717. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  718. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  719. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  720. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  721. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  722. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  723. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  724. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  725. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  726. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  727. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  728. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  729. claude_mpm/skills/bundled/git-workflow.md +414 -0
  730. claude_mpm/skills/bundled/imagemagick.md +204 -0
  731. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  732. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  733. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  734. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  735. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  736. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  737. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  738. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  739. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  740. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  741. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  742. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  743. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  744. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  745. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  746. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  747. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  748. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  749. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  750. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  751. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  752. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  753. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  754. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  755. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  756. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  757. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  758. claude_mpm/skills/bundled/pdf.md +141 -0
  759. claude_mpm/skills/bundled/performance-profiling.md +573 -0
  760. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  761. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  762. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  763. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  764. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  765. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  766. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  767. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  768. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  769. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  770. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  771. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  772. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  773. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  774. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  775. claude_mpm/skills/bundled/security-scanning.md +327 -0
  776. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  777. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  778. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  779. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  780. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  781. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  782. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  783. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  784. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  785. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  786. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  787. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  788. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  789. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  790. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  791. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  792. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  793. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  794. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  795. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  796. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  797. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  798. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  799. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  800. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  801. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  802. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  803. claude_mpm/skills/bundled/xlsx.md +157 -0
  804. claude_mpm/skills/registry.py +286 -0
  805. claude_mpm/skills/skill_manager.py +310 -0
  806. claude_mpm/skills/skills_registry.py +348 -0
  807. claude_mpm/skills/skills_service.py +739 -0
  808. claude_mpm/storage/state_storage.py +31 -31
  809. claude_mpm/tools/__init__.py +10 -0
  810. claude_mpm/tools/__main__.py +208 -0
  811. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  812. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  813. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  814. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  815. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  816. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  817. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  818. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  819. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  820. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  821. claude_mpm/tools/code_tree_builder.py +631 -0
  822. claude_mpm/tools/code_tree_events.py +420 -0
  823. claude_mpm/tools/socketio_debug.py +671 -0
  824. claude_mpm/utils/agent_dependency_loader.py +108 -27
  825. claude_mpm/utils/common.py +544 -0
  826. claude_mpm/utils/config_manager.py +12 -6
  827. claude_mpm/utils/database_connector.py +298 -0
  828. claude_mpm/utils/dependency_cache.py +2 -2
  829. claude_mpm/utils/dependency_strategies.py +15 -10
  830. claude_mpm/utils/display_helper.py +260 -0
  831. claude_mpm/utils/environment_context.py +4 -3
  832. claude_mpm/utils/error_handler.py +5 -3
  833. claude_mpm/utils/file_utils.py +13 -14
  834. claude_mpm/utils/git_analyzer.py +407 -0
  835. claude_mpm/utils/log_cleanup.py +627 -0
  836. claude_mpm/utils/path_operations.py +7 -4
  837. claude_mpm/utils/robust_installer.py +133 -24
  838. claude_mpm/utils/session_logging.py +2 -2
  839. claude_mpm/utils/subprocess_utils.py +9 -8
  840. claude_mpm/validation/agent_validator.py +6 -6
  841. claude_mpm/validation/frontmatter_validator.py +6 -6
  842. claude_mpm-4.24.0.dist-info/METADATA +675 -0
  843. claude_mpm-4.24.0.dist-info/RECORD +1018 -0
  844. {claude_mpm-4.1.6.dist-info → claude_mpm-4.24.0.dist-info}/entry_points.txt +1 -0
  845. claude_mpm/agents/INSTRUCTIONS.md +0 -237
  846. claude_mpm/agents/schema/agent_schema.json +0 -314
  847. claude_mpm/agents/templates/agent-manager.md +0 -304
  848. claude_mpm/cli/commands/configure_tui.py +0 -1921
  849. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  850. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  851. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1040
  852. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  853. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  854. claude_mpm/scripts/socketio_server_manager.py +0 -349
  855. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  856. claude_mpm/services/cli/dashboard_launcher.py +0 -424
  857. claude_mpm/services/cli/socketio_manager.py +0 -498
  858. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +0 -286
  859. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
  860. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
  861. claude_mpm/services/project/analyzer_refactored.py +0 -450
  862. claude_mpm-4.1.6.dist-info/METADATA +0 -325
  863. claude_mpm-4.1.6.dist-info/RECORD +0 -550
  864. {claude_mpm-4.1.6.dist-info → claude_mpm-4.24.0.dist-info}/WHEEL +0 -0
  865. {claude_mpm-4.1.6.dist-info → claude_mpm-4.24.0.dist-info}/licenses/LICENSE +0 -0
  866. {claude_mpm-4.1.6.dist-info → claude_mpm-4.24.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1612 @@
1
+ """
2
+ MCP Configuration Manager
3
+ ========================
4
+
5
+ Manages MCP service configurations, preferring pipx installations
6
+ over local virtual environments for better isolation and management.
7
+
8
+ This module provides utilities to detect, configure, and validate
9
+ MCP service installations.
10
+ """
11
+
12
+ import json
13
+ import subprocess
14
+ import sys
15
+ from datetime import datetime, timezone
16
+ from enum import Enum
17
+ from pathlib import Path
18
+ from typing import Dict, Optional, Tuple
19
+
20
+ from ..core.logger import get_logger
21
+
22
+
23
+ class ConfigLocation(Enum):
24
+ """Enumeration of Claude configuration file locations."""
25
+
26
+ CLAUDE_JSON = Path.home() / ".claude.json" # Primary Claude config
27
+ CLAUDE_DESKTOP = (
28
+ Path.home() / ".claude" / "claude_desktop_config.json"
29
+ ) # Not used by Claude Code
30
+ PROJECT_MCP = ".mcp.json" # Project-level MCP config (deprecated)
31
+
32
+
33
+ class MCPConfigManager:
34
+ """Manages MCP service configurations with pipx preference."""
35
+
36
+ # Standard MCP services that should use pipx
37
+ PIPX_SERVICES = {
38
+ "mcp-vector-search",
39
+ "mcp-browser",
40
+ "mcp-ticketer",
41
+ "kuzu-memory",
42
+ }
43
+
44
+ # Known missing dependencies for MCP services that pipx doesn't handle automatically
45
+ # Maps service names to list of missing dependencies that need injection
46
+ SERVICE_MISSING_DEPENDENCIES = {
47
+ "mcp-ticketer": [
48
+ "gql"
49
+ ], # mcp-ticketer v0.1.8+ needs gql but doesn't declare it
50
+ # Add more services here as needed, e.g.:
51
+ # "another-service": ["dep1", "dep2"],
52
+ }
53
+
54
+ # Static known-good MCP service configurations
55
+ # These are the correct, tested configurations that work reliably
56
+ # Note: Commands will be resolved to full paths dynamically in get_static_service_config()
57
+ STATIC_MCP_CONFIGS = {
58
+ "kuzu-memory": {
59
+ "type": "stdio",
60
+ # Use full path to kuzu-memory binary from pipx venv
61
+ # This ensures it runs with the correct Python version
62
+ "command": "kuzu-memory", # Will be resolved to pipx venv path
63
+ "args": ["mcp", "serve"], # v1.1.0+ uses 'mcp serve' command
64
+ },
65
+ "mcp-ticketer": {
66
+ "type": "stdio",
67
+ "command": "mcp-ticketer", # Will be resolved to full path
68
+ "args": ["mcp"],
69
+ },
70
+ "mcp-browser": {
71
+ "type": "stdio",
72
+ "command": "mcp-browser", # Will be resolved to full path
73
+ "args": ["mcp"],
74
+ "env": {"MCP_BROWSER_HOME": str(Path.home() / ".mcp-browser")},
75
+ },
76
+ "mcp-vector-search": {
77
+ "type": "stdio",
78
+ # Special handling: needs Python interpreter from pipx venv
79
+ "command": "python", # Will be resolved to pipx venv Python
80
+ "args": ["-m", "mcp_vector_search.mcp.server", "{project_root}"],
81
+ "env": {},
82
+ },
83
+ }
84
+
85
+ def __init__(self, config=None):
86
+ """Initialize the MCP configuration manager.
87
+
88
+ Args:
89
+ config: Optional Config object for filtering services
90
+ """
91
+ self.logger = get_logger(__name__)
92
+ self.pipx_base = Path.home() / ".local" / "pipx" / "venvs"
93
+ self.project_root = Path.cwd()
94
+
95
+ # Validate config type if provided
96
+ if config is not None:
97
+ from ..core.config import Config
98
+
99
+ if not isinstance(config, Config):
100
+ self.logger.warning(
101
+ f"Invalid config type provided to MCPConfigManager: "
102
+ f"{type(config).__name__}. Expected Config. "
103
+ f"Proceeding with config=None (all services enabled)."
104
+ )
105
+ config = None
106
+
107
+ self.config = config
108
+
109
+ # Use the proper Claude config file location
110
+ self.claude_config_path = ConfigLocation.CLAUDE_JSON.value
111
+
112
+ def should_enable_service(self, service_name: str) -> bool:
113
+ """
114
+ Check if an MCP service should be enabled based on startup configuration.
115
+
116
+ Args:
117
+ service_name: Name of the MCP service
118
+
119
+ Returns:
120
+ True if the service should be enabled, False otherwise
121
+ """
122
+ # If no config provided, enable all services by default
123
+ if self.config is None:
124
+ return True
125
+
126
+ # Import Config here to avoid circular import at module level
127
+ from ..core.config import Config
128
+
129
+ # Validate config type
130
+ if not isinstance(self.config, Config):
131
+ self.logger.warning(
132
+ f"Invalid config type: {type(self.config).__name__}, "
133
+ f"expected Config. Enabling all services by default."
134
+ )
135
+ return True
136
+
137
+ # Get startup configuration
138
+ enabled_services = self.config.get("startup.enabled_mcp_services", None)
139
+
140
+ # If no startup preferences configured, enable all services
141
+ if enabled_services is None:
142
+ return True
143
+
144
+ # Check if this service is in the enabled list
145
+ is_enabled = service_name in enabled_services
146
+
147
+ if not is_enabled:
148
+ self.logger.debug(
149
+ f"MCP service '{service_name}' disabled by startup configuration"
150
+ )
151
+
152
+ return is_enabled
153
+
154
+ def detect_service_path(self, service_name: str) -> Optional[str]:
155
+ """
156
+ Detect the best path for an MCP service.
157
+
158
+ Priority order:
159
+ 1. For kuzu-memory: prefer v1.1.0+ with MCP support
160
+ 2. Pipx installation (preferred)
161
+ 3. System PATH (likely from pipx or homebrew)
162
+ 4. Local venv (fallback)
163
+
164
+ Args:
165
+ service_name: Name of the MCP service
166
+
167
+ Returns:
168
+ Path to the service executable or None if not found
169
+ """
170
+ # Special handling for kuzu-memory - prefer v1.1.0+ with MCP support
171
+ if service_name == "kuzu-memory":
172
+ candidates = []
173
+
174
+ # Check pipx installation
175
+ pipx_path = self._check_pipx_installation(service_name)
176
+ if pipx_path:
177
+ candidates.append(pipx_path)
178
+
179
+ # Check system PATH (including homebrew)
180
+ import shutil
181
+
182
+ system_path = shutil.which(service_name)
183
+ if system_path and system_path not in candidates:
184
+ candidates.append(system_path)
185
+
186
+ # Choose the best candidate (prefer v1.1.0+ with MCP support)
187
+ for path in candidates:
188
+ try:
189
+ result = subprocess.run(
190
+ [path, "--help"],
191
+ capture_output=True,
192
+ text=True,
193
+ timeout=5,
194
+ check=False,
195
+ )
196
+ # Check if this version has MCP support
197
+ if "claude" in result.stdout or "mcp" in result.stdout:
198
+ self.logger.debug(
199
+ f"Found kuzu-memory with MCP support at {path}"
200
+ )
201
+ return path
202
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
203
+ pass
204
+
205
+ # If no MCP-capable version found, log warning but return None
206
+ if candidates:
207
+ self.logger.warning(
208
+ f"Found kuzu-memory at {candidates[0]} but it lacks MCP support. "
209
+ f"Upgrade to v1.1.0+ for MCP integration: pipx upgrade kuzu-memory"
210
+ )
211
+ return None # Don't configure MCP for incompatible versions
212
+
213
+ # Standard detection for other services
214
+ # Check pipx installation first
215
+ pipx_path = self._check_pipx_installation(service_name)
216
+ if pipx_path:
217
+ self.logger.debug(f"Found {service_name} via pipx: {pipx_path}")
218
+ return pipx_path
219
+
220
+ # Check system PATH
221
+ system_path = self._check_system_path(service_name)
222
+ if system_path:
223
+ self.logger.debug(f"Found {service_name} in PATH: {system_path}")
224
+ return system_path
225
+
226
+ # Fallback to local venv
227
+ local_path = self._check_local_venv(service_name)
228
+ if local_path:
229
+ self.logger.warning(
230
+ f"Using local venv for {service_name} (consider installing via pipx)"
231
+ )
232
+ return local_path
233
+
234
+ self.logger.debug(
235
+ f"Service {service_name} not found - will auto-install when needed"
236
+ )
237
+ return None
238
+
239
+ def _check_pipx_installation(self, service_name: str) -> Optional[str]:
240
+ """Check if service is installed via pipx."""
241
+ pipx_venv = self.pipx_base / service_name
242
+
243
+ if not pipx_venv.exists():
244
+ return None
245
+
246
+ # Special handling for mcp-vector-search (needs Python interpreter)
247
+ if service_name == "mcp-vector-search":
248
+ python_bin = pipx_venv / "bin" / "python"
249
+ if python_bin.exists() and python_bin.is_file():
250
+ return str(python_bin)
251
+ else:
252
+ # Other services use direct binary
253
+ service_bin = pipx_venv / "bin" / service_name
254
+ if service_bin.exists() and service_bin.is_file():
255
+ return str(service_bin)
256
+
257
+ return None
258
+
259
+ def _check_system_path(self, service_name: str) -> Optional[str]:
260
+ """Check if service is available in system PATH."""
261
+ try:
262
+ result = subprocess.run(
263
+ ["which", service_name],
264
+ capture_output=True,
265
+ text=True,
266
+ check=False,
267
+ )
268
+ if result.returncode == 0:
269
+ path = result.stdout.strip()
270
+ # Verify it's from pipx
271
+ if "/.local/bin/" in path or "/pipx/" in path:
272
+ return path
273
+ except Exception as e:
274
+ self.logger.debug(f"Error checking system PATH: {e}")
275
+
276
+ return None
277
+
278
+ def _check_local_venv(self, service_name: str) -> Optional[str]:
279
+ """Check for local virtual environment installation (fallback)."""
280
+ # Common local development paths
281
+ possible_paths = [
282
+ Path.home() / "Projects" / "managed" / service_name / ".venv" / "bin",
283
+ self.project_root / ".venv" / "bin",
284
+ self.project_root / "venv" / "bin",
285
+ ]
286
+
287
+ for base_path in possible_paths:
288
+ if service_name == "mcp-vector-search":
289
+ python_bin = base_path / "python"
290
+ if python_bin.exists():
291
+ return str(python_bin)
292
+ else:
293
+ service_bin = base_path / service_name
294
+ if service_bin.exists():
295
+ return str(service_bin)
296
+
297
+ return None
298
+
299
+ def test_service_command(self, service_name: str, config: Dict) -> bool:
300
+ """
301
+ Test if a service configuration actually works.
302
+
303
+ Args:
304
+ service_name: Name of the MCP service
305
+ config: Service configuration to test
306
+
307
+ Returns:
308
+ True if service responds correctly, False otherwise
309
+ """
310
+ try:
311
+ import shutil
312
+
313
+ # Build command - handle pipx PATH issues
314
+ command = config["command"]
315
+
316
+ # If command is pipx and not found, try common paths
317
+ if command == "pipx":
318
+ pipx_path = shutil.which("pipx")
319
+ if not pipx_path:
320
+ # Try common pipx locations
321
+ for possible_path in [
322
+ "/opt/homebrew/bin/pipx",
323
+ "/usr/local/bin/pipx",
324
+ str(Path.home() / ".local" / "bin" / "pipx"),
325
+ ]:
326
+ if Path(possible_path).exists():
327
+ command = possible_path
328
+ break
329
+ else:
330
+ command = pipx_path
331
+
332
+ cmd = [command]
333
+
334
+ # Add test args (--help or --version)
335
+ if "args" in config:
336
+ # For MCP services, test with --help after the subcommand
337
+ test_args = config["args"].copy()
338
+ # Replace project root placeholder for testing
339
+ test_args = [
340
+ (
341
+ arg.replace("{project_root}", str(self.project_root))
342
+ if "{project_root}" in arg
343
+ else arg
344
+ )
345
+ for arg in test_args
346
+ ]
347
+
348
+ # Add --help at the end
349
+ if service_name == "mcp-vector-search":
350
+ # For Python module invocation, just test if Python can import the module
351
+ cmd.extend(test_args[:2]) # Just python -m module_name
352
+ cmd.extend(["--help"])
353
+ else:
354
+ cmd.extend(test_args)
355
+ cmd.append("--help")
356
+ else:
357
+ cmd.append("--help")
358
+
359
+ # Run test command with timeout
360
+ result = subprocess.run(
361
+ cmd,
362
+ capture_output=True,
363
+ text=True,
364
+ timeout=5,
365
+ check=False,
366
+ env=config.get("env", {}),
367
+ )
368
+
369
+ # Check if command executed (exit code 0 or 1 for help)
370
+ if result.returncode in [0, 1]:
371
+ # Additional check for import errors in stderr
372
+ if (
373
+ "ModuleNotFoundError" in result.stderr
374
+ or "ImportError" in result.stderr
375
+ ):
376
+ self.logger.debug(f"Service {service_name} has import errors")
377
+ return False
378
+ return True
379
+
380
+ except subprocess.TimeoutExpired:
381
+ # Timeout might mean the service started successfully and is waiting for input
382
+ return True
383
+ except Exception as e:
384
+ self.logger.debug(f"Error testing {service_name}: {e}")
385
+
386
+ return False
387
+
388
+ def get_static_service_config(
389
+ self, service_name: str, project_path: Optional[str] = None
390
+ ) -> Optional[Dict]:
391
+ """
392
+ Get the static, known-good configuration for an MCP service.
393
+
394
+ Args:
395
+ service_name: Name of the MCP service
396
+ project_path: Optional project path to use (defaults to current project)
397
+
398
+ Returns:
399
+ Static service configuration dict or None if service not known
400
+ """
401
+ if service_name not in self.STATIC_MCP_CONFIGS:
402
+ return None
403
+
404
+ config = self.STATIC_MCP_CONFIGS[service_name].copy()
405
+ import shutil
406
+
407
+ # Resolve service binary commands to full paths
408
+ if service_name in ["kuzu-memory", "mcp-ticketer", "mcp-browser"]:
409
+ # Try to find the full path of the binary
410
+ binary_name = config["command"]
411
+
412
+ # First check pipx location
413
+ pipx_bin = (
414
+ Path.home()
415
+ / ".local"
416
+ / "pipx"
417
+ / "venvs"
418
+ / service_name
419
+ / "bin"
420
+ / binary_name
421
+ )
422
+ if pipx_bin.exists():
423
+ binary_path = str(pipx_bin)
424
+ else:
425
+ # Try which command
426
+ binary_path = shutil.which(binary_name)
427
+
428
+ if not binary_path:
429
+ # Try common installation locations
430
+ possible_paths = [
431
+ Path.home() / ".local" / "bin" / binary_name,
432
+ Path("/opt/homebrew/bin") / binary_name,
433
+ Path("/usr/local/bin") / binary_name,
434
+ ]
435
+ for path in possible_paths:
436
+ if path.exists():
437
+ binary_path = str(path)
438
+ break
439
+
440
+ if binary_path:
441
+ config["command"] = binary_path
442
+ else:
443
+ # Fall back to pipx run method if binary not found
444
+ self.logger.debug(
445
+ f"Could not find {binary_name}, using pipx run fallback"
446
+ )
447
+ config["command"] = "pipx"
448
+ config["args"] = ["run", service_name] + config["args"]
449
+
450
+ # Resolve pipx command to full path if needed (for fallback configs)
451
+ if config.get("command") == "pipx":
452
+ pipx_path = shutil.which("pipx")
453
+ if not pipx_path:
454
+ # Try common pipx locations
455
+ possible_pipx_paths = [
456
+ Path.home() / ".local" / "bin" / "pipx",
457
+ Path("/opt/homebrew/bin/pipx"),
458
+ Path("/usr/local/bin/pipx"),
459
+ ]
460
+ for path in possible_pipx_paths:
461
+ if path.exists():
462
+ pipx_path = str(path)
463
+ break
464
+ if pipx_path:
465
+ config["command"] = pipx_path
466
+
467
+ # Handle user-specific paths for mcp-vector-search
468
+ if service_name == "mcp-vector-search":
469
+ # Get the correct pipx venv path for the current user
470
+ home = Path.home()
471
+ python_path = (
472
+ home
473
+ / ".local"
474
+ / "pipx"
475
+ / "venvs"
476
+ / "mcp-vector-search"
477
+ / "bin"
478
+ / "python"
479
+ )
480
+
481
+ # Check if the Python interpreter exists
482
+ if python_path.exists():
483
+ config["command"] = str(python_path)
484
+ else:
485
+ # Fallback to pipx run method
486
+ pipx_path = shutil.which("pipx")
487
+ if not pipx_path:
488
+ # Try common pipx locations
489
+ possible_pipx_paths = [
490
+ Path.home() / ".local" / "bin" / "pipx",
491
+ Path("/opt/homebrew/bin/pipx"),
492
+ Path("/usr/local/bin/pipx"),
493
+ ]
494
+ for path in possible_pipx_paths:
495
+ if path.exists():
496
+ pipx_path = str(path)
497
+ break
498
+
499
+ if pipx_path:
500
+ config["command"] = pipx_path
501
+ else:
502
+ config["command"] = "pipx" # Hope it's in PATH
503
+
504
+ # Use pipx run with the spec argument
505
+ config["args"] = [
506
+ "run",
507
+ "--spec",
508
+ "mcp-vector-search",
509
+ "python",
510
+ "-m",
511
+ "mcp_vector_search.mcp.server",
512
+ "{project_root}",
513
+ ]
514
+
515
+ # Use provided project path or current project
516
+ project_root = project_path if project_path else str(self.project_root)
517
+ config["args"] = [
518
+ (
519
+ arg.replace("{project_root}", project_root)
520
+ if "{project_root}" in arg
521
+ else arg
522
+ )
523
+ for arg in config["args"]
524
+ ]
525
+
526
+ return config
527
+
528
+ def generate_service_config(self, service_name: str) -> Optional[Dict]:
529
+ """
530
+ Generate configuration for a specific MCP service.
531
+
532
+ Prefers static configurations over detection. Falls back to detection
533
+ only for unknown services.
534
+
535
+ Args:
536
+ service_name: Name of the MCP service
537
+
538
+ Returns:
539
+ Service configuration dict or None if service not found
540
+ """
541
+ # First try to get static configuration
542
+ static_config = self.get_static_service_config(service_name)
543
+ if static_config:
544
+ # Validate that the static config actually works
545
+ if self.test_service_command(service_name, static_config):
546
+ self.logger.debug(
547
+ f"Static config for {service_name} validated successfully"
548
+ )
549
+ return static_config
550
+ self.logger.warning(
551
+ f"Static config for {service_name} failed validation, trying fallback"
552
+ )
553
+
554
+ # Fall back to detection-based configuration for unknown services
555
+ import shutil
556
+
557
+ # Check for pipx run first (preferred for isolation)
558
+ use_pipx_run = False
559
+ use_uvx = False
560
+
561
+ # Try pipx run test
562
+ if shutil.which("pipx"):
563
+ try:
564
+ result = subprocess.run(
565
+ ["pipx", "run", service_name, "--version"],
566
+ capture_output=True,
567
+ text=True,
568
+ timeout=5,
569
+ check=False,
570
+ )
571
+ if result.returncode == 0 or "version" in result.stdout.lower():
572
+ use_pipx_run = True
573
+ self.logger.debug(f"Will use 'pipx run' for {service_name}")
574
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
575
+ pass
576
+
577
+ # Try uvx if pipx run not available
578
+ if not use_pipx_run and shutil.which("uvx"):
579
+ try:
580
+ result = subprocess.run(
581
+ ["uvx", service_name, "--version"],
582
+ capture_output=True,
583
+ text=True,
584
+ timeout=5,
585
+ check=False,
586
+ )
587
+ if result.returncode == 0 or "version" in result.stdout.lower():
588
+ use_uvx = True
589
+ self.logger.debug(f"Will use 'uvx' for {service_name}")
590
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
591
+ pass
592
+
593
+ # If neither work, try to find direct path
594
+ service_path = None
595
+ if not use_pipx_run and not use_uvx:
596
+ service_path = self.detect_service_path(service_name)
597
+ if not service_path:
598
+ return None
599
+
600
+ # Build configuration
601
+ config = {"type": "stdio"}
602
+
603
+ # Service-specific configurations
604
+ if service_name == "mcp-vector-search":
605
+ if use_pipx_run:
606
+ config["command"] = "pipx"
607
+ config["args"] = [
608
+ "run",
609
+ "mcp-vector-search",
610
+ "-m",
611
+ "mcp_vector_search.mcp.server",
612
+ str(self.project_root),
613
+ ]
614
+ elif use_uvx:
615
+ config["command"] = "uvx"
616
+ config["args"] = [
617
+ "mcp-vector-search",
618
+ "-m",
619
+ "mcp_vector_search.mcp.server",
620
+ str(self.project_root),
621
+ ]
622
+ else:
623
+ config["command"] = service_path
624
+ config["args"] = [
625
+ "-m",
626
+ "mcp_vector_search.mcp.server",
627
+ str(self.project_root),
628
+ ]
629
+ config["env"] = {}
630
+
631
+ elif service_name == "mcp-browser":
632
+ if use_pipx_run:
633
+ config["command"] = "pipx"
634
+ config["args"] = ["run", "mcp-browser", "mcp"]
635
+ elif use_uvx:
636
+ config["command"] = "uvx"
637
+ config["args"] = ["mcp-browser", "mcp"]
638
+ else:
639
+ config["command"] = service_path
640
+ config["args"] = ["mcp"]
641
+ config["env"] = {"MCP_BROWSER_HOME": str(Path.home() / ".mcp-browser")}
642
+
643
+ elif service_name == "mcp-ticketer":
644
+ if use_pipx_run:
645
+ config["command"] = "pipx"
646
+ config["args"] = ["run", "mcp-ticketer", "mcp"]
647
+ elif use_uvx:
648
+ config["command"] = "uvx"
649
+ config["args"] = ["mcp-ticketer", "mcp"]
650
+ else:
651
+ config["command"] = service_path
652
+ config["args"] = ["mcp"]
653
+
654
+ elif service_name == "kuzu-memory":
655
+ # For kuzu-memory, prefer using the binary from pipx venv
656
+ # This ensures it runs with Python 3.12 instead of system Python 3.13
657
+ pipx_binary = (
658
+ Path.home()
659
+ / ".local"
660
+ / "pipx"
661
+ / "venvs"
662
+ / "kuzu-memory"
663
+ / "bin"
664
+ / "kuzu-memory"
665
+ )
666
+
667
+ if pipx_binary.exists():
668
+ # Use pipx venv binary directly - this runs with the correct Python
669
+ config["command"] = str(pipx_binary)
670
+ config["args"] = ["mcp", "serve"]
671
+ elif use_pipx_run:
672
+ # Fallback to pipx run
673
+ config["command"] = "pipx"
674
+ config["args"] = ["run", "kuzu-memory", "mcp", "serve"]
675
+ elif use_uvx:
676
+ # UVX fallback
677
+ config["command"] = "uvx"
678
+ config["args"] = ["kuzu-memory", "mcp", "serve"]
679
+ elif service_path:
680
+ # Direct binary path
681
+ config["command"] = service_path
682
+ config["args"] = ["mcp", "serve"]
683
+ else:
684
+ # Default fallback
685
+ config["command"] = "pipx"
686
+ config["args"] = ["run", "kuzu-memory", "mcp", "serve"]
687
+
688
+ # Generic config for unknown services
689
+ elif use_pipx_run:
690
+ config["command"] = "pipx"
691
+ config["args"] = ["run", service_name]
692
+ elif use_uvx:
693
+ config["command"] = "uvx"
694
+ config["args"] = [service_name]
695
+ else:
696
+ config["command"] = service_path
697
+ config["args"] = []
698
+
699
+ return config
700
+
701
+ def ensure_mcp_services_configured(self) -> Tuple[bool, str]:
702
+ """
703
+ Ensure MCP services are configured correctly in ~/.claude.json on startup.
704
+
705
+ This method checks ALL projects in ~/.claude.json and ensures each has
706
+ the correct, static MCP service configurations. It will:
707
+ 1. Add missing services
708
+ 2. Fix incorrect configurations
709
+ 3. Update all projects, not just the current one
710
+
711
+ Returns:
712
+ Tuple of (success, message)
713
+ """
714
+ updated = False
715
+ fixed_services = []
716
+ added_services = []
717
+
718
+ # Load existing Claude config or create minimal structure
719
+ claude_config = {}
720
+ if self.claude_config_path.exists():
721
+ try:
722
+ with self.claude_config_path.open() as f:
723
+ claude_config = json.load(f)
724
+ except Exception as e:
725
+ self.logger.error(f"Error reading {self.claude_config_path}: {e}")
726
+ return False, f"Failed to read Claude config: {e}"
727
+
728
+ # Ensure projects structure exists
729
+ if "projects" not in claude_config:
730
+ claude_config["projects"] = {}
731
+ updated = True
732
+
733
+ # Note: fix_mcp_service_issues() is already called during CLI initialization
734
+ # Calling it here would duplicate the service health checks
735
+
736
+ # Process ALL projects in the config, not just current one
737
+ projects_to_update = list(claude_config.get("projects", {}).keys())
738
+
739
+ # Also add the current project if not in list
740
+ current_project_key = str(self.project_root)
741
+ if current_project_key not in projects_to_update:
742
+ projects_to_update.append(current_project_key)
743
+ # Initialize new project structure
744
+ claude_config["projects"][current_project_key] = {
745
+ "allowedTools": [],
746
+ "history": [],
747
+ "mcpContextUris": [],
748
+ "mcpServers": {},
749
+ "enabledMcpjsonServers": [],
750
+ "disabledMcpjsonServers": [],
751
+ "hasTrustDialogAccepted": False,
752
+ "projectOnboardingSeenCount": 0,
753
+ "hasClaudeMdExternalIncludesApproved": False,
754
+ "hasClaudeMdExternalIncludesWarningShown": False,
755
+ }
756
+ updated = True
757
+
758
+ # Update each project's MCP configurations
759
+ for project_key in projects_to_update:
760
+ project_config = claude_config["projects"][project_key]
761
+
762
+ # Ensure mcpServers section exists
763
+ if "mcpServers" not in project_config:
764
+ project_config["mcpServers"] = {}
765
+ updated = True
766
+
767
+ # Check and fix each service configuration - now filtered by startup config
768
+ services_to_configure = self.get_filtered_services()
769
+
770
+ for service_name, correct_config in services_to_configure.items():
771
+ # Check if service exists and has correct configuration
772
+ existing_config = project_config["mcpServers"].get(service_name)
773
+
774
+ # Determine if we need to update
775
+ needs_update = False
776
+ if not existing_config:
777
+ # Service is missing
778
+ needs_update = True
779
+ added_services.append(f"{service_name} in {Path(project_key).name}")
780
+ # Service exists, check if configuration is correct
781
+ # Compare command and args (the most critical parts)
782
+ elif existing_config.get("command") != correct_config.get(
783
+ "command"
784
+ ) or existing_config.get("args") != correct_config.get("args"):
785
+ needs_update = True
786
+ fixed_services.append(f"{service_name} in {Path(project_key).name}")
787
+
788
+ # Update configuration if needed
789
+ if needs_update:
790
+ project_config["mcpServers"][service_name] = correct_config
791
+ updated = True
792
+ self.logger.debug(
793
+ f"Updated MCP service config for {service_name} in project {Path(project_key).name}"
794
+ )
795
+
796
+ # Remove disabled services from configuration
797
+ if self.config is not None:
798
+ # Import Config here to avoid circular import
799
+ from ..core.config import Config
800
+
801
+ if isinstance(self.config, Config):
802
+ enabled_services = self.config.get(
803
+ "startup.enabled_mcp_services", None
804
+ )
805
+ if enabled_services is not None:
806
+ # Remove services that are not in the enabled list
807
+ services_to_remove = []
808
+ for service_name in project_config["mcpServers"]:
809
+ if service_name not in enabled_services:
810
+ services_to_remove.append(service_name)
811
+
812
+ for service_name in services_to_remove:
813
+ del project_config["mcpServers"][service_name]
814
+ updated = True
815
+ self.logger.debug(
816
+ f"Removed disabled service {service_name} from project {Path(project_key).name}"
817
+ )
818
+
819
+ # Write updated config if changes were made
820
+ if updated:
821
+ try:
822
+ # Create backup if file exists and is large (> 100KB)
823
+ if self.claude_config_path.exists():
824
+ file_size = self.claude_config_path.stat().st_size
825
+ if file_size > 100000: # 100KB
826
+ backup_path = self.claude_config_path.with_suffix(
827
+ f".backup.{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}.json"
828
+ )
829
+ import shutil
830
+
831
+ shutil.copy2(self.claude_config_path, backup_path)
832
+ self.logger.debug(f"Created backup: {backup_path}")
833
+
834
+ # Write updated config
835
+ with self.claude_config_path.open("w") as f:
836
+ json.dump(claude_config, f, indent=2)
837
+
838
+ messages = []
839
+ if added_services:
840
+ messages.append(
841
+ f"Added MCP services: {', '.join(added_services[:3])}"
842
+ )
843
+ if fixed_services:
844
+ messages.append(
845
+ f"Fixed MCP services: {', '.join(fixed_services[:3])}"
846
+ )
847
+
848
+ if messages:
849
+ return True, "; ".join(messages)
850
+ return True, "All MCP services already configured correctly"
851
+ except Exception as e:
852
+ self.logger.error(f"Failed to write Claude config: {e}")
853
+ return False, f"Failed to write configuration: {e}"
854
+
855
+ return True, "All MCP services already configured correctly"
856
+
857
+ def update_mcp_config(self, force_pipx: bool = True) -> Tuple[bool, str]:
858
+ """
859
+ Update the MCP configuration in ~/.claude.json.
860
+
861
+ Args:
862
+ force_pipx: If True, only use pipx installations
863
+
864
+ Returns:
865
+ Tuple of (success, message)
866
+ """
867
+ # This method now delegates to ensure_mcp_services_configured
868
+ # since we're updating the Claude config directly
869
+ return self.ensure_mcp_services_configured()
870
+
871
+ def update_project_mcp_config(self, force_pipx: bool = True) -> Tuple[bool, str]:
872
+ """
873
+ Update the .mcp.json configuration file (legacy method).
874
+
875
+ Args:
876
+ force_pipx: If True, only use pipx installations
877
+
878
+ Returns:
879
+ Tuple of (success, message)
880
+ """
881
+ mcp_config_path = self.project_root / ConfigLocation.PROJECT_MCP.value
882
+
883
+ # Load existing config if it exists
884
+ existing_config = {}
885
+ if mcp_config_path.exists():
886
+ try:
887
+ with mcp_config_path.open() as f:
888
+ existing_config = json.load(f)
889
+ except Exception as e:
890
+ self.logger.error(f"Error reading existing config: {e}")
891
+
892
+ # Generate new configurations
893
+ new_config = {"mcpServers": {}}
894
+ missing_services = []
895
+
896
+ for service_name in self.PIPX_SERVICES:
897
+ config = self.generate_service_config(service_name)
898
+ if config:
899
+ new_config["mcpServers"][service_name] = config
900
+ elif force_pipx:
901
+ missing_services.append(service_name)
902
+ # Keep existing config if not forcing pipx
903
+ elif service_name in existing_config.get("mcpServers", {}):
904
+ new_config["mcpServers"][service_name] = existing_config["mcpServers"][
905
+ service_name
906
+ ]
907
+
908
+ # Add any additional services from existing config
909
+ for service_name, config in existing_config.get("mcpServers", {}).items():
910
+ if service_name not in new_config["mcpServers"]:
911
+ new_config["mcpServers"][service_name] = config
912
+
913
+ # Write the updated configuration
914
+ try:
915
+ with mcp_config_path.open("w") as f:
916
+ json.dump(new_config, f, indent=2)
917
+
918
+ if missing_services:
919
+ message = f"Updated .mcp.json. Missing services (install via pipx): {', '.join(missing_services)}"
920
+ return True, message
921
+ return True, "Successfully updated .mcp.json with pipx paths"
922
+ except Exception as e:
923
+ return False, f"Failed to update .mcp.json: {e}"
924
+
925
+ def validate_configuration(self) -> Dict[str, bool]:
926
+ """
927
+ Validate that all configured MCP services are accessible.
928
+
929
+ Returns:
930
+ Dict mapping service names to availability status
931
+ """
932
+ project_key = str(self.project_root)
933
+
934
+ # Check Claude config
935
+ if not self.claude_config_path.exists():
936
+ # Also check legacy .mcp.json
937
+ mcp_config_path = self.project_root / ConfigLocation.PROJECT_MCP.value
938
+ if mcp_config_path.exists():
939
+ try:
940
+ with mcp_config_path.open() as f:
941
+ config = json.load(f)
942
+ results = {}
943
+ for service_name, service_config in config.get(
944
+ "mcpServers", {}
945
+ ).items():
946
+ command_path = service_config.get("command", "")
947
+ results[service_name] = Path(command_path).exists()
948
+ return results
949
+ except Exception:
950
+ pass
951
+ return {}
952
+
953
+ try:
954
+ with self.claude_config_path.open() as f:
955
+ claude_config = json.load(f)
956
+
957
+ # Get project's MCP servers
958
+ if "projects" in claude_config and project_key in claude_config["projects"]:
959
+ mcp_servers = claude_config["projects"][project_key].get(
960
+ "mcpServers", {}
961
+ )
962
+ results = {}
963
+ for service_name, service_config in mcp_servers.items():
964
+ command_path = service_config.get("command", "")
965
+ results[service_name] = Path(command_path).exists()
966
+ return results
967
+ except Exception as e:
968
+ self.logger.error(f"Error reading config: {e}")
969
+
970
+ return {}
971
+
972
+ def install_missing_services(self) -> Tuple[bool, str]:
973
+ """
974
+ Install missing MCP services via pipx with verification and fallbacks.
975
+
976
+ Returns:
977
+ Tuple of (success, message)
978
+ """
979
+ missing = []
980
+ for service_name in self.PIPX_SERVICES:
981
+ if not self.detect_service_path(service_name):
982
+ missing.append(service_name)
983
+
984
+ if not missing:
985
+ return True, "All MCP services are already installed"
986
+
987
+ installed = []
988
+ failed = []
989
+
990
+ for service_name in missing:
991
+ # Try pipx install first
992
+ success, method = self._install_service_with_fallback(service_name)
993
+ if success:
994
+ installed.append(f"{service_name} ({method})")
995
+ self.logger.info(f"Successfully installed {service_name} via {method}")
996
+ else:
997
+ failed.append(service_name)
998
+ self.logger.error(f"Failed to install {service_name}")
999
+
1000
+ if failed:
1001
+ return False, f"Failed to install: {', '.join(failed)}"
1002
+ if installed:
1003
+ return True, f"Successfully installed: {', '.join(installed)}"
1004
+ return True, "No services needed installation"
1005
+
1006
+ def _install_service_with_fallback(self, service_name: str) -> Tuple[bool, str]:
1007
+ """
1008
+ Install a service with multiple fallback methods.
1009
+
1010
+ Returns:
1011
+ Tuple of (success, installation_method)
1012
+ """
1013
+ import shutil
1014
+
1015
+ # Method 1: Try pipx install
1016
+ if shutil.which("pipx"):
1017
+ try:
1018
+ self.logger.debug(f"Attempting to install {service_name} via pipx...")
1019
+ result = subprocess.run(
1020
+ ["pipx", "install", service_name],
1021
+ capture_output=True,
1022
+ text=True,
1023
+ timeout=120, # 2 minute timeout
1024
+ check=False,
1025
+ )
1026
+
1027
+ if result.returncode == 0:
1028
+ # Inject any missing dependencies if needed
1029
+ if service_name in self.SERVICE_MISSING_DEPENDENCIES:
1030
+ self.logger.debug(
1031
+ f"Injecting missing dependencies for newly installed {service_name}..."
1032
+ )
1033
+ self._inject_missing_dependencies(service_name)
1034
+
1035
+ # Verify installation worked
1036
+ if self._verify_service_installed(service_name, "pipx"):
1037
+ return True, "pipx"
1038
+
1039
+ self.logger.warning(
1040
+ f"pipx install succeeded but verification failed for {service_name}"
1041
+ )
1042
+ else:
1043
+ self.logger.debug(f"pipx install failed: {result.stderr}")
1044
+ except subprocess.TimeoutExpired:
1045
+ self.logger.warning(f"pipx install timed out for {service_name}")
1046
+ except Exception as e:
1047
+ self.logger.debug(f"pipx install error: {e}")
1048
+
1049
+ # Method 2: Try uvx (if available)
1050
+ if shutil.which("uvx"):
1051
+ try:
1052
+ self.logger.debug(f"Attempting to install {service_name} via uvx...")
1053
+ result = subprocess.run(
1054
+ ["uvx", "install", service_name],
1055
+ capture_output=True,
1056
+ text=True,
1057
+ timeout=120,
1058
+ check=False,
1059
+ )
1060
+
1061
+ if result.returncode == 0:
1062
+ if self._verify_service_installed(service_name, "uvx"):
1063
+ return True, "uvx"
1064
+ except Exception as e:
1065
+ self.logger.debug(f"uvx install error: {e}")
1066
+
1067
+ # Method 3: Try pip install --user
1068
+ try:
1069
+ self.logger.debug(f"Attempting to install {service_name} via pip --user...")
1070
+ result = subprocess.run(
1071
+ [sys.executable, "-m", "pip", "install", "--user", service_name],
1072
+ capture_output=True,
1073
+ text=True,
1074
+ timeout=120,
1075
+ check=False,
1076
+ )
1077
+
1078
+ if result.returncode == 0:
1079
+ if self._verify_service_installed(service_name, "pip"):
1080
+ return True, "pip --user"
1081
+
1082
+ self.logger.warning(
1083
+ f"pip install succeeded but verification failed for {service_name}"
1084
+ )
1085
+ except Exception as e:
1086
+ self.logger.debug(f"pip install error: {e}")
1087
+
1088
+ return False, "none"
1089
+
1090
+ # COMMENTED OUT: These functions are no longer used
1091
+ # Package maintainers should fix dependency declarations in their packages
1092
+ # Automatic dependency injection can cause conflicts and is not recommended
1093
+
1094
+ # def _get_mcp_ticketer_version(self) -> Optional[str]:
1095
+ # """Get the installed version of mcp-ticketer.
1096
+ #
1097
+ # Returns:
1098
+ # Version string (e.g., "0.1.8") or None if not installed
1099
+ # """
1100
+ # try:
1101
+ # result = subprocess.run(
1102
+ # ["pipx", "runpip", "mcp-ticketer", "show", "mcp-ticketer"],
1103
+ # capture_output=True,
1104
+ # text=True,
1105
+ # timeout=5,
1106
+ # check=False,
1107
+ # )
1108
+ #
1109
+ # if result.returncode == 0:
1110
+ # # Parse version from output
1111
+ # for line in result.stdout.split("\n"):
1112
+ # if line.startswith("Version:"):
1113
+ # return line.split(":", 1)[1].strip()
1114
+ # return None
1115
+ # except Exception:
1116
+ # return None
1117
+ #
1118
+ # def _check_and_fix_mcp_ticketer_dependencies(self) -> bool:
1119
+ # """Check and fix mcp-ticketer missing gql dependency.
1120
+ #
1121
+ # DEPRECATED: This workaround is no longer used.
1122
+ # Package maintainers should fix dependency declarations.
1123
+ #
1124
+ # Returns:
1125
+ # False (no longer performs injection)
1126
+ # """
1127
+ # return False
1128
+
1129
+ def fix_mcp_service_issues(self) -> Tuple[bool, str]:
1130
+ """
1131
+ Detect and fix corrupted MCP service installations.
1132
+
1133
+ NOTE: Proactive health checking has been disabled.
1134
+ Each MCP service should stand on its own and handle its own issues.
1135
+ This function now only returns success without checking services.
1136
+
1137
+ Returns:
1138
+ Tuple of (success, message)
1139
+ """
1140
+ # Services should stand on their own - no proactive health checking
1141
+ return True, "MCP services managing their own health"
1142
+
1143
+ def _detect_service_issue(self, service_name: str) -> Optional[str]:
1144
+ """
1145
+ Detect what type of issue a service has.
1146
+
1147
+ Returns:
1148
+ Issue type: 'not_installed', 'import_error', 'missing_dependency', 'path_issue', or None
1149
+ """
1150
+ import shutil
1151
+
1152
+ # First check if pipx is available
1153
+ if not shutil.which("pipx"):
1154
+ return "not_installed" # Can't use pipx services without pipx
1155
+
1156
+ # Try to run the service with --help to detect issues
1157
+ try:
1158
+ # First check if service is installed in pipx venv
1159
+ pipx_venv_bin = self.pipx_base / service_name / "bin" / service_name
1160
+ if pipx_venv_bin.exists():
1161
+ # Test the installed version directly (has injected dependencies)
1162
+ # This avoids using pipx run which downloads a fresh cache copy without dependencies
1163
+ self.logger.debug(
1164
+ f" Testing {service_name} from installed pipx venv: {pipx_venv_bin}"
1165
+ )
1166
+ result = subprocess.run(
1167
+ [str(pipx_venv_bin), "--help"],
1168
+ capture_output=True,
1169
+ text=True,
1170
+ timeout=10,
1171
+ check=False,
1172
+ )
1173
+
1174
+ # Check for specific error patterns in installed version
1175
+ stderr_lower = result.stderr.lower()
1176
+ stdout_lower = result.stdout.lower()
1177
+ combined_output = stderr_lower + stdout_lower
1178
+
1179
+ # Import errors in installed version (should be rare if dependencies injected)
1180
+ if (
1181
+ "modulenotfounderror" in combined_output
1182
+ or "importerror" in combined_output
1183
+ ):
1184
+ # Check if it's specifically the gql dependency for mcp-ticketer
1185
+ if service_name == "mcp-ticketer" and "gql" in combined_output:
1186
+ return "missing_dependency"
1187
+ return "import_error"
1188
+
1189
+ # Path issues
1190
+ if "no such file or directory" in combined_output:
1191
+ return "path_issue"
1192
+
1193
+ # If help text appears, service is working
1194
+ if (
1195
+ "usage:" in combined_output
1196
+ or "help" in combined_output
1197
+ or result.returncode in [0, 1]
1198
+ ):
1199
+ self.logger.debug(
1200
+ f" {service_name} is working correctly (installed in venv)"
1201
+ )
1202
+ return None # Service is working
1203
+
1204
+ # Unknown issue
1205
+ if result.returncode not in [0, 1]:
1206
+ self.logger.debug(
1207
+ f"{service_name} returned unexpected exit code: {result.returncode}"
1208
+ )
1209
+ return "unknown_error"
1210
+
1211
+ return None # Default to working if no issues detected
1212
+
1213
+ # Service not installed in pipx venv - use pipx run for detection
1214
+ # Note: pipx run uses cache which may not have injected dependencies
1215
+ self.logger.debug(
1216
+ f" Testing {service_name} via pipx run (not installed in venv)"
1217
+ )
1218
+ result = subprocess.run(
1219
+ ["pipx", "run", service_name, "--help"],
1220
+ capture_output=True,
1221
+ text=True,
1222
+ timeout=10,
1223
+ check=False,
1224
+ )
1225
+
1226
+ # Check for specific error patterns
1227
+ stderr_lower = result.stderr.lower()
1228
+ stdout_lower = result.stdout.lower()
1229
+ combined_output = stderr_lower + stdout_lower
1230
+
1231
+ # Not installed
1232
+ if (
1233
+ "no apps associated" in combined_output
1234
+ or "not found" in combined_output
1235
+ ):
1236
+ return "not_installed"
1237
+
1238
+ # Import errors when using pipx run (cache version)
1239
+ if (
1240
+ "modulenotfounderror" in combined_output
1241
+ or "importerror" in combined_output
1242
+ ):
1243
+ # Don't report missing_dependency for cache version - it may be missing injected deps
1244
+ # Just report that service needs to be installed properly
1245
+ self.logger.debug(
1246
+ f"{service_name} has import errors in pipx run cache - needs proper installation"
1247
+ )
1248
+ return "not_installed"
1249
+
1250
+ # Path issues
1251
+ if "no such file or directory" in combined_output:
1252
+ return "path_issue"
1253
+
1254
+ # If help text appears, service is working
1255
+ if (
1256
+ "usage:" in combined_output
1257
+ or "help" in combined_output
1258
+ or result.returncode in [0, 1]
1259
+ ):
1260
+ return None # Service is working
1261
+
1262
+ # Unknown issue
1263
+ if result.returncode not in [0, 1]:
1264
+ return "unknown_error"
1265
+
1266
+ except subprocess.TimeoutExpired:
1267
+ # Timeout might mean service is actually working but waiting for input
1268
+ return None
1269
+ except Exception as e:
1270
+ self.logger.debug(f"Error detecting issue for {service_name}: {e}")
1271
+ return "unknown_error"
1272
+
1273
+ return None
1274
+
1275
+ def _reinstall_service(self, service_name: str) -> bool:
1276
+ """
1277
+ Reinstall a corrupted MCP service.
1278
+
1279
+ Args:
1280
+ service_name: Name of the service to reinstall
1281
+
1282
+ Returns:
1283
+ True if successful, False otherwise
1284
+ """
1285
+ try:
1286
+ self.logger.debug(f"Uninstalling {service_name}...")
1287
+
1288
+ # First uninstall the corrupted version
1289
+ uninstall_result = subprocess.run(
1290
+ ["pipx", "uninstall", service_name],
1291
+ capture_output=True,
1292
+ text=True,
1293
+ timeout=30,
1294
+ check=False,
1295
+ )
1296
+
1297
+ # Don't check return code - uninstall might fail if partially corrupted
1298
+ self.logger.debug(f"Uninstall result: {uninstall_result.returncode}")
1299
+
1300
+ # Now reinstall
1301
+ self.logger.debug(f"Installing fresh {service_name}...")
1302
+ install_result = subprocess.run(
1303
+ ["pipx", "install", service_name],
1304
+ capture_output=True,
1305
+ text=True,
1306
+ timeout=120,
1307
+ check=False,
1308
+ )
1309
+
1310
+ if install_result.returncode == 0:
1311
+ # Inject any missing dependencies if needed
1312
+ if service_name in self.SERVICE_MISSING_DEPENDENCIES:
1313
+ self.logger.debug(
1314
+ f"Injecting missing dependencies for {service_name}..."
1315
+ )
1316
+ self._inject_missing_dependencies(service_name)
1317
+
1318
+ # Verify the reinstall worked
1319
+ issue = self._detect_service_issue(service_name)
1320
+ if issue is None:
1321
+ self.logger.info(f"✅ Successfully reinstalled {service_name}")
1322
+ return True
1323
+ self.logger.warning(
1324
+ f"Reinstalled {service_name} but still has issue: {issue}"
1325
+ )
1326
+ return False
1327
+ self.logger.error(
1328
+ f"Failed to reinstall {service_name}: {install_result.stderr}"
1329
+ )
1330
+ return False
1331
+
1332
+ except Exception as e:
1333
+ self.logger.error(f"Error reinstalling {service_name}: {e}")
1334
+ return False
1335
+
1336
+ def _inject_missing_dependencies(self, service_name: str) -> bool:
1337
+ """
1338
+ Inject missing dependencies into a pipx-installed MCP service.
1339
+
1340
+ Some MCP services don't properly declare all their dependencies in their
1341
+ package metadata, which causes import errors when pipx creates isolated
1342
+ virtual environments. This method injects the missing dependencies using
1343
+ pipx inject.
1344
+
1345
+ Args:
1346
+ service_name: Name of the MCP service to fix
1347
+
1348
+ Returns:
1349
+ True if dependencies were injected successfully or no injection needed, False otherwise
1350
+ """
1351
+ # Check if this service has known missing dependencies
1352
+ if service_name not in self.SERVICE_MISSING_DEPENDENCIES:
1353
+ return True # No dependencies to inject
1354
+
1355
+ missing_deps = self.SERVICE_MISSING_DEPENDENCIES[service_name]
1356
+ if not missing_deps:
1357
+ return True # No dependencies to inject
1358
+
1359
+ self.logger.info(
1360
+ f" → Injecting missing dependencies for {service_name}: {', '.join(missing_deps)}"
1361
+ )
1362
+
1363
+ all_successful = True
1364
+ for dep in missing_deps:
1365
+ try:
1366
+ self.logger.debug(f" Injecting {dep} into {service_name}...")
1367
+ result = subprocess.run(
1368
+ ["pipx", "inject", service_name, dep],
1369
+ capture_output=True,
1370
+ text=True,
1371
+ timeout=60,
1372
+ check=False,
1373
+ )
1374
+
1375
+ if result.returncode == 0:
1376
+ self.logger.debug(f" ✅ Successfully injected {dep}")
1377
+ # Check if already injected (pipx will complain if package already exists)
1378
+ elif (
1379
+ "already satisfied" in result.stderr.lower()
1380
+ or "already installed" in result.stderr.lower()
1381
+ ):
1382
+ self.logger.debug(f" {dep} already present in {service_name}")
1383
+ else:
1384
+ self.logger.error(f" Failed to inject {dep}: {result.stderr}")
1385
+ all_successful = False
1386
+
1387
+ except subprocess.TimeoutExpired:
1388
+ self.logger.error(f" Timeout while injecting {dep}")
1389
+ all_successful = False
1390
+ except Exception as e:
1391
+ self.logger.error(f" Error injecting {dep}: {e}")
1392
+ all_successful = False
1393
+
1394
+ return all_successful
1395
+
1396
+ def _auto_reinstall_mcp_service(self, service_name: str) -> bool:
1397
+ """
1398
+ Automatically reinstall an MCP service with missing dependencies.
1399
+
1400
+ This method:
1401
+ 1. Uninstalls the corrupted/incomplete service
1402
+ 2. Reinstalls it fresh from pipx
1403
+ 3. Verifies the reinstall was successful
1404
+ 4. Updates status after successful reinstall
1405
+
1406
+ Args:
1407
+ service_name: Name of the MCP service to reinstall
1408
+
1409
+ Returns:
1410
+ True if reinstall successful, False otherwise
1411
+ """
1412
+ try:
1413
+ import shutil
1414
+
1415
+ # Verify pipx is available
1416
+ if not shutil.which("pipx"):
1417
+ self.logger.error("pipx not found - cannot auto-reinstall")
1418
+ return False
1419
+
1420
+ self.logger.info(f" → Uninstalling {service_name}...")
1421
+ uninstall_result = subprocess.run(
1422
+ ["pipx", "uninstall", service_name],
1423
+ capture_output=True,
1424
+ text=True,
1425
+ timeout=30,
1426
+ check=False,
1427
+ )
1428
+
1429
+ # Log result but don't fail if uninstall had issues
1430
+ if uninstall_result.returncode != 0:
1431
+ self.logger.debug(
1432
+ f"Uninstall had warnings (expected if corrupted): {uninstall_result.stderr}"
1433
+ )
1434
+
1435
+ self.logger.info(f" → Installing fresh {service_name}...")
1436
+ install_result = subprocess.run(
1437
+ ["pipx", "install", service_name],
1438
+ capture_output=True,
1439
+ text=True,
1440
+ timeout=120,
1441
+ check=False,
1442
+ )
1443
+
1444
+ if install_result.returncode != 0:
1445
+ self.logger.error(
1446
+ f"Install failed for {service_name}: {install_result.stderr}"
1447
+ )
1448
+ return False
1449
+
1450
+ # Inject any missing dependencies that pipx doesn't handle automatically
1451
+ if service_name in self.SERVICE_MISSING_DEPENDENCIES:
1452
+ self.logger.info(
1453
+ f" → Fixing missing dependencies for {service_name}..."
1454
+ )
1455
+ if not self._inject_missing_dependencies(service_name):
1456
+ self.logger.warning(
1457
+ f"Failed to inject all dependencies for {service_name}, but continuing..."
1458
+ )
1459
+
1460
+ # Verify the reinstall worked
1461
+ self.logger.debug(f" → Verifying {service_name} installation...")
1462
+ issue = self._detect_service_issue(service_name)
1463
+
1464
+ if issue is None:
1465
+ self.logger.info(f" ✅ Successfully reinstalled {service_name}")
1466
+ return True
1467
+
1468
+ # If still has missing dependency issue after injection, log specific instructions
1469
+ if issue == "missing_dependency" and service_name == "mcp-ticketer":
1470
+ self.logger.error(
1471
+ f" {service_name} still has missing dependencies after injection. "
1472
+ f"Manual fix: pipx inject {service_name} gql"
1473
+ )
1474
+ else:
1475
+ self.logger.warning(
1476
+ f"Reinstalled {service_name} but still has issue: {issue}"
1477
+ )
1478
+ return False
1479
+
1480
+ except subprocess.TimeoutExpired:
1481
+ self.logger.error(f"Timeout while reinstalling {service_name}")
1482
+ return False
1483
+ except Exception as e:
1484
+ self.logger.error(f"Error auto-reinstalling {service_name}: {e}")
1485
+ return False
1486
+
1487
+ def _verify_service_installed(self, service_name: str, method: str) -> bool:
1488
+ """
1489
+ Verify that a service was successfully installed and is functional.
1490
+
1491
+ Args:
1492
+ service_name: Name of the service
1493
+ method: Installation method used
1494
+
1495
+ Returns:
1496
+ True if service is installed and functional
1497
+ """
1498
+ import time
1499
+
1500
+ # Give the installation a moment to settle
1501
+ time.sleep(1)
1502
+
1503
+ # Note: mcp-ticketer dependency fix is now handled once in ensure_mcp_services_configured()
1504
+ # to avoid running the same pipx inject command multiple times
1505
+
1506
+ # Check if we can find the service
1507
+ service_path = self.detect_service_path(service_name)
1508
+ if not service_path:
1509
+ # Try pipx run as fallback for pipx installations
1510
+ if method == "pipx":
1511
+ try:
1512
+ result = subprocess.run(
1513
+ ["pipx", "run", service_name, "--version"],
1514
+ capture_output=True,
1515
+ text=True,
1516
+ timeout=10,
1517
+ check=False,
1518
+ )
1519
+ if result.returncode == 0 or "version" in result.stdout.lower():
1520
+ self.logger.debug(f"{service_name} accessible via 'pipx run'")
1521
+ return True
1522
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
1523
+ pass
1524
+ return False
1525
+
1526
+ # Try to verify it works
1527
+ try:
1528
+ # Different services may need different verification
1529
+ test_commands = [
1530
+ [service_path, "--version"],
1531
+ [service_path, "--help"],
1532
+ ]
1533
+
1534
+ for cmd in test_commands:
1535
+ result = subprocess.run(
1536
+ cmd,
1537
+ capture_output=True,
1538
+ text=True,
1539
+ timeout=10,
1540
+ check=False,
1541
+ )
1542
+
1543
+ output = (result.stdout + result.stderr).lower()
1544
+ # Check for signs of success
1545
+ if result.returncode == 0:
1546
+ return True
1547
+ # Some tools return non-zero but still work
1548
+ if any(
1549
+ indicator in output
1550
+ for indicator in ["version", "usage", "help", service_name.lower()]
1551
+ ):
1552
+ # Make sure it's not an error message
1553
+ if not any(
1554
+ error in output
1555
+ for error in ["error", "not found", "traceback", "no such"]
1556
+ ):
1557
+ return True
1558
+ except Exception as e:
1559
+ self.logger.debug(f"Verification error for {service_name}: {e}")
1560
+
1561
+ return False
1562
+
1563
+ def _get_fallback_config(
1564
+ self, service_name: str, project_path: str
1565
+ ) -> Optional[Dict]:
1566
+ """
1567
+ Get a fallback configuration for a service if the primary config fails.
1568
+
1569
+ Args:
1570
+ service_name: Name of the MCP service
1571
+ project_path: Project path to use
1572
+
1573
+ Returns:
1574
+ Fallback configuration or None
1575
+ """
1576
+ # Special fallback for mcp-vector-search using pipx run
1577
+ if service_name == "mcp-vector-search":
1578
+ return {
1579
+ "type": "stdio",
1580
+ "command": "pipx",
1581
+ "args": [
1582
+ "run",
1583
+ "--spec",
1584
+ "mcp-vector-search",
1585
+ "python",
1586
+ "-m",
1587
+ "mcp_vector_search.mcp.server",
1588
+ project_path,
1589
+ ],
1590
+ "env": {},
1591
+ }
1592
+
1593
+ # For other services, try pipx run
1594
+ return None
1595
+
1596
+ def get_filtered_services(self) -> Dict[str, Dict]:
1597
+ """Get all MCP service configurations filtered by startup configuration.
1598
+
1599
+ Returns:
1600
+ Dictionary of service configurations, filtered based on startup settings
1601
+ """
1602
+ filtered_services = {}
1603
+
1604
+ for service_name in self.STATIC_MCP_CONFIGS:
1605
+ if self.should_enable_service(service_name):
1606
+ # Get the actual service configuration with proper paths
1607
+ service_config = self.get_static_service_config(service_name)
1608
+ if service_config:
1609
+ filtered_services[service_name] = service_config
1610
+ # Removed noisy debug logging that was called multiple times per startup
1611
+
1612
+ return filtered_services