Como construir un servidor MCP para tu Data Lake
MCP (Model Context Protocol) permite a los agentes de IA llamar herramientas — cómo consultar una base de datos. DataSpoc Lens incluye un servidor MCP integrado que convierte tu data lake de Parquet en una API consultable para Claude, GPT, agentes LangGraph y cualquier cliente compatible con MCP.
Este tutorial te muestra cómo pasar de datos crudos en S3 a un servidor MCP completamente funciónal en menos de 10 minutos.
El resultado final
Despues de la configuración, cualquier agente de IA puede:
- Descubrir tablas en tu data lake
- Inspeccionar esquemas y datos de muestra
- Ejecutar consultas SQL con DuckDB
- Hacer preguntas en lenguaje natural
- Obtener métricas y KPIs preconstruidos
Todo sin código personalizado.
Paso 1: Ingestar datos con Pipe
Si tus datos ya estan en Parquet en S3/GCS/Azure, salta al Paso 2. De lo contrario, extrae de tus fuentes:
pip install dataspoc-pipe
dataspoc-pipe init sales-pipelinedataspoc-pipe add postgres \ --host db.company.com \ --database sales \ --tables orders,customers,products,revenue \ --incremental updated_at \ --destination s3://company-lake
dataspoc-pipe runTu bucket ahora tiene:
s3://company-lake/ .dataspoc/manifest.json raw/postgres/orders/*.parquet raw/postgres/customers/*.parquet raw/postgres/products/*.parquet raw/postgres/revenue/*.parquetPaso 2: Instalar DataSpoc Lens con MCP
pip install dataspoc-lens[mcp]Esto instala el CLI principal de Lens mas las dependencias del servidor MCP.
Paso 3: Configurar e iniciar el servidor MCP
# Point Lens at your bucketdataspoc-lens add-bucket s3://company-lake
# Discover tablesdataspoc-lens discover# Found 4 tables: orders, customers, products, revenue
# Start the MCP serverdataspoc-lens mcpSalida:
DataSpoc Lens MCP Server runningTransport: stdioTables: 4 discoveredTools: 7 available - list_tables - get_schema - sample_data - query - ask - get_metrics - explain_table
Waiting for MCP client connection...Paso 4: Configurar Claude Desktop
Agrega a la configuración de Claude Desktop (~/.config/claude/claude_desktop_config.json en Linux, ~/Library/Application Support/Claude/claude_desktop_config.json en macOS):
{ "mcpServers": { "data-lake": { "command": "dataspoc-lens", "args": ["mcp"], "env": { "AWS_PROFILE": "data-lake", "DATASPOC_BUCKET": "s3://company-lake" } } }}Reinicia Claude Desktop. Veras el icono de herramientas indicando 7 herramientas disponibles.
Las 7 herramientas MCP
1. list_tables — Descubrir datos disponibles
Llamada:
{ "tool": "list_tables"}Respuesta:
{ "tables": [ {"name": "raw.postgres.orders", "rows": 1247893, "size_mb": 340}, {"name": "raw.postgres.customers", "rows": 89421, "size_mb": 12}, {"name": "raw.postgres.products", "rows": 2431, "size_mb": 1.1}, {"name": "raw.postgres.revenue", "rows": 4201847, "size_mb": 890} ]}2. get_schema — Inspeccionar estructura de tabla
Llamada:
{ "tool": "get_schema", "arguments": {"table": "raw.postgres.orders"}}Respuesta:
{ "table": "raw.postgres.orders", "columns": [ {"name": "id", "type": "INTEGER", "nullable": false}, {"name": "customer_id", "type": "INTEGER", "nullable": false}, {"name": "product_id", "type": "INTEGER", "nullable": false}, {"name": "amount", "type": "DECIMAL(10,2)", "nullable": false}, {"name": "status", "type": "VARCHAR", "nullable": false}, {"name": "created_at", "type": "TIMESTAMP", "nullable": false}, {"name": "updated_at", "type": "TIMESTAMP", "nullable": false} ], "row_count": 1247893}3. sample_data — Vista previa de filas
Llamada:
{ "tool": "sample_data", "arguments": {"table": "raw.postgres.orders", "limit": 5}}Respuesta:
{ "table": "raw.postgres.orders", "sample": [ {"id": 1, "customer_id": 42, "product_id": 7, "amount": 299.00, "status": "completed", "created_at": "2024-01-15T10:23:00"}, {"id": 2, "customer_id": 15, "product_id": 3, "amount": 49.99, "status": "completed", "created_at": "2024-01-15T11:05:00"}, {"id": 3, "customer_id": 42, "product_id": 12, "amount": 799.00, "status": "pending", "created_at": "2024-01-15T14:30:00"} ]}4. query — Ejecutar SQL
Llamada:
{ "tool": "query", "arguments": { "sql": "SELECT DATE_TRUNC('month', created_at) AS month, SUM(amount) AS revenue FROM raw.postgres.orders WHERE created_at >= '2024-01-01' GROUP BY 1 ORDER BY 1" }}Respuesta:
{ "columns": ["month", "revenue"], "rows": [ {"month": "2024-01-01", "revenue": 342891.50}, {"month": "2024-02-01", "revenue": 389012.75}, {"month": "2024-03-01", "revenue": 421547.00} ], "row_count": 3, "execution_time_ms": 234}5. ask — Preguntas en lenguaje natural
Llamada:
{ "tool": "ask", "arguments": { "question": "Which customers spent the most last quarter?" }}Respuesta:
{ "answer": "The top 5 customers by spending last quarter were: Acme Corp ($89,234), GlobalTech ($67,891), Initech ($54,320), Umbrella Inc ($48,900), and Wayne Enterprises ($45,670).", "sql": "SELECT c.name, SUM(o.amount) AS total_spent FROM raw.postgres.orders o JOIN raw.postgres.customers c ON o.customer_id = c.id WHERE o.created_at >= '2024-07-01' AND o.created_at < '2024-10-01' GROUP BY c.name ORDER BY total_spent DESC LIMIT 5", "data": [ {"name": "Acme Corp", "total_spent": 89234.00}, {"name": "GlobalTech", "total_spent": 67891.00}, {"name": "Initech", "total_spent": 54320.00}, {"name": "Umbrella Inc", "total_spent": 48900.00}, {"name": "Wayne Enterprises", "total_spent": 45670.00} ]}6. get_metrics — KPIs preconstruidos
Llamada:
{ "tool": "get_metrics", "arguments": {"period": "last_30_days"}}Respuesta:
{ "period": "2024-09-15 to 2024-10-15", "metrics": { "total_revenue": 487293.00, "order_count": 12847, "avg_order_value": 37.93, "unique_customers": 3421, "new_customers": 289 }}7. explain_table — Documentacion de datos
Llamada:
{ "tool": "explain_table", "arguments": {"table": "raw.postgres.orders"}}Respuesta:
{ "table": "raw.postgres.orders", "description": "Transaction records from the e-commerce platform. Each row represents one purchase.", "columns": { "id": "Unique order identifier", "customer_id": "FK to customers table", "amount": "Total order value in USD", "status": "One of: pending, completed, refunded, cancelled", "created_at": "When the order was placed" }, "relationships": ["customers (customer_id)", "products (product_id)"], "common_queries": [ "Revenue by month: SELECT DATE_TRUNC('month', created_at), SUM(amount) FROM orders GROUP BY 1", "Orders by status: SELECT status, COUNT(*) FROM orders GROUP BY 1" ]}Conversacion multi-turno realista con un agente
Asi se ve cuando Claude usa el servidor MCP:
Usuario: “Como esta la tendencia de retencion? Estamos perdiendo clientes?”
Llamadas internas de herramientas de Claude:
- Llama a
list_tablespara ver que datos estan disponibles - Llama a
get_schema("raw.postgres.orders")para entender la estructura de ordenes - Llama a
get_schema("raw.postgres.customers")para verificar fechas de registro - Llama a
querycon:
WITH monthly_cohorts AS ( SELECT DATE_TRUNC('month', c.created_at) AS cohort_month, DATE_TRUNC('month', o.created_at) AS order_month, COUNT(DISTINCT o.customer_id) AS active_customers FROM raw.postgres.customers c JOIN raw.postgres.orders o ON c.id = o.customer_id WHERE c.created_at >= '2024-01-01' GROUP BY 1, 2),cohort_sizes AS ( SELECT cohort_month, COUNT(DISTINCT id) AS cohort_size FROM raw.postgres.customers WHERE created_at >= '2024-01-01' GROUP BY 1)SELECT mc.cohort_month, mc.order_month, mc.active_customers, cs.cohort_size, ROUND(mc.active_customers::FLOAT / cs.cohort_size * 100, 1) AS retention_pctFROM monthly_cohorts mcJOIN cohort_sizes cs ON mc.cohort_month = cs.cohort_monthORDER BY mc.cohort_month, mc.order_monthRespuesta de Claude:
Analizando los datos de retencion por cohorte, esta es la tendencia:
Cohorte Mes 1 Mes 2 Mes 3 Mes 6 Ene 2024 100% 68% 52% 34% Abr 2024 100% 71% 55% 38% Jul 2024 100% 74% 59% — Buenas noticias: la retencion esta mejorando. La retencion del Mes 2 paso de 68% a 74% a lo largo del ano. Los cambios en el producto del Q2 (onboarding autoservicio) parecen estar funciónando.
Sin embargo, la caida al 34% en el Mes 6 sugiere que perdemos clientes alrededor de la marca de 6 meses — posiblemente cuando los contratos anuales llegan a renovacion.
Modelo de seguridad
El servidor MCP es solo lectura por diseno:
- No se permiten sentencias INSERT, UPDATE, DELETE, DROP o CREATE
- Las consultas se validan antes de la ejecucion
- El servidor se ejecuta con los permisos IAM del proceso (tu perfil de AWS)
- El control de acceso a nivel de bucket significa que los agentes solo ven datos que estan autorizados a ver
# Agent can only access buckets your IAM role permitsexport AWS_PROFILE=analyst # Has access to s3://company-product onlydataspoc-lens mcp # Only exposes tables in permitted bucketsNo hay API keys que gestionar. No hay tokens de autenticacion. Solo IAM.
SDK de Python para CrewAI / LangGraph
Si estas construyendo agentes en Python en lugar de usar Claude Desktop, usa el SDK directamente:
from dataspoc_lens import LensClient
# Initialize (reads from same config as CLI)client = LensClient(bucket="s3://company-lake")
# Same capabilities as MCP toolstables = client.list_tables()schema = client.get_schema("raw.postgres.orders")result = client.query("SELECT COUNT(*) FROM raw.postgres.orders")answer = client.ask("What's our churn rate?")Integracion con CrewAI
from crewai import Agent, Task, Crewfrom crewai_tools import toolfrom dataspoc_lens import LensClient
client = LensClient()
@tooldef query_data_lake(sql: str) -> str: """Execute a SQL query against the company data lake.""" result = client.query(sql) return str(result)
@tooldef discover_tables() -> str: """List all available tables in the data lake.""" tables = client.list_tables() return "\n".join([f"{t['name']} ({t['rows']} rows)" for t in tables])
analyst = Agent( role="Data Analyst", goal="Answer business questions using SQL on the data lake", tools=[query_data_lake, discover_tables], llm="gpt-4o",)
task = Task( description="Analyze customer lifetime value by acquisition channel", agent=analyst,)
crew = Crew(agents=[analyst], tasks=[task])result = crew.kickoff()Integracion con LangGraph
Consulta el tutorial completo: Construyendo un agente analista de datos con LangGraph.
El patrón AGENT.md
Para agentes que trabajan de forma autonoma con tu data lake, crea un archivo AGENT.md que describa los datos disponibles:
# Data Lake Agent Instructions
## Available Data
You have access to a data lake via the DataSpoc Lens MCP server.
## Tables
- `raw.postgres.orders` — All orders (1.2M rows). Key columns: customer_id, amount, status, created_at- `raw.postgres.customers` — Customer profiles (89K rows). Key columns: name, email, plan, created_at- `raw.postgres.products` — Product catalog (2.4K rows). Key columns: name, category, price- `curated.finance.revenue` — Monthly revenue rollups (4.2M rows). Key columns: month, mrr, arr, churn_rate
## Common Queries
- Revenue by month: `SELECT DATE_TRUNC('month', created_at), SUM(amount) FROM raw.postgres.orders GROUP BY 1`- Active customers: `SELECT COUNT(DISTINCT customer_id) FROM raw.postgres.orders WHERE created_at > CURRENT_DATE - INTERVAL '30 days'`- Churn: `SELECT month, churn_rate FROM curated.finance.revenue ORDER BY month DESC LIMIT 12`
## Rules
- Always use the `query` tool for exact numbers- Use `ask` for exploratory questions when you're not sure about the schema- Never modify data — all queries are read-only- When presenting numbers, always show the SQL you used (for auditability)Coloca esto en la raiz de tu proyecto. Los agentes que leen el contexto del proyecto (Claude Code, Cursor, Cline) lo usaran para entender cómo interactuar con tus datos.
Ejecucion en producción
Para despliegues en producción, ejecuta el servidor MCP cómo un proceso persistente:
# systemd service[Unit]Description=DataSpoc Lens MCP ServerAfter=network.target
[Service]Type=simpleUser=dataspocExecStart=/usr/local/bin/dataspoc-lens mcp --transport sse --port 8080Environment=AWS_PROFILE=data-lakeEnvironment=DATASPOC_BUCKET=s3://company-lakeRestart=always
[Install]WantedBy=multi-user.targetO con Docker:
FROM python:3.11-slimRUN pip install dataspoc-lens[mcp]ENV DATASPOC_BUCKET=s3://company-lakeCMD ["dataspoc-lens", "mcp", "--transport", "sse", "--port", "8080"]docker run -d \ -p 8080:8080 \ -e AWS_ACCESS_KEY_ID \ -e AWS_SECRET_ACCESS_KEY \ -e DATASPOC_BUCKET=s3://company-lake \ dataspoc-lens-mcpResumen
En 4 pasos, convertiste un monton de archivos Parquet en una API inteligente para agentes de IA:
- Ingesta datos con Pipe (o trae tu propio Parquet)
- Instala
dataspoc-lens[mcp] - Inicia
dataspoc-lens mcp - Conecta Claude Desktop, LangGraph, CrewAI o cualquier cliente MCP
Tu data lake ahora es una herramienta de primera clase para cualquier agente de IA — descubrible, consultable y segura.