Blog
28 de agosto de 20268 min

Cómo uso Claude Code para mantener este portafolio

No es "la IA escribe mi código". Es un MCP propio sobre mi CMS, un bucle de diseño con capturas de pantalla, y reglas escritas para no repetirme. Esto es lo que sí funciona — con un bug real de ejemplo.

Claude CodeMCPPayload CMSWorkflowIA
Cómo uso Claude Code para mantener este portafolio

Llevo meses usando Claude Code como parte normal de mi día de trabajo, y este mismo portafolio es el mejor ejemplo de cómo lo uso. No para "que la IA escriba todo", sino para quitarme de encima el trabajo mecánico y para tener un compañero que se acuerda del contexto cuando yo ya no.

Esto es lo concreto que hago.

1. Le enseñé mi CMS a Claude (con un MCP propio)

El contenido del sitio vive en Payload CMS. Antes, cada cambio era el mismo ritual: abrir el admin, buscar el proyecto, subir la imagen, acomodar el orden, revalidar. Diez minutos de clics para algo que se dice en una frase.

Así que construí un servidor MCP para mi propio portafolio: 2,030 líneas y 36 herramientas que exponen el CMS como acciones que Claude puede ejecutar directo.

  • Contenido: list_projects, create_project, update_project, create_experience, create_post, publish_post
  • Media: upload_media, set_project_image, add_project_screenshot, add_experience_photo, upload_cv
  • Orden y mantenimiento: reorder_projects, auto_sort_experiences, normalize_all_dates, lint_portfolio
  • Publicación: export_content, import_content, revalidate_site

El resultado es que ahora le digo "sube estas tres fotos a la experiencia de Talent Land y ponla arriba de la de VITRAS", y pasa. Sin abrir el navegador.

Dos detalles de diseño que valieron la pena:

Todo lo destructivo tiene dry_run. Antes de escribir, la herramienta me devuelve un diff campo por campo, antes → después. Ese solo detalle es lo que me deja soltar el control sin nervios: veo exactamente qué va a cambiar antes de que cambie.

Un linter de contenido. lint_portfolio no escribe nada; revisa locales faltantes, fechas a medianoche UTC (que se corren de día según la zona horaria), Markdown crudo metido en campos HTML, proyectos sin imagen, huecos en las fechas de trabajo. Es el tipo de revisión aburrida que nunca haces a mano y que la máquina hace en dos segundos.

Justo hoy usé esto para un problema chiquito pero feo: los filtros de /projects mostraban Tailwind CSS, TailwindCSS, Tailwind CSS v4 y Tailwind CSS 4 como si fueran cuatro tecnologías distintas. Lo mismo pasaba con Next.js / Next.js 15 / Next.js 15.3.4. Una instrucción, cinco proyectos actualizados, filtros revalidados. Cinco minutos en vez de una tarde de clics.

2. El bucle de diseño: que vea la pantalla, no que se la describa

Esta es la parte que más cambió cómo trabajo. Claude Code puede manejar mi Chrome: abre localhost, navega, hace scroll, toma capturas y lee la consola.

Busto clásico con lentes redondos que reflejan código, mirando un teléfono
Le presto el navegador: ve la pantalla en vez de que yo se la describa.

Eso convierte el diseño en una conversación corta y directa. Mi feedback literal en las últimas sesiones fue así:

  • "no me gusto, se solapa feo"
  • "están desincronizados los puntos con las tarjetas, deberíamos iniciar en un punto"
  • "dale algo de protagonismo a las imágenes si hay"
  • "haz que se vea más joya"

Y de ahí salió el rediseño de /experience: un recorrido 3D con react-three-fiber donde la cámara viaja por una ruta en el espacio conforme haces scroll, con una parada por cada experiencia y un nodo final con un ? — la siguiente parada, todavía sin escribir. Nada de eso lo especifiqué de entrada. Salió de iterar en pasos chicos, mirando capturas.

La regla que aprendí: pasos chicos y feedback inmediato le ganan al brief perfecto. Cuando pido cinco cosas juntas, reviso mal. Cuando pido una y la veo, corrijo en diez segundos.

3. Las reglas que no quiero volver a explicar

Dos mecanismos, y los dos importan.

CLAUDE.md es el archivo de instrucciones permanentes. El mío apunta a mis propias reglas de gusto para frontend, y se leen antes de tocar cualquier componente. Es la diferencia entre pedir estilo cada vez y que el estilo sea el default.

La memoria persistente es más interesante, porque guarda lo que aprendimos peleando. Ejemplo real: pedí propuestas de rediseño para el home y rechacé cinco variantes en dos rondas — versión editorial clara, grid oscuro de ingeniería, bento, scroll-telling estilo Awwwards. Al final dije "la original es mejor". Eso quedó escrito: no volver a proponer rediseños completos del home; iterar sobre lo que ya hay. No lo tengo que repetir cada sesión.

En esa misma nota vive una regla técnica que me costó una tarde: nunca animar UI crítica con un gsap.from(). Si el tween se interrumpe — un remount de React StrictMode, un refresh de ScrollTrigger — el elemento se queda en su estado inicial, o sea invisible. Los chips de filtros de /projects desaparecieron exactamente por eso. La regla ahora es fromTo con un onInterrupt que limpia las props, o de plano no animar lo que tiene que verse sí o sí.

4. Un bug real: el 403 que no era lo que yo creía

Este es mi ejemplo favorito, porque muestra la parte que de verdad vale.

Síntoma: en el MCP, create_project y set_project_image funcionaban perfecto, pero update_project, update_experience e import_content tronaban con 403 — "You are not allowed to perform this action". Además reorder_projects en dry-run reportaba cero proyectos… existiendo trece.

Mi hipótesis era razonable y estaba equivocada: "las que fallan comparten un cliente de Payload distinto, o llaman payload.update sin overrideAccess". Sonaba lógico — falla justo el grupo de escrituras.

La lectura completa del archivo la tiró: todas las herramientas pasan por el mismo helper api(). No había dos clientes. Lo que sí había era esto:

let _token = null;

async function getToken() {
  if (_token) return _token;   // ← se cachea para siempre
  _token = await login();
  return _token;
}

El JWT de Payload expira a las ~2 horas. En una sesión larga de MCP, el token cacheado se vuelve basura — pero las lecturas siguen pasando porque las colecciones tienen acceso público de lectura. Sólo mueren las escrituras. De ahí el patrón engañoso: no fallaba un grupo de herramientas, fallaba cualquier cosa que escribiera después de la marca de las dos horas. Y el "cero proyectos" del reorder era un mensaje mal redactado: leía los trece, pero decía cuántos cambiarían, que eran cero.

El arreglo son dos cosas chicas: TTL para refrescar el token antes de que caduque, y un reintento cuando la API responde 401/403.

async function api(method, path, body, _retried = false) {
  const res = await fetch(url, { headers: { Authorization: await getToken() }, ... });

  if ((res.status === 401 || res.status === 403) && !_retried) {
    _token = null;                          // fuerza re-login
    return api(method, path, body, true);   // y un solo reintento
  }
  return res.json();
}

Y arriba de eso, cuatro tests de regresión que escriben sobre registros reales y verifican que updatedAt avanzó — o sea, que el PATCH de verdad aterrizó. Uno de ellos envenena el token a propósito para comprobar que el reintento lo salva.

La lección: la hipótesis del que reporta el bug es un dato, no una conclusión. Yo di un diagnóstico con mucha seguridad y estaba mal; lo valioso fue que se leyeran las 2,030 líneas antes de arreglar lo que yo creía.

5. Commits por funcionalidad

Una instrucción que repito siempre: "commitea por funcionalidad". No un wip gigante al final del día, sino un commit por cosa terminada, con el porqué en el cuerpo. Así se ve el historial de las últimas sesiones:

fix(experience): keep the route off the hero text in the intro
fix(experience): make the 3D journey work on phones
feat(projects): cinematic detail page — parallax hero, gallery, nav cards
fix(mcp): survive stale Payload JWTs — proactive refresh + 403 retry
perf(home): server-fetch featured projects and resume with ISR
feat(experience): 3D journey redesign — scroll-driven route through space

Cada uno se revierte solo si algo sale mal. Y cuando vuelvo en tres meses, el mensaje me dice qué estaba pensando.

6. Lo que no delego

  • Qué construir. La dirección, el gusto y el alcance son míos. Rechacé cinco rediseños del home porque el original era mejor; ningún modelo iba a decidir eso por mí.
  • Lo irreversible. Publicar, borrar, tocar producción: eso lo apruebo yo, herramienta por herramienta.
  • Creerle sin verificar. Si dice que algo funciona, quiero la captura, el test o la salida del comando. Y sí, a veces se equivoca — el punto es que quede a la vista.

Lo que me llevo

Lo que más rinde no es escribir código más rápido. Es darle a la herramienta el mismo acceso que tengo yo — mi CMS por MCP, mi navegador para ver el resultado, mis reglas escritas — y quedarme yo con la parte de decidir y de revisar.

Todo lo que salió de ese flujo está en este sitio: el recorrido 3D de /experience, las páginas de proyecto, el servidor MCP, y este post.

¿Te sirvió, o quieres discutirlo?

Escríbeme — me interesa cómo lo estás resolviendo tú.

Hablemos