· 11 min de lectura

Construye un Sistema de Conocimiento Persistente para IA con MCP y PostgreSQL

Cómo construí Neural, un servidor MCP que da memoria persistente a Claude Code usando PostgreSQL, pgvector y embeddings locales con Ollama. Arquitectura, decisiones de diseño y lecciones aprendidas tras 900+ memories en producción.

Imagen de Construye un Sistema de Conocimiento Persistente para IA con MCP y PostgreSQL

Los asistentes de IA tienen un problema fundamental: no recuerdan nada entre conversaciones. Cada sesión empieza de cero. Después de meses trabajando con Claude Code y repitiendo las mismas explicaciones sobre mis proyectos, decidí resolver el problema construyendo Neural — un servidor MCP que conecta Claude Code con PostgreSQL y pgvector para darle memoria persistente, búsqueda semántica y contexto de proyecto.

Hoy gestiona 913 memories activas de 15 proyectos, con 60 skills que automatizan flujos de trabajo completos. En este artículo explico la arquitectura, las decisiones de diseño, y cómo puedes construir algo similar.

El Problema: IA Sin Memoria

Si usas un asistente de IA para programar, conoces la frustración:

  • Repites contexto: “Este proyecto usa Vue 3 con Pinia, el backend es Express con Prisma…”
  • Pierdes decisiones: “Ya decidimos no usar mocks en los tests de integración, ¿recuerdas? No, claro que no.”
  • No aprende de errores: El mismo gotcha te muerde en cada sesión
  • Sin continuidad: Cada conversación es una isla

El archivo CLAUDE.md ayuda, pero tiene límites: es texto plano, no escala, y no permite búsqueda semántica. Necesitaba algo que creciera con mis proyectos.

¿Qué es MCP?

Model Context Protocol (MCP) es un estándar abierto creado por Anthropic que permite a los LLMs conectarse con herramientas externas. En lugar de que la IA solo lea y escriba archivos, MCP le da acceso a APIs, bases de datos, servicios web — cualquier cosa que expongas como un servidor MCP.

Un servidor MCP expone tools (funciones que la IA puede llamar) y resources (datos que la IA puede leer). Claude Code soporta MCP nativamente, así que cualquier servidor que implementes aparece como herramientas disponibles en tu sesión.

La Arquitectura de Neural

Stack Tecnológico

ComponenteTecnologíaPor qué
Servidor MCPTypeScript + @modelcontextprotocol/sdkSDK oficial, tipado fuerte
Base de datosPostgreSQL 17Robusta, extensible, JSONB nativo
Embeddingspgvector + OllamaBúsqueda semántica sin dependencias cloud
AlmacenamientoMinIO (S3-compatible)Planes, adjuntos y backups
Modelo de embeddingsnomic-embed-text (Ollama)Local, rápido, 768 dimensiones

Estructura Modular

src/
├── cli.ts          # Entrypoint: install/uninstall/run
├── index.ts        # McpServer setup + instructions
├── db.ts           # Pool PostgreSQL + helpers
├── embedding.ts    # Generación de embeddings via Ollama
└── tools/
    ├── memories.ts # CRUD + búsqueda semántica
    ├── projects.ts # Registro de proyectos
    ├── secrets.ts  # Gestión de credenciales
    ├── plans.ts    # Planes en S3
    └── ...         # clients, proposals, devices, etc.

Cada módulo de tools se registra independientemente. Añadir un nuevo dominio es crear un archivo en tools/ y registrarlo en index.ts.

El Modelo de Datos

Memories: El Corazón del Sistema

La tabla memories es donde vive el conocimiento:

CREATE TABLE memories (
  id SERIAL PRIMARY KEY,
  type TEXT NOT NULL,        -- decision, pattern, gotcha, context
  project TEXT,              -- NULL = global (aplica a todos)
  content TEXT NOT NULL,
  tags TEXT[],
  embedding VECTOR(768),     -- pgvector para búsqueda semántica
  valid_until TIMESTAMPTZ,   -- expiración opcional
  created_at TIMESTAMPTZ DEFAULT now(),
  updated_at TIMESTAMPTZ DEFAULT now()
);

Cuatro tipos de memoria, cada uno con un propósito claro:

TipoPropósitoEjemplo
decisionDecisiones de arquitectura y diseño”Usamos repository pattern con inyección de dependencias”
patternPatrones recurrentes y convenciones”Los tests de integración usan PostgreSQL real, nunca mocks”
gotchaTrampas y errores conocidos”pg_dump v15 contra PostgreSQL v17 genera backups vacíos”
contextInformación contextual temporal”El cliente necesita la demo lista para el 15 de abril”

Búsqueda Semántica con pgvector

Cada memory genera un embedding automáticamente al crearse. Esto permite buscar por significado, no solo por texto:

-- Búsqueda semántica: "cómo manejar autenticación"
-- encuentra memories sobre OAuth, JWT, OIDC, passkeys...
SELECT *, 1 - (embedding <=> $1) AS similarity
FROM memories
WHERE project = $2 OR project IS NULL
ORDER BY embedding <=> $1
LIMIT 10;

La combinación de project = $2 OR project IS NULL es clave: las memories globales (sin proyecto) aplican a todos, las específicas solo a su proyecto. Esto permite tener patterns universales (“siempre usar conventional commits”) y específicos (“en este proyecto el deploy es via tags”).

Proyectos como Hub de Integración

CREATE TABLE projects (
  name TEXT PRIMARY KEY,
  content TEXT,
  repo_url TEXT,
  notion JSONB,     -- { kb_id, adrs_id, bugs_id }
  services JSONB,   -- { plane: { project_id, identifier } }
  tags TEXT[],
  embedding VECTOR(768)
);

Cada proyecto enlaza Neural con servicios externos: Notion para documentación, Plane para gestión de tareas, GitLab para código. Cuando la IA necesita crear un work item, consulta project_get para saber qué tracker usar y con qué configuración.

Embeddings Locales con Ollama

Una decisión importante fue generar embeddings localmente con Ollama en lugar de usar APIs cloud:

// embedding.ts (simplificado)
async function generateEmbedding(text: string): Promise<number[]> {
  const response = await fetch(`${OLLAMA_URL}/api/embeddings`, {
    method: 'POST',
    body: JSON.stringify({
      model: 'nomic-embed-text',
      prompt: text.substring(0, 8192) // truncar a 8K chars
    })
  });
  return response.json().then(r => r.embedding);
}

Ventajas de embeddings locales:

  • Sin costes por llamada — genera tantos embeddings como necesites
  • Sin dependencias externas — funciona offline
  • Privacidad — tu código y decisiones nunca salen de tu red
  • Velocidad — Ollama en un Mac Mini M2 genera embeddings en ~50ms

Trade-off: El modelo local (nomic-embed-text, 768d) es menos preciso que text-embedding-3-large de OpenAI (3072d). En la práctica, para buscar dentro de 900 memories la diferencia es imperceptible.

El Sistema de Skills

Las tools del MCP dan acceso a los datos. Los skills son prompts estructurados que definen flujos de trabajo completos. Se instalan como archivos en ~/.claude/skills/ y Claude Code los detecta automáticamente.

Ejemplo: neural-do (Tomar una tarea)

Cuando digo /neural-do NEURAL-42, el skill:

  1. Consulta project_get para obtener el tracker del proyecto
  2. Carga el work item de Plane con su descripción y estado
  3. Busca memories relacionadas (decisions, patterns, gotchas)
  4. Carga el prompt del módulo Plane si el item tiene uno asignado
  5. Presenta el contexto completo y transiciona a /neural-plan o /neural-debug

Ejemplo: neural-capture (Guardar conocimiento)

Al final de una sesión productiva, /neural-capture:

  1. Revisa qué se hizo en la sesión (commits, archivos modificados)
  2. Propone memories para guardar (decisions, patterns, gotchas detectados)
  3. Antes de guardar, hace búsqueda semántica cross-project para detectar duplicados
  4. Si encuentra similarity >= 0.80, sugiere globalizar en vez de crear una específica
  5. Genera embeddings y persiste

Los 60 Skills

Se organizan en dos familias:

22 neural-* (workflow): brainstorm, plan, execute, tdd, debug, do, finish, worktree, release, check, capture, init, status, load-patterns, refactor, document, sync-notion, write-skill, help, review, cycle-close, audit.

38 stack-* (expertos tecnológicos): typescript, vue, prisma, postgresql, docker, git, y muchos más. Cada uno carga patterns específicos de su tecnología desde Neural antes de responder.

Integraciones

Neural no vive aislado. Se conecta con el ecosistema de herramientas:

Plane (Gestión de Tareas)

project_get("nexus") → services.plane.project_id
→ mcp__plane__list_work_items(project_id)
→ mcp__plane__create_work_item(...)

Los skills detectan automáticamente si un proyecto usa Plane, GitLab Issues, o ningún tracker, y adaptan su comportamiento.

Notion (Documentación)

Sincronización on-demand de memories a Knowledge Base de Notion. Neural es la fuente de verdad (SSoT); Notion es la capa de presentación para documentación editorial.

MinIO (Almacenamiento S3)

  • Planes de implementación: neural/plans/{project}/{date}-{slug}.md
  • Adjuntos: neural/attachments/{memory_id}/{filename}
  • Backups: neural/backups/{YYYY-MM-DD}.sql.gz — pg_dump diario automatizado

Lecciones Aprendidas

1. Los Gotchas Son Oro

De los 4 tipos de memoria, los gotchas son los más valiosos en la práctica. Representan el 38% de todas las memories (346 de 913). Son errores que ya cometiste y documentaste — la IA los encuentra antes de que vuelvas a caer en la misma trampa.

2. Global vs. Específico

Las memories sin proyecto (project IS NULL) aplican a todos los proyectos. Esto es poderoso: un pattern como “siempre usar conventional commits en español” se escribe una vez y aplica en los 15 proyectos. La tentación es hacer todo global, pero la regla es: global solo si aplica universalmente.

3. Los Embeddings No Son Magia

La búsqueda semántica es potente, pero no reemplaza al texto. Una query de “autenticación” encontrará memories sobre OAuth, JWT y passkeys, pero no encontrará una gotcha sobre “el header X-Forwarded-For se pierde en el reverse proxy” aunque sea crítica para auth. Por eso Neural ofrece ambas: memory_search (texto/tags) y memory_semantic_search (embeddings).

4. La Memoria Necesita Mantenimiento

Las memories se vuelven obsoletas. Un skill como /neural-audit revisa periódicamente: memories sin embedding, duplicados semánticos (similarity > 0.9), memories expiradas, y patterns que ya no coinciden con el código actual. Sin esta higiene, la base de conocimiento se degrada.

5. Las Instructions del MCP Son el Contexto Más Barato

El campo instructions del servidor MCP se inyecta en cada conversación. Es el lugar perfecto para reglas que aplican siempre: “Neural es SSoT”, “consultar project_get antes de interactuar con Plane”, “las queries de memoria se pueden ejecutar en paralelo”. Cero tokens extra por consulta.

Cómo Empezar Tu Propio Sistema

No necesitas replicar Neural al completo. Empieza con lo mínimo:

Paso 1: PostgreSQL + pgvector

CREATE EXTENSION vector;

CREATE TABLE memories (
  id SERIAL PRIMARY KEY,
  type TEXT NOT NULL CHECK (type IN ('decision', 'pattern', 'gotcha', 'context')),
  project TEXT,
  content TEXT NOT NULL,
  tags TEXT[] DEFAULT '{}',
  embedding VECTOR(768),
  created_at TIMESTAMPTZ DEFAULT now()
);

CREATE INDEX ON memories USING ivfflat (embedding vector_cosine_ops);

Paso 2: MCP Server Mínimo

import { McpServer } from "@modelcontextprotocol/sdk/server/stdio.js";
import pg from "pg";

const server = new McpServer({ name: "my-memory", version: "1.0.0" });
const pool = new pg.Pool({ connectionString: process.env.DSN });

server.tool("memory_create", { type: "string", project: "string", content: "string" },
  async ({ type, project, content }) => {
    const embedding = await generateEmbedding(content);
    await pool.query(
      "INSERT INTO memories (type, project, content, embedding) VALUES ($1, $2, $3, $4)",
      [type, project, content, JSON.stringify(embedding)]
    );
    return { content: [{ type: "text", text: "Memory saved" }] };
  }
);

server.tool("memory_search", { query: "string", project: "string" },
  async ({ query, project }) => {
    const embedding = await generateEmbedding(query);
    const result = await pool.query(
      `SELECT *, 1 - (embedding <=> $1) AS similarity
       FROM memories WHERE (project = $2 OR project IS NULL)
       ORDER BY embedding <=> $1 LIMIT 10`,
      [JSON.stringify(embedding), project]
    );
    return { content: [{ type: "text", text: JSON.stringify(result.rows) }] };
  }
);

Paso 3: Registrar en Claude Code

// ~/.claude.json
{
  "mcpServers": {
    "my-memory": {
      "command": "node",
      "args": ["/path/to/dist/index.js", "--dsn", "postgresql://..."]
    }
  }
}

Con esto ya tienes un sistema funcional. La IA puede guardar y buscar conocimiento entre sesiones. A partir de aquí, iteras: añades proyectos, tags, skills, integraciones.

Números en Producción

Después de ~3 meses de uso diario con Neural:

MétricaValor
Memories activas913
Memories archivadas74
Proyectos registrados15
Skills instalados60
Cobertura de embeddings99.9% (912/913)
Tipos: gotcha / pattern / decision / context346 / 264 / 207 / 96
Tablas adicionalesclients, proposals, devices, finances, secrets

Conclusión

Darle memoria persistente a una IA cambia fundamentalmente la relación de trabajo. Pasa de ser un asistente genérico que necesita contexto constante a ser un colaborador que conoce tus proyectos, recuerda tus decisiones, y aprende de los errores pasados.

MCP hace esto posible de forma estándar y extensible. PostgreSQL + pgvector dan la base robusta que necesitas. Y Ollama mantiene todo local y privado.

Neural empezó como un experimento para no repetirme. Tres meses después, es la pieza central de mi flujo de desarrollo. Si trabajas con IA a diario, construir tu propio sistema de memoria es probablemente la mejor inversión de tiempo que puedes hacer.

¿Tienes preguntas sobre la implementación o quieres compartir tu enfoque para dar memoria a la IA? Únete al grupo de Telegram para comentar.