diff --git a/atomizer-dashboard/backend/api/services/session_manager.py b/atomizer-dashboard/backend/api/services/session_manager.py index d01e0c30..b322a075 100644 --- a/atomizer-dashboard/backend/api/services/session_manager.py +++ b/atomizer-dashboard/backend/api/services/session_manager.py @@ -45,7 +45,13 @@ class SessionManager: self.store = ConversationStore() self.context_builder = ContextBuilder() self._cleanup_task: Optional[asyncio.Task] = None - self._lock = asyncio.Lock() + self._lock: Optional[asyncio.Lock] = None # Created lazily in async context + + def _get_lock(self) -> asyncio.Lock: + """Get or create the async lock (must be called from async context)""" + if self._lock is None: + self._lock = asyncio.Lock() + return self._lock async def start(self): """Start the session manager""" @@ -82,7 +88,7 @@ class SessionManager: Returns: ClaudeSession object """ - async with self._lock: + async with self._get_lock(): # Resume existing session if requested and alive if resume_session_id and resume_session_id in self.sessions: session = self.sessions[resume_session_id] @@ -201,12 +207,20 @@ class SessionManager: # Build CLI arguments based on mode cli_args = ["claude", "--print"] + # Ensure MCP config exists for atomizer tools + mcp_config_path = ATOMIZER_ROOT / f".claude-mcp-{session_id}.json" + if not mcp_config_path.exists(): + mcp_config = self._build_mcp_config(session.mode) + with open(mcp_config_path, "w") as f: + json.dump(mcp_config, f) + cli_args.extend(["--mcp-config", str(mcp_config_path)]) + if session.mode == "user": # User mode: Allow safe operations including report generation # Allow Write tool for report files (STUDY_REPORT.md, *.md in study dirs) cli_args.extend([ "--allowedTools", - "Read Write(**/STUDY_REPORT.md) Write(**/3_results/*.md) Bash(python:*)" + "Read Write(**/STUDY_REPORT.md) Write(**/3_results/*.md) Bash(python:*) mcp__atomizer-tools__*" ]) else: # Power mode: Full access