Mutable state shared across all MCP tool calls within a session.
| 29 | |
| 30 | @dataclass |
| 31 | class SessionState: |
| 32 | """Mutable state shared across all MCP tool calls within a session.""" |
| 33 | |
| 34 | session_id: str |
| 35 | repo_path: str |
| 36 | output_dir: str |
| 37 | components: Dict[str, Node] |
| 38 | leaf_nodes: List[str] |
| 39 | module_tree: Dict[str, Any] = field(default_factory=dict) |
| 40 | registry: Dict[str, Any] = field(default_factory=dict) |
| 41 | workspace: Optional[SessionWorkspace] = field(default=None) |
| 42 | # HEAD commit at analyze_repo time — the incremental-update baseline. |
| 43 | # close_session must record this commit (not the HEAD at close time), |
| 44 | # otherwise commits made mid-session are baselined away undocumented. |
| 45 | analyzed_commit: Optional[str] = None |
| 46 | # Number of successful write_doc_file/edit_doc_file operations; used to |
| 47 | # skip the metadata.json baseline update when a session wrote no docs. |
| 48 | docs_written: int = 0 |
| 49 | created_at: float = field(default_factory=time.time) |
| 50 | last_accessed: float = field(default_factory=time.time) |
| 51 | |
| 52 | def touch(self) -> None: |
| 53 | """Update the last-accessed timestamp.""" |
| 54 | self.last_accessed = time.time() |
| 55 | |
| 56 | @property |
| 57 | def is_expired(self) -> bool: |
| 58 | return (time.time() - self.last_accessed) > _SESSION_TTL_SECONDS |
| 59 | |
| 60 | |
| 61 | class SessionStore: |