"""Database MCP Server for 7000%AUTO Provides database operations for idea management """ import asyncio import logging from typing import Optional from mcp.server.fastmcp import FastMCP logger = logging.getLogger(__name__) mcp = FastMCP("Database Server") # Database initialization flag for MCP server process _db_ready = False async def _init_db_if_needed(): """Initialize database if not already initialized. MCP servers run in separate processes.""" global _db_ready if not _db_ready: try: from database.db import init_db await init_db() _db_ready = True logger.info("Database initialized in MCP server") except Exception as e: logger.error(f"Failed to initialize database in MCP server: {e}") raise @mcp.tool() async def get_previous_ideas(limit: int = 50) -> dict: """ Get list of previously generated ideas. Args: limit: Maximum number of ideas to return (default 50) Returns: Dictionary with list of ideas """ try: await _init_db_if_needed() from database import get_db, Idea from sqlalchemy import select async with get_db() as session: query = select(Idea).order_by(Idea.created_at.desc()).limit(limit) result = await session.execute(query) ideas = result.scalars().all() return { "success": True, "ideas": [ { "id": idea.id, "title": idea.title, "description": idea.description[:200], "source": idea.source, "used": idea.used } for idea in ideas ], "count": len(ideas) } except Exception as e: logger.error(f"Error getting previous ideas: {e}") return {"success": False, "error": str(e), "ideas": []} @mcp.tool() async def check_idea_exists(title: str) -> dict: """ Check if a similar idea already exists. Args: title: Title to check for similarity Returns: Dictionary with exists flag and similar ideas if found """ try: await _init_db_if_needed() from database import get_db, Idea from sqlalchemy import select, func title_lower = title.lower() title_words = set(title_lower.split()) async with get_db() as session: # Get all ideas for comparison query = select(Idea) result = await session.execute(query) ideas = result.scalars().all() similar = [] for idea in ideas: idea_title_lower = idea.title.lower() idea_words = set(idea_title_lower.split()) # Check exact match if title_lower == idea_title_lower: similar.append({ "id": idea.id, "title": idea.title, "match_type": "exact" }) continue # Check partial match (title contains or is contained) if title_lower in idea_title_lower or idea_title_lower in title_lower: similar.append({ "id": idea.id, "title": idea.title, "match_type": "partial" }) continue # Check word overlap (>50%) overlap = len(title_words & idea_words) total = len(title_words | idea_words) if total > 0 and overlap / total > 0.5: similar.append({ "id": idea.id, "title": idea.title, "match_type": "similar" }) return { "success": True, "exists": len(similar) > 0, "similar_ideas": similar[:5], "count": len(similar) } except Exception as e: logger.error(f"Error checking idea existence: {e}") return {"success": False, "error": str(e), "exists": False} @mcp.tool() async def save_idea(title: str, description: str, source: str) -> dict: """ Save a new idea to the database. Args: title: Idea title description: Idea description source: Source of the idea (arxiv, reddit, x, hn, ph) Returns: Dictionary with saved idea details """ try: await _init_db_if_needed() from database import create_idea idea = await create_idea( title=title, description=description, source=source ) return { "success": True, "idea": { "id": idea.id, "title": idea.title, "description": idea.description, "source": idea.source, "created_at": idea.created_at.isoformat() if idea.created_at else None } } except Exception as e: logger.error(f"Error saving idea: {e}") return {"success": False, "error": str(e)} @mcp.tool() async def get_database_stats() -> dict: """ Get database statistics. Returns: Dictionary with database stats """ try: await _init_db_if_needed() from database import get_stats stats = await get_stats() return { "success": True, "stats": stats } except Exception as e: logger.error(f"Error getting database stats: {e}") return {"success": False, "error": str(e)} @mcp.tool() async def submit_idea( project_id: int, title: str, description: str, source: str, tech_stack: list[str] = None, target_audience: str = None, key_features: list[str] = None, complexity: str = None, estimated_time: str = None, inspiration: str = None ) -> dict: """ Submit a generated project idea. Use this tool to finalize and save your idea. The idea will be saved directly to the database for the given project. Args: project_id: The project ID to associate this idea with (required) title: Short project name (required) description: Detailed description of the project (required) source: Source of inspiration - arxiv, reddit, x, hn, or ph (required) tech_stack: List of technologies to use (e.g., ["python", "fastapi"]) target_audience: Who would use this project key_features: List of key features complexity: low, medium, or high estimated_time: Estimated implementation time (e.g., "2-4 hours") inspiration: Brief note on what inspired this idea Returns: Dictionary with success status """ try: await _init_db_if_needed() from database.db import set_project_idea_json # Build the complete idea dict idea_data = { "title": title, "description": description, "source": source, "tech_stack": tech_stack or [], "target_audience": target_audience or "", "key_features": key_features or [], "complexity": complexity or "medium", "estimated_time": estimated_time or "", "inspiration": inspiration or "", } # Save to database success = await set_project_idea_json(project_id, idea_data) if success: logger.info(f"Idea submitted for project {project_id}: {title}") return {"success": True, "message": f"Idea '{title}' saved successfully"} else: logger.error(f"Project {project_id} not found") return {"success": False, "error": f"Project {project_id} not found"} except Exception as e: logger.error(f"Error submitting idea: {e}") return {"success": False, "error": str(e)} @mcp.tool() async def submit_plan( project_id: int, project_name: str, overview: str, display_name: str = None, tech_stack: dict = None, file_structure: dict = None, features: list[dict] = None, implementation_steps: list[dict] = None, testing_strategy: dict = None, configuration: dict = None, error_handling: dict = None, readme_sections: list[str] = None ) -> dict: """ Submit an implementation plan. Use this tool to finalize your project plan. The plan will be saved directly to the database for the given project. Args: project_id: The project ID to associate this plan with (required) project_name: kebab-case project name (required) overview: 2-3 sentence summary of what will be built (required) display_name: Human readable project name tech_stack: Technology stack details with language, runtime, framework, key_dependencies file_structure: File structure with root_files and directories features: List of features with name, priority, description, implementation_notes implementation_steps: Ordered list of implementation steps testing_strategy: Testing approach with unit_tests, integration_tests, test_files, test_commands configuration: Config details with env_variables and config_files error_handling: Error handling strategies readme_sections: List of README section titles Returns: Dictionary with success status """ try: await _init_db_if_needed() from database.db import set_project_plan_json # Build the complete plan dict plan_data = { "project_name": project_name, "display_name": display_name or project_name.replace("-", " ").title(), "overview": overview, "tech_stack": tech_stack or {}, "file_structure": file_structure or {}, "features": features or [], "implementation_steps": implementation_steps or [], "testing_strategy": testing_strategy or {}, "configuration": configuration or {}, "error_handling": error_handling or {}, "readme_sections": readme_sections or [] } # Save to database success = await set_project_plan_json(project_id, plan_data) if success: logger.info(f"Plan submitted for project {project_id}: {project_name}") return {"success": True, "message": f"Plan '{project_name}' saved successfully"} else: logger.error(f"Project {project_id} not found") return {"success": False, "error": f"Project {project_id} not found"} except Exception as e: logger.error(f"Error submitting plan: {e}") return {"success": False, "error": str(e)} if __name__ == "__main__": mcp.run()