Files
xer-mcp/src/xer_mcp/db/__init__.py
Bill Ballou ccc8296418 feat: implement XER MCP Server with 9 schedule query tools
Implement complete MCP server for parsing Primavera P6 XER files and
exposing schedule data through MCP tools. All 4 user stories complete.

Tools implemented:
- load_xer: Parse XER files into SQLite database
- list_activities: Query activities with pagination and filtering
- get_activity: Get activity details by ID
- list_relationships: Query activity dependencies
- get_predecessors/get_successors: Query activity relationships
- get_project_summary: Project overview with counts
- list_milestones: Query milestone activities
- get_critical_path: Query driving path activities

Features:
- Tab-delimited XER format parsing with pluggable table handlers
- In-memory SQLite database for fast queries
- Pagination with 100-item default limit
- Multi-project file support with project selection
- ISO8601 date formatting
- NO_FILE_LOADED error handling for all query tools

Test coverage: 81 tests (contract, integration, unit)
2026-01-06 21:27:35 -05:00

67 lines
2.0 KiB
Python

"""Database connection management for XER MCP Server."""
import sqlite3
from collections.abc import Generator
from contextlib import contextmanager
from xer_mcp.db.schema import get_schema
class DatabaseManager:
"""Manages SQLite database connections and schema initialization."""
def __init__(self) -> None:
"""Initialize database manager with in-memory database."""
self._connection: sqlite3.Connection | None = None
def initialize(self) -> None:
"""Initialize the in-memory database with schema."""
self._connection = sqlite3.connect(":memory:", check_same_thread=False)
self._connection.row_factory = sqlite3.Row
self._connection.executescript(get_schema())
def clear(self) -> None:
"""Clear all data from the database."""
if self._connection is None:
return
tables = ["relationships", "activities", "wbs", "calendars", "projects"]
for table in tables:
self._connection.execute(f"DELETE FROM {table}") # noqa: S608
self._connection.commit()
@property
def connection(self) -> sqlite3.Connection:
"""Get the database connection."""
if self._connection is None:
raise RuntimeError("Database not initialized. Call initialize() first.")
return self._connection
@contextmanager
def cursor(self) -> Generator[sqlite3.Cursor]:
"""Get a cursor for executing queries."""
cur = self.connection.cursor()
try:
yield cur
finally:
cur.close()
def commit(self) -> None:
"""Commit the current transaction."""
self.connection.commit()
def close(self) -> None:
"""Close the database connection."""
if self._connection is not None:
self._connection.close()
self._connection = None
@property
def is_initialized(self) -> bool:
"""Check if the database is initialized."""
return self._connection is not None
# Global database manager instance
db = DatabaseManager()