Skip to content

MCP Server #247

Description

@Entropy-rgb

Problem Statement

BoloDB cannot be used as a tool by AI assistants like Claude or Cursor. Developers cannot integrate BoloDB's text-to-SQL capabilities into their AI workflows.

Proposed Solution

Implement an MCP (Model Context Protocol) server that exposes BoloDB's text-to-SQL capabilities as a tool for AI assistants.

Acceptance Criteria

  • MCP server implementation following protocol spec
  • Tool definitions: query_database, get_schema, explain_sql
  • Authentication via API keys
  • Streaming support for long queries
  • Error handling with descriptive messages
  • Documentation for AI assistant integration
  • Example configurations for Claude, Cursor
  • Rate limiting per API key

Technical Approach

Backend Changes

1. MCP server (backend/app/mcp/server.py):

from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.server.stdio

class BoloDBMCPServer:
    def __init__(self, db, llm):
        self.server = Server("bolodb")
        self.db = db
        self.llm = llm
        
        self._setup_tools()
    
    def _setup_tools(self):
        @self.server.list_tools()
        async def list_tools():
            return [
                Tool(
                    name="query_database",
                    description="Execute a natural language query against the connected database",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "question": {
                                "type": "string",
                                "description": "Natural language question to ask"
                            }
                        },
                        "required": ["question"]
                    }
                ),
                Tool(
                    name="get_schema",
                    description="Get the schema of the connected database",
                    inputSchema={
                        "type": "object",
                        "properties": {}
                    }
                ),
                Tool(
                    name="explain_sql",
                    description="Explain what a SQL query does in plain English",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "sql": {
                                "type": "string",
                                "description": "SQL query to explain"
                            }
                        },
                        "required": ["sql"]
                    }
                )
            ]
        
        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict):
            if name == "query_database":
                result = await self._query_database(arguments["question"])
                return [TextContent(type="text", text=json.dumps(result))]
            elif name == "get_schema":
                result = await self._get_schema()
                return [TextContent(type="text", text=json.dumps(result))]
            elif name == "explain_sql":
                result = await self._explain_sql(arguments["sql"])
                return [TextContent(type="text", text=result)]
    
    async def _query_database(self, question: str) -> dict:
        """Execute natural language query."""
        # Use existing query pipeline
        from backend.app.controllers.query import run_query
        result = await run_query(question, self.db, self.llm)
        return {
            "sql": result.get("sql"),
            "restatement": result.get("restatement"),
            "columns": result.get("columns"),
            "rows": result.get("rows"),
            "confidence": result.get("confidence")
        }
    
    async def _get_schema(self) -> dict:
        """Get database schema."""
        return self.db.get_schema()
    
    async def _explain_sql(self, sql: str) -> str:
        """Explain SQL in plain English."""
        from backend.app.llm import explain_sql
        return await explain_sql(sql, self.llm)

2. Authentication middleware (backend/app/mcp/auth.py):

class MCPAuthMiddleware:
    def __init__(self, api_key_service):
        self.api_key_service = api_key_service
    
    async def authenticate(self, request) -> dict:
        """Authenticate MCP request via API key."""
        api_key = request.headers.get("X-API-Key")
        if not api_key:
            raise ValueError("Missing API key")
        
        user = await self.api_key_service.validate_key(api_key)
        if not user:
            raise ValueError("Invalid API key")
        
        return user

3. MCP server entry point (backend/app/mcp/main.py):

import asyncio
from mcp.server.stdio import stdio_server

async def main():
    """Run MCP server on stdio."""
    server = BoloDBMCPServer(db, llm)
    
    async with stdio_server() as (read_stream, write_stream):
        await server.server.run(
            read_stream,
            write_stream,
            server.server.create_initialization_options()
        )

if __name__ == "__main__":
    asyncio.run(main())

4. Configuration examples:

// Claude Desktop config
{
  "mcpServers": {
    "bolodb": {
      "command": "python",
      "args": ["-m", "backend.app.mcp.main"],
      "env": {
        "BOLODB_API_KEY": "your-api-key-here"
      }
    }
  }
}

// Cursor config
{
  "mcp": {
    "servers": {
      "bolodb": {
        "command": "python -m backend.app.mcp.main",
        "env": {
          "BOLODB_API_KEY": "your-api-key-here"
        }
      }
    }
  }
}

Frontend Changes

1. MCP documentation page (frontend/src/routes/docs/mcp/+page.svelte):

<div class="mcp-docs">
    <h1>MCP Server</h1>
    
    <section>
        <h2>Overview</h2>
        <p>BoloDB exposes its text-to-SQL capabilities as an MCP server, allowing AI assistants like Claude and Cursor to query your database directly.</p>
    </section>
    
    <section>
        <h2>Installation</h2>
        <pre>
# Install dependencies
pip install mcp

# Run the server
python -m backend.app.mcp.main
        </pre>
    </section>
    
    <section>
        <h2>Configuration</h2>
        <h3>Claude Desktop</h3>
        <pre>{JSON.stringify(claudeConfig, null, 2)}</pre>
        
        <h3>Cursor</h3>
        <pre>{JSON.stringify(cursorConfig, null, 2)}</pre>
    </section>
    
    <section>
        <h2>Available Tools</h2>
        <ul>
            <li><code>query_database</code> - Execute natural language queries</li>
            <li><code>get_schema</code> - Get database schema</li>
            <li><code>explain_sql</code> - Explain SQL queries</li>
        </ul>
    </section>
</div>

2. Quick start guide - Step-by-step setup instructions

3. API key generation link - Link to API key settings

Key Files

  • backend/app/mcp/server.py - MCP server (new)
  • backend/app/mcp/auth.py - Authentication (new)
  • backend/app/mcp/main.py - Entry point (new)
  • frontend/src/routes/docs/mcp/+page.svelte - Documentation

Related Issues

Metadata

Metadata

Assignees

Labels

P-5Future considerationfeature-requestNew feature request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions