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