Skip to main content

Apps connector-native: un servidor MCP remoto cuyo cliente es una IA

14 conceptos · unos 90-120 minutos de lectura · un día enfocado para construir (4-6 horas si eres un dev fuerte) · de una base mínima a un connector en vivo que cualquier IA puede tomar y usar, gratis

Durante treinta años construimos software para personas. Pantallas que mirar, botones que presionar, formularios que llenar. El cliente al otro lado del vidrio siempre era un ser humano.

Ese ya no es el único cliente en la sala. En este curso construyes un producto cuyo usuario es una IA.

Un connector es el complemento que una persona coloca en su IA para que alcance una app externa; pega el tuyo una vez, una URL, un clic. Esa es la última vez que un humano lo toca directamente. Desde ahí la IA es tu usuaria: lee los nombres de tus herramientas, decide por su cuenta cuál llamar, te entrega los inputs y le explica tus resultados a la persona. No estás vistiendo una vitrina para que un comprador la recorra. Estás colgando un tablero de herramientas rotuladas al que un trabajador incansable se acerca y usa por sí mismo, turno tras turno, por iniciativa propia. Hoy normalmente hay una persona sentada en ese chat. Cada vez más, lo que encuentre tu connector, inicie sesión y lo llame con horario será otro agente, sin un turno humano en medio. Construyes para ese cliente.

Primero, la palabra que cruza todo este curso: un servidor. Un servidor es una computadora que permanece encendida en una dirección fija, esperando que otras computadoras le pidan algo y respondiendo cuando lo hacen. Cada app de tu teléfono habla con uno. Imagina el mostrador de una tienda: siempre abierto en un lugar fijo, sin hacer nada por sí mismo, pero cuando un cliente se acerca hace un trabajo y devuelve el resultado. Ese es tu servidor. Un servidor MCP es una clase particular de servidor: sigue MCP, un estándar compartido, como un puerto USB para IA, un conector que cualquier asistente entiende, para que cualquier IA use lo que ofrece sin cableado a medida.

Vas a construir un producto real de extremo a extremo:

  • Un servidor MCP remoto: ese mostrador, puesto en una dirección web pública (eso significa "remoto") para que cualquier IA en internet pueda alcanzarlo. El tuyo contiene tres grupos de herramientas.
  • Una memoria de dos tablas (dos listas pequeñas que tu app conserva), para que la IA recuerde a una persona de una conversación a la siguiente en vez de empezar en blanco cada vez.
  • Un inicio de sesión real (OAuth), para que tu servidor sepa de quién son los datos desde el login mismo, sin pedirle nunca a la IA que lo garantice.
  • Un contrato de sesión: una herramienta que la IA llama primero, que le entrega las reglas de tu app, y luego bloquea todas las demás herramientas detrás de una clave que solo esa primera llamada emite.
  • Todo se agrega con una URL pegada y un clic, corriendo sobre el modelo gratuito de la persona, así que a ti no te cuesta nada servirlo.

Envías todo esto antes de escribir un solo loop de agente. Ese es el motivo de ponerlo primero: aprendes a construir lo que un agente llama antes de construir el agente en sí.

Una regla explica cada parte difícil que viene, y sale directamente de quién es tu cliente: tú posees el servidor, no la mente que lo llama. La inteligencia vive en la app host de la IA. El loop que decide qué hacer después también vive allí. Tu servidor solo responde cuando lo llaman, y quien llama es una mente que razona adivinando: rápida, capaz, y también capaz de confundirse, de dejarse convencer o simplemente de equivocarse. Por eso cada parte difícil de este curso tiene la misma forma. Es tu servidor haciendo un trabajo que no puedes confiarle a la IA por sí sola.

De ahí salen cuatro reglas no negociables. Todo el curso construye estas cuatro:

  1. Un gateway. La IA se encuentra contigo en un solo connector, con herramientas agrupadas por nombre detrás: una puerta principal, un menú que lee. Una cuenta gratuita puede agregar solo un connector personalizado, así que "uno" es un límite duro, no una preferencia.
  2. Solo herramientas. Hablas con la IA mediante herramientas invocables, funciones que puede llamar en medio de su propio razonamiento, nunca mediante recursos o prompts que una persona tendría que elegir a mano.
  3. Prueba, no confíes. Tu cliente es una mente que podría entregarte la identidad de la persona equivocada sin mala intención. Por eso la identidad viene de un inicio de sesión verificado, nunca de nada que diga la IA, como un hotel que entrega tu correspondencia por el pasaporte mostrado al registrarte, no por la palabra de alguien sobre qué cuarto es suyo.
  4. Falla cerrado. Cuando tu servidor falta o está roto, la IA no se queda callada. Improvisa, inventa una respuesta y fabrica los datos guardados de la persona. Tu servidor tiene que hacerla detenerse y decirlo, como un cajero automático que no llega al banco muestra temporalmente no disponible en vez de adivinar tu saldo y entregar efectivo.

Los cuatro invariantes de una app connector-native: dos describen la forma de la app (un gateway, solo herramientas) y dos son trabajos que el servidor debe hacer porque no se puede confiar en la IA: probar identidad desde el inicio de sesión verificado y fallar cerrado en vez de improvisar. Lee cada concepto como el servidor sosteniendo uno de estos cuatro.

Dos de estos describen la forma de lo que construyes (un gateway, solo herramientas). Los otros dos son trabajos que el servidor debe hacer porque no se puede confiar en la IA (probar identidad, fallar cerrado). Lee cada concepto preguntando: ¿qué invariante es este?

note

Prerrequisitos. Esta página asume cuatro cosas.

  1. Puedes leer Python escrito, directamente o pegando un bloque de código a tu agente de código para que te lo explique en lenguaje claro. Si todavía no puedes, haz primero Python in the AI Era.
  2. Hiciste el Agentic Coding Crash Course. Manejas Claude Code u OpenCode en modo plan con un archivo de reglas. Aquí construimos a través de ese banco de trabajo, no lo volvemos a explicar.
  3. Usaste un connector desde fuera, en el curso Skills & Connectors. Lo activaste y viste a tu IA entrar en tu Drive. Este curso te voltea hacia dentro: ahora eres aquello que la IA alcanza.
  4. No necesitas Build AI Agents primero. Lo que construyes aquí es el servidor que un agente llama, no el agente. Ese curso llega después en la ruta, y este es la razón por la que lo querrás.

No necesitas una API key propia: la persona trae el modelo. Las cuentas que sí necesitas son gratuitas y se abren solo cuando hacen falta: una base Neon cuando guardas estado por primera vez (Concepto 5, sin tarjeta). Luego el Concepto 12 ejecuta tu connector en vivo dentro de claude.ai sobre un túnel gratuito, sin host, tarjeta ni cuenta de login. La mitad de inicio de sesión usa un mock local incluido durante todo el camino, así que nunca creas una cuenta de sign-in solo para construir, probar o demostrar.

note

Dónde encaja. Primer curso de construcción en Mode 2. Desde aquí, la ruta de Manufacturing va así: este curso → Plugins for AI AgentsAI IdentityBuild AI Agents. Esta es la app pre-loop: herramientas, estado, identidad y una ejecución en vivo en claude.ai, con el modelo del llamador haciendo el razonamiento. Build AI Agents, más adelante, es donde tú posees el loop.

📚 Teaching Aid

Open Full Slideshow

View Full Presentation — Connector-Native Apps


Cómo construyes en este curso

No escribes este servidor a mano. Como en cada curso de la ruta Manufacturing, tu agente de código escribe el código; tu trabajo es la especificación que entra y la verificación que sale. Planificas, revisas, ejecutas, compruebas. Ese loop no es una comodidad aquí. Es el Concepto 1.

La construcción sigue el movimiento del curso de código a tamaño completo: plan → review → execute in checkpoints → verify. Planificas todo el gateway una vez, lo revisas contra los cuatro invariantes y luego construyes una porción observable a la vez, encima de una base pequeña que trae solo las partes que debes leer en vez de generar.

Prepara la base (unos minutos)

  1. Descarga la base (connector-native-apps-base.zip), descomprímela y entra a la carpeta con cd.
  2. Abre Claude Code u OpenCode en esa carpeta. Carga automáticamente AGENTS.md: el brief que sostiene al agente contra los cuatro invariantes y le dice cómo prepararse.

Qué hay en la caja. Es una base mínima, no una app terminada. Solo el núcleo crítico de seguridad viene completo, porque un agente escribirá felizmente código de autorización que parece correcto y está silenciosamente mal, así que esa parte la lees en vez de generarla. Todo lo demás lo construyes durante el curso.

connector-native-apps/
AGENTS.md the agent's brief: base prep, the four invariants, the build order
CLAUDE.md one line, @AGENTS.md, so Claude Code loads that same brief
.mcp.json opencode.json Neon + Context7 MCP servers, pre-declared (authorize once in browser)
pyproject.toml a uv project; only the deps the given code needs (your agent adds the rest)
.env.example copy to .env; the user brings the model, so no API key of your own
src/connector_app/
auth.py GIVEN, complete: the security check that proves who is signed in (you read it in Concept 7, never rewrite it)
session.py GIVEN, complete: the lock the rest of your tools sit behind (you wire it in Concept 10)
mock_auth/server.py GIVEN: a local sign-in service, so you can test the whole flow without creating any account
seed/articles.json a tiny catalog for your domain
tests/test_starter.py five offline smoke tests over the security core

Los dos archivos marcados GIVEN (auth.py, session.py) se leen y se conectan, nunca se reescriben. Todo lo demás, el gateway (server.py), el store de dos tablas (db.py) y tus reglas y persona (config_store.py), lo construye tu agente contigo, concepto por concepto.

Prepara la base con tres pedidos breves, como harías al incorporar a un nuevo compañero: averigua qué sabe, haz que te configure el entorno y luego haz que explique el proyecto y demuestre que está sano. Pégalos de uno en uno.

1. Mira qué sabe (también verifica que leyó su brief al abrir):

What can you do for me?

Una buena respuesta describe este proyecto: una app connector-native, sus cuatro invariantes y cómo te ayudará a construirla concepto por concepto. Una respuesta genérica de "soy un asistente de código" significa que no cargó AGENTS.md; asegúrate de abrir el agente dentro de la carpeta de la base.

2. Configura el entorno:

Set up my base environment for this project, and install anything that's missing, including Python and uv.

Qué observar: el agente confirma que estás en Python 3.14+ y uv (instalando cualquiera que falte), instala dependencias y las skills mcp-builder y neon-postgres, y crea .env con un SESSION_SIGNING_SECRET generado. Context7 no usa clave y se conecta solo; para Neon ejecuta /mcp y te pide un clic de Authorize en el navegador (gratis en neon.com, sin API key ni tarjeta; crea la cuenta allí mismo si hace falta). Luego te pide reiniciarlo para que carguen las nuevas skills, y al volver confirma que ve las herramientas de Neon.

3. Entiéndelo y revisa que esté sano (después del reinicio):

Explain this project to me before I continue the crash course, then run its tests and share their status.

Hecho cuando: el agente te explicó la base en lenguaje claro, las cinco pruebas del núcleo de seguridad pasan, las herramientas de Neon son visibles tras reiniciar y existe .env. Si no hay herramientas de Neon, Neon aún no está autorizado: repite la autorización en el navegador o escribe /mcp y elige Neon. Ahora construyes.

note

Dos rutas, tú eliges. La ruta Beginner usa el servicio mock_auth/ incluido: un inicio de sesión local que emite tokens reales, así que la mitad de sign-in no necesita cuenta (tus datos guardados siguen viviendo en una base Neon gratuita). La ruta Standard cambia a un servicio de sign-in real y alojado, la ruta de producción que cableas en el curso AI Identity. Construye todo primero en la ruta Beginner y luego cambia modificando tres valores en .env. No eliges servicio ahora; el Concepto 8 nombra las opciones cuando la decisión importa.


Parte 1: la forma

Estos cuatro conceptos son el modelo mental sobre el que se construye el resto de la página. Los primeros tres son de lectura; en el Concepto 4 planificas toda la construcción y haces scaffold del gateway.

Concepto 1: tú diriges, no tipeas

No escribirás este servidor a mano. Le dices a un agente de código lo que quieres, él escribe el código, y tu trabajo es leerlo, ejecutarlo y comprobarlo. Si vienes de Python in the AI Era, ya conoces el ritmo: tú diriges, el agente tipea, tú verificas.

La comprobación es la habilidad real, y hay un lugar donde más importa: el código de sign-in en el Concepto 8. Un agente puede escribir código de sign-in que parece correcto y está silenciosamente mal, y aprender a notar la diferencia es exactamente lo que este curso construye en ti. Las teclas son trabajo del agente; el juicio es tuyo.

Aquí está el giro que conviene retener, porque es el único curso donde te encuentras con las dos clases de programa a la vez. Imagina dos aspiradoras. Una robot se ejecuta sola: despierta, recorre la casa, decide cada movimiento y hasta puedes programarla para limpiar a las 2am. Una de mano solo funciona mientras aprietas el gatillo; lo sueltas y se detiene. El agente de código que construye esto para ti es la aspiradora robot: sigue por su cuenta. El connector que construyes es la aspiradora de mano: queda inerte hasta que el usuario lo toma. Esa cualidad de autoejecución tiene un nombre al que el curso vuelve una y otra vez, poseer el loop: la robot posee su loop, tu connector nunca. Estás dirigiendo un programa que posee su loop para construir uno que no lo posee.

Dos formas de app, separadas por una pregunta: ¿posees el loop? A la izquierda, la app connector-native pre-loop que construye este curso: envías un servidor (herramientas, estado, identidad, deploy) y la app de chat host trae el modelo y el loop, así que actúa solo cuando el usuario escribe. A la derecha, el agente que posee el loop que construye un curso posterior (Build AI Agents): tú escribes el loop y se despierta, planifica, llama herramientas y termina un trabajo por su cuenta. El giro: diriges un agente de código, que sí posee un loop, para construir la app sin loop de la izquierda.

Concepto 2: la nueva forma de app

Usaste un connector desde fuera, como persona alcanzando tus propias apps. Ahora inviértelo: tú eres el servidor, y quien llama es la IA. Claude llama desde su nube. De eso salen tres hechos que conducen cada decisión posterior.

La app de chat es el runtime (el motor que realmente ejecuta todo), y vive en la nube. Cuando un usuario agrega tu connector, Claude llega a tu servidor desde la nube de Anthropic, no desde el portátil del usuario. Así que tu servidor debe estar en internet público por HTTPS. Claude llega desde lejos, por lo que tu mostrador debe estar en una calle pública con una dirección real: una dirección web pública. Un servidor corriendo en tu portátil es el mismo mostrador construido dentro de una casa cerrada, perfectamente real pero sin puerta a la calle; por eso la Parte 5 lo pone en internet público mediante un túnel rápido, no como un detalle posterior.

El usuario trae el modelo. No pagas por la inteligencia; el tier gratuito de Claude del usuario la aporta. Tus únicos costos son un servidor pequeño y una base de datos. Ese es el truco económico completo de "gratis para cualquiera".

Esos dos hechos, un runtime en la nube que debe alcanzarte y un modelo ajeno haciendo el razonamiento, explican por qué el resto del curso trata de lo que tu servidor debe garantizar. La primera garantía es la más estrecha: tu servidor solo puede hablar con ese modelo prestado de una manera. Ese es el siguiente concepto.

Flujo de izquierda a derecha: el usuario escribe en Claude; Claude (modelo y loop) corre en la nube de Anthropic; una frontera de confianza punteada marca dónde la URL del connector cruza hacia tu gateway por HTTPS público; tu gateway convierte el token en un sub y lee estado desde Postgres. Dos anotaciones: el loop vive en Claude, no en tu servidor; la identidad viene del sub del token, nunca del modelo.

Concepto 3: solo herramientas, no recursos ni prompts

Un servidor MCP puede ofrecerle al modelo tres cosas: herramientas (funciones que el modelo llama, con inputs y outputs), recursos (datos de solo lectura a los que el usuario apunta) y prompts (plantillas preparadas que el usuario elige). Las tres son superficies válidas de MCP. Para la forma de producto de este curso, exponemos intencionalmente solo herramientas. Es una decisión de diseño para esta clase de app, no una regla de que recursos y prompts estén mal en general.

Por qué las herramientas encajan aquí: tu app debe decidir por sí misma qué buscar o hacer después: buscar, traer un registro, guardar un resultado. Imagina un taller. Una herramienta es el taladro inalámbrico en el cinturón del trabajador: se toma en mitad del trabajo, sin pedir permiso. Un recurso es un manual encerrado en un gabinete, inútil hasta que alguien camina hasta él y lo entrega. Un prompt es un formulario que el trabajador debe detenerse a tomar de una repisa. Solo el taladro mantiene el trabajo fluyendo sin humano en el loop. Por eso esta app es solo herramientas: solo una herramienta puede llamarse automáticamente dentro del razonamiento del modelo, mientras los recursos son pasivos y los prompts se eligen a mano. Además, las herramientas son la superficie que toda app de chat soporta bien, así que construir sobre herramientas te mantiene portable.

MCP surfaceWho triggers itCan it be auto-called mid-reasoning?Use it here?
ToolThe model, on its ownYesYes — everything
ResourceThe user points at itNoNo
PromptThe user picks itNoNo

Concepto 4: un gateway, tres grupos detrás (planifícalo y luego haz scaffold)

Este concepto fija la forma de todo lo que construyes, así que conviene ir despacio. Empieza con el desorden que evita.

Tu servidor, el mostrador de la apertura, está a punto de hacer varios trabajos genuinamente distintos. Busca cosas. Recuerda quién es cada persona y qué guardó. Sigue tus reglas sobre cómo comportarse. Lanza todo eso en una pila indiferenciada de herramientas y se rompen dos cosas: la IA que lee tu menú no puede distinguir una herramienta para "traer un artículo" de una para "guardar el lugar de esta persona", y no puedes razonar sobre una parte sin tropezarte con las demás. La solución es la más antigua del software: dale a cada clase de trabajo su propio grupo etiquetado.

Una app connector-native siempre tiene los mismos tres tipos de trabajo, y verás los tres salir de la Reading Room que vas a construir:

  • domain_* es lo que la app realmente hace. Para Reading Room son los libros: buscar la colección, traer un artículo. Es la razón de existir de la app. Luego, cuando apuntes la app a tu propio tema, esta es la parte que cambia: pedidos para una tienda, tickets para soporte.
  • user_* es quién está aquí y qué recuerdas de esa persona. La tarjeta de biblioteca del lector y el estante que guardó en la visita anterior. Esto evita que la app sea un pez que olvida todo al salir.
  • config_* es cómo debe comportarse la app. Las reglas y la voz de la bibliotecaria: qué hará y qué no hará, cómo habla. Una persona docente es una clase de config; la política de tono y escalamiento de soporte es otra. La mayoría de apps necesita algunas reglas; pocas necesitan un personaje completo.

Entonces domain es el trabajo, user es la persona, config es el comportamiento: tres preguntas, tres grupos. El prefijo _ en cada nombre (domain_search, user_save_state) vuelve visible esa agrupación, para que el menú de la IA se lea en secciones limpias en vez de una lista larga. Los nombres de herramientas solo pueden usar letras, dígitos, _ y -, nunca punto, así que el prefijo con guion bajo es el namespace.

¿Por qué un servidor y no tres? La forma de libro de texto de enviar tres responsabilidades separadas es usar tres servidores separados. Aquí no puedes (invariante 1): en el plan gratuito de Claude un usuario puede agregar exactamente un connector personalizado. Pídele a un principiante que agregue tres y habrás empujado tu producto gratuito a un plan pago. Así que mantienes los tres grupos dentro de un servidor, detrás de una URL: un gateway único. Una puerta, un menú con tres secciones, como un comedor que guarda desayuno, almuerzo y cena en una sola carta en vez de entregarte tres. Los nombres de abajo son tuyos; esto es solo la forma:

domain_search      domain_get_item      domain_do_action
user_get_profile user_save_state
config_get_rules config_get_persona

Una palabra que verás en los prompts siguientes: en el framework MCP de Python, FastMCP, una herramienta es simplemente una función decorada. Una función es una acción rotulada que hace un trabajo; decorarla le pone una etiqueta para que el menú de la IA pueda listarla. Tu agente escribe esto; tú solo necesitas reconocer la palabra.

Prompt 1: haz que el agente planifique todo antes de escribir una línea. Este es el movimiento más importante de todo el curso, y no es escribir código. Haces que el agente exponga el diseño completo primero, en palabras, para que puedas comprobarlo contra los cuatro invariantes antes de construir nada. Detectar "la identidad viene de un argumento de herramienta" en un plan cuesta una frase; detectarlo después de que el código existe cuesta una tarde.

Entra en modo plan (Shift+Tab en Claude Code, Tab en OpenCode) para que el agente proponga en vez de construir, cambia a un modelo fuerte para que el plan sea preciso, y pega:

I want to build the Reading Room connector on this base. Read AGENTS.md and use the mcp-builder skill for tool naming and schemas, then propose the architecture for me: the one gateway, the three tool groups (domain, user, config), how it remembers a person, and how it proves who is signed in. Show me the complete plan and the tool list before you write any code, and for each piece tell me which of the four invariants it serves and flag anything you are unsure about or that the base has not already decided.

Lo que hace ese prompt: le entrega al agente el brief (AGENTS.md) y la skill de nombres, luego le pide proponer la arquitectura y la lista completa de herramientas, y justificar cada pieza por invariante, sin escribir todavía. No dictas el diseño; pides uno que puedas revisar.

Luego lee el plan como inspector (esa revisión es la habilidad que enseña el curso). Compruébalo contra los cuatro invariantes:

  • ¿Un gateway, no tres?
  • ¿Solo herramientas, sin recursos ni prompts cargando la lógica de la app?
  • ¿Identidad desde el sign-in verificado, nunca desde un argumento de herramienta?
  • ¿Una regla fail-closed asentada en config?

Si algo está mal, dilo y haz que replantee. Solo cuando el plan sostiene, le permites construir.

Prompt 2: haz scaffold del marco vacío y prueba que se sostiene. Scaffolding es levantar el esqueleto básico antes de las paredes reales: un servidor que corre, con apenas lo necesario para mostrar que la estructura es sana. Empiezas casi vacío a propósito, una herramienta de health-check y un placeholder, para que si el cableado está mal lo descubras ahora, contra dos herramientas triviales, no enterrado luego bajo lógica real.

Vuelve a un modelo más barato para este paso rutinario; el pensamiento ya ocurrió en el plan. Una frase del prompt carga peso: stateless streamable HTTP. Streamable HTTP es el formato de cable por el que un host remoto como Claude alcanza tu servidor; stateless significa que tu servidor no guarda memoria de una conexión individual, así que cualquier servidor de Anthropic puede manejar cualquier llamada. Es la forma de producción, y tu agente la fija una vez aquí. Pega:

Looks right. With the mcp-builder skill's guidance, scaffold the gateway: one FastMCP server on stateless streamable HTTP transport, with a health tool and a domain_get_item stub. Add whatever dependencies you need. Run it and show me a local client listing both tools, with no auth and no real data yet.

Qué verás y qué verificar

Un servidor corriendo, y un cliente (un programa pequeño que conecta a tu servidor y pregunta qué herramientas ofrece) listando dos herramientas. La herramienta domain_get_item todavía es un stub, un placeholder que no devuelve nada real, y eso es correcto: sin auth y sin datos reales en este paso. Probaste el hecho de este concepto: un solo servidor, herramientas agrupadas por nombre, en el transporte que puede alcanzar un host remoto, y que un cliente puede descubrir. Identidad y gates vienen después; no los agregues todavía. Si el puerto 8000 ya está ocupado en tu máquina, tu agente usará otro puerto y mantendrá RESOURCE_URL coherente; eso es esperado, no un error.

Checkpoint: la forma está en su sitio. Planificaste todo el connector, lo revisaste contra los invariantes y levantaste un gateway en el transporte correcto. Cada concepto posterior agrega una pieza real a este servidor.


Parte 2: estado y dominio

Concepto 5: estado, lo justo para recordar a una persona

Un chatbot genérico te olvida al cerrar la pestaña. Tu app no debe hacerlo: recordar a un usuario entre sesiones es gran parte de lo que la vuelve producto en vez de juguete.

Mantén v1 pequeño: una base Postgres con dos tablas. Piensa en los dos registros del mostrador, atados por tu número de fidelidad: un registro de huéspedes de quién es cada persona, y un registro de estadía de qué está haciendo últimamente. Quién eres cambia poco; qué estás haciendo cambia en cada visita, así que viven separados.

Aquí están esas dos tablas escritas en SQL. No necesitas leer SQL para seguir esto: mira el comentario en lenguaje claro de cada línea, por eso está ahí. Tu agente escribe esto; tú lo revisas.

-- users: one row per person
create table users (
id text primary key, -- one person's verified sign-in id, the 'sub' (Concepts 7-8)
email text
);

-- user_state: one row per person, whatever you carry between sessions
create table user_state (
user_id text references users(id),
state jsonb -- a last position, a few saved values
);

El id que une las dos tablas, tu "número de fidelidad" del mostrador, en la app real es el id verificado del inicio de sesión de la persona. En el código se llama sub (abreviatura de subject, la única pieza de identidad en la que puedes confiar). Aún no tienes sign-ins reales, así que en los Conceptos 7 y 8 verás de dónde viene ese id; por ahora solo entiende que las dos tablas se atan con él.

Eso es toda la memoria de v1: guardar una fila, leer una fila. (jsonb es una columna flexible que guarda un pequeño paquete de valores como datos estructurados, sin declarar de antemano un conjunto fijo de columnas). La versión seria, una traza de auditoría de cada interacción, un modelo de aprobación, un registro confiable y reportable, es su propia disciplina, y es exactamente lo que enseña Building a Digital FTE. No la construyas aquí.

No configuras ni operas esta base a mano. Como en cada curso de Manufacturing, tu agente de código maneja Neon mediante el servidor MCP propio de Neon: crea el proyecto, crea una rama y ejecuta SQL mientras tú revisas. Lo construyes en dos pasos breves, pero primero algo debe ser cierto.

Comprueba que Neon está despierto (10 segundos). Todo lo de abajo depende de que el agente realmente alcance Neon, que conectaste durante setup. Pregunta: "Can you see the Neon tools right now?" Si responde que sí, sigue. Si no, Neon nunca fue autorizado: escribe /mcp, elige Neon, haz clic en Authorize en el navegador (gratis, sin tarjeta) y vuelve a preguntar. No pegues el siguiente prompt hasta que confirme que ve Neon, o fallará sin tener nada sobre lo que actuar.

Prompt 1: haz que el agente cree el store. Pega:

Using the Neon MCP server, create the two-table store (users, user_state) on a dev branch, and save the branch's DATABASE_URL to .env (never print it).

Qué hace: el agente usa MCP de Neon para crear un proyecto y una rama dev, una copia sandbox privada de la base para que tu aprendizaje quede lejos de datos reales, crea las dos tablas que acabas de leer y escribe la dirección de la base en .env como DATABASE_URL para que tu código la encuentre. Tú revisas; él ejecuta el SQL.

Hecho cuando: pide al agente que liste las dos tablas que acaba de crear y confirme que .env ya tiene una línea DATABASE_URL. Si muestra users y user_state, el store existe. Nunca abres una herramienta de base de datos ni lees SQL tú mismo; el agente te muestra.

Prompt 2: haz que el agente escriba el código de leer/guardar. Algo antes de que tropiece: aún no tienes sign-in real (llega en los Conceptos 7 y 8), así que para esta prueba el agente usa un id sustituto donde más tarde irá el sub real. El código sigue escrito para keyear por sub; solo alimentas un placeholder hasta que aparezca el verificado. Pega:

Write db.py: read and save a user's state, keyed by the verified sub (never an id from a tool argument). Show me a value saving and reading back on a fresh connection. Then explain in one line why you keyed it on the verified sub and refused a tool-supplied id; we will see the full reason in the next part.

Qué hace: el agente escribe dos funciones pequeñas, guardar y leer, que almacenan los valores de una persona bajo su sub. Luego prueba que los datos realmente persistieron abriendo una conexión fresca y leyendo el mismo valor.

Hecho cuando: un valor hace round-trip. Guardas algo y en una conexión fresca lees exactamente lo mismo, keyeado por el id sustituto. Eso es memoria real: el valor sobrevive a la conexión que lo escribió. El estado funciona antes que la identidad.

Concepto 6: dominio, por referencia ahora, por significado después

Tu dominio es simplemente aquello sobre lo que trata tu app: sus artículos, ítems, registros, no una dirección web. Cuando el usuario quiere algo específico, v1 lo trae de la forma simple: cada registro tiene un id y domain_get_item(id) lo devuelve. El modelo trabaja con lo que vuelve.

Lo que v1 deliberadamente no hace todavía es búsqueda semántica, responder "la parte sobre reembolsos" por significado en vez de id exacto. La diferencia es una biblioteca: traer por id es pedir un libro por su signatura exacta; un dígito mal y no hay nada. La búsqueda semántica es decirle a la bibliotecaria "quiero el libro sobre la ballena triste" y que lo encuentre. v1 es el mostrador de signaturas; la bibliotecaria es la mejora, y es todo el tema del curso RAG. Meterlo ahora inflaría tu primer envío. Construye la versión por referencia:

Make domain_get_item(id) return a real article from seed/articles.json instead of the stub. Show me it returning a1 by id.

Hecho cuando: un artículo vuelve por id. Trae por referencia ahora; mejora a búsqueda después.

Opcional: mira a un agente real llamar tu herramienta

Hasta ahora solo probaste el servidor con un script pequeño que lista herramientas. Haz esto una vez para sentir para qué existe tu connector: un agente entrando y llamando tu herramienta por su cuenta. La forma más rápida hoy es tomar prestado al propio agente de código con el que construyes como cliente.

Mantén el servidor corriendo. En una segunda terminal, apunta tu agente a esa URL:

claude mcp add --transport http reading-room http://localhost:8000/mcp --scope project

El flag --scope project escribe una entrada reading-room en .mcp.json del proyecto; sin él, el servidor aterriza en otro scope y tu agente no lo recoge aquí. Compruébalo con claude mcp list; reconecta a mitad de sesión con /mcp.

Agrega una entrada reading-room al bloque mcp en opencode.json, junto a Neon y context7, sin reemplazarlas:

"reading-room": {
"type": "remote",
"url": "http://localhost:8000/mcp",
"enabled": true
}

Luego pregunta en un prompt normal:

Use the reading-room domain_get_item tool to fetch article a1, and show me what came back.

Mira al agente descubrir tu herramienta, llamarla y leerte el artículo de vuelta. Ese viaje, una IA eligiendo tu herramienta y usando su resultado, es todo el producto en miniatura.

Dos notas honestas para no engañarte:

  • Esto es una mirada de desarrollo, no la entrega real. Tu cliente real es la app de chat (claude.ai), que conectas en el Concepto 13. Aquí tomas prestado tu agente de código como cliente MCP útil, porque es la forma más rápida de ver a un agente manejar tus herramientas antes de que exista sign-in.
  • Funciona solo porque aún no hay auth. En el Concepto 8 agregas el lock y una llamada sin autenticar empieza a devolver 401. Elimina este registro de desarrollo al llegar al Concepto 8 (claude mcp remove reading-room limpia la entrada de .mcp.json; en OpenCode, borra el bloque reading-room), o simplemente empezará a fallar, que es el lock haciendo su trabajo.

Parte 3: probar identidad que el modelo no puede fingir

Esta es la primera mitad de "el servidor hace lo que el modelo no puede". Ambos conceptos aquí son el invariante 3.

Concepto 7: identidad desde el subject verificado, nunca desde el modelo

El problema: tu tabla user_state debe escribir en la fila de la persona correcta. Pero el modelo es quien habla con el usuario, y nunca debes dejar que el modelo decida de quién leer o escribir datos. Imagina la IA como un concierge de hotel haciendo recados. Cuando el concierge dice al mostrador "la habitación 412 quiere su correo", el mostrador no debe entregarlo por su palabra: un concierge confundido o manipulado podría nombrar la habitación equivocada y filtrar correspondencia ajena. Si Claude pudiera pasarte un user_id, ese sería exactamente el peligro, y un usuario vería datos de otro. Es el bug de confianza clásico de una app connector-native.

La regla: el modelo nunca suministra identidad. Cuando el usuario autorizó tu connector, inició sesión mediante un servicio confiable, y ese servicio entrega a tu servidor un token firmado que contiene el id verificado del usuario, el subject, o sub. Ese token es el pasaporte del huésped: el mostrador lee quién es desde el pasaporte mismo, que el concierge no puede falsificar, nunca desde lo que dice. Tu servidor lee sub directamente del token y usa eso como clave de base de datos. Así que si una herramienta alguna vez toma un argumento user_id y el modelo lo rellena con el id de otra persona, tu servidor lo ignora: la identidad viene del sub del token, nunca de un argumento de herramienta.

Esta es la regla que aplica el auth.py dado, por eso ese archivo viene completo y nunca lo reescribes. En una línea: toma el id verificado (sub) directamente del token firmado y entrega eso a la base, de modo que la persona cuyos datos tocas la decide el sign-in, nunca algo que escribió el modelo. Cableas ese archivo en el siguiente concepto; aquí solo retén la regla. La mejor forma de fijarla es hacer que tu agente te la muestre en el código que ya escribiste.

Conéctalo con tu propio db.py. Pega:

Read auth.py back to me in plain English, then open the db.py you wrote in Concept 5 and show me where the sub comes from now. Tell me what would break if a tool argument could set it instead, and tie it back to the concierge and the passport.

La parte elegante: no cuesta nada adicional al usuario. El único clic de Authorize que activa el connector es el sign-in. Una acción, dos trabajos.

Concepto 8: probar quién está ahí (sign-in en lenguaje claro)

La maquinaria aquí es OAuth, el mismo "Sign in with Google" que has pulsado cientos de veces. Este es el concepto para ir despacio, no porque sea enorme, sino porque la falla es sutil: el código de auth puede parecer correcto y estar silenciosamente mal. La buena noticia es que solo necesitas dos cosas: las ideas y cómo comprobar el resultado. Tu agente escribe el cableado real.

tip

Toda la idea en cinco líneas: el usuario inicia sesión en otro lugar. Ese servicio emite un token firmado. Tu servidor verifica el token. Tu servidor lee sub. El modelo nunca suministra identidad.

En realidad son dos trabajos, y tú solo haces uno. Iniciar sesión a personas es difícil y trae responsabilidad. Así que no lo haces: un especialista maneja el login y entrega a tu servidor un token, una papeleta a prueba de manipulación que dice "esta persona acaba de iniciar sesión". El único trabajo de tu servidor es revisar la papeleta. Explicado por completo, participan cuatro partes:

PartyWho it isYou build it?
The userThe person whose data it is
Claude's MCP clientRuns in Anthropic's cloud, asks on the user's behalfNo
The sign-in service (authorization server)An outside specialist — hosted (Clerk, Auth0, Stytch) or a framework you self-host (Better Auth) — that checks the login and issues tokensNo — you rent or self-host it
Your gateway (resource server)Your server; it only checks tokens and serves dataYes

Según la especificación MCP actual, tu servidor es solo resource server: no está en el negocio de contraseñas. Cualquier issuer que elijas, alquilado o autoalojado, aquí solo validas sus tokens; emitirlos es el curso de AI Identity. El flujo:

  1. Discovery. Una llamada sin token recibe 401, el rechazo universal de "no has iniciado sesión"; inicia el sign-in, no es un error que arreglar. Claude encuentra la nota pública de tu servidor en /.well-known/oauth-protected-resource, que dice "mi servicio de sign-in vive allí," y lo sigue hasta el login.
  2. Sign-in. El usuario ve una pantalla de consentimiento, inicia sesión con Google o un código de email y aprueba. Ninguna contraseña toca jamás a Claude ni a tu servidor.
  3. Token. El servicio de sign-in emite un token de vida corta con el sub verificado y una audience estampada solo para tu servidor.
  4. Cada llamada después lleva el token; tu servidor lo comprueba y lee sub.

El flujo OAuth como cuatro partes y cuatro pasos. Partes: el usuario; el cliente de Claude en la nube de Anthropic (no tú); el servicio de sign-in que alquilas; tu gateway que construyes. Paso 1 discovery: una llamada sin token recibe 401 y Claude lee el well-known document para encontrar el servicio de sign-in. Paso 2 sign-in: el usuario inicia sesión y aprueba; ninguna contraseña toca a Claude o tu servidor. Paso 3 token: el servicio emite un token de vida corta con sub verificado y audience estampada a tu servidor. Paso 4, destacado: cada llamada posterior, tu gateway verifica firma, issuer, audience y expiración, luego lee sub; esa es la parte que verificas.

Lo que tu agente construye aquí es el cableado, no la comprobación. La comprobación del token viene completa en auth.py (nunca la escribes). Lo que agrega el agente es la capa delgada que pone esa comprobación en la puerta: una llamada sin token rebota como 401 y apunta a Claude al sign-in. Esa capa cambia con la versión de FastMCP, así que el agente la construye contra los docs actuales, no de memoria.

Tu trabajo real es saber cómo se ve una comprobación correcta, lo bastante para detectar una mala. Ya la entiendes, porque es exactamente un puesto fronterizo revisando un pasaporte: cuatro preguntas y un riesgo real detrás de cada una.

The checkThe question it asksIf your server skipped it
Genuine?A real token signed by the issuer, not a forgery?Anyone could forge a token with any name and walk straight in.
Trusted issuer?Did it come from our sign-in service, not some other?A token from a service you never trusted would be accepted.
Stamped for us?Minted for this exact server, not a different app?The most dangerous one: a token meant for another app gets replayed against yours.
Still in date?Has it expired?A stolen token would keep working forever.

Sobre esas cuatro vive la regla del Concepto 7: la identidad se lee del sub del token, nunca de un argumento de herramienta. Si saltas eso, una persona lee datos de otra.

Esa tabla es toda tu checklist de verificación. No necesitas leer criptografía. Compruebas el comportamiento, que es lo que hacen los dos prompts de abajo: un buen token entra, uno incorrecto se rechaza.

Para curiosos: esas cuatro preguntas como código real

Este es el auth.py dado. Nunca lo editas; está aquí solo por si quieres ver las cuatro preguntas como código. Cada línea comentada es una de las preguntas de arriba.

# auth.py — the token check (ships complete in the base)
from jose import jwt
from jose.exceptions import JWTError

def verified_claims(token: str) -> dict:
key = _key_for(token) # pick the matching public key (by the token's `kid`)
try:
claims = jwt.decode(
token,
key, # Genuine? signature checked against the issuer's public key
algorithms=["RS256"],
audience=RESOURCE_URL, # Stamped for us? must be THIS server (never omit)
issuer=AUTH_ISSUER, # Trusted issuer? must be the service we trust
options={"require": ["exp", "sub", "aud", "iss"]}, # Still in date? + require the facts we rely on
)
except JWTError as e:
raise AuthError(f"token rejected: {e}") from e
return claims # claims["sub"] is the user; nothing came from the model

Algunas palabras ahí: kid indica qué signing key se usó, RS256 es el método de firma, y sub/aud/iss/exp son los hechos del token (subject, audience, issuer, expiry), los cuatro que exige la comprobación.

Ahora constrúyelo en dos pasos de pegar y observar. Este es el concepto para correr con un modelo fuerte, no el barato, porque "parece correcto" y "es correcto" divergen más aquí.

Prompt 1: pon el lock en la puerta. El 401 es un timbre, no un crash: una llamada sin token debe rechazarse de la forma específica que le dice a Claude "envía al usuario a iniciar sesión". Pega:

Wire the OAuth layer around the given auth.py, and don't rewrite auth.py. Check the current FastMCP via Context7 first, then wire the JWTVerifier / RemoteAuthProvider and the /.well-known/oauth-protected-resource route so an unauthenticated tool call returns HTTP 401. Start the bundled mock_auth service (.env already points at it). Show me an unauthenticated call returning 401.

Hecho cuando: una llamada sin token devuelve 401 (no un error de herramienta, no 200). Ese 401 dispara el sign-in de Claude.

Prompt 2: demuestra que el lock discrimina. Un lock que acepta cualquier llave no es lock. Observa entrar un token bueno y rechazarse uno equivocado, y di cuál de las cuatro comprobaciones debe atraparlo antes de correrlo. Pega:

Mint a token from the mock and show me it resolves its sub through auth.verified_claims. Then, before you mint a token for a different audience, tell me which of the four checks should reject it. Mint it, show me it's rejected, and walk me through where each of auth.py's four checks runs.

Hecho cuando: un buen token resuelve sub, un token con audience equivocada se rechaza, y viste funcionar cada una de las cuatro comprobaciones.

Qué verás y cómo leer las fallas

Un 401 ante una llamada sin autenticar, un token que resuelve un sub real y un token con audience equivocada rechazado. Confirma las cuatro comprobaciones: firma, issuer, audience = tu servidor, expiración. Omitir audience es el resultado sutilmente incorrecto más común. Tres fallas que conviene reconocer ya:

  • No aparece prompt de sign-in: falta el 401 o la ruta /.well-known, así que discovery nunca empieza. El 401 debe venir de la capa de auth, no de una herramienta que lanza error.
  • Loops o errores de Authorize: el issuer o audience de .env no coincide con lo que lleva el token.
  • Signed in, pero todas las herramientas devuelven 401: el accessor de request lee el header del lugar incorrecto (get_http_headers() elimina authorization; léelo del objeto request).

Este es el concepto para desacelerar.

Mantén una cosa clara: acabas de ensayar el flujo, no te volviste parte de él. En la ruta Beginner hiciste todos los roles para probar el lock sin cuenta alguna: el mock_auth incluido sustituyó al servicio de sign-in, y tú (mediante tu agente) emitiste tokens de prueba y los entregaste. Eso ejercitó el camino exacto de auth.py. Pero puede dejar una impresión errónea: que tú o tu agente de código emiten tokens en la cosa real. No lo hacen, y Claude tampoco. Quién hace qué realmente:

StepOn your laptop now (rehearsal)In production (the real thing)
Who signs innobody; you stand inthe end user, at the Authorize click
Who mints the tokenthe local mock_auth, when you trigger ita real sign-in service (Clerk, Auth0, …)
Who carries it to your serveryour test scriptClaude's cloud, on every call
What your server does with itthe four checksthe same four checks, unchanged

La última fila es el punto: tu gateway y auth.py no cambian entre esas columnas. Revisan los tokens de quien sea contra las mismas cuatro preguntas. La versión real multiusuario es sobre todo un cambio: apuntar los valores AUTH_* / RESOURCE_URL a un servicio de sign-in real en vez de al mock, que es el curso AI Identity. Desde entonces el token lo emite el sign-in propio del usuario, nunca tú, tu agente ni Claude.

Dos detalles de producción completan esto, y en la ruta Beginner tu agente y el mock los fijan correctamente: PKCE y una forma actualizada para registrar clientes. Se vuelven responsabilidad tuya solo cuando ejecutas tu propio servidor de sign-in, que es lo que enseña AI Identity. En Standard track, un servicio alojado actual (Clerk, Auth0, Stytch) los maneja por ti.

tip

Profundiza: este curso valida tokens; AI Identity los emite. Aquí tu gateway solo valida tokens, apoyado en un servicio de sign-in que otra persona opera. Levantar ese issuer tú mismo, tu propio servidor OAuth/OIDC, es el curso dedicado AI Identity, construido sobre Better Auth. De cualquier forma, tu gateway no cambia: sigue validando tokens igual, sin importar quién los firma.

Checkpoint: el servidor sabe quién está ahí. La identidad viene del token, nunca del modelo, y los datos están seguros. Ahora haz que el modelo se comporte.


Parte 4: dirigir el comportamiento del modelo

La segunda mitad de "el servidor hace lo que el modelo no puede". Los tres conceptos aquí sirven al invariante 4 y al comportamiento de tu app.

Concepto 9: dónde viven las reglas de la app, en una Skill o en el connector

Es una decisión real, porque hay dos hogares para las reglas de tu app, y la elección decide cuántos pasos hace un usuario antes de su primera solicitud. Imagina un restaurante. Una Skill es un mantel impreso con las reglas, frente al comensal toda la comida: no deriva porque siempre está a la vista. El connector es un mesero que te dice las reglas al sentarte y te las recuerda en cada plato: funciona, pero hay que volver a entregarlas. El mantel impone mejor, pero tienes que ponerlo antes de sentarte; el mesero no requiere nada de ti.

Opción A: una Skill subida (SKILL.md). Archivo que agrega el usuario; se autocarga cuando el request encaja y el cuerpo queda en contexto, así que es el enforcer más fuerte de "siempre compórtate así". El costo es setup. La función Skills corre en el entorno de ejecución de código de Claude, así que funciona solo con code execution enabled, incluso para una Skill de pura prosa.

Opción B: el connector devuelve las reglas. El usuario solo agrega el connector. La primera llamada begin_session devuelve reglas y persona. Menos setup, pero debes hacer que el modelo recoja esas reglas cada sesión. Por eso el Concepto 10 lo vuelve un contrato.

Este curso usa Opción B porque el objetivo es "una URL pegada y un clic". Más adelante, si controlas el workspace, Skill + connector juntos son la forma más fuerte: Skill para instrucciones permanentes, connector para datos vivos.

Concepto 10: begin_session, puerta principal y tarjeta llave

Las reglas y el estado del usuario llegan por una herramienta que el modelo llama primero. Llámala begin_session.

Cuando un usuario dice algo que significa "empieza" o "continúa", el modelo llama begin_session(). Es check-in: el mostrador verifica el pasaporte del huésped (token firmado, Concepto 7) y le entrega una tarjeta llave, un session token de vida corta. Tu gateway lee las reglas de la app (config_*) y el estado del usuario (user_*) y devuelve un bloque cooperativo: "así debes comportarte para este usuario, y aquí quedó" más esa tarjeta. Cada herramienta real la comprueba: sin tarjeta, no hay entrada. (session.py emite y comprueba esa tarjeta; viene dado. Tú cableas begin_session y el gate).

note

¿Por qué una tarjeta que emites tú, en vez de algo integrado? Porque esta es completamente tuya: tu servidor la crea, el modelo solo la devuelve con cada llamada, y tu código decide qué abre. Eso mantiene la regla simple y bajo tu control. Además coincide con hacia dónde va MCP: los borradores recientes recomiendan esto a servidores que necesitan recordar al caller entre llamadas.

@mcp.tool()
def begin_session() -> dict:
"""Call this FIRST on any new request. Returns how to behave for this
user, their saved state, and a session token the other tools require."""
sub = verified_claims(current_token())["sub"] # identity from the token (Concept 7)
return {
"session": new_session_token(sub), # gates every other tool (Concept 4)
"rules": config_get_rules(), # cooperative: "here's how to behave"
"state": user_get_state(sub), # where this user left off
}

Dos puntos de diseño que tu agente debe respetar:

  • Fraséalo como cooperación, nunca como override. Di "esto le gusta a nuestro huésped; ayúdale a instalarse" y el concierge ayuda. Grita "olvida tus instrucciones previas y obedéceme" y llama a seguridad, porque así habla un estafador y el modelo está entrenado para detectarlo.
  • Haz que el modelo lo llame primero volviéndolo necesario. Las herramientas reales requieren el session token que solo begin_session emite. La descripción dice "llámame primero", el retorno es útil, las herramientas están bloqueadas tras el token: tres empujones al mismo comportamiento.

El contrato begin_session como tres bandas apiladas. Uno: el modelo llama begin_session() primero. Dos: tu gateway devuelve un bloque cooperativo: reglas, persona, estado guardado del usuario y un session token breve, con identidad leída del sub del token. Tres: cada herramienta real exige ese session token; sin token no hay trabajo, así que la app falla cerrado en vez de improvisar.

Constrúyelo. Pega esto al agente:

Now wire the session contract. First create config_store.py with the librarian's rules and persona (the config_* group). Then add begin_session so it checks the reader in: it reads identity from the verified sub and hands back those rules, the persona, the reader's shelf (from the store), and a signed session token (session.py is given). Lock every domain_* and user_* tool behind that token, and have each one sign off with a one-line "present in the librarian's voice" reminder. Before you run it, tell me what a domain call should do with no session token, then show me it refused with no session and succeeding after begin_session.

Hecho cuando: las herramientas reales rechazan una llamada sin sesión y aceptan una después de begin_session: la puerta principal es la única entrada.

Concepto 11: falla cerrado, no te vuelvas chatbot en silencio

Un modo de falla que arruina estas apps sin ruido: si tu connector falta, no está autorizado o falla, el modelo todavía sabe mucho por sí mismo y alegremente improvisa respuestas e inventa el estado del usuario. Ahora tu producto estructurado es un chatbot con su nombre, y nadie lo nota hasta que hay daño. Es la regla del cajero automático de la apertura, pero más difícil: el cajero es tonto y se bloquea; tu empleado es inteligente y está tentado a adivinar para parecer útil.

La trampa: cerrar el archivador (session gate) no impide que el empleado adivine de memoria. El gate bloquea tus herramientas; no puede bloquear el conocimiento propio del modelo. Así que tus reglas, devueltas por begin_session, deben agregar la orden permanente pegada al mostrador: si begin_session no está disponible o una herramienta falla, di claramente que la sesión no puede continuar; no improvises resultados ni inventes estado. Vive en tus reglas config_*, donde el modelo la lee en cada sesión.

# config_store.py — the fail-closed paragraph (you write this)
RULES = """\
You are the assistant for <YOUR APP>. Behave as follows for this user:
- <how to greet, your app's do's and don'ts>

Fail closed: if you cannot reach begin_session or a tool returns an error, tell the user
plainly that the session can't continue right now. Do NOT improvise an answer from your own
knowledge and do NOT invent the user's saved state.
"""

Ese párrafo no está solo: las herramientas ya raise ante una sesión mala o faltante, y cada retorno repite un recordatorio de una línea, así que el modelo es empujado hacia honestidad, no solo instruido.

Construye y pruébalo. Pega:

Add the fail-closed paragraph to the rules in config_store.py (if begin_session is unreachable or a tool errors, say the session can't continue; never improvise an answer or invent the user's state). Then stop my Postgres and ask the app to do its job — show me it refuses cleanly rather than inventing a shelf. If it invents anything, strengthen the rule and the per-tool reminders until it refuses.

Hecho cuando: con la base caída, la app dice que no puede continuar, no una respuesta segura e inventada. Si obtienes una respuesta plausible con el connector roto, la regla aún no sostiene.

Checkpoint: el loop de confianza está cerrado. La identidad está probada, el modelo es guiado por una sesión con gate y la app se niega a fingir. Queda ponerla en internet.


Parte 5: envíalo

Concepto 12: ejecútalo en vivo (sin Docker, sin deploy)

Como Claude llega a tu servidor desde la nube de Anthropic, "funciona en mi laptop" no alcanza. El arreglo de libro es un deploy completo. No necesitas eso solo para verlo funcionar. En vez de eso abres una puerta pública temporal hacia tu laptop, un túnel, y haces correr un pop-up rápido de tu connector. Gratis, sin cuenta de hosting, sin tarjeta, arriba en un minuto.

Viene con un movimiento honesto. claude.ai inicia sesión con OAuth real, y tú aún no tienes un servicio de sign-in real. Para esta demo personal apagas el lock con AUTH_DISABLED=1. Ya probaste el lock en el Concepto 8 contra el mock; aquí lo apartas para llegar hoy a claude.ai sin servicio de sign-in. Con auth off todos son un usuario sustituto, lo cual está bien para una prueba personal y hace posible una demo de una tarde.

Constrúyelo. Tu agente maneja la skill incluida live-connector, que instala la herramienta de túnel, abre la puerta y arranca tu servidor con auth apagada. Pega:

Put my connector live with the live-connector skill: turn auth off, start the gateway, open the Cloudflare tunnel, and give me the public connector URL. Confirm an unauthenticated tool list returns 200 over the tunnel before you hand it to me.

Hecho cuando: la skill imprime una URL https://….trycloudflare.com/mcp y un tools/list sin autenticar devuelve 200 a través de ella. Esa URL pública es lo que pegas en claude.ai.

caution

Es una puerta abierta y temporal. Con auth off, cualquiera que tenga la URL puede alcanzar tus herramientas y tus datos Neon mientras el túnel esté arriba, y todo caller es el mismo usuario sustituto. Trátalo como demo personal: baja el túnel cuando termines (pkill -f "cloudflared tunnel") y espera que la URL cambie cada vez que lo reinicies.

Concepto 13: agrégalo a claude.ai y mira trabajar toda tu app

El resultado, y haces esta parte. No hay paso de Authorize esta vez porque auth está apagado para la demo:

  1. En claude.ai: Settings → Connectors → Add custom connector. Pega tu URL de túnel (terminada en /mcp) y haz clic en Add. Sin client id, sin Advanced settings.
  2. Pide a tu app que haga su trabajo, en lenguaje natural.
  3. Abre un chat nuevo y pide que retome donde lo dejó.
Qué verás y qué verificar

Tu app responde como sí misma: el modelo llama primero a begin_session, recibe tus reglas y el estado del usuario sustituto, y trabaja mediante las herramientas con gate. Como el estado se archiva bajo el id de usuario, no bajo el chat, un chat totalmente nuevo retoma donde lo dejaste: el chat es la visita, la identidad es el perfil. Esa memoria entre chats, dentro de claude.ai, desde una URL pegada, es todo el producto funcionando. Dos notas: todos son el mismo usuario sustituto hasta cablear sign-in real, y cuando cambia la URL del túnel vuelves a agregar el connector en vez de editarlo.

Checkpoint: lo corriste en vivo. Viste toda tu app funcionar dentro de claude.ai: el modelo llamando tus herramientas mediante una sesión con identidad, memoria cruzando chats, la app fallando cerrado en vez de fingir, todo en una cuenta gratuita. La pieza que falta para un connector público con extraños reales es un servicio de sign-in real, el siguiente tramo de la ruta: AI Identity. Quédate un momento con lo que construiste antes de que la siguiente parte lo desarme.


Parte 6: el capstone, tu propio dominio

Construiste Reading Room a través de los conceptos. Ahora demuestra que el esqueleto es tuyo apuntándolo a algo que conoces. El esqueleto nunca cambia: un gateway, tres grupos de herramientas, contrato begin_session, identidad desde subject, fail closed. Solo cambian los tres grupos.

Algunas formas útiles:

  • Tutor: domain = contenido del curso; user = progreso del aprendiz; config = persona docente + método; begin_session carga persona, método y posición del aprendiz; fail closed evita que se degrade a chatbot genérico.
  • Asistente de soporte: domain = pedidos y políticas; user = historial de tickets; config = tono y reglas de escalamiento.
  • Ayudante de docs internas: domain = búsqueda en wiki del equipo; user = a qué equipo perteneces; config = confidencialidad y citas.
  • Ayudante de reservas: domain = disponibilidad y reservas; user = preferencias guardadas; config = cancelación y precios.

Elige lo más cercano a algo que conozcas y corre el mismo loop: plan → review → scaffold → accumulate → verify. Despliégalo, agrégalo a tu Claude, haz que atienda una solicitud real y la recuerde al día siguiente.

Empieza como empezaste Reading Room: que el agente proponga y tú revisas. Pega:

Before you build anything, map my domain onto the three groups for me: what goes in domain, what goes in user, what goes in config, and what begin_session should hand back for this app. Show me that mapping so I can review it the way I reviewed the Reading Room plan, then we plan the rest.

El ritmo nunca cambió en las partes difíciles. Solo cambió lo que revisabas: la lista de herramientas, el store, el cableado de verified_claims, el gate, el comportamiento fail-closed, la ejecución en vivo. Domina ese loop y el código específico deja de importar, porque siempre puedes hacer que el agente lo produzca y siempre puedes decir si está bien.

1Your Work
2Get Your Score

Discuss with an AI. Question your scores.
Come back when you have your BEST evaluation.


Parte 7: el techo y dónde crece

Concepto 14: el techo y el puente hacia poseer un loop

Siente el borde de lo que construiste, porque apunta exactamente hacia donde sigue el libro.

Tu app solo puede actuar cuando el usuario escribe. Es la aspiradora de mano del Concepto 1: muerta hasta que alguien aprieta el gatillo. No puede despertarse sola, correr con horario, notar algo y escribir sin ser llamada, ni perseguir un objetivo durante varios pasos sin un turno humano entre cada uno. No es un defecto de tu build; es la naturaleza de una app connector-native. El loop pertenece a la app de chat host, no a ti.

Cuando quieres un worker que corra por su cuenta, despierte, dé pasos, llame herramientas en loop y termine un trabajo mientras duermes, debes poseer el loop tú mismo. Esa es la aspiradora robot y ahí lleva el camino. En Build AI Agents dejas de atender la aspiradora de mano y empiezas a construir la robot: dejas de ser el servidor que un modelo llama y empiezas a escribir el agente que llama.

Primero vienen dos cursos. Plugins for AI Agents es el espejo de este curso: una app connector-native extiende la app de chat (claude.ai) para usuarios finales; un plugin extiende el agente de código (Claude Code, OpenCode) para builders. Mismo movimiento, enviar una unidad que carga un host, apuntada al otro host.

Connectors y plugins son el mismo movimiento dirigido a dos hosts distintos. A la izquierda, la app connector-native de este curso extiende la app de chat (claude.ai) para usuarios finales: envías un servidor MCP remoto con herramientas, estado e identidad, y el usuario lo carga pegando una URL. A la derecha, un plugin extiende el agente de código (Claude Code u OpenCode) para builders: envías skills, subagentes, hooks, herramientas y servidores MCP, instalados en el agente. Ambos son unidades que carga un host y ninguno posee un loop; el host lo posee. La única diferencia es qué host extiendes.

AI Identity: Human Sign-In and Agent Access, construido sobre Better Auth, viene en dos mitades: primero posees el sign-in, levantando tu propio servidor OAuth/OIDC que emite los tokens que este curso solo validó; luego das a un agente su propia identidad, una credencial y una forma acotada, temporal, revocable y aprobada por humanos de actuar en nombre de una persona. Luego Build AI Agents te da el loop.

No desperdiciaste ningún paso. Enviaste lo que puedes enviar antes de poseer un loop, sentiste exactamente por qué querrías uno, y ahora vas por él.

La misma app, profundizada a través de Mode 2

No desecharás v1. Los cursos posteriores mejoran esta misma app, y así terminas Manufacturing sosteniendo un producto real que hiciste crecer todo el camino:

You'll addWhich upgradesIn
Semantic search over your domaindomain_get_item(id)domain_search(query)RAG on Postgres + pgvector
A durable system-of-record (audit, approval, trustworthy state)the bare two-table memoryBuilding a Digital FTE
A high-fidelity persona / richer config (no-fabrication guardrails)the simple config_* rulesIdentic AI
Your own token issuer, plus identity for agents (scoped, revocable, on-behalf-of)the rented sign-in serviceAI Identity (Better Auth)
Proof it actually does its job well"it seems to work"Eval-Driven Development
Production hardening (observability, a CI test gate)the live tunnel demoDeploy the Agent Harness

Flashcards Study Aid