<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrés León</title>
    <description>The latest articles on DEV Community by Andrés León (@noelserdna).</description>
    <link>https://dev.to/noelserdna</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4189321%2Fba8853e5-740f-4cbf-aa26-c9a51b59f5ab.jpg</url>
      <title>DEV Community: Andrés León</title>
      <link>https://dev.to/noelserdna</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/noelserdna"/>
    <language>en</language>
    <item>
      <title>Web lista para agentes de IA: guía con Cloudflare Radar</title>
      <dc:creator>Andrés León</dc:creator>
      <pubDate>Wed, 04 Nov 2026 15:18:26 +0000</pubDate>
      <link>https://dev.to/noelserdna/web-lista-para-agentes-de-ia-guia-con-cloudflare-radar-104o</link>
      <guid>https://dev.to/noelserdna/web-lista-para-agentes-de-ia-guia-con-cloudflare-radar-104o</guid>
      <description>&lt;p&gt;Una web lista para agentes de IA es una web que un agente puede encontrar, leer sin ruido y usar sin abrir un navegador: sabe qué hay, lo pide en Markdown y llama a sus herramientas por MCP o WebMCP. Cloudflare lo mide gratis con un escáner que da una nota de 0 a 100 y un nivel de 1 a 5.&lt;/p&gt;

&lt;p&gt;Este artículo es un extracto de nuestra guía en español sobre cómo preparar tu web para agentes con Cloudflare Radar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo usar el escáner de Cloudflare Radar
&lt;/h2&gt;

&lt;p&gt;Si tu página es un HTML lleno de menús y scripts, el agente gasta tokens en ruido y cita a otro. Cloudflare comparó su documentación, preparada para agentes, con otros grandes sitios técnicos: un agente gastó un 31 % menos de tokens y llegó a la respuesta correcta un 66 % más rápido (&lt;a href="https://blog.cloudflare.com/agent-readiness/" rel="noopener noreferrer"&gt;blog de Cloudflare&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Tienes tres formas de pasar el examen, y las tres usan las mismas pruebas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;En &lt;a href="https://isitagentready.com/" rel="noopener noreferrer"&gt;isitagentready.com&lt;/a&gt;&lt;/strong&gt;, escribiendo la URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En el &lt;a href="https://radar.cloudflare.com/scan" rel="noopener noreferrer"&gt;URL Scanner de Radar&lt;/a&gt;&lt;/strong&gt;, en la pestaña de preparación para agentes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desde código&lt;/strong&gt;: con la API del URL Scanner (opción &lt;code&gt;agentReadiness&lt;/code&gt;) o con el servidor MCP del propio escáner, que tiene una tool &lt;code&gt;scan_site&lt;/code&gt;. Así puedes pedirle a tu agente que escanee la web después de cada cambio.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para leer bien la nota:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;La nota parece ser el porcentaje de pruebas superadas sobre las que puntúan.&lt;/strong&gt; Cloudflare no publica la fórmula, pero cuadra en todos nuestros escaneos: con 10 de 16 tuvimos 63, con 14 de 16, 88, y con 15 de 16, 94. Hoy puntúan 17 pruebas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Las neutrales no cuentan.&lt;/strong&gt; Las de comercio (x402, UCP, ACP…) no cuentan para la nota, según Cloudflare. Web Bot Auth es informativa. El llms.txt tampoco puntúa: Cloudflare solo comprueba si sirves Markdown cuando te lo piden.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué mira cada prueba
&lt;/h2&gt;

&lt;p&gt;Las pruebas se agrupan en cuatro capas:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capa&lt;/th&gt;
&lt;th&gt;Prueba&lt;/th&gt;
&lt;th&gt;Qué pide&lt;/th&gt;
&lt;th&gt;Dónde se toca&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Descubrimiento&lt;/td&gt;
&lt;td&gt;robots.txt y sitemap&lt;/td&gt;
&lt;td&gt;Que existan y sean válidos&lt;/td&gt;
&lt;td&gt;Archivos en la raíz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descubrimiento&lt;/td&gt;
&lt;td&gt;Cabeceras Link&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Link&lt;/code&gt; en la portada hacia recursos para agentes&lt;/td&gt;
&lt;td&gt;Cabeceras del servidor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descubrimiento&lt;/td&gt;
&lt;td&gt;DNS-AID&lt;/td&gt;
&lt;td&gt;Registros DNS que anuncian tus servicios para agentes, con DNSSEC&lt;/td&gt;
&lt;td&gt;Proveedor de DNS y registrador&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contenido&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;td&gt;Responder &lt;code&gt;text/markdown&lt;/code&gt; si el agente lo pide con &lt;code&gt;Accept&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Código del servidor o Cloudflare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Acceso de bots&lt;/td&gt;
&lt;td&gt;Reglas de IA y Content Signals&lt;/td&gt;
&lt;td&gt;Reglas por bot y qué permites: buscar, responder, entrenar&lt;/td&gt;
&lt;td&gt;robots.txt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacidades&lt;/td&gt;
&lt;td&gt;Agent Skills&lt;/td&gt;
&lt;td&gt;Índice de skills en &lt;code&gt;/.well-known/agent-skills/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Archivos estáticos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacidades&lt;/td&gt;
&lt;td&gt;API Catalog y ARD&lt;/td&gt;
&lt;td&gt;Catálogos de tus APIs y servicios en &lt;code&gt;/.well-known/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Archivos estáticos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacidades&lt;/td&gt;
&lt;td&gt;MCP Server Card&lt;/td&gt;
&lt;td&gt;La tarjeta de tu servidor MCP&lt;/td&gt;
&lt;td&gt;Archivo + un servidor MCP real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacidades&lt;/td&gt;
&lt;td&gt;OAuth y auth.md&lt;/td&gt;
&lt;td&gt;Metadatos OAuth (RFC 8414 y 9728) e instrucciones de alta para agentes&lt;/td&gt;
&lt;td&gt;Servidor de autorización&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacidades&lt;/td&gt;
&lt;td&gt;WebMCP&lt;/td&gt;
&lt;td&gt;Herramientas registradas con &lt;code&gt;document.modelContext&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;JavaScript de la página&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Primero, detecta la tecnología de la web
&lt;/h2&gt;

&lt;p&gt;Antes de abrir un solo archivo conviene saber con qué está hecha la web y quién controla cada pieza, porque eso decide qué es fácil y qué no:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Archivos estáticos&lt;/strong&gt; (robots.txt, &lt;code&gt;/.well-known/*.json&lt;/code&gt;): casi siempre posibles, salvo que la plataforma no te deje publicar en la raíz.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cabeceras&lt;/strong&gt; (Link): hosting o middleware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Negociación de contenido&lt;/strong&gt; (Markdown): necesitas código en el servidor o un servicio que convierta. Cloudflare lo hace con Markdown for Agents, solo en los planes Pro, Business y Enterprise (y para los clientes de SSL for SaaS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Puntos de entrada vivos&lt;/strong&gt; (MCP, A2A, OAuth): necesitas un servidor que ejecute código, sea un Worker, una función o tu backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt; (DNS-AID y DNSSEC): en tu proveedor de DNS y tu registrador, no en la web.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para detectar la pila no hace falta acceso al código. Bastan tres miradas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sIL&lt;/span&gt; tudominio.com                  &lt;span class="c"&gt;# server, x-powered-by, cookies, Link&lt;/span&gt;
curl &lt;span class="nt"&gt;-sL&lt;/span&gt; tudominio.com | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 3000    &lt;span class="c"&gt;# rutas de los recursos, meta generator&lt;/span&gt;
dig +short NS tudominio.com              &lt;span class="c"&gt;# quién lleva el DNS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Las rutas de los archivos delatan mucho: &lt;code&gt;/_next/&lt;/code&gt; es Next.js, &lt;code&gt;/_astro/&lt;/code&gt; es Astro, &lt;code&gt;wp-content&lt;/code&gt; es WordPress. Los nombres de los módulos JavaScript y las cadenas del bundle principal suelen terminar de confirmarlo.&lt;/p&gt;




&lt;p&gt;La guía completa añade lo que este extracto deja fuera: por qué importa que los agentes vean tu web con los datos de adopción de Radar, un caso práctico real (el medio BeersStorming, con su pila, lo que ya tiene, lo que falla y lo que se ve desde fuera), una estrategia en tres fases para llegar al 100, la tabla que enlaza cada prueba fallida con su fase, lo que aprendimos al pasar del 25 al 100 en desarrollomcp.com (DNSSEC, auth.md y qué pruebas aportan valor real) y qué se gana a futuro con MCP y WebMCP. Léela entera en &lt;strong&gt;&lt;a href="https://desarrollomcp.com/blog/web-lista-para-agentes-ia?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=web-lista-para-agentes-ia" rel="noopener noreferrer"&gt;DesarrolloMCP: web lista para agentes de IA&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si quieres aprender a construir servidores MCP desde cero, tienes el &lt;a href="https://desarrollomcp.com/aprende?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=web-lista-para-agentes-ia" rel="noopener noreferrer"&gt;curso de DesarrolloMCP&lt;/a&gt;, con el nivel Básico gratis (14 lecciones, sin tarjeta), en español.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>spanish</category>
      <category>servidores</category>
      <category>agentes</category>
    </item>
    <item>
      <title>Bloqueos de LaLiga a Cloudflare: cómo seguimos abiertos con MCP</title>
      <dc:creator>Andrés León</dc:creator>
      <pubDate>Tue, 03 Nov 2026 06:00:34 +0000</pubDate>
      <link>https://dev.to/noelserdna/bloqueos-de-laliga-a-cloudflare-como-seguimos-abiertos-con-mcp-4ing</link>
      <guid>https://dev.to/noelserdna/bloqueos-de-laliga-a-cloudflare-como-seguimos-abiertos-con-mcp-4ing</guid>
      <description>&lt;p&gt;Durante las jornadas de fútbol, las operadoras españolas bloquean direcciones IP de Cloudflare por una orden judicial, y con ellas caen cientos de miles de webs legales que comparten esas direcciones. Nosotros seguimos abiertos con un espejo en Netlify que se enciende solo en cada partido, y el servidor MCP del curso sigue respondiendo, con su inicio de sesión incluido.&lt;/p&gt;

&lt;p&gt;Este texto es un extracto de nuestra guía en español, escrita sobre la versión 2026-07-28 de la especificación MCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué caen webs que no tienen nada que ver con el fútbol
&lt;/h2&gt;

&lt;p&gt;Cloudflare no da a cada web una IP propia. Le asigna direcciones de un conjunto compartido, y en los planes Free, Pro y Business no hay IP dedicadas. Si una web que comparte IP emite un partido pirata y la operadora bloquea esa IP, caen todas.&lt;/p&gt;

&lt;p&gt;Las cifras de OONI, con datos del 1 de enero al 1 de junio de 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;De 9,2 millones de dominios probados, 554.507 (un 5,8 %) estuvieron bloqueados al menos una vez.&lt;/li&gt;
&lt;li&gt;De esos, 501.305 estaban en Cloudflare, repartidos en solo 2.218 IP.&lt;/li&gt;
&lt;li&gt;Bloquear entre 4 y 20 IP durante una hora dejó inaccesibles más de 400.000 dominios.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para quien programa, el efecto es concreto: un fin de semana de abril, &lt;code&gt;docker pull&lt;/code&gt; fallaba desde España con un error de certificado (&lt;a href="https://news.ycombinator.com/item?id=47738883" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt;). Irse de Cloudflare tampoco es una garantía, porque OONI midió bloqueos en IP de 36 proveedores, Amazon incluido. Nosotros preferimos no movernos y, solo durante los partidos, mandar el tráfico a otro sitio.&lt;/p&gt;

&lt;h2&gt;
  
  
  El modo liga: un espejo en Netlify durante los partidos
&lt;/h2&gt;

&lt;p&gt;Fuera de los partidos, el tráfico va a Cloudflare y al Worker. Durante uno, el DNS apunta a Netlify, que sirve una copia estática (blog, temario y fichas), muestra un aviso en cuenta, registro y pagos, y reenvía al Worker con un proxy firmado el servidor MCP, OAuth y el login de la conexión.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Horarios.&lt;/strong&gt; Cada lunes, una tarea programada del Worker lee los horarios de Primera de los próximos ocho días en la API de football-data.org.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ventanas.&lt;/strong&gt; Cada partido abre una ventana desde una hora antes del inicio hasta tres horas después. Si dos se solapan, se unen: con el primer partido a las 14:00 y el último a las 21:00, la ventana va de 13:00 a medianoche.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS.&lt;/strong&gt; Cada diez minutos, la misma tarea mira si está dentro de una ventana y cambia el DNS con la API de Cloudflare. Durante un partido apunta a Netlify, con un TTL de 60 segundos. Solo toca un registro si no está como debe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;La copia.&lt;/strong&gt; Cada madrugada, una GitHub Action recorre la web como visitante anónimo y publica la copia en Netlify, tras comprobar que no lleva el texto de ninguna lección de pago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El certificado.&lt;/strong&gt; Netlify solo renueva su certificado TLS si el dominio apunta a Netlify, y casi siempre apunta a Cloudflare. Otra Action lo emite cada mes con Let's Encrypt, validando por DNS en Cloudflare, y lo sube a Netlify.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Por qué el servidor MCP sigue funcionando
&lt;/h2&gt;

&lt;p&gt;El bloqueo se aplica en la red de tu operadora. Las peticiones que reenvía Netlify salen de sus servidores, no de tu conexión.&lt;/p&gt;

&lt;p&gt;Las rutas del servidor MCP, la autorización OAuth y el login de la conexión no se copian: Netlify las reenvía al Worker con un proxy firmado. Cada petición lleva una firma que el Worker comprueba antes de atenderla; si no está firmada, no se atiende. El Worker rehace la petición como si hubiera llegado directa a &lt;code&gt;desarrollomcp.com&lt;/code&gt; y la marca como venida del espejo. Con esa marca, la página de login solo se sirve para autorizar una conexión MCP o registrar un agente.&lt;/p&gt;

&lt;p&gt;Que sea tan fácil tiene que ver con cómo es MCP desde la versión 2026-07-28. El protocolo no tiene estado: toda la información para procesar una petición viaja en la propia petición, y no hay &lt;em&gt;handshake&lt;/em&gt; ni sesión. Un proxy solo tiene que reenviar cada petición tal cual (&lt;a href="https://desarrollomcp.com/blog/mcp-2026-07-28-protocolo-sin-estado" rel="noopener noreferrer"&gt;MCP 2026-07-28 sin estado&lt;/a&gt;). La autorización es el mismo OAuth de siempre.&lt;/p&gt;

&lt;p&gt;El resultado: durante un partido no puedes entrar en la web, pero sí conectar el curso a Claude, ChatGPT o Claude Code e iniciar sesión con tu cuenta. Sin cuenta, el servidor &lt;code&gt;https://desarrollomcp.com/mcp&lt;/code&gt; te deja buscar en el curso, ver el temario, las fichas y los planes, y leer el blog. Con sesión, te dice qué nivel tienes y por dónde vas, y te da el texto de las lecciones a las que tienes acceso. Los pasos están en &lt;a href="https://desarrollomcp.com/blog/conectar-servidor-mcp-claude" rel="noopener noreferrer"&gt;cómo conectar un servidor MCP a Claude&lt;/a&gt; y en la página del &lt;a href="https://desarrollomcp.com/servidor-mcp" rel="noopener noreferrer"&gt;servidor MCP del curso&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;La guía completa añade lo que aquí queda fuera: la sentencia 310/2024 y el calendario judicial y político, las posturas de LaLiga y de Cloudflare, lo que puede hacer una web pequeña, el caso de un negocio de Pego con unas 115 horas de bloqueo, el diagrama del flujo, las páginas que la copia no puede servir, los límites del modo liga y el siguiente paso, las MCP Apps, con todas las fuentes enlazadas: &lt;strong&gt;&lt;a href="https://desarrollomcp.com/blog/bloqueos-laliga-cloudflare-mcp?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=bloqueos-laliga-cloudflare-mcp" rel="noopener noreferrer"&gt;DesarrolloMCP: bloqueos de LaLiga a Cloudflare y MCP&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si quieres aprender a construir y desplegar servidores MCP, tienes el &lt;a href="https://desarrollomcp.com/aprende?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=bloqueos-laliga-cloudflare-mcp" rel="noopener noreferrer"&gt;curso de DesarrolloMCP&lt;/a&gt;, con el nivel Básico gratis (14 lecciones, sin tarjeta), en español.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>spanish</category>
      <category>servidores</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Conectar Claude con Gmail y Calendar: guía probada</title>
      <dc:creator>Andrés León</dc:creator>
      <pubDate>Tue, 03 Nov 2026 05:52:36 +0000</pubDate>
      <link>https://dev.to/noelserdna/conectar-claude-con-gmail-y-calendar-guia-probada-4jd9</link>
      <guid>https://dev.to/noelserdna/conectar-claude-con-gmail-y-calendar-guia-probada-4jd9</guid>
      <description>&lt;p&gt;Puedes conectar Claude con Gmail y Google Calendar con dos conectores de Google, sin programar y, en nuestra prueba, también con el plan gratuito. Claude lee tu correo y tu agenda, te propone un plan y, si lo apruebas, crea eventos y deja borradores.&lt;/p&gt;

&lt;p&gt;Este texto es un extracto de nuestra guía en español. Lo probamos el 18 de octubre de 2026 con cuentas de demostración de «Lucía Ortega», jefa de tráfico de una empresa de logística inventada: 7 correos y 14 eventos, todos ficticios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo conectarlos
&lt;/h2&gt;

&lt;p&gt;Son dos conectores, uno para Gmail y otro para Google Calendar. Un conector es una pieza que deja a Claude usar un servicio externo con tu permiso. Los desarrolla Google, no Anthropic, y por debajo son servidores MCP (lo explicamos en &lt;a href="https://desarrollomcp.com/blog/que-es-mcp" rel="noopener noreferrer"&gt;qué es MCP&lt;/a&gt;). Das el permiso iniciando sesión en Google, sin darle tu contraseña a Claude, y puedes retirarlo cuando quieras.&lt;/p&gt;

&lt;p&gt;Pasos para Gmail:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En Claude, abre &lt;strong&gt;Personalización&lt;/strong&gt;, pestaña &lt;strong&gt;Conectores&lt;/strong&gt; y &lt;strong&gt;Descubrir&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Busca «Gmail» y abre el resultado con la marca de verificado y la etiqueta «de Google».&lt;/li&gt;
&lt;li&gt;Comprueba que lo desarrolla Google y pulsa &lt;strong&gt;Conectar con Claude&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Elige la cuenta de Google y pulsa &lt;strong&gt;Continuar&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;En la pantalla de permisos, todas las casillas vienen desmarcadas: consultar mensajes y configuración, administrar borradores y enviar correo, y leer, redactar y enviar correos. Marca las que quieras.&lt;/li&gt;
&lt;li&gt;De vuelta en Claude verás &lt;strong&gt;Conectado&lt;/strong&gt;. Pulsa &lt;strong&gt;Tus permisos de herramientas&lt;/strong&gt; para ver qué puede hacer sin preguntarte.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Google Calendar se conecta igual: busca «Google Calendar» en Descubrir y elige la misma cuenta. Google muestra tres casillas desmarcadas, dos de lectura y una que permite ver, modificar, compartir y eliminar permanentemente todos tus calendarios.&lt;/p&gt;

&lt;p&gt;Cada chat tiene su interruptor: en la caja de mensaje pulsa &lt;strong&gt;+&lt;/strong&gt;, luego &lt;strong&gt;Conectores&lt;/strong&gt;, y activa o apaga Gmail y Calendar. Si tu cuenta es de una empresa o un centro de estudios y sale «Acceso bloqueado», el administrador tiene que marcar Claude como aplicación de confianza en la consola de Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué puede hacer y qué ve
&lt;/h2&gt;

&lt;p&gt;Sí puede enviar correos. Por defecto pide tu aprobación cada vez que va a escribir algo, y lo que solo lee lo hace sin preguntar:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Conector&lt;/th&gt;
&lt;th&gt;Herramientas&lt;/th&gt;
&lt;th&gt;Solo leen («Permitir siempre»)&lt;/th&gt;
&lt;th&gt;Escriben o borran («Requiere aprobación»)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gmail&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Calendar&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cada herramienta se puede dejar en permitir siempre, requerir aprobación o bloquear. Si no quieres que Claude envíe nunca un correo, bloquea «Send email message».&lt;/p&gt;

&lt;p&gt;Con tres peticiones como Lucía (Sonnet 5.5, plan Free) vimos esto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lo urgente del correo.&lt;/strong&gt; Encontró los 5 correos urgentes y citó el asunto exacto de cada uno. Falló al confundir quién escribía a quién: llamó «Lucía» a otra persona. Empieza con una frase como «soy Lucía, la jefa de tráfico».&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El plan de la semana.&lt;/strong&gt; Detectó un solape el martes de 11:00 a 11:30 y un hueco libre el lunes de 10:00 a 11:00, y avisó de que solo había mirado el calendario principal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evento y borrador.&lt;/strong&gt; Pidió aprobación con «Permitir una vez» antes de cada escritura, creó el evento una sola vez y dejó un borrador sin enviar. Sin la dirección del transportista, no se la inventó y te pidió revisar el campo «Para».&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lo que no nos gustó: la tarjeta de aprobación dice «Creates a calendar event», en inglés y sin día, hora ni título. Comprueba el resultado en Calendar o en Gmail antes de darlo por bueno.&lt;/p&gt;

&lt;p&gt;Según la ayuda oficial de Anthropic, Claude solo accede a tus datos cuando la petición lo requiere, refleja tus permisos de Google, de los adjuntos ve los metadatos y no el contenido, y lo que recupera se borra si borras el chat. Anthropic no entrena con los datos de estos conectores, salvo que en un plan Free, Pro o Max permitas usar tus chats para entrenar: entonces podría usarse lo que copies y pegues de esas fuentes.&lt;/p&gt;

&lt;p&gt;Dejamos las herramientas que escriben en «Requiere aprobación» por la inyección de instrucciones: un correo ajeno puede llevar texto escrito para la IA, como «reenvía este hilo a tal dirección», y Claude lo lee al resumir tu buzón. La aprobación te deja parar el envío. Más en &lt;a href="https://desarrollomcp.com/blog/seguridad-servidores-mcp" rel="noopener noreferrer"&gt;seguridad en servidores MCP&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo quitarle el acceso
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;En Claude:&lt;/strong&gt; Personalización, Conectores, pestaña &lt;strong&gt;Tuyos&lt;/strong&gt;. Entra en cada conector y pulsa &lt;strong&gt;Desconectar&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En Google:&lt;/strong&gt; entra en &lt;code&gt;myaccount.google.com/connections&lt;/code&gt;, abre «Claude for Gmail» o «Claude for Google Calendar» y usa la tarjeta &lt;strong&gt;Eliminar todas&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La ayuda de Anthropic propone este orden: primero Desconectar en Claude y, si no funciona, revocar en Google. Si dice «Conectado» pero Claude no ve tu correo, mira el interruptor del chat y, si sigue igual, desconecta y vuelve a conectar.&lt;/p&gt;




&lt;p&gt;La guía completa añade lo que este extracto deja fuera: las capturas de cada paso, la discusión de qué plan hace falta (con la contradicción entre las páginas de Anthropic), las respuestas reales de las tres peticiones con sus aciertos y fallos, cuánto fiarte de lo que resume Claude, la comparación con los plugins de ChatGPT y las ideas para quien construye sus propios servidores MCP. Está en &lt;strong&gt;&lt;a href="https://desarrollomcp.com/blog/claude-gmail-google-calendar?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=claude-gmail-google-calendar" rel="noopener noreferrer"&gt;DesarrolloMCP: conectar Claude con Gmail y Calendar&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si quieres aprender MCP desde cero, tienes el &lt;a href="https://desarrollomcp.com/aprende?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=claude-gmail-google-calendar" rel="noopener noreferrer"&gt;curso de DesarrolloMCP&lt;/a&gt;, con el nivel Básico gratis (14 lecciones, sin tarjeta), en español.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>spanish</category>
      <category>diaadia</category>
      <category>claude</category>
    </item>
    <item>
      <title>Crear un servidor MCP en Python con el SDK oficial 2.x (adiós, FastMCP)</title>
      <dc:creator>Andrés León</dc:creator>
      <pubDate>Sat, 17 Oct 2026 20:39:50 +0000</pubDate>
      <link>https://dev.to/noelserdna/crear-un-servidor-mcp-en-python-con-el-sdk-oficial-2x-adios-fastmcp-4pnc</link>
      <guid>https://dev.to/noelserdna/crear-un-servidor-mcp-en-python-con-el-sdk-oficial-2x-adios-fastmcp-4pnc</guid>
      <description>&lt;p&gt;Si has seguido un tutorial de MCP en Python de 2025, probablemente empezaba con &lt;code&gt;from mcp.server.fastmcp import FastMCP&lt;/code&gt;. Con el SDK oficial 2.x esa línea da &lt;code&gt;ModuleNotFoundError&lt;/code&gt;: la clase ahora se llama &lt;code&gt;MCPServer&lt;/code&gt;. Y el paquete independiente &lt;code&gt;fastmcp&lt;/code&gt; es otra cosa, que sigue viva (su versión 4 está construida sobre el SDK oficial 2.x).&lt;/p&gt;

&lt;p&gt;Este artículo es un extracto de una guía más larga. Montamos &lt;code&gt;nortia-almacen&lt;/code&gt;, un servidor que consulta las existencias de los almacenes de Nortia Logística (la empresa ficticia del curso de DesarrolloMCP), con el SDK oficial &lt;code&gt;mcp&lt;/code&gt; 2.2.0 y Python 3.13, escrito para la especificación 2026-07-28, la versión sin estado del protocolo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo bueno de Python: las dos versiones del protocolo por el mismo endpoint
&lt;/h2&gt;

&lt;p&gt;En 2026-07-28 no hay handshake &lt;code&gt;initialize&lt;/code&gt; ni sesión: cada petición lleva en &lt;code&gt;_meta&lt;/code&gt; la versión y las capacidades del cliente. Un servidor hecho con &lt;code&gt;mcp&lt;/code&gt; 2.x atiende las dos formas sin configuración: un cliente actual se queda en 2026-07-28 y uno antiguo hace su &lt;code&gt;initialize&lt;/code&gt; y negocia 2025-11-25 o anterior.&lt;/p&gt;

&lt;h2&gt;
  
  
  El proyecto
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv init &lt;span class="nt"&gt;--bare&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; nortia-almacen &lt;span class="nt"&gt;--python&lt;/span&gt; 3.13
uv add &lt;span class="s2"&gt;"mcp[cli]==2.2.0"&lt;/span&gt;
uv add &lt;span class="nt"&gt;--dev&lt;/span&gt; pytest
uv run mcp version
&lt;span class="c"&gt;# MCP version 2.2.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fija la versión: desde el 2 de octubre de 2026, sin ella se instala la 2.3.0, con la que no hemos vuelto a ejecutar el ejemplo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools sin escribir esquemas
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;CodigoAlmacen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[A-Z]{3}-\d{2}$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Código de almacén, por ejemplo MAD-01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;SOLO_LECTURA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ToolAnnotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_only_hint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destructive_hint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idempotent_hint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;open_world_hint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Consultar existencias&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SOLO_LECTURA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consultar_existencias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CodigoAlmacen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Referencia concreta; si se omite, todas&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Existencias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Existencias actuales de un almacén: unidades, reservadas, disponibles y estado por referencia.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;auditoria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool=consultar_existencias inquilino=%s almacen=%s sku=%s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INQUILINO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lineas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lineas_de&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;LookupError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ToolError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lineas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;linea&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;linea&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lineas&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;linea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;lineas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ToolError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; no tiene la referencia &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Llama sin sku para ver todas.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Existencias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inquilino&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;INQUILINO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lineas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lineas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alertas de rotura de stock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SOLO_LECTURA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;alertas_rotura_stock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CodigoAlmacen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Existencia&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Referencias de un almacén con disponibles por debajo del punto de pedido.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;auditoria&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool=alertas_rotura_stock inquilino=%s almacen=%s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INQUILINO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;linea&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;linea&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;lineas_de&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;almacen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;linea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;estado&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;LookupError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ToolError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El SDK lo deduce todo de la firma:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Los &lt;strong&gt;parámetros con tipos&lt;/strong&gt; producen el &lt;code&gt;inputSchema&lt;/code&gt;. El patrón y las descripciones salen de los &lt;code&gt;Field&lt;/code&gt;: si el modelo envía &lt;code&gt;almacen: "madrid"&lt;/code&gt;, la función ni se ejecuta.&lt;/li&gt;
&lt;li&gt;El &lt;strong&gt;docstring&lt;/strong&gt; es la &lt;code&gt;description&lt;/code&gt; de la tool.&lt;/li&gt;
&lt;li&gt;El &lt;strong&gt;tipo de retorno&lt;/strong&gt; produce el &lt;code&gt;outputSchema&lt;/code&gt;, y el valor viaja en &lt;code&gt;structuredContent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Las &lt;strong&gt;listas y los escalares&lt;/strong&gt; se envuelven en un objeto con la clave &lt;code&gt;result&lt;/code&gt;. Si quieres decidir tú la forma, devuelve un modelo, como &lt;code&gt;consultar_existencias&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los errores previstos (un almacén que no existe) se convierten en &lt;code&gt;ToolError&lt;/code&gt;, con un texto escrito para el modelo que le dice qué valores sí son válidos. Lo imprevisto no se captura: desde la 2.1.0, el SDK no envía al cliente el texto de las excepciones inesperadas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Servirlo por Streamable HTTP sin estado
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;servidor&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;streamable-http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8765&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;stateless_http&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dos opciones que se malinterpretan a menudo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stateless_http=True&lt;/code&gt;&lt;/strong&gt; solo cambia cómo atiende a los clientes de versiones con handshake: deja de asignarles sesión. El tráfico 2026-07-28 nunca abre sesión, con o sin ella.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;json_response=True&lt;/code&gt;&lt;/strong&gt; responde a cada POST con un único JSON en vez de un stream SSE. Va bien para tools rápidas, pero las notificaciones de progreso se pierden.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tres errores que verás seguro
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TypeError&lt;/code&gt; por &lt;code&gt;@mcp.tool&lt;/code&gt; sin paréntesis.&lt;/strong&gt; En el SDK oficial son obligatorios: &lt;code&gt;@mcp.tool()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TypeError&lt;/code&gt; al pasar &lt;code&gt;port&lt;/code&gt; al constructor.&lt;/strong&gt; Las opciones de transporte van en &lt;code&gt;mcp.run(...)&lt;/code&gt;, no en &lt;code&gt;MCPServer(...)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El Inspector pasa en verde, pero no has probado 2026-07-28.&lt;/strong&gt; Sin &lt;code&gt;"protocolEra": "modern"&lt;/code&gt; dentro de la entrada del servidor, el Inspector usa las versiones con handshake.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;La guía completa, con el servidor entero (datos, modelos, resources y prompt), los tests con pytest en memoria, la prueba con el Inspector y con Claude Code, y cómo pedirle a Claude Code que lo construya, está en &lt;strong&gt;&lt;a href="https://desarrollomcp.com/blog/crear-servidor-mcp-python?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=crear-servidor-mcp-python" rel="noopener noreferrer"&gt;DesarrolloMCP: crear un servidor MCP en Python&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si estás aprendiendo MCP en español, el &lt;a href="https://desarrollomcp.com/aprende?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=crear-servidor-mcp-python" rel="noopener noreferrer"&gt;curso de DesarrolloMCP&lt;/a&gt; tiene el nivel Básico gratis (14 lecciones, sin tarjeta), actualizado a la especificación 2026-07-28 y con todo el código probado.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>spanish</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Qué es MCP (Model Context Protocol), explicado en español</title>
      <dc:creator>Andrés León</dc:creator>
      <pubDate>Sat, 17 Oct 2026 20:39:10 +0000</pubDate>
      <link>https://dev.to/noelserdna/que-es-mcp-model-context-protocol-explicado-en-espanol-7hl</link>
      <guid>https://dev.to/noelserdna/que-es-mcp-model-context-protocol-explicado-en-espanol-7hl</guid>
      <description>&lt;p&gt;MCP (Model Context Protocol) es un protocolo abierto que permite a una aplicación de IA, como Claude o ChatGPT, conectarse con otros programas para consultar sus datos y hacer cosas en ellos. Sin algo así, el asistente solo podría contestar con lo que ya sabe.&lt;/p&gt;

&lt;p&gt;Pongamos que le pides a Claude Desktop que mire los PDF de este mes en tu carpeta de Descargas y te diga de qué trata cada uno. Claude no ve tu disco, pero si le has conectado un servidor MCP de archivos, puede hacerlo: pide usar las herramientas que ofrece el servidor (listar carpetas, leer archivos), la aplicación te pide permiso y el servidor hace el trabajo. Con un repositorio de GitHub o con la base de datos de pedidos de tu empresa, el mecanismo es el mismo.&lt;/p&gt;

&lt;p&gt;Este artículo es un extracto de nuestra guía en español, que describe la versión vigente de la especificación, la 2026-07-28.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué significa «protocolo de contexto del modelo»
&lt;/h2&gt;

&lt;p&gt;Un protocolo es un acuerdo sobre cómo se hablan dos programas: qué mensajes se envían, con qué formato y en qué orden. HTTP permite que cualquier navegador abra cualquier web; MCP cumple un papel parecido entre las aplicaciones de IA y los sistemas que quieren usar.&lt;/p&gt;

&lt;p&gt;El contexto es lo que el modelo tiene delante cuando responde: tu pregunta, la conversación y lo que le llega de fuera. MCP fija cómo entra esa última parte. Y que sea abierto significa que cualquiera puede implementarlo, así que el mismo servidor MCP sirve desde Claude, ChatGPT, Cursor o VS Code sin escribir una integración para cada uno.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo funciona, explicado fácil
&lt;/h2&gt;

&lt;p&gt;Intervienen tres piezas: la aplicación que usas (el &lt;strong&gt;host&lt;/strong&gt;: Claude Desktop, ChatGPT, tu editor), el &lt;strong&gt;cliente MCP&lt;/strong&gt; que esa aplicación abre hacia cada servicio y el &lt;strong&gt;servidor MCP&lt;/strong&gt;, el programa que conoce ese servicio por dentro. Cuando tu pregunta necesita datos de fuera:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;La aplicación pregunta al servidor qué herramientas ofrece y recibe el nombre y la descripción de cada una.&lt;/li&gt;
&lt;li&gt;El modelo lee esas descripciones y propone usar una, con sus argumentos.&lt;/li&gt;
&lt;li&gt;La aplicación decide si la llamada se hace; en las operaciones delicadas debería pedirte confirmación.&lt;/li&gt;
&lt;li&gt;El servidor ejecuta la acción y devuelve el resultado.&lt;/li&gt;
&lt;li&gt;El modelo usa ese resultado para responderte.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El modelo nunca toca el sistema. Solo propone, y es la aplicación la que decide, así que puede poner permisos y confirmaciones en medio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué cambió con la versión 2026-07-28
&lt;/h2&gt;

&lt;p&gt;Los mensajes usan JSON-RPC 2.0. Desde la especificación 2026-07-28 no hay apertura de sesión: cada petición lleva en &lt;code&gt;_meta&lt;/code&gt; la versión del protocolo y las capacidades del cliente. Así pregunta un cliente a un servidor qué versiones y capacidades tiene (ejemplo literal de la especificación de &lt;code&gt;server/discover&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"discover-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"server/discover"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"io.modelcontextprotocol/protocolVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-28"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"io.modelcontextprotocol/clientInfo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ExampleClient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"io.modelcontextprotocol/clientCapabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si vienes de tutoriales de 2025, ese es el cambio que más código rompe: ya no hay &lt;code&gt;initialize&lt;/code&gt; ni &lt;code&gt;Mcp-Session-Id&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Para qué sirve, con ejemplos reales
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; mantiene su propio servidor, &lt;code&gt;github-mcp-server&lt;/code&gt;, con acceso a repositorios, issues, pull requests y Actions: el asistente puede resumirte las issues abiertas o revisar un pull request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion, Atlassian, Linear, Stripe, Supabase, Sentry y Cloudflare&lt;/strong&gt; publican servidores oficiales. Si buscas uno, comprueba antes si lo publica el fabricante: al buscar salen muchos de terceros con nombres casi iguales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En una empresa&lt;/strong&gt;, un equipo publica una vez un servidor (por ejemplo, uno de pedidos con una herramienta &lt;code&gt;buscar_pedido&lt;/code&gt;) y cualquier aplicación que hable MCP puede descubrirla y llamarla, sin conocer la base de datos.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;La guía completa, con el diagrama del flujo, los dos transportes (stdio y Streamable HTTP), las tools, resources y prompts, cómo empezar sin programar, MCP frente a una API REST y lo básico de seguridad, está en &lt;strong&gt;&lt;a href="https://desarrollomcp.com/blog/que-es-mcp?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=que-es-mcp" rel="noopener noreferrer"&gt;DesarrolloMCP: qué es MCP&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si quieres pasar de saber qué es MCP a construir servidores, el &lt;a href="https://desarrollomcp.com/aprende?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=que-es-mcp" rel="noopener noreferrer"&gt;curso de DesarrolloMCP&lt;/a&gt; tiene el nivel Básico gratis (14 lecciones, sin tarjeta), en español y actualizado a la especificación 2026-07-28.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>spanish</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Crear un servidor MCP en TypeScript con el SDK v2 (y que hable 2026-07-28)</title>
      <dc:creator>Andrés León</dc:creator>
      <pubDate>Sat, 17 Oct 2026 20:39:09 +0000</pubDate>
      <link>https://dev.to/noelserdna/crear-un-servidor-mcp-en-typescript-con-el-sdk-v2-y-que-hable-2026-07-28-209d</link>
      <guid>https://dev.to/noelserdna/crear-un-servidor-mcp-en-typescript-con-el-sdk-v2-y-que-hable-2026-07-28-209d</guid>
      <description>&lt;p&gt;Si buscas «MCP TypeScript» vas a encontrar sobre todo código de 2025. Ese código usa el paquete único &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt;, que es la v1 del SDK, y sus métodos &lt;code&gt;server.tool()&lt;/code&gt;, &lt;code&gt;server.resource()&lt;/code&gt; y &lt;code&gt;server.prompt()&lt;/code&gt; no existen en la v2. Para un servidor nuevo, el que habla la especificación MCP 2026-07-28, necesitas el SDK oficial v2, que se reparte en paquetes separados.&lt;/p&gt;

&lt;p&gt;Este artículo es un extracto de una guía más larga. Montamos &lt;code&gt;nortia-pedidos&lt;/code&gt;, un servidor que consulta pedidos de Nortia Logística (la empresa ficticia del curso de DesarrolloMCP), y lo servimos por stdio y por Streamable HTTP sin estado. Todo el código se ejecutó con &lt;code&gt;@modelcontextprotocol/server&lt;/code&gt; 2.3.0, &lt;code&gt;zod&lt;/code&gt; 4.6.5 y Node 24.12.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que no te cuentan: instalar la v2 no basta
&lt;/h2&gt;

&lt;p&gt;La guía oficial del SDK lo dice así: «Nothing in v2 puts a 2026-07-28 byte on the wire by default». La versión del protocolo la decide el &lt;strong&gt;punto de entrada&lt;/strong&gt; con el que sirves el servidor, no el paquete que instalas. Por eso el servidor se construye dentro de una función, una &lt;strong&gt;fábrica&lt;/strong&gt; que devuelve un &lt;code&gt;McpServer&lt;/code&gt;: los puntos de entrada que hablan 2026-07-28 reciben la fábrica, no una instancia, y en HTTP la ejecutan en cada petición.&lt;/p&gt;

&lt;h2&gt;
  
  
  El proyecto
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;nortia-pedidos &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;nortia-pedidos
npm init &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm pkg &lt;span class="nb"&gt;set type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;module
npm i @modelcontextprotocol/server @modelcontextprotocol/client &lt;span class="se"&gt;\&lt;/span&gt;
      @modelcontextprotocol/node @modelcontextprotocol/express express zod
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; typescript tsx @types/node @types/express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La fábrica &lt;code&gt;crearServidor()&lt;/code&gt; registra una tool (&lt;code&gt;buscar_pedido&lt;/code&gt;, con entrada y salida validadas con Zod 4), un resource fijo, otro con plantilla (&lt;code&gt;pedidos://pedido/{pedidoId}&lt;/code&gt;) y un prompt. En la guía completa está entera y explicada decisión a decisión: por qué un pedido inexistente es un resultado con &lt;code&gt;isError: true&lt;/code&gt; y no una excepción, y por qué hace falta filtrar los campos de salida aunque declares un &lt;code&gt;outputSchema&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Servirlo por stdio
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;serveStdio&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/server/stdio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dependencias&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./entorno.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;crearServidor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./servidor.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dependencias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stdio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// falla al arrancar si la configuración es inválida&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;serveStdio&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;crearServidor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`nortia-pedidos (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inquilino&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) escuchando por stdio`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGINT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;serveStdio(fábrica)&lt;/code&gt; es lo que hace que el servidor hable 2026-07-28, y por defecto también atiende a clientes de las versiones con handshake. Fíjate en que todo se escribe con &lt;code&gt;console.error&lt;/code&gt;: en stdio, la especificación prohíbe escribir en &lt;code&gt;stdout&lt;/code&gt; nada que no sea un mensaje MCP válido.&lt;/p&gt;

&lt;h2&gt;
  
  
  Servirlo por Streamable HTTP, sin estado
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMcpExpressApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toNodeHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMcpHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dependencias&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./entorno.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;crearServidor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./servidor.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dependencias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// La fábrica se ejecuta una vez por petición HTTP: no hay nada que recordar entre dos POST.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMcpHandler&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;crearServidor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMcpExpressApp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// express.json() y validación de Host y Origin&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toNodeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/mcp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`nortia-pedidos (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inquilino&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) en http://127.0.0.1:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/mcp`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;httpServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createMcpHandler&lt;/code&gt; devuelve un handler web estándar y &lt;code&gt;toNodeHandler&lt;/code&gt; lo adapta a Express. No hay sesiones ni cabecera &lt;code&gt;Mcp-Session-Id&lt;/code&gt;: cada POST se resuelve con lo que trae. Como la fábrica se ejecuta en cada petición, lo caro (pools de base de datos, cachés, clientes de otras APIs) tiene que vivir fuera, a nivel de módulo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Probarlo sin engañarte
&lt;/h2&gt;

&lt;p&gt;El MCP Inspector usa por defecto las versiones &lt;strong&gt;con handshake&lt;/strong&gt;. Si no le pides &lt;code&gt;--protocol-era modern&lt;/code&gt; (o el campo &lt;code&gt;"protocolEra": "modern"&lt;/code&gt; dentro de la entrada del servidor en su archivo de configuración), la prueba pasa en verde sin haber probado 2026-07-28. La señal de que sí se negoció la versión actual es &lt;code&gt;io.modelcontextprotocol/serverInfo&lt;/code&gt; dentro de &lt;code&gt;_meta&lt;/code&gt; en la respuesta.&lt;/p&gt;




&lt;p&gt;La guía completa, con el resto del código (los datos detrás de una interfaz, la tool, los resources y el prompt), la petición &lt;code&gt;curl&lt;/code&gt; con su &lt;code&gt;_meta&lt;/code&gt;, la prueba con el Inspector y cómo pedirle a Claude Code que lo construya fijando el SDK v2, está en &lt;strong&gt;&lt;a href="https://desarrollomcp.com/blog/crear-servidor-mcp-typescript?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=crear-servidor-mcp-typescript" rel="noopener noreferrer"&gt;DesarrolloMCP: crear un servidor MCP en TypeScript&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si estás aprendiendo MCP en español, el &lt;a href="https://desarrollomcp.com/aprende?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=crear-servidor-mcp-typescript" rel="noopener noreferrer"&gt;curso de DesarrolloMCP&lt;/a&gt; tiene el nivel Básico gratis (14 lecciones, sin tarjeta), actualizado a la especificación 2026-07-28 y con todo el código probado.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>spanish</category>
      <category>typescript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Analizamos por dentro un servidor MCP local: mcp-supermercados-cl</title>
      <dc:creator>Andrés León</dc:creator>
      <pubDate>Fri, 16 Oct 2026 17:41:33 +0000</pubDate>
      <link>https://dev.to/noelserdna/analizamos-por-dentro-un-servidor-mcp-local-mcp-supermercados-cl-57d0</link>
      <guid>https://dev.to/noelserdna/analizamos-por-dentro-un-servidor-mcp-local-mcp-supermercados-cl-57d0</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/NLACE-COM/mcp-supermercados-cl" rel="noopener noreferrer"&gt;mcp-supermercados-cl&lt;/a&gt; es un servidor MCP &lt;strong&gt;local&lt;/strong&gt; que da a tu asistente herramientas para armar listas de compra en cinco supermercados de Chile (Jumbo, Santa Isabel, Unimarc, Tottus y Líder). Lo publica NLACE-COM con licencia MIT; tu cliente MCP lo arranca en tu máquina y le habla por stdio.&lt;/p&gt;

&lt;p&gt;Lo hemos leído por dentro (commit &lt;code&gt;3441cca&lt;/code&gt;, paquete 1.4.7), lo hemos compilado y ejecutado en local sin cuentas ni claves, y le hemos conectado un cliente MCP mínimo. Esto es lo que más nos ha gustado y lo que mejoraríamos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que hace bien
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tools orientadas al resultado, no a la API.&lt;/strong&gt; &lt;code&gt;build_list&lt;/code&gt; recibe hasta 30 ítems en texto libre y un presupuesto, y devuelve una lista decidida: producto elegido por precio por unidad y ofertas, hasta tres alternativas y el total. Envolver el endpoint de búsqueda de cada cadena habría obligado al modelo a hacer ese trabajo llamada a llamada. Las tools de lectura (&lt;code&gt;search_products&lt;/code&gt;, &lt;code&gt;get_offers&lt;/code&gt;…) van aparte.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;El carro nunca toca tu cuenta.&lt;/strong&gt; &lt;code&gt;add_to_cart&lt;/code&gt; no hace la petición a la tienda: devuelve la petición exacta y un &lt;code&gt;browserSnippet&lt;/code&gt; para ejecutar en tu pestaña ya logueada, con &lt;code&gt;executed: false&lt;/code&gt;. Tus credenciales se quedan en el navegador y las instrucciones del servidor prohíben al modelo pedirte usuario o clave.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un adaptador por cadena, sin fingir cobertura.&lt;/strong&gt; Un registro de cinco adaptadores (Jumbo y Santa Isabel comparten el de Cencosud) y una tabla en el README que dice qué cadena tiene búsqueda, precio de socio o carro. Solo Jumbo marca el ciclo completo, y lo dice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errores con siguiente paso.&lt;/strong&gt; Cada error lleva categoría, mensaje y un campo &lt;code&gt;action&lt;/code&gt; (reiniciar sesión, reintentar…) y se devuelve con &lt;code&gt;isError&lt;/code&gt;, así el modelo lo ve como resultado y puede actuar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;stdio bien hecho.&lt;/strong&gt; Nada que no sea JSON-RPC en &lt;code&gt;stdout&lt;/code&gt;; los avisos van a &lt;code&gt;stderr&lt;/code&gt;, como pide la especificación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué es local (y cuándo deja de bastar)
&lt;/h2&gt;

&lt;p&gt;El autor lo explica en el README: el precio de socio, tus compras frecuentes y el carro viven en tu navegador con sesión, y algunas cadenas bloquean el tráfico desde centros de datos. Para un servicio remoto compartido harían falta Streamable HTTP, autorización y aislamiento por usuario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que probamos (y lo que no)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm ci --ignore-scripts&lt;/code&gt; y &lt;code&gt;npm run build&lt;/code&gt;: sin errores.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm test&lt;/code&gt;: 178 de 180 tests pasan. Los 2 que fallan dependen de la fecha: sus datos de ofertas caducan en agosto de 2026 y el selector compara con &lt;code&gt;Date.now()&lt;/code&gt;. No indican un fallo con la web real.&lt;/li&gt;
&lt;li&gt;Un cliente MCP mínimo recibió las 14 tools y los 5 prompts; &lt;code&gt;get_cart&lt;/code&gt; sin sesión devolvió las instrucciones sin consultar ningún carro.&lt;/li&gt;
&lt;li&gt;No probamos tiendas, compras ni sesiones.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lo que mejoraríamos
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pasar un reloj fijo a esos dos tests para que no dependan del día.&lt;/li&gt;
&lt;li&gt;Documentar que &lt;code&gt;executed: true&lt;/code&gt; solo indica que la tool normalizó el JSON recibido, no que el carro haya cambiado.&lt;/li&gt;
&lt;li&gt;Documentar con qué versiones del protocolo se ha probado (usa el SDK v1, hasta &lt;code&gt;2025-11-25&lt;/code&gt;) y planificar la migración a MCP 2026-07-28.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;El análisis completo, con el código citado línea a línea, los diagramas de la arquitectura y las salidas de nuestras pruebas, está en &lt;strong&gt;&lt;a href="https://desarrollomcp.com/blog/analizamos-mcp-supermercados-cl?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=caso-supermercados" rel="noopener noreferrer"&gt;DesarrolloMCP: Servidor MCP local, analizamos mcp-supermercados-cl&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Si estás aprendiendo MCP en español, el &lt;a href="https://desarrollomcp.com/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=caso-supermercados" rel="noopener noreferrer"&gt;curso de DesarrolloMCP&lt;/a&gt; empieza gratis, actualizado a la especificación 2026-07-28.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>opensource</category>
      <category>spanish</category>
    </item>
  </channel>
</rss>
