En los últimos años han aparecido cientos de modelos de lenguaje capaces de razonar, generar código, analizar documentos, planificar tareas y un largo etcétera. Sin embargo, para mí el gran avance se encuentra en dotar a todos ellos de la posibilidad de actuar. Un modelo puede pensar qué consulta lanzar contra una base de datos, pero si no puede ejecutarla, se queda en un simple pensamiento. Para solucionar esto, surgió Model Context Protocol. MCP es el estándar que permite a cualquier asistente de IA ejecutar herramientas externas sin necesidad de intermediarios: consultar bases de datos, leer logs… todo ello sin requerir de acción humana. Es, por tanto, el sistema nervioso que conecta el cerebro de la IA con el mundo real. Lo que permite que los modelos no solo piensen, sino que hagan.
Hasta ahora, el foco ha estado en lo grande: agentes autónomos, pipelines de agentes de IA en desarrollo de software, integraciones empresariales. Pero MCP también es útil para las cosas más pequeñas, las de nuestro día a día como desarrolladores. En mi caso, he encontrado en MCP un gran aliado para la depuración de incidencias.
Cansado de necesitar consultar DynamoDB manualmente y todo lo que conlleva: abrir la terminal, pelearme con las comillas escapadas, interpretar un JSON tipado ilegible, lidiar con el cambio de contexto… decidí construir dynamodb-explorer, un servidor MCP de solo lectura para DynamoDB.
El problema: los clientes oficiales de AWS y la fricción de contexto
Aunque DynamoDB es una base de datos potente, su experiencia de uso en el día a día deja bastante que desear. Y no es culpa de DynamoDB en sí, sino de las herramientas de las que disponemos para interactuar con ella.
DynamoDB y sus herramientas: misma fricción, distinta ventana
DynamoDB usa un JSON tipado pensado para serialización, pero no para el ojo humano. Un string no es "valor": es {"S": "valor"}. Un número no es 42: es {"N": "42"}. Esto no es un detalle menor, es el mayor de los inconvenientes, ya que da igual la herramienta que uses, CLI, consola, NoSQL Workbench, PartiQL, todas devuelven los datos en este formato. Por ello, cada consulta se convierte también en un trabajo mental de descodificación, con la carga que supone.
La CLI de AWS agrava el problema con una sintaxis críptica: --expression-attribute-values, --projection-expression, --key-condition-expression. Una comilla mal puesta y el comando falla. La consola web no lo resuelve: entre login, región, menús y pantallas de carga, llegar a una consulta simple se convierte en un tedioso camino. Y herramientas como NoSQL Workbench o PartiQL mejoran la sintaxis, sí, pero sigues atado a otra ventana y al mismo DynamoDB JSON en las respuestas.
Además, independientemente de la herramienta elegida, el patrón es siempre el mismo: salir de tu entorno de trabajo para entrar en una herramienta que no habla tu idioma.
Lo que de verdad necesitas no es otra herramienta de consulta. Es poder preguntar desde donde ya estás trabajando:
«¿Cuántas notificaciones sin enviar tiene el usuario 12345?»
«¿Hay registros huérfanos en la tabla de producción?»
«¿Qué entidades existen y cómo se relacionan?»
Y que alguien, o algo, traduzca esa pregunta a las queries correctas, las ejecute, deserialice el resultado y devuelva una respuesta en JSON limpio. Sin salir del chat. Sin recordar sintaxis, ni procesar mentalmente estructuras de DynamoDB JSON
Ese «algo» es justo lo que permite Model Context Protocol. Y eso fue lo que me llevó a construir el explorador.
Qué es MCP (Model Context Protocol)
A nivel técnico, MCP (Model Context Protocol) funciona sobre JSON-RPC 2.0: el cliente y el servidor intercambian mensajes estructurados en JSON a través de un canal de transporte. El ciclo es simple: el cliente inicia la conexión, negocia capacidades con el servidor, descubre las tools disponibles (tools/list) y luego las invoca bajo demanda (tools/call). Cada tool recibe argumentos tipados y devuelve contenido estructurado.
Model Context Protocol es un protocolo abierto, creado y liberado inicialmente por Anthropic, que permite a los modelos de lenguaje interactuar con herramientas externas de forma estructurada. Básicamente: una forma estándar de conectar tus herramientas con tu asistente de código.
Piensa en ello como un USB-C para herramientas de IA. Antes de MCP, cada integración requería su propio adaptador, su propia lógica de parsing… MCP estandariza esa conexión.
En una integración local típica, la arquitectura se ve así:
Cliente MCP (Claude Code / Cursor / Codex...) ←→ JSON-RPC sobre stdin/stdout ←→ Servidor MCP (tu código)
A nivel de transporte, stdio es el óptimo para herramientas locales porque Claude Code lanza el servidor como proceso hijo y se comunica con él por entrada y salida estándar. Sin puertos, sin autenticación HTTP, sin CORS. MCP también soporta otros transportes como SSE (Server-Sent Events, para herramientas remotas) y Streamable HTTP, pensados para escenarios donde el servidor no vive en la misma máquina que el cliente.
Un servidor MCP puede exponer tres tipos de capacidades:
- Tools: funciones invocables por el modelo. Por ejemplo:
leer_registropara consultar DynamoDB. - Resources: datos accesibles de forma pasiva. Por ejemplo: un esquema de base de datos.
- Prompts: plantillas reutilizables para guiar interacciones concretas.
En mi caso me centré en tools, porque el objetivo era permitir consultas y análisis sobre DynamoDB. Pero el protocolo da para más.
La solución: un explorador de DynamoDB vía MCP (Model Context Protocol)
Diseñé dynamodb-explorer, un servidor MCP que expone herramientas diseñadas para consultar DynamoDB desde el chat de Claude Code.
Stack
- Runtime: Node.js con
tsx(TypeScript directo, sin paso de compilación). - AWS:
@aws-sdk/client-dynamodbv3. - Protocolo:
@modelcontextprotocol/sdkv1.x (la rama estable en el momento de escribir esto). - Transporte: stdio.
- Código: ~700 líneas en un solo archivo.
- Variables de entorno:
AWS_REGION,AWS_PROFILEyDYNAMODB_TABLE_NAME.
Para integrarlo en Claude Code, la configuración es así de simple:
{
"mcpServers": {
"dynamodb-explorer": {
"command": "npx",
"args": ["tsx", "/ruta/dynamodb-explorer/src/index.ts"],
"env": {
"AWS_PROFILE": "dev",
"AWS_REGION": "eu-west-1",
"DYNAMODB_TABLE_NAME": "mi-tabla"
}
}
}
}
Usando AWS_PROFILE, se pueden separar credenciales por entorno y evitar que el servidor use accidentalmente permisos más amplios de los necesarios. En local apunta a staging; en producción, solo cuando es imprescindible y con límites más agresivos.
Read-only por construcción: menos riesgo, no cero riesgo
Algo que tuve claro desde el minuto uno: el servidor no escribe. Solo importa GetItemCommand, QueryCommand, ScanCommand, DescribeTableCommand y ListTablesCommand. No hay rastro de PutItemCommand, UpdateItemCommand ni DeleteItemCommand en el código.
Eso elimina el riesgo de modificar datos accidentalmente. El asistente puede explorar sin miedo, pero teniendo claro que read-only no es sinónimo de inofensivo. El riesgo cambia de naturaleza, ya que no solo se trata de la integridad (no puedes romper datos) sino también de la confidencialidad (qué datos puedes leer, cuántos, y a dónde llegan).
Un servidor mal configurado podría leer PII sin filtro, ejecutar scans costosos o devolver más información de la necesaria al modelo. Por eso apliqué varias capas:
{
"Effect": "Allow",
"Action": [
"dynamodb:ListTables",
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": [
"arn:aws:dynamodb:eu-west-1:123456789012:table/mi-tabla"
]
}
Además apliqué límites de resultados por defecto en cada tool, separación de credenciales por entorno, logs de invocación y redacción de campos sensibles antes de devolver la respuesta.
Las herramientas, organizadas por capacidad
En lugar de exponer un genérico query(table, expression) , que para eso ya está la CLI, diseñé herramientas que encapsulan operaciones compuestas, agrupándolas en tres capas:
Exploración básica — lo primero que necesitas al abrir una tabla:
listar_tablasydescribe_table: saber qué hay y cómo está organizado.get_itemyquery_partition: leer registros concretos sin pelearte con la sintaxis de DynamoDB.
Análisis de modelo — para entender la forma de los datos:
analyze_table_schemaresume entidades, patrones de clave y tipos observados.entity_attributesdetalla los atributos de una entidad concreta con ejemplos.relationship_overviewmapea cómo se relacionan las entidades entre sí.
Auditoría y debugging — para detectar lo que falla:
find_database_inconsistenciesdetecta huérfanos, tipos mezclados y TTLs expirados.read_entityagrupa todos los registros de una entidad lógica en una sola consulta.scan_tablepermite muestreos rápidos con límites por defecto.
Si tuviera que quedarme con tres: analyze_table_schema para aterrizar en una tabla desconocida, read_entity para el día a día, y find_database_inconsistencies para auditoría.
Cada tool devuelve JSON plano legible. Nada de {"S": "valor"}. El servidor hace la deserialización antes de devolver la respuesta, así que el modelo recibe datos que entiende a la primera. De esta forma es capaz de interpretar y responderte en lenguaje natural.
Un ejemplo real de salida
Lo importante no es solo que la tool encuentre problemas: es cómo los devuelve. Cada issue tiene severidad, tipo y un mensaje pensado para que el asistente razone, no para que regurgite un Scan en crudo.
Así se ve una respuesta de find_database_inconsistencies:
{
"summary": {
"totalIssues": 12,
"high": 3,
"medium": 5,
"low": 4
},
"issues": [
{
"severity": "high",
"type": "orphan_reference",
"entity": "ORDER#a1b2c3d4",
"detail": "Referencia a USER#5678 que no existe en la tabla."
},
{
"severity": "medium",
"type": "mixed_attribute_type",
"attribute": "created_at",
"entity": "PRODUCT",
"detail": "El atributo aparece con tipos S y N en entidades del mismo tipo."
},
{
"severity": "low",
"type": "expired_ttl",
"entity": "SESSION#xyz",
"detail": "TTL expirado hace 3 días. DynamoDB aún no lo ha eliminado."
}
]
}
Esta estructura es lo que permite que el asistente no se limite a volcar datos, puede priorizar problemas, explicar impacto y sugerir siguientes pasos. Esa es justo la diferencia entre envolver una API y construir una herramienta de verdad.
En mi flujo diario, consultas que antes me llevaban uno o dos minutos entre CLI, JSON y cambios de contexto pasaron a resolverse en unos segundos desde el chat.
Construye tu propio servidor MCP (Model Context Protocol) en 10 minutos
Si quieres integrar tu propia herramienta, montar un servidor MCP es sorprendentemente rápido. Siguiendo con el ejemplo del artículo, aquí tienes una tool que consulta DynamoDB y devuelve un JSON limpio.
1. Inicializa el proyecto
mkdir mi-mcp && cd mi-mcp npm init -y npm install @modelcontextprotocol/sdk @aws-sdk/client-dynamodb npm install -D tsx typescript @types/node
2. Crea el servidor con una tool real
Este esqueleto expone leer_registro, que recibe una PK, una SK y devuelve el ítem en JSON plano, sin DynamoDB JSON tipado:
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
import type { AttributeValue } from "@aws-sdk/client-dynamodb";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const dynamo = new DynamoDBClient({ region: process.env.AWS_REGION || "eu-west-1" });
const TABLE = process.env.DYNAMODB_TABLE_NAME || "mi-tabla";
const server = new Server(
{ name: "dynamodb-light", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// Deserializar DynamoDB JSON → JSON plano
function attrToJs(value: AttributeValue | undefined): unknown {
if (!value) return undefined;
if ("S" in value) return value.S;
if ("N" in value) return Number(value.N);
if ("BOOL" in value) return value.BOOL;
if ("NULL" in value) return null;
if ("L" in value) return (value.L ?? []).map(attrToJs);
if ("M" in value) {
return Object.fromEntries(
Object.entries(value.M ?? {}).map(([k, v]) => [k, attrToJs(v)])
);
}
return value;
}
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "leer_registro",
description: "Lee un ítem de DynamoDB por PK y SK y lo devuelve en JSON plano.",
inputSchema: {
type: "object",
properties: {
PK: { type: "string", description: "Partition key exacta." },
SK: { type: "string", description: "Sort key exacta." }
},
required: ["PK", "SK"]
}
}
]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "leer_registro") {
const args = request.params.arguments as Record<string, unknown> | undefined;
const PK = args?.PK as string;
const SK = args?.SK as string;
const result = await dynamo.send(
new GetItemCommand({
TableName: TABLE,
Key: { PK: { S: PK }, SK: { S: SK } }
})
);
const item = result.Item
? Object.fromEntries(
Object.entries(result.Item).map(([k, v]) => [k, attrToJs(v)])
)
: null;
return {
content: [{ type: "text", text: JSON.stringify({ PK, SK, item }, null, 2) }]
};
}
throw new Error(Tool desconocida: ${request.params.name});
});
const transport = new StdioServerTransport();
await server.connect(transport);
Presta atención a attrToJs, ya que esta pequeña función recursiva es lo que convierte {"S": "valor"} en "valor". Puede parecer una tontería, pero es justo lo que elimina la fricción que describí al principio. La versión real maneja más tipos de DynamoDB (binarios, sets de strings, sets de números), pero esta ya cubre el 95% de los casos. La idea es que veas el patrón, no que copies un deserializador de producción.
3. Conéctalo a Claude Code
En Claude Code, añades el servidor a tu archivo .mcp.json en la raíz del proyecto:
{
"mcpServers": {
"dynamodb-light": {
"command": "npx",
"args": ["tsx", "/ruta/a/mi-mcp/index.ts"],
"env": {
"AWS_PROFILE": "dev",
"AWS_REGION": "eu-west-1",
"DYNAMODB_TABLE_NAME": "mi-tabla"
}
}
}
}
También puedes usar claude mcp add dynamodb-light -- npx tsx /ruta/a/mi-mcp/index.ts desde la terminal y Claude Code lo registra automáticamente.
¿Y si usas otra herramienta?
La configuración cambia ligeramente según el cliente MCP, pero el concepto es idéntico en todas:
- Cursor: se añade en los ajustes del editor, sección MCP.
- VS Code + Copilot: en
.vscode/mcp.jsono en los settings del editor. - Codex (OpenAI): en su archivo de configuración propio.
- OpenCode: en
opencode.jsono~/.config/opencode/opencode.json.
En todos los casos, el servidor es el mismo. Solo cambia dónde declaras el bloque JSON y cómo el cliente lanza el proceso hijo. El estándar MCP garantiza que tu servidor funcione en cualquiera de ellos sin modificaciones.
El patrón se repite para cualquier integración: registras tools en ListToolsRequestSchema, implementas su lógica en CallToolRequestSchema y conectas por stdio. Añadir más herramientas es cuestión de sumar casos al switch.
Lecciones que aprendí
El valor está en el conocimiento de dominio, no en el wrapper
Un buen servidor MCP no envuelve una API: encapsula conocimiento de dominio. read_entity no es un query genérico: sabe qué campos son claves, cómo se relacionan las entidades, y compone varias consultas para devolver una respuesta coherente. Esa es la diferencia entre un wrapper y una herramienta de verdad.
Cosas que tuve que ajustar después de usarlo
Dejarlo correctamente configurado y adaptado a mis necesidades fue fruto de iteraciones de refinamiento. Algunos de los problemas en los que tuve que trabajar fueron:
- Las respuestas eran demasiado grandes. El modelo recibía cientos de items cuando solo necesitaba un resumen. Añadí límites por defecto y paginación en todas las tools.
- Algunas tools eran demasiado genéricas.
scan_tablesin acotar era un arma cargada. La dividí en operaciones más específicas comoanalyze_table_schemayentity_attributes. - La deserialización era inconsistente. Unas tools devolvían JSON plano y otras DynamoDB JSON. El asistente se liaba. Normalicé todo a JSON plano.
- Los nombres de las tools eran comandos técnicos, no preguntas que el equipo se hacía. Los cambié por nombres que reflejan intenciones reales:
find_database_inconsistenciesen vez devalidate_referential_integrity. - No separaba queries baratas de análisis caros. Ahora las tools que implican scans llevan límites visibles y advertencias en la descripción.
Cuándo usar MCP (Model Context Protocol) y cuándo no
A pesar de todos los beneficios y capacidades que nos otorga MCP, debemos tener claro que no en todos los escenarios encaja como un guante.
Ideal para
- Tareas exploratorias y repetitivas que te sacan de Claude Code.
- Consultas donde el conocimiento de dominio marca la diferencia.
- Operaciones de solo lectura con bajo riesgo.
- Herramientas internas que todo el equipo necesita pero nadie recuerda cómo usar.
- Depuración y auditoría de datos en entornos de desarrollo.
No lo usaría para
- Procesos críticos de escritura sin validaciones externas.
- Consultas masivas donde el coste o la latencia sean importantes.
- Operaciones que requieran una interfaz visual compleja.
- Casos donde un script de 10 líneas ya lo resuelve.
- Datos sensibles que no puedes filtrar antes de devolver al modelo.
Bajo mi punto de vista, el punto dulce de MCP (Model Context Protocol) está justo donde estaba mi problema inicial: tareas exploratorias, repetitivas y con suficiente lógica de dominio como para que un aws dynamodb query no baste.
Conclusión
Al final, dynamodb-explorer es solo un ejemplo. Lo que de verdad importa es el patrón: identificar esas pequeñas fricciones diarias como consultar logs, revisar PRs, comprobar el estado de un deploy, buscar en la documentación interna… y convertirlas en tools que tu asistente ejecute sin que tú cambies de ventana.
MCP (Model Context Protocol) es la pieza que faltaba para que esta revolución industrial de la IA aterrice en nuestro día a día como developers. No hace falta un proyecto de meses ni un equipo dedicado. Con cincuenta líneas de TypeScript y el SDK montas un servidor que te ahorra horas de contexto perdido cada semana.
Yo empecé con DynamoDB porque era mi mayor fricción en el día a día. Tú tendrás la tuya. Lo que sea que te haga salir del chat varias veces al día: logs, incidencias, métricas, tickets de Jira… Construye un MCP para eso. La inversión es mínima y el retorno en fluidez es inmediato.
La revolución ya está aquí. MCP es la herramienta para no quedarte mirándola desde la barrera.
¿Quieres seguir al tanto de lo último en Inteligencia Artificial y desarrollo de software? Permanece atento/a a nuestras Redes Sociales y Canal de YouTube.