TiendaTech Support MCP Server
Agente de soporte con servidor MCP propio
Proyecto 1 de una serie de 7 proyectos chicos, cada uno enfocado en una habilidad puntual que aparece hoy en avisos de trabajo para desarrolladores de agentes de IA. Este cubre: construir y consumir un servidor MCP (Model Context Protocol) desde cero.
El problema que resuelve
Una tienda de electrónica ("TiendaTech") necesita un agente de soporte que pueda:
Responder preguntas de clientes sobre políticas (devoluciones, garantía, envíos) sin inventar información.
Consultar y crear tickets de soporte reales, no simulados.
En vez de meter toda esa lógica adentro del prompt o hardcodeada en el cliente del LLM, ese acceso a datos vive en un servidor MCP separado: un proceso independiente que expone qué información y qué acciones están disponibles, con un contrato bien definido. El agente (el cliente que habla con Claude) no sabe nada de archivos ni de JSON — solo sabe que existe un servidor con ciertos recursos y herramientas.
Esto es exactamente el problema que MCP fue diseñado para resolver: separar "qué puede hacer un agente" de "qué modelo lo está operando", para no tener que reescribir la integración de datos cada vez que cambiás de LLM o de cliente.
Arquitectura
flowchart LR
subgraph Cliente["src/agent.ts (proceso 1)"]
CLI[REPL en terminal]
MCPClient[Cliente MCP]
Claude[Anthropic SDK]
end
subgraph Servidor["src/server.ts (proceso 2, hijo)"]
MCPServer[Servidor MCP]
Tools[Tools: search_tickets / get_ticket / create_ticket]
Resources[Resources: artículos de la KB]
end
DB[(data/tickets.json)]
KB[(data/kb/*.md)]
CLI --> MCPClient
MCPClient <-->|stdio JSON-RPC| MCPServer
MCPServer --> Tools
MCPServer --> Resources
Tools <--> DB
Resources <--> KB
MCPClient --> Claude
Claude -->|decide qué tool llamar| MCPClientSon dos procesos Node separados que se comunican por stdio con el protocolo MCP (JSON-RPC 2.0):
src/server.ts— el servidor MCP. No sabe nada de Claude ni de IA. Solo expone:Resources (
kb://politica-devoluciones.md, etc.): los 3 artículos de la base de conocimiento, como contenido de solo lectura pensado para dar contexto.Tools (
search_tickets,get_ticket,create_ticket): acciones que leen y escriben sobredata/tickets.json, una base de tickets de soporte.
src/agent.ts— el agente. Levanta el servidor como proceso hijo, se conecta como cliente MCP, y hace de puente entre MCP y la API de Claude:Al arrancar, lee todos los resources y los inyecta en el system prompt como contexto fijo.
Traduce la lista de tools del servidor al formato de tool-use de la API de Claude.
Corre un loop de conversación: si Claude decide llamar una tool, el agente ejecuta esa llamada contra el servidor MCP real y le devuelve el resultado, hasta que Claude da una respuesta final en texto.
Decisiones técnicas y por qué
Resources vs. Tools, a propósito. MCP distingue explícitamente entre datos de contexto (resources, decisión de cuándo leerlos la toma el host/agente) y acciones invocables por el modelo (tools, decisión de cuándo usarlas la toma el LLM). Usar ambos primitivos — y no meter todo como tools — muestra que la separación es intencional, no que "tools es lo único que existe en MCP".
Transporte stdio, no HTTP/SSE. Para un servidor MCP local de un solo cliente, stdio es más simple: no hay puertos, no hay CORS, no hay que levantar un server HTTP aparte. El SDK oficial de Anthropic soporta stdio, SSE y Streamable HTTP — stdio es el punto de entrada correcto para entender el protocolo antes de pasar a un despliegue remoto.
JSON planos en vez de SQLite/Postgres. El foco del proyecto es demostrar el protocolo MCP (cómo se define un tool, cómo se valida su input, cómo un cliente lo descubre y lo invoca), no la capa de persistencia. Cambiar
data/tickets.jsonpor una base real implica tocar únicamentereadTickets/writeTicketsenserver.ts— el contrato MCP hacia el agente no cambia.TypeScript + SDK oficiales.
@modelcontextprotocol/sdk(servidor y cliente) y@anthropic-ai/sdkson los paquetes oficiales de Anthropic. Se usa la API de alto nivelMcpServer(registerTool/registerResource) en vez de la API de bajo nivel consetRequestHandler, porque valida automáticamente el input contra el schema de Zod y da mensajes de error más claros — importante cuando el que "llena" los argumentos es un LLM y no un humano.Sin frameworks de agentes (LangChain, etc.). El loop de tool-use (llamar al modelo → detectar
tool_use→ ejecutar → devolvertool_result→ repetir) se escribe a mano en ~40 líneas. Para un solo agente con 3 tools, un framework agrega abstracción sin resolver un problema real; y entender ese loop sin magia es la habilidad que se quiere mostrar acá.
Stack
Node.js 22 + TypeScript
@modelcontextprotocol/sdk— servidor y cliente MCP@anthropic-ai/sdk— Claude (modeloclaude-sonnet-4-5)zod— validación de inputs de las toolsSin base de datos externa:
data/tickets.json+data/kb/*.md
Cómo correrlo
npm install
cp .env.example .env # completar ANTHROPIC_API_KEY
npm start # compila y levanta el agente (que a su vez levanta el servidor MCP)Ejemplo de conversación:
Vos: hola, tengo un teclado que no me terminó gustando, ¿lo puedo devolver?
Agente: Sí, podés devolverlo dentro de los 30 días de la compra siempre que esté sin uso...
Vos: se me rompió el auricular izquierdo de mis auriculares, ¿tienen algún ticket mío?
→ llamando herramienta MCP: search_tickets({"query":"auricular"})
Agente: Sí, encontré el ticket #1 a nombre de Marcos Videla, sigue abierto...Si querés inspeccionar el servidor MCP de forma aislada (sin Claude), se puede correr con el MCP Inspector oficial:
npm run build
npx @modelcontextprotocol/inspector node dist/server.jsEstructura
src/
server.ts # servidor MCP: resources + tools
agent.ts # cliente MCP + integración con Claude + REPL
data/
kb/*.md # base de conocimiento (resources)
tickets.json # base de tickets (tools)Qué demuestra este proyecto
Entender MCP como protocolo (no solo como "una librería más"): distinción resources/tools, transporte, ciclo de vida de conexión.
Diseñar el input schema de una tool con Zod pensando en que quien la va a llamar es un LLM, no un formulario.
Integrar el tool-use de la API de Claude con una fuente de tools que no está hardcodeada, sino descubierta dinámicamente en runtime contra un servidor externo.