claude-mpm 4.5.1__py3-none-any.whl → 4.5.5__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 (48) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/PM_INSTRUCTIONS.md +33 -0
  3. claude_mpm/cli/__init__.py +27 -11
  4. claude_mpm/cli/commands/doctor.py +1 -4
  5. claude_mpm/core/config.py +2 -2
  6. claude_mpm/core/framework/__init__.py +6 -6
  7. claude_mpm/core/unified_paths.py +13 -12
  8. claude_mpm/hooks/kuzu_memory_hook.py +1 -1
  9. claude_mpm/init.py +19 -0
  10. claude_mpm/services/async_session_logger.py +6 -2
  11. claude_mpm/services/claude_session_logger.py +2 -2
  12. claude_mpm/services/diagnostics/checks/mcp_services_check.py +2 -2
  13. claude_mpm/services/diagnostics/doctor_reporter.py +0 -2
  14. claude_mpm/services/mcp_config_manager.py +156 -105
  15. claude_mpm/services/mcp_gateway/core/process_pool.py +258 -36
  16. claude_mpm/services/mcp_gateway/utils/__init__.py +14 -0
  17. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +160 -0
  18. claude_mpm/services/mcp_gateway/utils/update_preferences.py +170 -0
  19. claude_mpm/services/mcp_service_verifier.py +4 -4
  20. claude_mpm/services/monitor/event_emitter.py +6 -2
  21. claude_mpm/services/project/archive_manager.py +7 -9
  22. claude_mpm/services/project/documentation_manager.py +2 -3
  23. claude_mpm/services/project/enhanced_analyzer.py +1 -1
  24. claude_mpm/services/project/project_organizer.py +15 -12
  25. claude_mpm/services/unified/__init__.py +13 -13
  26. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -8
  27. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +0 -1
  28. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +8 -9
  29. claude_mpm/services/unified/config_strategies/__init__.py +37 -37
  30. claude_mpm/services/unified/config_strategies/config_schema.py +18 -22
  31. claude_mpm/services/unified/config_strategies/context_strategy.py +6 -7
  32. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +6 -10
  33. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -9
  34. claude_mpm/services/unified/config_strategies/unified_config_service.py +2 -4
  35. claude_mpm/services/unified/config_strategies/validation_strategy.py +1 -1
  36. claude_mpm/services/unified/deployment_strategies/__init__.py +8 -8
  37. claude_mpm/services/unified/deployment_strategies/local.py +2 -5
  38. claude_mpm/services/unified/deployment_strategies/utils.py +13 -17
  39. claude_mpm/services/unified/deployment_strategies/vercel.py +5 -6
  40. claude_mpm/services/unified/unified_analyzer.py +1 -1
  41. claude_mpm/utils/common.py +3 -7
  42. claude_mpm/utils/database_connector.py +9 -12
  43. {claude_mpm-4.5.1.dist-info → claude_mpm-4.5.5.dist-info}/METADATA +2 -2
  44. {claude_mpm-4.5.1.dist-info → claude_mpm-4.5.5.dist-info}/RECORD +48 -45
  45. {claude_mpm-4.5.1.dist-info → claude_mpm-4.5.5.dist-info}/WHEEL +0 -0
  46. {claude_mpm-4.5.1.dist-info → claude_mpm-4.5.5.dist-info}/entry_points.txt +0 -0
  47. {claude_mpm-4.5.1.dist-info → claude_mpm-4.5.5.dist-info}/licenses/LICENSE +0 -0
  48. {claude_mpm-4.5.1.dist-info → claude_mpm-4.5.5.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 4.5.1
1
+ 4.5.5
@@ -61,6 +61,10 @@
61
61
  ❌ "No issues found" without scan results → MUST have scan evidence
62
62
  ❌ "Performance improved" without metrics → MUST have measurement data
63
63
  ❌ "Security enhanced" without audit → MUST have security verification
64
+ ❌ "Running on localhost:XXXX" without fetch verification → MUST have HTTP response evidence
65
+ ❌ "Server started successfully" without log evidence → MUST have process/log verification
66
+ ❌ "Application available at..." without accessibility test → MUST have endpoint check
67
+ ❌ "You can now access..." without verification → MUST have browser/fetch test
64
68
 
65
69
  ## ONLY ALLOWED PM TOOLS
66
70
  ✓ Task - For delegation to agents (PRIMARY TOOL - USE THIS 90% OF TIME)
@@ -267,6 +271,30 @@ Requirements:
267
271
  5. Report: "Deployment VERIFIED" or "Deployment FAILED: [specific issues]"
268
272
  ```
269
273
 
274
+ ## LOCAL DEPLOYMENT MANDATORY VERIFICATION
275
+
276
+ **CRITICAL**: PM MUST NEVER claim "running on localhost" without verification.
277
+
278
+ ### Required for ALL Local Deployments (PM2, Docker, npm start, etc.):
279
+ 1. PM MUST delegate to local-ops-agent (or Ops) for deployment
280
+ 2. Ops agent MUST verify with ALL of these:
281
+ - Process status check (ps, pm2 status, docker ps)
282
+ - Log examination for startup errors
283
+ - Fetch test to claimed URL (e.g., curl http://localhost:3000)
284
+ - Response validation (HTTP status code, content check)
285
+ 3. PM can ONLY report success WITH evidence:
286
+ - ✅ "Verified running at localhost:3000: [HTTP 200 response]"
287
+ - ❌ "Should be running on localhost:3000" (VIOLATION)
288
+ - ❌ "Application is available at..." (VIOLATION without proof)
289
+
290
+ ### Automatic Violation Triggers for Localhost Claims:
291
+ These phrases without fetch evidence = IMMEDIATE VIOLATION:
292
+ - "running on localhost"
293
+ - "available at localhost"
294
+ - "access at http://localhost"
295
+ - "server started on port"
296
+ - "deployment successful" (without verification)
297
+
270
298
  ## QA Requirements
271
299
 
272
300
  **Rule**: No QA = Work incomplete
@@ -375,6 +403,11 @@ When PM attempts forbidden action:
375
403
  - "I think" → VIOLATION: Need agent analysis
376
404
  - "Probably" → VIOLATION: Need verification
377
405
 
406
+ **Localhost Assertion Red Flags:**
407
+ - "Running on localhost" → VIOLATION: Need fetch verification
408
+ - "Server is up" → VIOLATION: Need process + fetch proof
409
+ - "You can access" → VIOLATION: Need endpoint test
410
+
378
411
  ### ✅ CORRECT PM PHRASES:
379
412
  - "I'll delegate this to..."
380
413
  - "I'll have [Agent] handle..."
@@ -222,6 +222,14 @@ def _check_mcp_auto_configuration():
222
222
  logger = get_logger("cli")
223
223
  logger.debug(f"MCP auto-configuration check failed: {e}")
224
224
 
225
+ # Skip MCP service fixes for the doctor command
226
+ # The doctor command performs its own comprehensive MCP service check
227
+ # Running both would cause duplicate checks and log messages (9 seconds apart)
228
+ import sys
229
+
230
+ if len(sys.argv) > 1 and sys.argv[1] == "doctor":
231
+ return
232
+
225
233
  # Also ensure MCP services are properly configured in ~/.claude.json
226
234
  # This fixes incorrect paths and adds missing services
227
235
  try:
@@ -289,23 +297,31 @@ def _verify_mcp_gateway_startup():
289
297
  # Quick check first - if already configured, skip detailed verification
290
298
  gateway_configured = is_mcp_gateway_configured()
291
299
 
292
- # Pre-warm MCP servers regardless of gateway config
293
- # This eliminates the 11.9s delay on first agent invocation
300
+ # DISABLED: Pre-warming MCP servers can interfere with Claude Code's MCP management
301
+ # This was causing issues with MCP server initialization and stderr handling
302
+ # def run_pre_warming():
303
+ # loop = None
304
+ # try:
305
+ # start_time = time.time()
306
+ # loop = asyncio.new_event_loop()
307
+ # asyncio.set_event_loop(loop)
308
+ #
309
+ # # Pre-warm MCP servers (especially vector search)
310
+ # logger.info("Pre-warming MCP servers to eliminate startup delay...")
311
+ # loop.run_until_complete(pre_warm_mcp_servers())
312
+ #
313
+ # pre_warm_time = time.time() - start_time
314
+ # if pre_warm_time > 1.0:
315
+ # logger.info(f"MCP servers pre-warmed in {pre_warm_time:.2f}s")
316
+
317
+ # Dummy function to maintain structure
294
318
  def run_pre_warming():
295
319
  loop = None
296
320
  try:
297
- start_time = time.time()
321
+ time.time()
298
322
  loop = asyncio.new_event_loop()
299
323
  asyncio.set_event_loop(loop)
300
324
 
301
- # Pre-warm MCP servers (especially vector search)
302
- logger.info("Pre-warming MCP servers to eliminate startup delay...")
303
- loop.run_until_complete(pre_warm_mcp_servers())
304
-
305
- pre_warm_time = time.time() - start_time
306
- if pre_warm_time > 1.0:
307
- logger.info(f"MCP servers pre-warmed in {pre_warm_time:.2f}s")
308
-
309
325
  # Also run gateway verification if needed
310
326
  if not gateway_configured:
311
327
  loop.run_until_complete(verify_mcp_gateway_on_startup())
@@ -137,10 +137,7 @@ def doctor_command(args):
137
137
  output_format = "markdown"
138
138
  elif output_file:
139
139
  # Force markdown format when writing to file (unless json specified)
140
- if str(output_file).endswith(".json"):
141
- output_format = "json"
142
- else:
143
- output_format = "markdown"
140
+ output_format = "json" if str(output_file).endswith(".json") else "markdown"
144
141
  else:
145
142
  output_format = "terminal"
146
143
 
claude_mpm/core/config.py CHANGED
@@ -56,7 +56,7 @@ class Config:
56
56
  # Double-check locking pattern for thread safety
57
57
  if cls._instance is None:
58
58
  cls._instance = super().__new__(cls)
59
- logger.info("Creating new Config singleton instance")
59
+ logger.debug("Creating new Config singleton instance")
60
60
  else:
61
61
  logger.debug(
62
62
  "Reusing existing Config singleton instance (concurrent init)"
@@ -102,7 +102,7 @@ class Config:
102
102
  return
103
103
 
104
104
  Config._initialized = True
105
- logger.info("Initializing Config singleton for the first time")
105
+ logger.debug("Initializing Config singleton for the first time")
106
106
 
107
107
  # Initialize instance variables inside the lock to ensure thread safety
108
108
  self._config: Dict[str, Any] = {}
@@ -22,17 +22,17 @@ from .processors import (
22
22
  )
23
23
 
24
24
  __all__ = [
25
- # Loaders
26
- "FileLoader",
27
- "PackagedLoader",
28
- "InstructionLoader",
29
25
  "AgentLoader",
26
+ "CapabilityGenerator",
30
27
  # Formatters
31
28
  "ContentFormatter",
32
- "CapabilityGenerator",
33
29
  "ContextGenerator",
30
+ # Loaders
31
+ "FileLoader",
32
+ "InstructionLoader",
33
+ "MemoryProcessor",
34
34
  # Processors
35
35
  "MetadataProcessor",
36
+ "PackagedLoader",
36
37
  "TemplateProcessor",
37
- "MemoryProcessor",
38
38
  ]
@@ -178,7 +178,7 @@ class PathContext:
178
178
  """
179
179
  # Check for environment variable override
180
180
  if os.environ.get("CLAUDE_MPM_DEV_MODE", "").lower() in ("1", "true", "yes"):
181
- logger.info(
181
+ logger.debug(
182
182
  "Development mode forced via CLAUDE_MPM_DEV_MODE environment variable"
183
183
  )
184
184
  return DeploymentContext.DEVELOPMENT
@@ -198,10 +198,10 @@ class PathContext:
198
198
  'name = "claude-mpm"' in pyproject_content
199
199
  or '"claude-mpm"' in pyproject_content
200
200
  ):
201
- logger.info(
201
+ logger.debug(
202
202
  f"Detected claude-mpm development directory at {current}"
203
203
  )
204
- logger.info(
204
+ logger.debug(
205
205
  "Using development mode for local source preference"
206
206
  )
207
207
  return DeploymentContext.DEVELOPMENT
@@ -219,7 +219,7 @@ class PathContext:
219
219
  # First check if this is an editable install, regardless of path
220
220
  # This is important for cases where pipx points to a development installation
221
221
  if PathContext._is_editable_install():
222
- logger.info("Detected editable/development installation")
222
+ logger.debug("Detected editable/development installation")
223
223
  # Check if we should use development paths
224
224
  # This could be because we're in a src/ directory or running from dev directory
225
225
  if module_path.parent.name == "src":
@@ -233,7 +233,7 @@ class PathContext:
233
233
  if (current / "src" / "claude_mpm").exists() and (
234
234
  current / "pyproject.toml"
235
235
  ).exists():
236
- logger.info(
236
+ logger.debug(
237
237
  "Running pipx from development directory, using development mode"
238
238
  )
239
239
  return DeploymentContext.DEVELOPMENT
@@ -249,33 +249,33 @@ class PathContext:
249
249
  module_path.parent.name == "src"
250
250
  and (module_path.parent.parent / "src" / "claude_mpm").exists()
251
251
  ):
252
- logger.info(
252
+ logger.debug(
253
253
  f"Detected development mode via directory structure at {module_path}"
254
254
  )
255
255
  return DeploymentContext.DEVELOPMENT
256
256
 
257
257
  # Check for pipx install
258
258
  if "pipx" in str(module_path):
259
- logger.info(f"Detected pipx installation at {module_path}")
259
+ logger.debug(f"Detected pipx installation at {module_path}")
260
260
  return DeploymentContext.PIPX_INSTALL
261
261
 
262
262
  # Check for system package
263
263
  if "dist-packages" in str(module_path):
264
- logger.info(f"Detected system package installation at {module_path}")
264
+ logger.debug(f"Detected system package installation at {module_path}")
265
265
  return DeploymentContext.SYSTEM_PACKAGE
266
266
 
267
267
  # Check for site-packages (could be pip or editable)
268
268
  if "site-packages" in str(module_path):
269
269
  # Already checked for editable above, so this is a regular pip install
270
- logger.info(f"Detected pip installation at {module_path}")
270
+ logger.debug(f"Detected pip installation at {module_path}")
271
271
  return DeploymentContext.PIP_INSTALL
272
272
 
273
273
  # Default to pip install
274
- logger.info(f"Defaulting to pip installation for {module_path}")
274
+ logger.debug(f"Defaulting to pip installation for {module_path}")
275
275
  return DeploymentContext.PIP_INSTALL
276
276
 
277
277
  except ImportError:
278
- logger.info(
278
+ logger.debug(
279
279
  "ImportError during context detection, defaulting to development"
280
280
  )
281
281
  return DeploymentContext.DEVELOPMENT
@@ -321,7 +321,8 @@ class UnifiedPathManager:
321
321
  ]
322
322
  self._initialized = True
323
323
 
324
- logger.info(
324
+ # Use debug level for initialization details
325
+ logger.debug(
325
326
  f"UnifiedPathManager initialized with context: {self._deployment_context.value}"
326
327
  )
327
328
 
@@ -230,7 +230,7 @@ Note: Use the memories above to provide more informed and contextual responses.
230
230
  # Extract memory content and metadata
231
231
  content = memory.get("content", "")
232
232
  tags = memory.get("tags", [])
233
- timestamp = memory.get("timestamp", "")
233
+ memory.get("timestamp", "")
234
234
  relevance = memory.get("relevance", 0.0)
235
235
 
236
236
  # Format memory entry
claude_mpm/init.py CHANGED
@@ -141,6 +141,25 @@ class ProjectInitializer:
141
141
  if not gitignore.exists():
142
142
  gitignore.write_text("logs/\n*.log\n*.pyc\n__pycache__/\n")
143
143
 
144
+ # Also ensure MCP directories are in main project .gitignore
145
+ try:
146
+ from claude_mpm.services.project.project_organizer import (
147
+ ProjectOrganizer,
148
+ )
149
+
150
+ # Check if we're in a git repository
151
+ if (project_root / ".git").exists():
152
+ organizer = ProjectOrganizer(project_root)
153
+ # This will add MCP directories and other standard patterns
154
+ organizer.update_gitignore()
155
+ self.logger.debug(
156
+ "Updated project .gitignore with MCP and standard patterns"
157
+ )
158
+ except Exception as e:
159
+ self.logger.debug(
160
+ f"Could not update project gitignore with MCP patterns: {e}"
161
+ )
162
+
144
163
  # Log successful creation with details
145
164
  self.logger.info(f"Initialized project directory at {self.project_dir}")
146
165
  self.logger.debug("Created directories: agents, config, responses, logs")
@@ -484,8 +484,12 @@ class AsyncSessionLogger:
484
484
  if self._worker_thread.is_alive():
485
485
  logger.warning("Worker thread did not shutdown cleanly")
486
486
 
487
- # Log final statistics
488
- logger.info(f"Logger stats: {self.stats}")
487
+ # Log final statistics only if we actually logged something
488
+ if self.stats.get("logged", 0) > 0:
489
+ logger.info(f"Logger stats: {self.stats}")
490
+ else:
491
+ # Use debug level when nothing was logged
492
+ logger.debug(f"Logger stats (no sessions logged): {self.stats}")
489
493
 
490
494
  def get_stats(self) -> Dict[str, Any]:
491
495
  """Get logger statistics."""
@@ -135,9 +135,9 @@ class ClaudeSessionLogger:
135
135
  if not session_id:
136
136
  # Use a timestamp-based session ID as fallback
137
137
  session_id = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
138
- logger.info(f"No Claude session ID found, using generated: {session_id}")
138
+ logger.info(f"Session ID: {session_id} (generated)")
139
139
  else:
140
- logger.info(f"Using Claude session ID: {session_id}")
140
+ logger.info(f"Session ID: {session_id}")
141
141
 
142
142
  return session_id
143
143
 
@@ -195,7 +195,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
195
195
 
196
196
  # Determine overall status
197
197
  errors = [r for r in sub_results if r.status == DiagnosticStatus.ERROR]
198
- warnings = [r for r in sub_results if r.status == DiagnosticStatus.WARNING]
198
+ [r for r in sub_results if r.status == DiagnosticStatus.WARNING]
199
199
 
200
200
  if errors:
201
201
  status = DiagnosticStatus.ERROR
@@ -508,7 +508,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
508
508
  # Special case for kuzu-memory with args
509
509
  mcp_command = ["pipx", "run", base_cmd[0]] + base_cmd[1:]
510
510
  else:
511
- mcp_command = ["pipx", "run"] + base_cmd
511
+ mcp_command = ["pipx", "run", *base_cmd]
512
512
  else:
513
513
  mcp_command = config["mcp_command"]
514
514
 
@@ -508,8 +508,6 @@ class DoctorReporter:
508
508
  recommendations = []
509
509
 
510
510
  # Analyze results for recommendations
511
- has_errors = summary.error_count > 0
512
- has_warnings = summary.warning_count > 0
513
511
 
514
512
  # Check specific conditions
515
513
  for result in summary.results: