structured.sh
Servidor MCP autoalojado para memoria persistente de agentes. Define esquemas tipados, escribe registros y consulta con SQL mediante DuckDB sobre archivos Parquet en disco.
Documentación
■ structured
memoria nativa de esquema para agentes de IA
structured.sh · Inicio rápido · Conectar con Claude · Herramientas MCP · Referencia de API · Desplegar
Define esquemas. Escribe datos estructurados. Consulta con SQL. Todo como archivos Parquet que tú posees.
docker compose up
¿Qué es esto?
Structured brinda a los agentes de IA (y a los humanos) memoria persistente y consultable:
- Define una memoria — nombre + esquema tipado (como una tabla)
- Escribe registros — almacenados en búfer y vaciados automáticamente a archivos Parquet
- Consulta con SQL — DuckDB se ejecuta directamente sobre los archivos Parquet
- Posee tus datos — todo son archivos locales en disco, sin dependencia de proveedor
Funciona con Claude Desktop, Cursor, Windsurf, Cline o cualquier cliente compatible con MCP.
Arquitectura
┌──────────────────────────────────────────────────────┐
│ docker compose up │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌────────────┐ │
│ │Dashboard │ │ REST API │ │ MCP Server │ │
│ │ :3000 │───▶│ :3001 │◀───│ stdio │ │
│ │ │ │ │ │ │ │
│ │Vite+React│ │ Hono + WASM │ │ 9 tools │ │
│ └──────────┘ │ │ └────────────┘ │
│ │ SQLite (sql.js) │
│ │ DuckDB (wasm) │
│ │ Parquet (tiny-parquet) │
│ └──────┬───────┘ │
│ │ │
│ ./data/ │
│ ├── structured.db ← metadata │
│ └── parquet/ ← your data │
│ ├── user_prefs/ │
│ │ └── 2026/04/09/...parquet │
│ └── campaign_data/ │
│ └── 2026/04/09/...parquet │
└──────────────────────────────────────────────────────┘
Cero dependencias nativas. Todo se ejecuta en WASM: sin compilaciones de C++, sin problemas de plataforma.
Inicio rápido
1. Clona y configura
git clone https://github.com/structured-sh/structured.git
cd structured
Edita docker-compose.yml y establece tus credenciales:
environment:
- API_KEY=your-secret-api-key # Used by MCP clients & scripts
- DASHBOARD_PASSWORD=your-password # Protects the dashboard UI
Dos credenciales separadas:
API_KEY— autenticación de máquina para clientes MCP, scripts e ingesta de analíticasDASHBOARD_PASSWORD— autenticación humana para el panel web. Déjalo sin configurar para deshabilitar el inicio de sesión (uso solo local).
2. Inicia el stack
docker compose up -d
| Servicio | URL |
|---|---|
| Dashboard | http://localhost:3000 |
| API | http://localhost:3001 |
| MCP | stdio (conectado automáticamente) |
3. Crea una memoria
curl -X POST http://localhost:3001/memories \
-H "Authorization: Bearer your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "user_preferences",
"fields": [
{ "name": "key", "type": "string" },
{ "name": "value", "type": "string" },
{ "name": "priority", "type": "int32" }
],
"description": "User preference settings"
}'
4. Escribe datos
curl -X POST http://localhost:3001/memories/user_preferences/write \
-H "Authorization: Bearer your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{
"data": [
{ "key": "theme", "value": "dark", "priority": 1 },
{ "key": "language", "value": "en", "priority": 2 }
]
}'
5. Consulta con SQL
curl -X POST http://localhost:3001/query \
-H "Authorization: Bearer your-secret-api-key" \
-H "Content-Type: application/json" \
-d '{ "sql": "SELECT * FROM user_preferences ORDER BY priority" }'
Los nombres de memoria funcionan como nombres de tabla: DuckDB los resuelve a archivos Parquet automáticamente.
6. Accede a los archivos sin procesar
# Files on disk
ls ./data/parquet/user_preferences/
# DuckDB CLI
duckdb -c "SELECT * FROM read_parquet('./data/parquet/user_preferences/**/*.parquet')"
# Python
import duckdb
duckdb.sql("SELECT * FROM './data/parquet/user_preferences/**/*.parquet'").show()
Conectar con Claude / Cursor
Local (Docker)
Añade a ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"structured": {
"command": "docker",
"args": ["exec", "-i", "structured-mcp", "node", "index.js"]
}
}
}
Para Cursor, añade en Configuración → Funciones → Servidores MCP:
{
"structured": {
"command": "docker",
"args": ["exec", "-i", "structured-mcp", "node", "index.js"]
}
}
Cloud (structured.sh)
Próximamente: versión alojada en structured.sh
{
"mcpServers": {
"structured": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-proxy", "https://mcp.structured.sh"]
}
}
}
Herramientas MCP
Una vez conectado, simplemente habla con naturalidad. La IA elige la herramienta adecuada.
| Herramienta | Qué decir |
|---|---|
create_memory | "Crea una memoria para registrar ventas diarias con campos: fecha, ingresos, unidades" |
list_memories | "¿Qué memorias tengo?" |
describe_memory | "Muéstrame el esquema de daily_sales" |
write_memory | "Guarda las ventas de hoy: date=2026-04-09, revenue=1250.50, units=42" |
query_memory | "¿Cuál es el ingreso total de este mes?" |
store_document | "Recuerda esta configuración para más tarde" |
get_document | "Obtén el documento abc-123" |
flush_memory | "Vacía todos los datos pendientes al disco" |
delete_memory | "Elimina la memoria test_data" |
Ingesta de datos desde tus aplicaciones
Usa las plantillas en templates/ para enviar eventos desde sistemas externos:
| Plantilla | Caso de uso |
|---|---|
templates/ingest-app-analytics.js | Eventos de aplicaciones móviles/web (instalaciones, acciones, compras) |
templates/ingest-webhook.js | Webhooks de Stripe, GitHub, Shopify |
templates/query-report.js | Genera informes SQL → terminal, Markdown o Slack |
// Example: track an install from your iOS app
import { track } from './templates/ingest-app-analytics.js';
await track('install', 'user_abc', { platform: 'ios', app_version: '1.0.0' });
Luego consulta todos tus eventos:
SELECT event, COUNT(*) as n, COUNT(DISTINCT user_id) as users
FROM app_events
WHERE timestamp > now() - INTERVAL '30 days'
GROUP BY event ORDER BY n DESC
Cola de mensajes fallidos
Los registros rechazados por los modos de esquema strict o evolve no se descartan: se escriben automáticamente en _dlq_{memory_name} para su inspección:
-- See what was rejected and why
SELECT _reason, _payload, _rejected_at
FROM _dlq_app_events
ORDER BY _rejected_at DESC
LIMIT 20
Rotación de clave API
Rota tu clave API en cualquier momento desde la página Conectar del panel sin reiniciar:
- Ve a la sección Conectar → Clave API
- Haz clic en Rotar clave
- Copia la nueva clave (se muestra una sola vez)
- Actualiza tu configuración MCP y cualquier script
La clave anterior se invalida inmediatamente.
Referencia de API
Autenticación (Panel)
| Método | Ruta | Descripción |
|---|---|---|
GET | /auth/status | Comprueba si la autenticación del panel está habilitada |
POST | /auth/login | Inicia sesión con { password }, devuelve token de sesión |
GET | /auth/me | Valida la sesión actual (cabecera X-Session-Token) |
POST | /auth/logout | Cierra sesión (el cliente descarta el token) |
Memorias
| Método | Ruta | Descripción |
|---|---|---|
GET | /memories | Lista todas las memorias |
POST | /memories | Crea una memoria |
GET | /memories/:name | Obtén detalles de la memoria |
PUT | /memories/:name | Actualiza la memoria |
DELETE | /memories/:name | Elimina la memoria |
POST | /memories/:name/write | Escribe registros |
POST | /memories/:name/flush | Vacía a Parquet |
GET | /memories/:name/preview | Vista previa de datos |
GET | /memories/:name/files | Lista archivos Parquet |
Consulta
| Método | Ruta | Descripción |
|---|---|---|
POST | /query | Ejecuta SQL de DuckDB |
Almacenamiento (Documentos)
| Método | Ruta | Descripción |
|---|---|---|
POST | /documents | Almacena un documento JSON |
GET | /documents/collections | Lista colecciones |
GET | /documents/:collection | Lista documentos |
DELETE | /documents/:collection/:id | Elimina documento |
Configuración
| Método | Ruta | Descripción |
|---|---|---|
GET | /settings/api-key | Obtén la clave API actual enmascarada |
POST | /settings/rotate-key | Genera una nueva clave API |
Archivos
| Método | Ruta | Descripción |
|---|---|---|
GET | /files/* | Descarga archivo Parquet sin procesar |
MCP (SSE)
| Método | Ruta | Descripción |
|---|---|---|
GET | /sse | Flujo SSE para clientes MCP |
POST | /messages | Mensajes JSON-RPC |
Modos de esquema
| Modo | Comportamiento |
|---|---|
flex | Acepta todos los campos, sin validación (predeterminado) |
evolve | Acepta todos, detecta y registra desviaciones de esquema |
strict | Rechaza registros con campos no coincidentes → DLQ |
Tipos de campo
| Tipo | Tipo Parquet | Notas |
|---|---|---|
string | BYTE_ARRAY (UTF-8) | Predeterminado |
int32 | INT32 | |
int64 | INT64 | |
float32 | FLOAT | |
float64 | DOUBLE | |
boolean | BOOLEAN | |
timestamp | INT64 (millis) | ms Unix, UTC |
Stack
Solo 7 paquetes npm. Sin complementos nativos: todo se ejecuta en WASM o JS puro.
| Paquete | Qué hace |
|---|---|
hono + @hono/node-server | Enrutador HTTP: API rápida y estándar web |
@duckdb/duckdb-wasm | Motor de consultas SQL, se ejecuta completamente en WASM |
sql.js | SQLite en WASM: almacena metadatos de esquema |
tiny-parquet | Escritor Parquet en JS puro, cero dependencias nativas |
@modelcontextprotocol/sdk | Registro de servidor/herramientas MCP |
zod | Validación de esquema para entradas de API |
Runtime: Node.js ≥ 20
Como todo se ejecuta en WASM, no hay compilaciones de C++, no hay node-gyp, no hay binarios específicos de plataforma. La imagen de Docker funciona en cualquier arquitectura que Docker soporte.
Desarrollo
# Local dev (no Docker)
cd api && npm install && node index.js
cd dashboard && npm install && npm run dev
# Full stack
docker compose up --build
Licencia
Apache 2.0
Construido con tiny-parquet · Impulsado por DuckDB · structured.sh