claude-mpm 3.9.9__py3-none-any.whl → 4.0.3__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/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +143 -762
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1150
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- 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 +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -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 +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -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/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 +159 -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 +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -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/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -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 +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- 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 +4 -3
- 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 +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +571 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +40 -23
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +14 -21
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +110 -121
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +20 -534
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- 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 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -7,22 +7,22 @@ Defines and validates the configuration schema for MCP Gateway. | |
| 7 7 | 
             
            Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation
         | 
| 8 8 | 
             
            """
         | 
| 9 9 |  | 
| 10 | 
            -
            from typing import Any, Dict, List, Optional
         | 
| 11 10 | 
             
            from dataclasses import dataclass
         | 
| 11 | 
            +
            from typing import Any, Dict, List, Optional
         | 
| 12 12 |  | 
| 13 13 |  | 
| 14 14 | 
             
            @dataclass
         | 
| 15 15 | 
             
            class MCPConfigSchema:
         | 
| 16 16 | 
             
                """
         | 
| 17 17 | 
             
                Configuration schema definition for MCP Gateway.
         | 
| 18 | 
            -
             | 
| 18 | 
            +
             | 
| 19 19 | 
             
                This class defines the structure and validation rules for
         | 
| 20 20 | 
             
                MCP Gateway configuration.
         | 
| 21 21 | 
             
                """
         | 
| 22 | 
            -
             | 
| 22 | 
            +
             | 
| 23 23 | 
             
                # Schema version for migration support
         | 
| 24 24 | 
             
                SCHEMA_VERSION = "1.0.0"
         | 
| 25 | 
            -
             | 
| 25 | 
            +
             | 
| 26 26 | 
             
                # Configuration schema definition
         | 
| 27 27 | 
             
                SCHEMA = {
         | 
| 28 28 | 
             
                    "mcp": {
         | 
| @@ -43,21 +43,21 @@ class MCPConfigSchema: | |
| 43 43 | 
             
                                            "type": {
         | 
| 44 44 | 
             
                                                "type": "string",
         | 
| 45 45 | 
             
                                                "required": True,
         | 
| 46 | 
            -
                                                "enum": ["stdio", "websocket", "http"]
         | 
| 46 | 
            +
                                                "enum": ["stdio", "websocket", "http"],
         | 
| 47 47 | 
             
                                            },
         | 
| 48 48 | 
             
                                            "timeout": {
         | 
| 49 49 | 
             
                                                "type": "number",
         | 
| 50 50 | 
             
                                                "required": False,
         | 
| 51 51 | 
             
                                                "min": 1,
         | 
| 52 | 
            -
                                                "max": 3600
         | 
| 52 | 
            +
                                                "max": 3600,
         | 
| 53 53 | 
             
                                            },
         | 
| 54 54 | 
             
                                            "buffer_size": {
         | 
| 55 55 | 
             
                                                "type": "integer",
         | 
| 56 56 | 
             
                                                "required": False,
         | 
| 57 57 | 
             
                                                "min": 1024,
         | 
| 58 | 
            -
                                                "max": 1048576
         | 
| 58 | 
            +
                                                "max": 1048576,
         | 
| 59 59 | 
             
                                            },
         | 
| 60 | 
            -
                                        }
         | 
| 60 | 
            +
                                        },
         | 
| 61 61 | 
             
                                    },
         | 
| 62 62 | 
             
                                    "capabilities": {
         | 
| 63 63 | 
             
                                        "type": "object",
         | 
| @@ -66,9 +66,9 @@ class MCPConfigSchema: | |
| 66 66 | 
             
                                            "tools": {"type": "boolean", "required": False},
         | 
| 67 67 | 
             
                                            "resources": {"type": "boolean", "required": False},
         | 
| 68 68 | 
             
                                            "prompts": {"type": "boolean", "required": False},
         | 
| 69 | 
            -
                                        }
         | 
| 69 | 
            +
                                        },
         | 
| 70 70 | 
             
                                    },
         | 
| 71 | 
            -
                                }
         | 
| 71 | 
            +
                                },
         | 
| 72 72 | 
             
                            },
         | 
| 73 73 | 
             
                            "tools": {
         | 
| 74 74 | 
             
                                "type": "object",
         | 
| @@ -79,21 +79,21 @@ class MCPConfigSchema: | |
| 79 79 | 
             
                                    "discovery_paths": {
         | 
| 80 80 | 
             
                                        "type": "array",
         | 
| 81 81 | 
             
                                        "required": False,
         | 
| 82 | 
            -
                                        "items": {"type": "string"}
         | 
| 82 | 
            +
                                        "items": {"type": "string"},
         | 
| 83 83 | 
             
                                    },
         | 
| 84 84 | 
             
                                    "timeout_default": {
         | 
| 85 85 | 
             
                                        "type": "number",
         | 
| 86 86 | 
             
                                        "required": False,
         | 
| 87 87 | 
             
                                        "min": 1,
         | 
| 88 | 
            -
                                        "max": 300
         | 
| 88 | 
            +
                                        "max": 300,
         | 
| 89 89 | 
             
                                    },
         | 
| 90 90 | 
             
                                    "max_concurrent": {
         | 
| 91 91 | 
             
                                        "type": "integer",
         | 
| 92 92 | 
             
                                        "required": False,
         | 
| 93 93 | 
             
                                        "min": 1,
         | 
| 94 | 
            -
                                        "max": 100
         | 
| 94 | 
            +
                                        "max": 100,
         | 
| 95 95 | 
             
                                    },
         | 
| 96 | 
            -
                                }
         | 
| 96 | 
            +
                                },
         | 
| 97 97 | 
             
                            },
         | 
| 98 98 | 
             
                            "logging": {
         | 
| 99 99 | 
             
                                "type": "object",
         | 
| @@ -102,7 +102,7 @@ class MCPConfigSchema: | |
| 102 102 | 
             
                                    "level": {
         | 
| 103 103 | 
             
                                        "type": "string",
         | 
| 104 104 | 
             
                                        "required": False,
         | 
| 105 | 
            -
                                        "enum": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
         | 
| 105 | 
            +
                                        "enum": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
         | 
| 106 106 | 
             
                                    },
         | 
| 107 107 | 
             
                                    "file": {"type": "string", "required": False},
         | 
| 108 108 | 
             
                                    "max_size": {"type": "string", "required": False},
         | 
| @@ -110,14 +110,14 @@ class MCPConfigSchema: | |
| 110 110 | 
             
                                        "type": "integer",
         | 
| 111 111 | 
             
                                        "required": False,
         | 
| 112 112 | 
             
                                        "min": 1,
         | 
| 113 | 
            -
                                        "max": 100
         | 
| 113 | 
            +
                                        "max": 100,
         | 
| 114 114 | 
             
                                    },
         | 
| 115 115 | 
             
                                    "format": {
         | 
| 116 116 | 
             
                                        "type": "string",
         | 
| 117 117 | 
             
                                        "required": False,
         | 
| 118 | 
            -
                                        "enum": ["json", "text"]
         | 
| 118 | 
            +
                                        "enum": ["json", "text"],
         | 
| 119 119 | 
             
                                    },
         | 
| 120 | 
            -
                                }
         | 
| 120 | 
            +
                                },
         | 
| 121 121 | 
             
                            },
         | 
| 122 122 | 
             
                            "security": {
         | 
| 123 123 | 
             
                                "type": "object",
         | 
| @@ -129,44 +129,46 @@ class MCPConfigSchema: | |
| 129 129 | 
             
                                        "type": "integer",
         | 
| 130 130 | 
             
                                        "required": False,
         | 
| 131 131 | 
             
                                        "min": 1024,
         | 
| 132 | 
            -
                                        "max": 104857600  # 100MB max
         | 
| 132 | 
            +
                                        "max": 104857600,  # 100MB max
         | 
| 133 133 | 
             
                                    },
         | 
| 134 134 | 
             
                                    "allowed_tools": {
         | 
| 135 135 | 
             
                                        "type": "array",
         | 
| 136 136 | 
             
                                        "required": False,
         | 
| 137 | 
            -
                                        "items": {"type": "string"}
         | 
| 137 | 
            +
                                        "items": {"type": "string"},
         | 
| 138 138 | 
             
                                    },
         | 
| 139 139 | 
             
                                    "blocked_tools": {
         | 
| 140 140 | 
             
                                        "type": "array",
         | 
| 141 141 | 
             
                                        "required": False,
         | 
| 142 | 
            -
                                        "items": {"type": "string"}
         | 
| 142 | 
            +
                                        "items": {"type": "string"},
         | 
| 143 143 | 
             
                                    },
         | 
| 144 | 
            -
                                }
         | 
| 144 | 
            +
                                },
         | 
| 145 145 | 
             
                            },
         | 
| 146 | 
            -
                        }
         | 
| 146 | 
            +
                        },
         | 
| 147 147 | 
             
                    }
         | 
| 148 148 | 
             
                }
         | 
| 149 149 |  | 
| 150 150 |  | 
| 151 | 
            -
            def validate_config( | 
| 151 | 
            +
            def validate_config(
         | 
| 152 | 
            +
                config: Dict[str, Any], schema: Optional[Dict[str, Any]] = None
         | 
| 153 | 
            +
            ) -> List[str]:
         | 
| 152 154 | 
             
                """
         | 
| 153 155 | 
             
                Validate configuration against schema.
         | 
| 154 | 
            -
             | 
| 156 | 
            +
             | 
| 155 157 | 
             
                Args:
         | 
| 156 158 | 
             
                    config: Configuration dictionary to validate
         | 
| 157 159 | 
             
                    schema: Schema to validate against (uses default if not provided)
         | 
| 158 | 
            -
             | 
| 160 | 
            +
             | 
| 159 161 | 
             
                Returns:
         | 
| 160 162 | 
             
                    List of validation errors (empty if valid)
         | 
| 161 163 | 
             
                """
         | 
| 162 164 | 
             
                if schema is None:
         | 
| 163 165 | 
             
                    schema = MCPConfigSchema.SCHEMA
         | 
| 164 | 
            -
             | 
| 166 | 
            +
             | 
| 165 167 | 
             
                errors = []
         | 
| 166 | 
            -
             | 
| 168 | 
            +
             | 
| 167 169 | 
             
                def validate_value(value: Any, spec: Dict[str, Any], path: str) -> None:
         | 
| 168 170 | 
             
                    """Recursively validate a value against its specification."""
         | 
| 169 | 
            -
             | 
| 171 | 
            +
             | 
| 170 172 | 
             
                    # Check type
         | 
| 171 173 | 
             
                    expected_type = spec.get("type")
         | 
| 172 174 | 
             
                    if expected_type:
         | 
| @@ -188,47 +190,54 @@ def validate_config(config: Dict[str, Any], schema: Optional[Dict[str, Any]] = N | |
| 188 190 | 
             
                        elif expected_type == "boolean" and not isinstance(value, bool):
         | 
| 189 191 | 
             
                            errors.append(f"{path}: Expected boolean, got {type(value).__name__}")
         | 
| 190 192 | 
             
                            return
         | 
| 191 | 
            -
             | 
| 193 | 
            +
             | 
| 192 194 | 
             
                    # Check enum values
         | 
| 193 195 | 
             
                    if "enum" in spec and value not in spec["enum"]:
         | 
| 194 | 
            -
                        errors.append( | 
| 195 | 
            -
             | 
| 196 | 
            +
                        errors.append(
         | 
| 197 | 
            +
                            f"{path}: Value '{value}' not in allowed values: {spec['enum']}"
         | 
| 198 | 
            +
                        )
         | 
| 199 | 
            +
             | 
| 196 200 | 
             
                    # Check numeric constraints
         | 
| 197 201 | 
             
                    if isinstance(value, (int, float)):
         | 
| 198 202 | 
             
                        if "min" in spec and value < spec["min"]:
         | 
| 199 | 
            -
                            errors.append( | 
| 203 | 
            +
                            errors.append(
         | 
| 204 | 
            +
                                f"{path}: Value {value} is less than minimum {spec['min']}"
         | 
| 205 | 
            +
                            )
         | 
| 200 206 | 
             
                        if "max" in spec and value > spec["max"]:
         | 
| 201 | 
            -
                            errors.append( | 
| 202 | 
            -
             | 
| 207 | 
            +
                            errors.append(
         | 
| 208 | 
            +
                                f"{path}: Value {value} is greater than maximum {spec['max']}"
         | 
| 209 | 
            +
                            )
         | 
| 210 | 
            +
             | 
| 203 211 | 
             
                    # Validate object properties
         | 
| 204 212 | 
             
                    if expected_type == "object" and isinstance(value, dict):
         | 
| 205 213 | 
             
                        properties = spec.get("properties", {})
         | 
| 206 214 | 
             
                        for prop_name, prop_spec in properties.items():
         | 
| 207 215 | 
             
                            prop_path = f"{path}.{prop_name}"
         | 
| 208 | 
            -
             | 
| 216 | 
            +
             | 
| 209 217 | 
             
                            if prop_name in value:
         | 
| 210 218 | 
             
                                validate_value(value[prop_name], prop_spec, prop_path)
         | 
| 211 219 | 
             
                            elif prop_spec.get("required", False):
         | 
| 212 220 | 
             
                                errors.append(f"{prop_path}: Required field missing")
         | 
| 213 | 
            -
             | 
| 221 | 
            +
             | 
| 214 222 | 
             
                    # Validate array items
         | 
| 215 223 | 
             
                    if expected_type == "array" and isinstance(value, list):
         | 
| 216 224 | 
             
                        item_spec = spec.get("items", {})
         | 
| 217 225 | 
             
                        for i, item in enumerate(value):
         | 
| 218 226 | 
             
                            validate_value(item, item_spec, f"{path}[{i}]")
         | 
| 219 | 
            -
             | 
| 227 | 
            +
             | 
| 220 228 | 
             
                # Start validation from root
         | 
| 221 229 | 
             
                validate_value(config, {"type": "object", "properties": schema}, "config")
         | 
| 222 | 
            -
             | 
| 230 | 
            +
             | 
| 223 231 | 
             
                return errors
         | 
| 224 232 |  | 
| 225 233 |  | 
| 226 234 | 
             
            def generate_config_template() -> Dict[str, Any]:
         | 
| 227 235 | 
             
                """
         | 
| 228 236 | 
             
                Generate a configuration template with all possible options.
         | 
| 229 | 
            -
             | 
| 237 | 
            +
             | 
| 230 238 | 
             
                Returns:
         | 
| 231 239 | 
             
                    Configuration template dictionary
         | 
| 232 240 | 
             
                """
         | 
| 233 241 | 
             
                from ..config.configuration import MCPConfiguration
         | 
| 234 | 
            -
             | 
| 242 | 
            +
             | 
| 243 | 
            +
                return MCPConfiguration.DEFAULT_CONFIG
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            from pathlib import Path
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            """
         | 
| 2 4 | 
             
            MCP Gateway Configuration Implementation
         | 
| 3 5 | 
             
            ========================================
         | 
| @@ -9,27 +11,27 @@ Part of ISS-0034: Infrastructure Setup - MCP Gateway Project Foundation | |
| 9 11 |  | 
| 10 12 | 
             
            import os
         | 
| 11 13 | 
             
            from typing import Any, Dict, Optional
         | 
| 12 | 
            -
             | 
| 14 | 
            +
             | 
| 13 15 | 
             
            import yaml
         | 
| 14 16 |  | 
| 15 | 
            -
            from claude_mpm.services.mcp_gateway.core.interfaces import IMCPConfiguration
         | 
| 16 17 | 
             
            from claude_mpm.services.mcp_gateway.core.base import BaseMCPService
         | 
| 17 18 | 
             
            from claude_mpm.services.mcp_gateway.core.exceptions import MCPConfigurationError
         | 
| 19 | 
            +
            from claude_mpm.services.mcp_gateway.core.interfaces import IMCPConfiguration
         | 
| 18 20 |  | 
| 19 21 |  | 
| 20 22 | 
             
            class MCPConfiguration(BaseMCPService, IMCPConfiguration):
         | 
| 21 23 | 
             
                """
         | 
| 22 24 | 
             
                MCP Gateway configuration management service.
         | 
| 23 | 
            -
             | 
| 25 | 
            +
             | 
| 24 26 | 
             
                This service handles loading, validation, and access to MCP Gateway configuration.
         | 
| 25 27 | 
             
                It supports YAML-based configuration files and environment variable overrides.
         | 
| 26 | 
            -
             | 
| 28 | 
            +
             | 
| 27 29 | 
             
                WHY: Configuration is centralized in a service to ensure consistent access
         | 
| 28 30 | 
             
                patterns, validation, and the ability to reload configuration at runtime.
         | 
| 29 31 | 
             
                The service pattern also allows for dependency injection of configuration
         | 
| 30 32 | 
             
                into other MCP services.
         | 
| 31 33 | 
             
                """
         | 
| 32 | 
            -
             | 
| 34 | 
            +
             | 
| 33 35 | 
             
                DEFAULT_CONFIG = {
         | 
| 34 36 | 
             
                    "mcp": {
         | 
| 35 37 | 
             
                        "server": {
         | 
| @@ -44,7 +46,7 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 44 46 | 
             
                            "capabilities": {
         | 
| 45 47 | 
             
                                "tools": True,
         | 
| 46 48 | 
             
                                "resources": False,  # Not yet implemented
         | 
| 47 | 
            -
                                "prompts": False, | 
| 49 | 
            +
                                "prompts": False,  # Not yet implemented
         | 
| 48 50 | 
             
                            },
         | 
| 49 51 | 
             
                        },
         | 
| 50 52 | 
             
                        "tools": {
         | 
| @@ -73,11 +75,11 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 73 75 | 
             
                        },
         | 
| 74 76 | 
             
                    }
         | 
| 75 77 | 
             
                }
         | 
| 76 | 
            -
             | 
| 78 | 
            +
             | 
| 77 79 | 
             
                def __init__(self, config_path: Optional[Path] = None):
         | 
| 78 80 | 
             
                    """
         | 
| 79 81 | 
             
                    Initialize MCP configuration service.
         | 
| 80 | 
            -
             | 
| 82 | 
            +
             | 
| 81 83 | 
             
                    Args:
         | 
| 82 84 | 
             
                        config_path: Optional path to configuration file
         | 
| 83 85 | 
             
                    """
         | 
| @@ -85,76 +87,76 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 85 87 | 
             
                    self._config_path = config_path
         | 
| 86 88 | 
             
                    self._config_data: Dict[str, Any] = {}
         | 
| 87 89 | 
             
                    self._is_loaded = False
         | 
| 88 | 
            -
             | 
| 90 | 
            +
             | 
| 89 91 | 
             
                async def _do_initialize(self) -> bool:
         | 
| 90 92 | 
             
                    """
         | 
| 91 93 | 
             
                    Initialize the configuration service.
         | 
| 92 | 
            -
             | 
| 94 | 
            +
             | 
| 93 95 | 
             
                    Returns:
         | 
| 94 96 | 
             
                        True if initialization successful
         | 
| 95 97 | 
             
                    """
         | 
| 96 98 | 
             
                    # Start with default configuration
         | 
| 97 99 | 
             
                    self._config_data = self.DEFAULT_CONFIG.copy()
         | 
| 98 | 
            -
             | 
| 100 | 
            +
             | 
| 99 101 | 
             
                    # Load from file if path provided
         | 
| 100 102 | 
             
                    if self._config_path:
         | 
| 101 103 | 
             
                        if not self.load_config(self._config_path):
         | 
| 102 104 | 
             
                            return False
         | 
| 103 | 
            -
             | 
| 105 | 
            +
             | 
| 104 106 | 
             
                    # Apply environment variable overrides
         | 
| 105 107 | 
             
                    self._apply_env_overrides()
         | 
| 106 | 
            -
             | 
| 108 | 
            +
             | 
| 107 109 | 
             
                    # Validate configuration
         | 
| 108 110 | 
             
                    if not self.validate():
         | 
| 109 111 | 
             
                        return False
         | 
| 110 | 
            -
             | 
| 112 | 
            +
             | 
| 111 113 | 
             
                    self._is_loaded = True
         | 
| 112 114 | 
             
                    self.log_info("Configuration initialized successfully")
         | 
| 113 115 | 
             
                    return True
         | 
| 114 | 
            -
             | 
| 116 | 
            +
             | 
| 115 117 | 
             
                def load_config(self, config_path: Path) -> bool:
         | 
| 116 118 | 
             
                    """
         | 
| 117 119 | 
             
                    Load configuration from a file.
         | 
| 118 | 
            -
             | 
| 120 | 
            +
             | 
| 119 121 | 
             
                    Args:
         | 
| 120 122 | 
             
                        config_path: Path to configuration file
         | 
| 121 | 
            -
             | 
| 123 | 
            +
             | 
| 122 124 | 
             
                    Returns:
         | 
| 123 125 | 
             
                        True if configuration loaded successfully
         | 
| 124 126 | 
             
                    """
         | 
| 125 127 | 
             
                    try:
         | 
| 126 128 | 
             
                        # Expand user path
         | 
| 127 129 | 
             
                        config_path = Path(config_path).expanduser()
         | 
| 128 | 
            -
             | 
| 130 | 
            +
             | 
| 129 131 | 
             
                        if not config_path.exists():
         | 
| 130 132 | 
             
                            self.log_warning(f"Configuration file not found: {config_path}")
         | 
| 131 133 | 
             
                            return True  # Not an error, use defaults
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                        with open(config_path,  | 
| 134 | 
            -
                            if config_path.suffix in [ | 
| 134 | 
            +
             | 
| 135 | 
            +
                        with open(config_path, "r") as f:
         | 
| 136 | 
            +
                            if config_path.suffix in [".yaml", ".yml"]:
         | 
| 135 137 | 
             
                                loaded_config = yaml.safe_load(f) or {}
         | 
| 136 138 | 
             
                            else:
         | 
| 137 139 | 
             
                                raise MCPConfigurationError(
         | 
| 138 140 | 
             
                                    f"Unsupported configuration file format: {config_path.suffix}"
         | 
| 139 141 | 
             
                                )
         | 
| 140 | 
            -
             | 
| 142 | 
            +
             | 
| 141 143 | 
             
                        # Merge with existing configuration
         | 
| 142 144 | 
             
                        self._merge_config(self._config_data, loaded_config)
         | 
| 143 145 | 
             
                        self._config_path = config_path
         | 
| 144 | 
            -
             | 
| 146 | 
            +
             | 
| 145 147 | 
             
                        self.log_info(f"Configuration loaded from {config_path}")
         | 
| 146 148 | 
             
                        return True
         | 
| 147 | 
            -
             | 
| 149 | 
            +
             | 
| 148 150 | 
             
                    except yaml.YAMLError as e:
         | 
| 149 151 | 
             
                        raise MCPConfigurationError(f"Failed to parse YAML configuration: {e}")
         | 
| 150 152 | 
             
                    except Exception as e:
         | 
| 151 153 | 
             
                        self.log_error(f"Failed to load configuration: {e}")
         | 
| 152 154 | 
             
                        return False
         | 
| 153 | 
            -
             | 
| 155 | 
            +
             | 
| 154 156 | 
             
                def _merge_config(self, base: Dict[str, Any], overlay: Dict[str, Any]) -> None:
         | 
| 155 157 | 
             
                    """
         | 
| 156 158 | 
             
                    Recursively merge overlay configuration into base.
         | 
| 157 | 
            -
             | 
| 159 | 
            +
             | 
| 158 160 | 
             
                    Args:
         | 
| 159 161 | 
             
                        base: Base configuration dictionary
         | 
| 160 162 | 
             
                        overlay: Configuration to merge in
         | 
| @@ -164,30 +166,32 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 164 166 | 
             
                            self._merge_config(base[key], value)
         | 
| 165 167 | 
             
                        else:
         | 
| 166 168 | 
             
                            base[key] = value
         | 
| 167 | 
            -
             | 
| 169 | 
            +
             | 
| 168 170 | 
             
                def _apply_env_overrides(self) -> None:
         | 
| 169 171 | 
             
                    """
         | 
| 170 172 | 
             
                    Apply environment variable overrides to configuration.
         | 
| 171 | 
            -
             | 
| 173 | 
            +
             | 
| 172 174 | 
             
                    Environment variables follow the pattern: MCP_GATEWAY_<SECTION>_<KEY>
         | 
| 173 175 | 
             
                    For example: MCP_GATEWAY_SERVER_NAME=my-server
         | 
| 174 176 | 
             
                    """
         | 
| 175 177 | 
             
                    prefix = "MCP_GATEWAY_"
         | 
| 176 | 
            -
             | 
| 178 | 
            +
             | 
| 177 179 | 
             
                    for env_key, env_value in os.environ.items():
         | 
| 178 180 | 
             
                        if not env_key.startswith(prefix):
         | 
| 179 181 | 
             
                            continue
         | 
| 180 | 
            -
             | 
| 182 | 
            +
             | 
| 181 183 | 
             
                        # Parse environment variable into configuration path
         | 
| 182 | 
            -
                        config_path = env_key[len(prefix):].lower().split( | 
| 183 | 
            -
             | 
| 184 | 
            +
                        config_path = env_key[len(prefix) :].lower().split("_")
         | 
| 185 | 
            +
             | 
| 184 186 | 
             
                        # Navigate to the configuration location
         | 
| 185 187 | 
             
                        current = self._config_data
         | 
| 186 188 | 
             
                        for i, part in enumerate(config_path[:-1]):
         | 
| 187 189 | 
             
                            if part not in current:
         | 
| 188 190 | 
             
                                current[part] = {}
         | 
| 189 191 | 
             
                            elif not isinstance(current[part], dict):
         | 
| 190 | 
            -
                                self.log_warning( | 
| 192 | 
            +
                                self.log_warning(
         | 
| 193 | 
            +
                                    f"Cannot override non-dict config at {'.'.join(config_path[:i+1])}"
         | 
| 194 | 
            +
                                )
         | 
| 191 195 | 
             
                                break
         | 
| 192 196 | 
             
                            current = current[part]
         | 
| 193 197 | 
             
                        else:
         | 
| @@ -196,46 +200,47 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 196 200 | 
             
                            # Try to parse as JSON for complex types
         | 
| 197 201 | 
             
                            try:
         | 
| 198 202 | 
             
                                import json
         | 
| 203 | 
            +
             | 
| 199 204 | 
             
                                current[key] = json.loads(env_value)
         | 
| 200 205 | 
             
                            except:
         | 
| 201 206 | 
             
                                # Fall back to string value
         | 
| 202 207 | 
             
                                current[key] = env_value
         | 
| 203 | 
            -
             | 
| 208 | 
            +
             | 
| 204 209 | 
             
                            self.log_debug(f"Applied environment override: {env_key}")
         | 
| 205 | 
            -
             | 
| 210 | 
            +
             | 
| 206 211 | 
             
                def get(self, key: str, default: Any = None) -> Any:
         | 
| 207 212 | 
             
                    """
         | 
| 208 213 | 
             
                    Get configuration value by key.
         | 
| 209 | 
            -
             | 
| 214 | 
            +
             | 
| 210 215 | 
             
                    Args:
         | 
| 211 216 | 
             
                        key: Configuration key (supports dot notation, e.g., "mcp.server.name")
         | 
| 212 217 | 
             
                        default: Default value if key not found
         | 
| 213 | 
            -
             | 
| 218 | 
            +
             | 
| 214 219 | 
             
                    Returns:
         | 
| 215 220 | 
             
                        Configuration value or default
         | 
| 216 221 | 
             
                    """
         | 
| 217 | 
            -
                    parts = key.split( | 
| 222 | 
            +
                    parts = key.split(".")
         | 
| 218 223 | 
             
                    current = self._config_data
         | 
| 219 | 
            -
             | 
| 224 | 
            +
             | 
| 220 225 | 
             
                    for part in parts:
         | 
| 221 226 | 
             
                        if isinstance(current, dict) and part in current:
         | 
| 222 227 | 
             
                            current = current[part]
         | 
| 223 228 | 
             
                        else:
         | 
| 224 229 | 
             
                            return default
         | 
| 225 | 
            -
             | 
| 230 | 
            +
             | 
| 226 231 | 
             
                    return current
         | 
| 227 | 
            -
             | 
| 232 | 
            +
             | 
| 228 233 | 
             
                def set(self, key: str, value: Any) -> None:
         | 
| 229 234 | 
             
                    """
         | 
| 230 235 | 
             
                    Set configuration value.
         | 
| 231 | 
            -
             | 
| 236 | 
            +
             | 
| 232 237 | 
             
                    Args:
         | 
| 233 238 | 
             
                        key: Configuration key (supports dot notation)
         | 
| 234 239 | 
             
                        value: Configuration value
         | 
| 235 240 | 
             
                    """
         | 
| 236 | 
            -
                    parts = key.split( | 
| 241 | 
            +
                    parts = key.split(".")
         | 
| 237 242 | 
             
                    current = self._config_data
         | 
| 238 | 
            -
             | 
| 243 | 
            +
             | 
| 239 244 | 
             
                    # Navigate to parent
         | 
| 240 245 | 
             
                    for part in parts[:-1]:
         | 
| 241 246 | 
             
                        if part not in current:
         | 
| @@ -245,15 +250,15 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 245 250 | 
             
                                f"Cannot set value at {key}: parent is not a dictionary"
         | 
| 246 251 | 
             
                            )
         | 
| 247 252 | 
             
                        current = current[part]
         | 
| 248 | 
            -
             | 
| 253 | 
            +
             | 
| 249 254 | 
             
                    # Set the value
         | 
| 250 255 | 
             
                    current[parts[-1]] = value
         | 
| 251 256 | 
             
                    self.log_debug(f"Configuration updated: {key} = {value}")
         | 
| 252 | 
            -
             | 
| 257 | 
            +
             | 
| 253 258 | 
             
                def validate(self) -> bool:
         | 
| 254 259 | 
             
                    """
         | 
| 255 260 | 
             
                    Validate the current configuration.
         | 
| 256 | 
            -
             | 
| 261 | 
            +
             | 
| 257 262 | 
             
                    Returns:
         | 
| 258 263 | 
             
                        True if configuration is valid
         | 
| 259 264 | 
             
                    """
         | 
| @@ -264,66 +269,66 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 264 269 | 
             
                            "mcp.server.version",
         | 
| 265 270 | 
             
                            "mcp.server.communication.type",
         | 
| 266 271 | 
             
                        ]
         | 
| 267 | 
            -
             | 
| 272 | 
            +
             | 
| 268 273 | 
             
                        for field in required_fields:
         | 
| 269 274 | 
             
                            if self.get(field) is None:
         | 
| 270 275 | 
             
                                raise MCPConfigurationError(
         | 
| 271 276 | 
             
                                    f"Required configuration field missing: {field}",
         | 
| 272 | 
            -
                                    config_key=field
         | 
| 277 | 
            +
                                    config_key=field,
         | 
| 273 278 | 
             
                                )
         | 
| 274 | 
            -
             | 
| 279 | 
            +
             | 
| 275 280 | 
             
                        # Validate communication type
         | 
| 276 281 | 
             
                        comm_type = self.get("mcp.server.communication.type")
         | 
| 277 282 | 
             
                        if comm_type not in ["stdio", "websocket", "http"]:
         | 
| 278 283 | 
             
                            raise MCPConfigurationError(
         | 
| 279 284 | 
             
                                f"Invalid communication type: {comm_type}",
         | 
| 280 285 | 
             
                                config_key="mcp.server.communication.type",
         | 
| 281 | 
            -
                                expected_type="stdio|websocket|http"
         | 
| 286 | 
            +
                                expected_type="stdio|websocket|http",
         | 
| 282 287 | 
             
                            )
         | 
| 283 | 
            -
             | 
| 288 | 
            +
             | 
| 284 289 | 
             
                        # Validate numeric fields
         | 
| 285 290 | 
             
                        timeout = self.get("mcp.server.communication.timeout")
         | 
| 286 291 | 
             
                        if not isinstance(timeout, (int, float)) or timeout <= 0:
         | 
| 287 292 | 
             
                            raise MCPConfigurationError(
         | 
| 288 293 | 
             
                                "Invalid timeout value",
         | 
| 289 294 | 
             
                                config_key="mcp.server.communication.timeout",
         | 
| 290 | 
            -
                                expected_type="positive number"
         | 
| 295 | 
            +
                                expected_type="positive number",
         | 
| 291 296 | 
             
                            )
         | 
| 292 | 
            -
             | 
| 297 | 
            +
             | 
| 293 298 | 
             
                        self.log_debug("Configuration validation successful")
         | 
| 294 299 | 
             
                        return True
         | 
| 295 | 
            -
             | 
| 300 | 
            +
             | 
| 296 301 | 
             
                    except MCPConfigurationError:
         | 
| 297 302 | 
             
                        raise
         | 
| 298 303 | 
             
                    except Exception as e:
         | 
| 299 304 | 
             
                        self.log_error(f"Configuration validation failed: {e}")
         | 
| 300 305 | 
             
                        return False
         | 
| 301 | 
            -
             | 
| 306 | 
            +
             | 
| 302 307 | 
             
                def get_server_config(self) -> Dict[str, Any]:
         | 
| 303 308 | 
             
                    """
         | 
| 304 309 | 
             
                    Get MCP server configuration.
         | 
| 305 | 
            -
             | 
| 310 | 
            +
             | 
| 306 311 | 
             
                    Returns:
         | 
| 307 312 | 
             
                        Server configuration dictionary
         | 
| 308 313 | 
             
                    """
         | 
| 309 314 | 
             
                    return self.get("mcp.server", {})
         | 
| 310 | 
            -
             | 
| 315 | 
            +
             | 
| 311 316 | 
             
                def get_tools_config(self) -> Dict[str, Any]:
         | 
| 312 317 | 
             
                    """
         | 
| 313 318 | 
             
                    Get tools configuration.
         | 
| 314 | 
            -
             | 
| 319 | 
            +
             | 
| 315 320 | 
             
                    Returns:
         | 
| 316 321 | 
             
                        Tools configuration dictionary
         | 
| 317 322 | 
             
                    """
         | 
| 318 323 | 
             
                    return self.get("mcp.tools", {})
         | 
| 319 | 
            -
             | 
| 324 | 
            +
             | 
| 320 325 | 
             
                def save_config(self, path: Optional[Path] = None) -> bool:
         | 
| 321 326 | 
             
                    """
         | 
| 322 327 | 
             
                    Save current configuration to file.
         | 
| 323 | 
            -
             | 
| 328 | 
            +
             | 
| 324 329 | 
             
                    Args:
         | 
| 325 330 | 
             
                        path: Path to save configuration (uses loaded path if not specified)
         | 
| 326 | 
            -
             | 
| 331 | 
            +
             | 
| 327 332 | 
             
                    Returns:
         | 
| 328 333 | 
             
                        True if save successful
         | 
| 329 334 | 
             
                    """
         | 
| @@ -331,41 +336,43 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration): | |
| 331 336 | 
             
                    if not save_path:
         | 
| 332 337 | 
             
                        self.log_error("No path specified for saving configuration")
         | 
| 333 338 | 
             
                        return False
         | 
| 334 | 
            -
             | 
| 339 | 
            +
             | 
| 335 340 | 
             
                    try:
         | 
| 336 341 | 
             
                        save_path = Path(save_path).expanduser()
         | 
| 337 342 | 
             
                        save_path.parent.mkdir(parents=True, exist_ok=True)
         | 
| 338 | 
            -
             | 
| 339 | 
            -
                        with open(save_path,  | 
| 340 | 
            -
                            yaml.dump( | 
| 341 | 
            -
             | 
| 343 | 
            +
             | 
| 344 | 
            +
                        with open(save_path, "w") as f:
         | 
| 345 | 
            +
                            yaml.dump(
         | 
| 346 | 
            +
                                self._config_data, f, default_flow_style=False, sort_keys=True
         | 
| 347 | 
            +
                            )
         | 
| 348 | 
            +
             | 
| 342 349 | 
             
                        self.log_info(f"Configuration saved to {save_path}")
         | 
| 343 350 | 
             
                        return True
         | 
| 344 | 
            -
             | 
| 351 | 
            +
             | 
| 345 352 | 
             
                    except Exception as e:
         | 
| 346 353 | 
             
                        self.log_error(f"Failed to save configuration: {e}")
         | 
| 347 354 | 
             
                        return False
         | 
| 348 | 
            -
             | 
| 355 | 
            +
             | 
| 349 356 | 
             
                def reload(self) -> bool:
         | 
| 350 357 | 
             
                    """
         | 
| 351 358 | 
             
                    Reload configuration from file.
         | 
| 352 | 
            -
             | 
| 359 | 
            +
             | 
| 353 360 | 
             
                    Returns:
         | 
| 354 361 | 
             
                        True if reload successful
         | 
| 355 362 | 
             
                    """
         | 
| 356 363 | 
             
                    if not self._config_path:
         | 
| 357 364 | 
             
                        self.log_warning("No configuration file to reload")
         | 
| 358 365 | 
             
                        return True
         | 
| 359 | 
            -
             | 
| 366 | 
            +
             | 
| 360 367 | 
             
                    # Reset to defaults
         | 
| 361 368 | 
             
                    self._config_data = self.DEFAULT_CONFIG.copy()
         | 
| 362 | 
            -
             | 
| 369 | 
            +
             | 
| 363 370 | 
             
                    # Reload from file
         | 
| 364 371 | 
             
                    if not self.load_config(self._config_path):
         | 
| 365 372 | 
             
                        return False
         | 
| 366 | 
            -
             | 
| 373 | 
            +
             | 
| 367 374 | 
             
                    # Reapply environment overrides
         | 
| 368 375 | 
             
                    self._apply_env_overrides()
         | 
| 369 | 
            -
             | 
| 376 | 
            +
             | 
| 370 377 | 
             
                    # Revalidate
         | 
| 371 | 
            -
                    return self.validate()
         | 
| 378 | 
            +
                    return self.validate()
         |