claude-mpm 4.3.12__py3-none-any.whl → 4.3.14__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.
Files changed (206) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/PM_INSTRUCTIONS.md +414 -28
  3. claude_mpm/agents/templates/data_engineer.json +39 -14
  4. claude_mpm/agents/templates/engineer.json +11 -3
  5. claude_mpm/cli/commands/agent_manager.py +3 -3
  6. claude_mpm/cli/commands/agents.py +2 -2
  7. claude_mpm/cli/commands/aggregate.py +1 -1
  8. claude_mpm/cli/commands/config.py +2 -2
  9. claude_mpm/cli/commands/configure.py +5 -5
  10. claude_mpm/cli/commands/configure_tui.py +7 -7
  11. claude_mpm/cli/commands/dashboard.py +1 -1
  12. claude_mpm/cli/commands/debug.py +5 -5
  13. claude_mpm/cli/commands/mcp.py +1 -1
  14. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  15. claude_mpm/cli/commands/mcp_config.py +7 -10
  16. claude_mpm/cli/commands/mcp_external_commands.py +40 -32
  17. claude_mpm/cli/commands/mcp_install_commands.py +38 -10
  18. claude_mpm/cli/commands/mcp_setup_external.py +143 -102
  19. claude_mpm/cli/commands/monitor.py +2 -2
  20. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  21. claude_mpm/cli/commands/run.py +54 -2
  22. claude_mpm/cli/commands/search.py +41 -34
  23. claude_mpm/cli/interactive/agent_wizard.py +6 -2
  24. claude_mpm/cli/parsers/mcp_parser.py +1 -3
  25. claude_mpm/cli/parsers/search_parser.py +10 -4
  26. claude_mpm/cli/startup_logging.py +158 -5
  27. claude_mpm/cli/utils.py +1 -1
  28. claude_mpm/core/agent_registry.py +2 -2
  29. claude_mpm/core/agent_session_manager.py +8 -8
  30. claude_mpm/core/api_validator.py +6 -4
  31. claude_mpm/core/base_service.py +10 -10
  32. claude_mpm/core/cache.py +5 -5
  33. claude_mpm/core/config_constants.py +1 -1
  34. claude_mpm/core/container.py +1 -1
  35. claude_mpm/core/error_handler.py +2 -2
  36. claude_mpm/core/file_utils.py +1 -1
  37. claude_mpm/core/framework_loader.py +3 -3
  38. claude_mpm/core/hook_manager.py +8 -6
  39. claude_mpm/core/instruction_reinforcement_hook.py +2 -2
  40. claude_mpm/core/interactive_session.py +3 -1
  41. claude_mpm/core/lazy.py +3 -3
  42. claude_mpm/core/log_manager.py +16 -12
  43. claude_mpm/core/logger.py +16 -11
  44. claude_mpm/core/optimized_agent_loader.py +6 -6
  45. claude_mpm/core/output_style_manager.py +1 -1
  46. claude_mpm/core/pm_hook_interceptor.py +3 -3
  47. claude_mpm/core/service_registry.py +1 -1
  48. claude_mpm/core/session_manager.py +11 -9
  49. claude_mpm/core/socketio_pool.py +13 -13
  50. claude_mpm/core/types.py +2 -2
  51. claude_mpm/core/unified_agent_registry.py +2 -2
  52. claude_mpm/core/unified_paths.py +1 -1
  53. claude_mpm/dashboard/analysis_runner.py +4 -4
  54. claude_mpm/dashboard/api/simple_directory.py +1 -1
  55. claude_mpm/generators/agent_profile_generator.py +4 -2
  56. claude_mpm/hooks/base_hook.py +2 -2
  57. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-313.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  63. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  64. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  65. claude_mpm/hooks/claude_hooks/event_handlers.py +12 -12
  66. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -4
  67. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +3 -3
  68. claude_mpm/hooks/claude_hooks/hook_handler_original.py +15 -14
  69. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +4 -4
  70. claude_mpm/hooks/claude_hooks/installer.py +3 -3
  71. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  72. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -3
  73. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  74. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-313.pyc +0 -0
  75. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  76. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  77. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  78. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  79. claude_mpm/hooks/claude_hooks/services/connection_manager.py +8 -5
  80. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +3 -3
  81. claude_mpm/hooks/claude_hooks/services/state_manager.py +8 -7
  82. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  83. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  84. claude_mpm/hooks/memory_integration_hook.py +1 -1
  85. claude_mpm/hooks/tool_call_interceptor.py +2 -2
  86. claude_mpm/models/agent_session.py +7 -5
  87. claude_mpm/scripts/mcp_server.py +0 -0
  88. claude_mpm/scripts/start_activity_logging.py +0 -0
  89. claude_mpm/services/__init__.py +1 -1
  90. claude_mpm/services/agent_capabilities_service.py +1 -1
  91. claude_mpm/services/agents/agent_builder.py +3 -3
  92. claude_mpm/services/agents/deployment/agent_deployment.py +2 -1
  93. claude_mpm/services/agents/deployment/agent_discovery_service.py +9 -3
  94. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +5 -5
  95. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +3 -1
  96. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  97. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  98. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  99. claude_mpm/services/agents/deployment/agent_template_builder.py +1 -1
  100. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  101. claude_mpm/services/agents/deployment/deployment_wrapper.py +2 -3
  102. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +1 -1
  103. claude_mpm/services/agents/loading/agent_profile_loader.py +5 -3
  104. claude_mpm/services/agents/loading/base_agent_manager.py +2 -2
  105. claude_mpm/services/agents/local_template_manager.py +6 -6
  106. claude_mpm/services/agents/management/agent_management_service.py +3 -3
  107. claude_mpm/services/agents/memory/content_manager.py +3 -3
  108. claude_mpm/services/agents/memory/memory_format_service.py +2 -2
  109. claude_mpm/services/agents/memory/template_generator.py +3 -3
  110. claude_mpm/services/agents/registry/modification_tracker.py +2 -2
  111. claude_mpm/services/async_session_logger.py +3 -3
  112. claude_mpm/services/claude_session_logger.py +4 -4
  113. claude_mpm/services/cli/agent_listing_service.py +3 -1
  114. claude_mpm/services/cli/agent_validation_service.py +2 -0
  115. claude_mpm/services/cli/memory_crud_service.py +11 -6
  116. claude_mpm/services/cli/memory_output_formatter.py +1 -1
  117. claude_mpm/services/cli/session_manager.py +15 -11
  118. claude_mpm/services/core/memory_manager.py +81 -23
  119. claude_mpm/services/core/path_resolver.py +1 -1
  120. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  121. claude_mpm/services/event_aggregator.py +4 -2
  122. claude_mpm/services/event_bus/direct_relay.py +5 -3
  123. claude_mpm/services/event_bus/event_bus.py +3 -3
  124. claude_mpm/services/event_bus/relay.py +6 -4
  125. claude_mpm/services/events/consumers/dead_letter.py +5 -3
  126. claude_mpm/services/events/core.py +3 -3
  127. claude_mpm/services/events/producers/hook.py +6 -6
  128. claude_mpm/services/events/producers/system.py +8 -8
  129. claude_mpm/services/exceptions.py +5 -5
  130. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -3
  131. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  132. claude_mpm/services/hook_installer_service.py +1 -1
  133. claude_mpm/services/infrastructure/context_preservation.py +6 -4
  134. claude_mpm/services/infrastructure/daemon_manager.py +2 -2
  135. claude_mpm/services/infrastructure/logging.py +2 -2
  136. claude_mpm/services/mcp_config_manager.py +175 -30
  137. claude_mpm/services/mcp_gateway/__init__.py +1 -1
  138. claude_mpm/services/mcp_gateway/auto_configure.py +3 -3
  139. claude_mpm/services/mcp_gateway/config/config_loader.py +1 -1
  140. claude_mpm/services/mcp_gateway/config/configuration.py +1 -1
  141. claude_mpm/services/mcp_gateway/core/base.py +2 -2
  142. claude_mpm/services/mcp_gateway/main.py +21 -7
  143. claude_mpm/services/mcp_gateway/registry/tool_registry.py +10 -8
  144. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +4 -4
  145. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  146. claude_mpm/services/mcp_gateway/server/stdio_server.py +5 -2
  147. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  148. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +7 -5
  149. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +190 -137
  150. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +5 -5
  151. claude_mpm/services/mcp_gateway/tools/hello_world.py +9 -9
  152. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +16 -16
  153. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +16 -16
  154. claude_mpm/services/memory/builder.py +7 -5
  155. claude_mpm/services/memory/indexed_memory.py +4 -4
  156. claude_mpm/services/memory/optimizer.py +6 -6
  157. claude_mpm/services/memory/router.py +3 -3
  158. claude_mpm/services/monitor/daemon.py +1 -1
  159. claude_mpm/services/monitor/daemon_manager.py +6 -6
  160. claude_mpm/services/monitor/event_emitter.py +2 -2
  161. claude_mpm/services/monitor/management/lifecycle.py +3 -1
  162. claude_mpm/services/monitor/server.py +4 -4
  163. claude_mpm/services/monitor_build_service.py +2 -2
  164. claude_mpm/services/port_manager.py +3 -1
  165. claude_mpm/services/response_tracker.py +2 -2
  166. claude_mpm/services/session_management_service.py +3 -2
  167. claude_mpm/services/socketio/client_proxy.py +2 -2
  168. claude_mpm/services/socketio/dashboard_server.py +4 -3
  169. claude_mpm/services/socketio/event_normalizer.py +11 -7
  170. claude_mpm/services/socketio/handlers/base.py +2 -2
  171. claude_mpm/services/socketio/handlers/connection.py +10 -10
  172. claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
  173. claude_mpm/services/socketio/handlers/hook.py +16 -15
  174. claude_mpm/services/socketio/migration_utils.py +1 -1
  175. claude_mpm/services/socketio/monitor_client.py +5 -5
  176. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  177. claude_mpm/services/socketio/server/connection_manager.py +2 -2
  178. claude_mpm/services/socketio/server/core.py +7 -5
  179. claude_mpm/services/socketio/server/eventbus_integration.py +18 -12
  180. claude_mpm/services/socketio/server/main.py +13 -13
  181. claude_mpm/services/socketio_client_manager.py +4 -4
  182. claude_mpm/services/system_instructions_service.py +2 -2
  183. claude_mpm/services/utility_service.py +5 -2
  184. claude_mpm/services/version_control/branch_strategy.py +2 -2
  185. claude_mpm/services/version_control/git_operations.py +22 -20
  186. claude_mpm/services/version_control/semantic_versioning.py +3 -3
  187. claude_mpm/services/version_control/version_parser.py +7 -5
  188. claude_mpm/services/visualization/mermaid_generator.py +3 -1
  189. claude_mpm/storage/state_storage.py +1 -1
  190. claude_mpm/tools/code_tree_analyzer.py +23 -18
  191. claude_mpm/tools/code_tree_builder.py +2 -2
  192. claude_mpm/tools/code_tree_events.py +10 -8
  193. claude_mpm/tools/socketio_debug.py +3 -3
  194. claude_mpm/utils/agent_dependency_loader.py +6 -2
  195. claude_mpm/utils/dependency_strategies.py +8 -3
  196. claude_mpm/utils/environment_context.py +1 -1
  197. claude_mpm/utils/error_handler.py +2 -2
  198. claude_mpm/utils/file_utils.py +1 -1
  199. claude_mpm/utils/log_cleanup.py +21 -7
  200. claude_mpm/validation/agent_validator.py +2 -2
  201. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/METADATA +1 -1
  202. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/RECORD +204 -191
  203. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/WHEEL +0 -0
  204. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/entry_points.txt +0 -0
  205. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/licenses/LICENSE +0 -0
  206. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ WHY separate relay component:
11
11
  import logging
12
12
  import os
13
13
  import time
14
- from datetime import datetime
14
+ from datetime import datetime, timezone
15
15
  from typing import Any, Dict, Optional
16
16
 
17
17
  # Socket.IO imports
@@ -151,7 +151,7 @@ class SocketIORelay:
151
151
  # Verify connection is still alive
152
152
  if self.client.connected:
153
153
  return True
154
- except:
154
+ except Exception:
155
155
  pass
156
156
 
157
157
  # Need to create or reconnect
@@ -187,7 +187,9 @@ class SocketIORelay:
187
187
  "subtype": (
188
188
  event_type.split(".", 1)[1] if "." in event_type else "generic"
189
189
  ),
190
- "timestamp": data.get("timestamp", datetime.now().isoformat()),
190
+ "timestamp": data.get(
191
+ "timestamp", datetime.now(timezone.utc).isoformat()
192
+ ),
191
193
  "data": data,
192
194
  "source": "event_bus",
193
195
  },
@@ -195,7 +197,7 @@ class SocketIORelay:
195
197
 
196
198
  # Update statistics
197
199
  self.stats["events_relayed"] += 1
198
- self.stats["last_relay_time"] = datetime.now().isoformat()
200
+ self.stats["last_relay_time"] = datetime.now(timezone.utc).isoformat()
199
201
 
200
202
  if self.debug:
201
203
  logger.debug(f"Relayed event to Socket.IO: {event_type}")
@@ -6,7 +6,7 @@ Handles events that failed processing in other consumers.
6
6
  """
7
7
 
8
8
  import json
9
- from datetime import datetime
9
+ from datetime import datetime, timezone
10
10
  from pathlib import Path
11
11
  from typing import Any, Dict, List, Optional
12
12
 
@@ -284,7 +284,7 @@ class DeadLetterConsumer(IEventConsumer):
284
284
 
285
285
  def _rotate_file(self) -> None:
286
286
  """Rotate to a new output file."""
287
- timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
287
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
288
288
  self._current_file = self.output_dir / f"dead-letter-{timestamp}.jsonl"
289
289
  self._current_file_size = 0
290
290
  self._metrics["files_created"] += 1
@@ -293,7 +293,9 @@ class DeadLetterConsumer(IEventConsumer):
293
293
 
294
294
  async def _cleanup_old_files(self) -> None:
295
295
  """Remove files older than retention period."""
296
- cutoff_time = datetime.now().timestamp() - (self.retention_days * 86400)
296
+ cutoff_time = datetime.now(timezone.utc).timestamp() - (
297
+ self.retention_days * 86400
298
+ )
297
299
 
298
300
  for file_path in self.output_dir.glob("dead-letter-*.jsonl"):
299
301
  try:
@@ -12,7 +12,7 @@ import time
12
12
  import uuid
13
13
  from collections import defaultdict, deque
14
14
  from dataclasses import dataclass, field
15
- from datetime import datetime
15
+ from datetime import datetime, timezone
16
16
  from enum import Enum
17
17
  from typing import Any, Deque, Dict, List, Optional, Set
18
18
 
@@ -225,7 +225,7 @@ class EventBus(IEventBus):
225
225
 
226
226
  # Add metadata
227
227
  if event.metadata:
228
- event.metadata.published_at = datetime.now()
228
+ event.metadata.published_at = datetime.now(timezone.utc)
229
229
 
230
230
  # Queue event
231
231
  self._event_queues[event.priority].append(event)
@@ -353,7 +353,7 @@ class EventBus(IEventBus):
353
353
  # Update metrics
354
354
  if events_processed > 0:
355
355
  self._metrics["events_processed"] += events_processed
356
- self._metrics["last_event_time"] = datetime.now()
356
+ self._metrics["last_event_time"] = datetime.now(timezone.utc)
357
357
  self._metrics["queue_size"] = sum(
358
358
  len(q) for q in self._event_queues.values()
359
359
  )
@@ -7,7 +7,7 @@ This replaces direct Socket.IO emission in the hook handler.
7
7
  """
8
8
 
9
9
  import uuid
10
- from datetime import datetime
10
+ from datetime import datetime, timezone
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
13
  from claude_mpm.core.logging_config import get_logger
@@ -96,7 +96,7 @@ class HookEventProducer(IEventProducer):
96
96
  id=str(uuid.uuid4()),
97
97
  topic="hook.response",
98
98
  type="AssistantResponse",
99
- timestamp=datetime.now(),
99
+ timestamp=datetime.now(timezone.utc),
100
100
  source=self.source_name,
101
101
  data=response_data,
102
102
  correlation_id=correlation_id,
@@ -128,7 +128,7 @@ class HookEventProducer(IEventProducer):
128
128
  id=str(uuid.uuid4()),
129
129
  topic="hook.tool",
130
130
  type="ToolUse",
131
- timestamp=datetime.now(),
131
+ timestamp=datetime.now(timezone.utc),
132
132
  source=self.source_name,
133
133
  data={
134
134
  "tool": tool_name,
@@ -164,7 +164,7 @@ class HookEventProducer(IEventProducer):
164
164
  id=str(uuid.uuid4()),
165
165
  topic="hook.error",
166
166
  type="Error",
167
- timestamp=datetime.now(),
167
+ timestamp=datetime.now(timezone.utc),
168
168
  source=self.source_name,
169
169
  data={
170
170
  "error_type": error_type,
@@ -200,7 +200,7 @@ class HookEventProducer(IEventProducer):
200
200
  id=str(uuid.uuid4()),
201
201
  topic=f"hook.subagent.{event_type.lower()}",
202
202
  type=f"Subagent{event_type}",
203
- timestamp=datetime.now(),
203
+ timestamp=datetime.now(timezone.utc),
204
204
  source=self.source_name,
205
205
  data={
206
206
  "subagent": subagent_name,
@@ -255,7 +255,7 @@ class HookEventProducer(IEventProducer):
255
255
  id=str(uuid.uuid4()),
256
256
  topic=topic,
257
257
  type=hook_type,
258
- timestamp=datetime.now(),
258
+ timestamp=datetime.now(timezone.utc),
259
259
  source=self.source_name,
260
260
  data=hook_data,
261
261
  correlation_id=correlation_id,
@@ -6,7 +6,7 @@ Publishes system-level events to the event bus.
6
6
  """
7
7
 
8
8
  import uuid
9
- from datetime import datetime
9
+ from datetime import datetime, timezone
10
10
  from typing import Any, Dict, List, Optional
11
11
 
12
12
  from claude_mpm.core.logging_config import get_logger
@@ -100,7 +100,7 @@ class SystemEventProducer(IEventProducer):
100
100
  id=str(uuid.uuid4()),
101
101
  topic="system.lifecycle.startup",
102
102
  type="ServiceStartup",
103
- timestamp=datetime.now(),
103
+ timestamp=datetime.now(timezone.utc),
104
104
  source=self.source_name,
105
105
  data={
106
106
  "service": service_name,
@@ -133,7 +133,7 @@ class SystemEventProducer(IEventProducer):
133
133
  id=str(uuid.uuid4()),
134
134
  topic="system.lifecycle.shutdown",
135
135
  type="ServiceShutdown",
136
- timestamp=datetime.now(),
136
+ timestamp=datetime.now(timezone.utc),
137
137
  source=self.source_name,
138
138
  data={
139
139
  "service": service_name,
@@ -168,7 +168,7 @@ class SystemEventProducer(IEventProducer):
168
168
  id=str(uuid.uuid4()),
169
169
  topic="system.health",
170
170
  type="HealthStatus",
171
- timestamp=datetime.now(),
171
+ timestamp=datetime.now(timezone.utc),
172
172
  source=self.source_name,
173
173
  data={
174
174
  "service": service_name,
@@ -206,7 +206,7 @@ class SystemEventProducer(IEventProducer):
206
206
  id=str(uuid.uuid4()),
207
207
  topic="system.config",
208
208
  type="ConfigChange",
209
- timestamp=datetime.now(),
209
+ timestamp=datetime.now(timezone.utc),
210
210
  source=self.source_name,
211
211
  data={
212
212
  "service": service_name,
@@ -238,7 +238,7 @@ class SystemEventProducer(IEventProducer):
238
238
  id=str(uuid.uuid4()),
239
239
  topic="system.performance",
240
240
  type="PerformanceMetrics",
241
- timestamp=datetime.now(),
241
+ timestamp=datetime.now(timezone.utc),
242
242
  source=self.source_name,
243
243
  data={
244
244
  "service": service_name,
@@ -274,7 +274,7 @@ class SystemEventProducer(IEventProducer):
274
274
  id=str(uuid.uuid4()),
275
275
  topic="system.error",
276
276
  type="SystemError",
277
- timestamp=datetime.now(),
277
+ timestamp=datetime.now(timezone.utc),
278
278
  source=self.source_name,
279
279
  data={
280
280
  "service": service_name,
@@ -311,7 +311,7 @@ class SystemEventProducer(IEventProducer):
311
311
  id=str(uuid.uuid4()),
312
312
  topic="system.warning",
313
313
  type="SystemWarning",
314
- timestamp=datetime.now(),
314
+ timestamp=datetime.now(timezone.utc),
315
315
  source=self.source_name,
316
316
  data={
317
317
  "service": service_name,
@@ -15,7 +15,7 @@ Design Principles:
15
15
 
16
16
  import platform
17
17
  import time
18
- from datetime import datetime
18
+ from datetime import datetime, timezone
19
19
  from typing import Any, Dict, List, Optional
20
20
 
21
21
 
@@ -43,7 +43,7 @@ class SocketIOServerError(Exception):
43
43
  self.message = message
44
44
  self.error_code = error_code or self.__class__.__name__.lower()
45
45
  self.context = context or {}
46
- self.timestamp = datetime.utcnow().isoformat() + "Z"
46
+ self.timestamp = datetime.now(timezone.utc).isoformat() + "Z"
47
47
 
48
48
  def to_dict(self) -> Dict[str, Any]:
49
49
  """Convert error to dictionary format for structured logging/handling."""
@@ -125,9 +125,9 @@ class DaemonConflictError(SocketIOServerError):
125
125
  )
126
126
 
127
127
  if create_time:
128
- start_time = datetime.fromtimestamp(create_time).strftime(
129
- "%Y-%m-%d %H:%M:%S"
130
- )
128
+ start_time = datetime.fromtimestamp(
129
+ create_time, tz=timezone.utc
130
+ ).strftime("%Y-%m-%d %H:%M:%S")
131
131
  uptime = time.time() - create_time
132
132
  lines.append(f" • Started: {start_time} (uptime: {uptime:.0f}s)")
133
133
 
@@ -7,7 +7,7 @@ Assembles sections and applies template variable substitution.
7
7
  import hashlib
8
8
  import logging
9
9
  from collections import OrderedDict
10
- from datetime import datetime
10
+ from datetime import datetime, timezone
11
11
  from typing import Any, Dict, Optional
12
12
 
13
13
  from claude_mpm.services.agents.management import AgentCapabilitiesGenerator
@@ -35,7 +35,7 @@ class ContentAssembler:
35
35
  Returns:
36
36
  str: 16-character hash of content
37
37
  """
38
- timestamp = datetime.utcnow().isoformat()
38
+ timestamp = datetime.now(timezone.utc).isoformat()
39
39
  hash_obj = hashlib.sha256(timestamp.encode())
40
40
  return hash_obj.hexdigest()[:16]
41
41
 
@@ -176,7 +176,7 @@ class ContentAssembler:
176
176
  Returns:
177
177
  Dict: Metadata for header
178
178
  """
179
- timestamp = datetime.utcnow().isoformat()
179
+ timestamp = datetime.now(timezone.utc).isoformat()
180
180
 
181
181
  return {
182
182
  "version": version,
@@ -5,7 +5,7 @@ This module provides base classes and registry for section generators.
5
5
  """
6
6
 
7
7
  from abc import ABC, abstractmethod
8
- from datetime import datetime
8
+ from datetime import datetime, timezone
9
9
  from typing import Any, Dict, Optional
10
10
 
11
11
 
@@ -35,7 +35,7 @@ class BaseSectionGenerator(ABC):
35
35
 
36
36
  def get_timestamp(self) -> str:
37
37
  """Get current UTC timestamp."""
38
- return datetime.utcnow().isoformat()
38
+ return datetime.now(timezone.utc).isoformat()
39
39
 
40
40
 
41
41
  class SectionGeneratorRegistry:
@@ -24,7 +24,7 @@ class HookInstallerService:
24
24
  self.claude_dir = Path.home() / ".claude"
25
25
  self.settings_file = self.claude_dir / "settings.json"
26
26
 
27
- def is_hooks_configured(self) -> bool:
27
+ def is_hooks_configured(self) -> bool: # noqa: PLR0911
28
28
  """Check if hooks are configured in Claude settings.
29
29
 
30
30
  Returns:
@@ -16,7 +16,7 @@ import gzip
16
16
  import json
17
17
  import shutil
18
18
  from dataclasses import dataclass, field
19
- from datetime import datetime
19
+ from datetime import datetime, timezone
20
20
  from typing import Any, Dict, List, Optional
21
21
 
22
22
  import ijson # For streaming JSON parsing
@@ -203,7 +203,9 @@ class ContextPreservationService(BaseService):
203
203
  await self._create_backup()
204
204
 
205
205
  # Load and filter conversations
206
- cutoff_time = datetime.now().timestamp() - (keep_recent_days * 86400)
206
+ cutoff_time = datetime.now(timezone.utc).timestamp() - (
207
+ keep_recent_days * 86400
208
+ )
207
209
 
208
210
  with open(self.claude_json_path) as f:
209
211
  data = json.load(f)
@@ -292,7 +294,7 @@ class ContextPreservationService(BaseService):
292
294
  try:
293
295
  if Path(file_path).exists():
294
296
  valid_files.append(file_path)
295
- except:
297
+ except Exception:
296
298
  pass # Invalid path
297
299
 
298
300
  return valid_files
@@ -516,7 +518,7 @@ class ContextPreservationService(BaseService):
516
518
  async def _create_backup(self) -> Path:
517
519
  """Create backup of Claude configuration."""
518
520
  try:
519
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
521
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
520
522
  backup_name = f"claude_backup_{timestamp}.json"
521
523
 
522
524
  # Compress if large
@@ -107,7 +107,7 @@ class SocketIODaemonManager:
107
107
  )
108
108
  if response.status_code == 200:
109
109
  return response.json()
110
- except:
110
+ except Exception:
111
111
  pass
112
112
  return None
113
113
 
@@ -265,7 +265,7 @@ class SocketIODaemonManager:
265
265
  result = sock.connect_ex((self.host, self.port))
266
266
  sock.close()
267
267
  status_info["port_accessible"] = result == 0
268
- except:
268
+ except Exception:
269
269
  status_info["port_accessible"] = False
270
270
 
271
271
  # Check for conflicts
@@ -11,7 +11,7 @@ Part of TSK-0046: Service Layer Architecture Reorganization
11
11
 
12
12
  import json
13
13
  import logging
14
- from datetime import datetime
14
+ from datetime import datetime, timezone
15
15
  from typing import Any, Dict, List, Optional
16
16
 
17
17
  from claude_mpm.core.logger import get_logger
@@ -92,7 +92,7 @@ class LoggingService(SyncBaseService, IStructuredLogger):
92
92
 
93
93
  if self.structured_logging:
94
94
  log_entry = {
95
- "timestamp": datetime.utcnow().isoformat(),
95
+ "timestamp": datetime.now(timezone.utc).isoformat(),
96
96
  "level": level,
97
97
  "message": message,
98
98
  "context": context,
@@ -10,14 +10,25 @@ MCP service installations.
10
10
  """
11
11
 
12
12
  import json
13
- import os
14
13
  import subprocess
14
+ from datetime import datetime, timezone
15
+ from enum import Enum
15
16
  from pathlib import Path
16
17
  from typing import Dict, Optional, Tuple
17
18
 
18
19
  from ..core.logger import get_logger
19
20
 
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
+
21
32
  class MCPConfigManager:
22
33
  """Manages MCP service configurations with pipx preference."""
23
34
 
@@ -34,6 +45,9 @@ class MCPConfigManager:
34
45
  self.pipx_base = Path.home() / ".local" / "pipx" / "venvs"
35
46
  self.project_root = Path.cwd()
36
47
 
48
+ # Use the proper Claude config file location
49
+ self.claude_config_path = ConfigLocation.CLAUDE_JSON.value
50
+
37
51
  def detect_service_path(self, service_name: str) -> Optional[str]:
38
52
  """
39
53
  Detect the best path for an MCP service.
@@ -161,9 +175,7 @@ class MCPConfigManager:
161
175
  config["env"] = {}
162
176
  elif service_name == "mcp-browser":
163
177
  config["args"] = ["mcp"]
164
- config["env"] = {
165
- "MCP_BROWSER_HOME": str(Path.home() / ".mcp-browser")
166
- }
178
+ config["env"] = {"MCP_BROWSER_HOME": str(Path.home() / ".mcp-browser")}
167
179
  elif service_name == "mcp-ticketer":
168
180
  config["args"] = ["mcp"]
169
181
  else:
@@ -172,9 +184,109 @@ class MCPConfigManager:
172
184
 
173
185
  return config
174
186
 
187
+ def ensure_mcp_services_configured(self) -> Tuple[bool, str]:
188
+ """
189
+ Ensure MCP services are configured in ~/.claude.json on startup.
190
+
191
+ This method checks if the core MCP services are configured in the
192
+ current project's mcpServers section and automatically adds them if missing.
193
+
194
+ Returns:
195
+ Tuple of (success, message)
196
+ """
197
+ updated = False
198
+ added_services = []
199
+ project_key = str(self.project_root)
200
+
201
+ # Load existing Claude config or create minimal structure
202
+ claude_config = {}
203
+ if self.claude_config_path.exists():
204
+ try:
205
+ with open(self.claude_config_path) as f:
206
+ claude_config = json.load(f)
207
+ except Exception as e:
208
+ self.logger.error(f"Error reading {self.claude_config_path}: {e}")
209
+ return False, f"Failed to read Claude config: {e}"
210
+
211
+ # Ensure projects structure exists
212
+ if "projects" not in claude_config:
213
+ claude_config["projects"] = {}
214
+
215
+ if project_key not in claude_config["projects"]:
216
+ claude_config["projects"][project_key] = {
217
+ "allowedTools": [],
218
+ "history": [],
219
+ "mcpContextUris": [],
220
+ "mcpServers": {},
221
+ "enabledMcpjsonServers": [],
222
+ "disabledMcpjsonServers": [],
223
+ "hasTrustDialogAccepted": False,
224
+ "projectOnboardingSeenCount": 0,
225
+ "hasClaudeMdExternalIncludesApproved": False,
226
+ "hasClaudeMdExternalIncludesWarningShown": False,
227
+ }
228
+ updated = True
229
+
230
+ # Get the project's mcpServers section
231
+ project_config = claude_config["projects"][project_key]
232
+ if "mcpServers" not in project_config:
233
+ project_config["mcpServers"] = {}
234
+ updated = True
235
+
236
+ # Check each service and add if missing
237
+ for service_name in self.PIPX_SERVICES:
238
+ if service_name not in project_config["mcpServers"]:
239
+ # Try to detect and configure the service
240
+ service_path = self.detect_service_path(service_name)
241
+ if service_path:
242
+ config = self.generate_service_config(service_name)
243
+ if config:
244
+ project_config["mcpServers"][service_name] = config
245
+ added_services.append(service_name)
246
+ updated = True
247
+ self.logger.debug(
248
+ f"Added MCP service to config: {service_name}"
249
+ )
250
+ else:
251
+ self.logger.debug(
252
+ f"MCP service {service_name} not found for auto-configuration"
253
+ )
254
+
255
+ # Write updated config if changes were made
256
+ if updated:
257
+ try:
258
+ # Create backup if file exists and is large (> 100KB)
259
+ if self.claude_config_path.exists():
260
+ file_size = self.claude_config_path.stat().st_size
261
+ if file_size > 100000: # 100KB
262
+ backup_path = self.claude_config_path.with_suffix(
263
+ f".backup.{datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')}.json"
264
+ )
265
+ import shutil
266
+
267
+ shutil.copy2(self.claude_config_path, backup_path)
268
+ self.logger.debug(f"Created backup: {backup_path}")
269
+
270
+ # Write updated config
271
+ with open(self.claude_config_path, "w") as f:
272
+ json.dump(claude_config, f, indent=2)
273
+
274
+ if added_services:
275
+ message = (
276
+ f"Auto-configured MCP services: {', '.join(added_services)}"
277
+ )
278
+ # Don't log here - let the caller handle logging to avoid duplicates
279
+ return True, message
280
+ return True, "All MCP services already configured"
281
+ except Exception as e:
282
+ self.logger.error(f"Failed to write Claude config: {e}")
283
+ return False, f"Failed to write configuration: {e}"
284
+
285
+ return True, "All MCP services already configured"
286
+
175
287
  def update_mcp_config(self, force_pipx: bool = True) -> Tuple[bool, str]:
176
288
  """
177
- Update the .mcp.json configuration file.
289
+ Update the MCP configuration in ~/.claude.json.
178
290
 
179
291
  Args:
180
292
  force_pipx: If True, only use pipx installations
@@ -182,13 +294,27 @@ class MCPConfigManager:
182
294
  Returns:
183
295
  Tuple of (success, message)
184
296
  """
185
- mcp_config_path = self.project_root / ".mcp.json"
297
+ # This method now delegates to ensure_mcp_services_configured
298
+ # since we're updating the Claude config directly
299
+ return self.ensure_mcp_services_configured()
300
+
301
+ def update_project_mcp_config(self, force_pipx: bool = True) -> Tuple[bool, str]:
302
+ """
303
+ Update the .mcp.json configuration file (legacy method).
304
+
305
+ Args:
306
+ force_pipx: If True, only use pipx installations
307
+
308
+ Returns:
309
+ Tuple of (success, message)
310
+ """
311
+ mcp_config_path = self.project_root / ConfigLocation.PROJECT_MCP.value
186
312
 
187
313
  # Load existing config if it exists
188
314
  existing_config = {}
189
315
  if mcp_config_path.exists():
190
316
  try:
191
- with open(mcp_config_path, "r") as f:
317
+ with open(mcp_config_path) as f:
192
318
  existing_config = json.load(f)
193
319
  except Exception as e:
194
320
  self.logger.error(f"Error reading existing config: {e}")
@@ -203,12 +329,11 @@ class MCPConfigManager:
203
329
  new_config["mcpServers"][service_name] = config
204
330
  elif force_pipx:
205
331
  missing_services.append(service_name)
206
- else:
207
- # Keep existing config if not forcing pipx
208
- if service_name in existing_config.get("mcpServers", {}):
209
- new_config["mcpServers"][service_name] = existing_config[
210
- "mcpServers"
211
- ][service_name]
332
+ # Keep existing config if not forcing pipx
333
+ elif service_name in existing_config.get("mcpServers", {}):
334
+ new_config["mcpServers"][service_name] = existing_config["mcpServers"][
335
+ service_name
336
+ ]
212
337
 
213
338
  # Add any additional services from existing config
214
339
  for service_name, config in existing_config.get("mcpServers", {}).items():
@@ -223,8 +348,7 @@ class MCPConfigManager:
223
348
  if missing_services:
224
349
  message = f"Updated .mcp.json. Missing services (install via pipx): {', '.join(missing_services)}"
225
350
  return True, message
226
- else:
227
- return True, "Successfully updated .mcp.json with pipx paths"
351
+ return True, "Successfully updated .mcp.json with pipx paths"
228
352
  except Exception as e:
229
353
  return False, f"Failed to update .mcp.json: {e}"
230
354
 
@@ -235,23 +359,45 @@ class MCPConfigManager:
235
359
  Returns:
236
360
  Dict mapping service names to availability status
237
361
  """
238
- mcp_config_path = self.project_root / ".mcp.json"
239
- if not mcp_config_path.exists():
362
+ project_key = str(self.project_root)
363
+
364
+ # Check Claude config
365
+ if not self.claude_config_path.exists():
366
+ # Also check legacy .mcp.json
367
+ mcp_config_path = self.project_root / ConfigLocation.PROJECT_MCP.value
368
+ if mcp_config_path.exists():
369
+ try:
370
+ with open(mcp_config_path) as f:
371
+ config = json.load(f)
372
+ results = {}
373
+ for service_name, service_config in config.get(
374
+ "mcpServers", {}
375
+ ).items():
376
+ command_path = service_config.get("command", "")
377
+ results[service_name] = Path(command_path).exists()
378
+ return results
379
+ except Exception:
380
+ pass
240
381
  return {}
241
382
 
242
383
  try:
243
- with open(mcp_config_path, "r") as f:
244
- config = json.load(f)
384
+ with open(self.claude_config_path) as f:
385
+ claude_config = json.load(f)
386
+
387
+ # Get project's MCP servers
388
+ if "projects" in claude_config and project_key in claude_config["projects"]:
389
+ mcp_servers = claude_config["projects"][project_key].get(
390
+ "mcpServers", {}
391
+ )
392
+ results = {}
393
+ for service_name, service_config in mcp_servers.items():
394
+ command_path = service_config.get("command", "")
395
+ results[service_name] = Path(command_path).exists()
396
+ return results
245
397
  except Exception as e:
246
398
  self.logger.error(f"Error reading config: {e}")
247
- return {}
248
399
 
249
- results = {}
250
- for service_name, service_config in config.get("mcpServers", {}).items():
251
- command_path = service_config.get("command", "")
252
- results[service_name] = Path(command_path).exists()
253
-
254
- return results
400
+ return {}
255
401
 
256
402
  def install_missing_services(self) -> Tuple[bool, str]:
257
403
  """
@@ -274,7 +420,7 @@ class MCPConfigManager:
274
420
  for service_name in missing:
275
421
  try:
276
422
  self.logger.info(f"Installing {service_name} via pipx...")
277
- result = subprocess.run(
423
+ subprocess.run(
278
424
  ["pipx", "install", service_name],
279
425
  capture_output=True,
280
426
  text=True,
@@ -288,7 +434,6 @@ class MCPConfigManager:
288
434
 
289
435
  if failed:
290
436
  return False, f"Failed to install: {', '.join(failed)}"
291
- elif installed:
437
+ if installed:
292
438
  return True, f"Successfully installed: {', '.join(installed)}"
293
- else:
294
- return True, "No services needed installation"
439
+ return True, "No services needed installation"