Skip to main content
Glama
README.md12.2 kB
# MCP Tools/Services Architecture Arquitectura basada en plugins donde cada función MCP es una clase independiente. ## 📐 Estructura ``` mcp_sql/tools/ ├── __init__.py # Registry de todos los servicios ├── base.py # Clase base abstracta MCPTool ├── list_servers.py # Servicio: listar servidores ├── get_databases.py # Servicio: obtener bases de datos ├── get_tables.py # Servicio: obtener tablas ├── explore_table.py # Servicio: explorar tabla ├── describe_table.py # Servicio: describir tabla ├── query_columns.py # Servicio: consultar columnas └── README.md # Esta documentación ``` ## 🎯 Servicios Disponibles | Servicio | Clase | Descripción | |----------|-------|-------------| | `list_configured_servers` | `ListServersService` | Lista servidores configurados | | `get_databases` | `GetDatabasesService` | Obtiene lista de bases de datos | | `get_tables` | `GetTablesService` | Obtiene lista de tablas | | `explore_table` | `ExploreTableService` | Muestra datos de ejemplo | | `describe_table` | `DescribeTableService` | Descripción detallada de tabla | | `query_table_with_columns` | `QueryColumnsService` | Consulta columnas específicas | | `execute_select_query` | `ExecuteSelectService` | Ejecuta consultas SELECT seguras (bloquea ALTER, INSERT, CREATE, UPDATE, DELETE, etc.) | ## 🔧 Clase Base: MCPTool Todos los servicios heredan de `MCPTool`: ```python class MCPTool(ABC): """Abstract base class for MCP tools.""" def __init__( self, connection_manager: ConnectionManager, credentials_manager: CredentialsManager, inspector: DatabaseInspector, executor: QueryExecutor ): # Acceso a todos los componentes principales pass @property @abstractmethod def name(self) -> str: """Tool name (used for registration).""" pass @property @abstractmethod def description(self) -> str: """Tool description (used in MCP metadata).""" pass @abstractmethod async def execute(self, ctx: Context, **kwargs) -> Any: """Execute the tool logic.""" pass ``` ## ➕ Crear un Nuevo Servicio ### Paso 1: Crear el archivo del servicio Crea un nuevo archivo en `mcp_sql/tools/` (ej: `analyze_query.py`): ```python """Analyze query performance tool.""" from typing import Any, Optional, Dict from fastmcp import Context from .base import MCPTool class AnalyzeQueryService(MCPTool): """Service to analyze query performance and execution plan.""" @property def name(self) -> str: return "analyze_query" @property def description(self) -> str: return "Analyze SQL query performance and get execution plan" async def execute( self, ctx: Context, sql: str, database: Optional[str] = None, server_name: Optional[str] = None, user: Optional[str] = None, password: Optional[str] = None, driver: Optional[str] = None, port: Optional[int] = None, **kwargs ) -> Dict[str, Any]: """Analyze query execution. Args: ctx: FastMCP context sql: SQL query to analyze database: Database name server_name: Server hostname user: Username password: Password driver: ODBC driver port: Server port Returns: dict: Analysis results including execution plan """ # Obtener credenciales creds = self.creds_manager.get_from_context( ctx, user, password, server_name, database, driver, port ) if not creds.is_valid(): return {"error": "Missing credentials"} # Obtener engine engine = self.conn_manager.get_engine_with_credentials(creds) if not engine: return {"error": "Could not create connection"} # Tu lógica de análisis aquí try: with engine.connect() as conn: # Ejemplo: obtener execution plan en SQL Server result = conn.execute(f"SET SHOWPLAN_TEXT ON; {sql}") plan = [row for row in result] return { "query": sql, "execution_plan": plan, "database": creds.database, "server": creds.server } except Exception as e: return {"error": f"Analysis failed: {str(e)}"} ``` ### Paso 2: Registrar en `__init__.py` Edita `mcp_sql/tools/__init__.py`: ```python from .analyze_query import AnalyzeQueryService ALL_TOOLS = [ ListServersService, GetDatabasesService, GetTablesService, ExploreTableService, DescribeTableService, QueryColumnsService, AnalyzeQueryService, # ← Agregar aquí ] __all__ = [ # ... existentes ... 'AnalyzeQueryService', ] ``` ### Paso 3: ¡Listo! El servicio se registrará automáticamente al iniciar el servidor: ``` ✅ Initialized tool: analyze_query 🔧 Registered MCP tool: analyze_query ``` ## 🚀 Agregar Servicio Dinámicamente También puedes agregar servicios en tiempo de ejecución sin modificar archivos: ```python from mcp_sql import MCPSQLServer from mcp_sql.tools import MCPTool from fastmcp import Context class CustomReportService(MCPTool): @property def name(self) -> str: return "generate_report" @property def description(self) -> str: return "Generate custom database report" async def execute(self, ctx: Context, **kwargs): # Tu lógica aquí return {"report": "data"} # Crear servidor server = MCPSQLServer() # Agregar servicio personalizado custom_tool = CustomReportService( connection_manager=server.connection_manager, credentials_manager=server.credentials_manager, inspector=server.inspector, executor=server.executor ) server.add_custom_tool(custom_tool) # Iniciar servidor server.run(port=3939) ``` ## 💡 Ejemplos de Servicios Personalizados ### 1. Servicio de Backup ```python class BackupDatabaseService(MCPTool): @property def name(self) -> str: return "backup_database" @property def description(self) -> str: return "Create database backup" async def execute(self, ctx: Context, database: str, **kwargs): creds = self.creds_manager.get_from_context(ctx, **kwargs) engine = self.conn_manager.get_engine_with_credentials(creds) # Ejecutar BACKUP DATABASE... return {"status": "backup_created", "file": "path/to/backup"} ``` ### 2. Servicio de Estadísticas ```python class DatabaseStatsService(MCPTool): @property def name(self) -> str: return "get_database_stats" @property def description(self) -> str: return "Get comprehensive database statistics" async def execute(self, ctx: Context, database: str, **kwargs): creds = self.creds_manager.get_from_context(ctx, **kwargs) tables = self.inspector.get_tables(creds) stats = { "database": database, "table_count": len(tables), "tables": [] } for table in tables: table_info = self.inspector.describe_table(creds, table) stats["tables"].append({ "name": table, "rows": table_info.get("row_count", 0), "columns": len(table_info.get("columns", [])) }) return stats ``` ### 3. Servicio de Validación de Esquema ```python class ValidateSchemaService(MCPTool): @property def name(self) -> str: return "validate_schema" @property def description(self) -> str: return "Validate database schema against rules" async def execute( self, ctx: Context, database: str, rules: dict, **kwargs ): creds = self.creds_manager.get_from_context(ctx, **kwargs) tables = self.inspector.get_tables(creds) violations = [] for table in tables: table_info = self.inspector.describe_table(creds, table) # Validar reglas if rules.get("require_primary_key"): if not table_info.get("primary_key"): violations.append({ "table": table, "rule": "require_primary_key", "message": "Table missing primary key" }) return { "valid": len(violations) == 0, "violations": violations } ``` ## 🔍 Acceso a Componentes en Servicios Dentro de cualquier servicio, tienes acceso a: ```python # Gestión de conexiones self.conn_manager.get_engine_with_credentials(creds) self.conn_manager.get_configured_server_names() # Gestión de credenciales self.creds_manager.get_from_context(ctx, **kwargs) # Inspección de base de datos self.inspector.get_databases(creds) self.inspector.get_tables(creds) self.inspector.describe_table(creds, table) # Ejecución de queries self.executor.explore_table(creds, table, limit) self.executor.query_columns(creds, table, columns, limit) ``` ## 🎨 Ventajas de esta Arquitectura 1. ✅ **Modular**: Cada servicio es independiente 2. ✅ **Extensible**: Agregar servicios sin modificar código existente 3. ✅ **Testeable**: Cada servicio se puede probar aisladamente 4. ✅ **Organizado**: Responsabilidad única por servicio 5. ✅ **Discoverable**: Registro automático de servicios 6. ✅ **Reutilizable**: Componentes compartidos entre servicios 7. ✅ **Dinámico**: Agregar servicios en runtime ## 📊 Patrón Plugin Architecture Este diseño sigue el patrón **Plugin Architecture**: ``` MCPSQLServer │ ├─ ConnectionManager ├─ CredentialsManager ├─ Inspector ├─ Executor │ └─ Tools Registry │ ├─ Plugin 1 (ListServers) ├─ Plugin 2 (GetDatabases) ├─ Plugin 3 (GetTables) ├─ Plugin 4 (ExploreTable) ├─ Plugin 5 (DescribeTable) ├─ Plugin 6 (QueryColumns) └─ Plugin N (YourCustomTool) ``` Cada plugin: - Hereda de `MCPTool` - Implementa `name`, `description`, `execute` - Tiene acceso a todos los componentes - Se registra automáticamente ## 🧪 Testing de Servicios ```python import pytest from mcp_sql.tools.get_databases import GetDatabasesService from mcp_sql import ConnectionManager, CredentialsManager @pytest.fixture def service(): conn_mgr = ConnectionManager() creds_mgr = CredentialsManager() # ... inicializar componentes return GetDatabasesService( conn_mgr, creds_mgr, inspector, executor ) async def test_get_databases(service): result = await service.execute( ctx=mock_context, server_name="localhost", user="test", password="test" ) assert isinstance(result, list) assert len(result) > 0 ``` ## 📝 Mejores Prácticas 1. **Nombrado**: Usa sufijo `Service` en el nombre de la clase 2. **Documentación**: Documenta args y returns en `execute()` 3. **Validación**: Valida credenciales antes de ejecutar 4. **Errores**: Retorna diccionarios con `{"error": "..."}` en caso de fallo 5. **Logging**: Usa `print()` para mensajes importantes 6. **Límites**: Implementa límites en queries (max rows, timeout, etc.) 7. **Reutilización**: Usa componentes existentes en lugar de crear nuevos

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yuuues/mcp-sql'

If you have feedback or need assistance with the MCP directory API, please join our Discord server