Codync
A free, open-source 1:1 alternative to Grok Bot and Muse. Message Claude Code, Codex, Cursor and 40+ coding agents as persistent bots from your iPhone, Mac or Linux desktop. Group chats, threads, appr
Servidor MCP remoto sobre la API V8 de SinergiaCRM
Translation pending
Developer toolsTypeScript · JavaScriptGitHub @mcmespanaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Desarrollo de un sencillo servidor MCP para Sinergia CRM. Permite que agentes de IA interactúen con el CRM a través de la API de Suite CRM
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Servidor MCP remoto (Streamable HTTP), en Next.js, que expone la API V8 de SinergiaCRM (SuiteCRM 7.x) como herramientas para agentes de IA (Claude, ChatGPT, o cualquier otro cliente compatible con el protocolo MCP).
Las herramientas son genéricas: valen para cualquier módulo del CRM, incluidos los custom (Contactos, Proyectos, Atenciones, Valoraciones…), sin tocar código.
Además incluye el manual oficial de SinergiaCRM y de SinergiaDA (la wiki de SinergiaTIC), para que el agente pueda consultar cómo funciona algo antes de explicarlo.
⚠️ SinergiaCRM va sobre SuiteCRM 7.x, no 8.x.
| Tool | Para qué |
|---|---|
crm_fields_guide | La chuleta de tu CRM (CAMPOS-RESUMEN.md): qué campos usar en cada módulo, las claves de los desplegables (que la API ni devuelve ni valida) y las trampas. Entera o solo los módulos que se pidan |
El servidor le dice al agente, al conectar, que empiece por aquí: le ahorra la ronda de get_module_fields de
cada conversación. La guía es de cada organización y se lee de CRM_FIELDS_GUIDE_URL; para crear la tuya hay un
generador (npm run guide:generate). Todo en guia/README.md.
| Tool | Endpoint V8 | Para qué |
|---|---|---|
get_available_modules | GET /Api/V8/meta/modules | Módulos accesibles, con etiqueta y ACLs |
get_module_fields | GET /Api/V8/meta/fields/{module} | Campos de un módulo (tipo, obligatorio, si es custom) |
get_entry | GET /Api/V8/module/{module}/{id} | Un registro por id |
get_entry_list | GET /Api/V8/module/{module} | Listado con filtros, orden y paginación |
get_field_full | GET /Api/V8/module/{module}/{id}?fields[…] | Un campo de un registro entero (sin recortar), en trozos |
get_relationships | GET /Api/V8/module/{module}/{id}/relationships/{rel} | Registros relacionados |
ALLOW_WRITES=true, ver más abajo)| Tool | Endpoint V8 |
|---|---|
create_entry | POST /Api/V8/module |
update_entry | PATCH /Api/V8/module |
set_relationship | POST /Api/V8/module/{module}/{id}/relationships/{rel} |
| Tool | Para qué |
|---|---|
docs_index | Índice del Manual de SinergiaCRM y del Manual de SinergiaDA, por bloques |
docs_search | Búsqueda de texto completo en los dos manuales; devuelve secciones con fragmento y URL |
docs_read | Lee una página (o solo una sección) en castellano, convertida a markdown |
Cómo funciona:
/es; si no existe, la original.docs_read siempre lee la página en vivo.Estado: GET /api/docs-health (público, no expone nada del CRM) dice si
el despliegue llega a la wiki: 200 si va bien, 503 si no. Para probarlo
desde tu máquina: npm run test:docs -- "texto a buscar".
CRM_BBDD_*)| Tool | Para qué |
|---|---|
sql_schema | Lista las tablas de la BBDD del CRM, o describe una (columnas, índices y su tabla _cstm) |
sql_query | Ejecuta una consulta de lectura (SELECT, WITH, SHOW, DESCRIBE, EXPLAIN) directamente contra MySQL |
sql_read_field | Una columna de una fila entera (sin recortar), en trozos; busca sola en <tabla>_cstm |
Sirve para probar las queries que usan otras herramientas (área privada, Comunica Tools…) y para cruces y agregados que la API V8 no permite. La escritura sigue yendo siempre por la API. Barreras:
INSERT,
UPDATE, INTO OUTFILE, FOR UPDATE, varias sentencias…).READ ONLY, con un tope de 25 s y de
500 filas devueltas (100 por defecto). Los textos largos se truncan.Sin CRM_BBDD_IP, CRM_BBDD_USER y CRM_BBDD_PASS estas tools ni se registran.
Ojo: si el servidor MySQL filtra por IP, tiene que aceptar las de Vercel.
Para probar desde tu máquina: npm run test:db -- "SELECT COUNT(*) FROM contacts".
Los listados recortan cada texto a 800 caracteres para no llenar el contexto
del modelo, y lo marcan con [truncado, N caracteres: léelo entero con …].
Para leer un valor entero están get_field_full (API) y sql_read_field (SQL):
max_chars). Si
queda más, la respuesta trae siguiente_offset y el modelo vuelve a llamar
con ese offset hasta tenerlo todo.format: "text" convierten el HTML a texto plano (sin estilos ni
etiquetas, enlaces como texto (url)), que en un email suele ocupar una
fracción del original.Emails, campo description_html; o por
SQL, tabla emails_text, id_column: "email_id".No existe ninguna tool de borrado, no hay tanta necesidad de arriesgar a que un agente se ponga a borrar cositas :)
Los filtros son estructurados. El modelo envía algo así:
{
"module": "Contacts",
"filter": {
"operator": "and",
"conditions": [
{ "field": "last_name", "operator": "like", "value": "%García%" },
{ "field": "date_entered", "operator": "gte", "value": "2024-01-01" }
]
},
"sort": "-date_entered",
"page": 1,
"page_size": 20,
"fields": ["name", "email1"]
}
y el servidor lo traduce a la query real de la API V8. Operadores admitidos:
eq, neq, gt, gte, lt, lte, like (comparación) y and/or
(lógico). Nombres de módulo y campo se validan contra
^[A-Za-z][A-Za-z0-9_]{0,63}$ antes de salir a la red, y page_size está
topado a 50 para no reventar el contexto del modelo.
openssl rand -hex 32 o con un generador
web como este.
Guárdalo: será tu variable de entorno SINERGIA_CLIENT_SECRET.SINERGIA_CLIENT_ID.
El nombre que has puesto antes es el nombre del registro es solo para identificarlo a simple vista, no
se usa para nada más.Requisito de sistemas (se hace una vez por instalación): la API V8 necesita
las claves RSA en Api/V8/OAuth2/ (private.key en 600, public.key en
644). Si faltan, /Api/access_token responde 500.
(no parece un problema en SinergiaCRM)
Puedes crear un usuario dedicado en el CRM y asignarle el rol que consideres
MCP - solo lectura): para cada módulo que quieras exponer, Ver y
Listar = Sí; Editar, Eliminar, Importar, Exportar = No. Los
módulos que no quieras exponer, Acceso = Deshabilitado.Puedes usar el rol es la barrera de seguridad por encima de el ALLOW_WRITES
¿Por qué 4 variables (client id/secret y usuario/contraseña) y no solo el cliente OAuth2? Porque el grant
passwordde OAuth2 identifica dos cosas a la vez: la aplicación que llama (el cliente) y la persona en cuyo nombre actúa (el usuario del CRM). Sin usuario, el CRM no sabe qué rol y qué permisos aplicar a cada llamada, y las acciones no quedarían a nombre de nadie auditable. Si prefieres no crear un usuario dedicado, puedes usarclient_credentialsen su lugar (dejaSINERGIA_USERNAMEySINERGIA_PASSWORDvacíos: el servidor lo detecta solo), que es el grant de los ejemplos oficiales de SinergiaTIC. Conpasswordes más fácil auditar quién hizo qué en el CRM.
cp .env.example .env.local
| Variable | Obligatoria | Descripción |
|---|---|---|
SINERGIA_URL | Sí | URL base de la instancia, sin /Api y sin /legacy |
SINERGIA_CLIENT_ID | Sí | Campo "ID" del cliente OAuth2 (paso 1.1) |
SINERGIA_CLIENT_SECRET | Sí | El secreto que pegaste en "cambiar la clave" (paso 1.1) |
SINERGIA_USERNAME | Con grant password | Usuario API dedicado (paso 1.2) |
SINERGIA_PASSWORD | Con grant password | Su contraseña |
MCP_AUTH_SECRET | Sí | Secreto propio de este servidor. Genéralo igual que el anterior, con openssl rand -hex 32 o el generador web |
ALLOW_WRITES | No | false por defecto; true registra las tools de escritura |
PUBLIC_BASE_URL | No | Solo con dominio propio, para fijar la URL que anuncia el descubrimiento OAuth |
CRM_BBDD_IP / CRM_BBDD_PORT / CRM_BBDD_USER / CRM_BBDD_PASS | No | BBDD MySQL de solo lectura del CRM; activan sql_schema y sql_query |
CRM_BBDD_NAME | No | Base de datos a usar; si falta, la única que vea el usuario |
CRM_BBDD_SSL | No | true si el servidor MySQL exige TLS |
WIKI_URL | No | URL de la wiki de documentación. Por defecto https://wiki.sinergiatic.org |
CRM_FIELDS_GUIDE_URL | No | URL del CAMPOS-RESUMEN.md de tu organización, para crm_fields_guide (guia/README.md) |
MCP_AUTH_SECRET es distinto del cliente OAuth2 del CRM: es la clave de
entrada a este servidor MCP, y hace tres papeles — bearer estático, clave
de la pantalla de consentimiento OAuth, y semilla para firmar los tokens que
emite este mismo servidor. Cambiarla invalida todas las sesiones de golpe (a
propósito, es la forma de revocar acceso).
Nunca se hardcodean credenciales en el código; .env* está en .gitignore.
npm install
npm run test:auth # login OAuth2 + listado de módulos, no escribe nada
npm run dev # http://localhost:3000/api/mcp
npm run test:auth valida URL, cliente OAuth2, usuario y permisos del rol, e
imprime los primeros módulos accesibles. Es el paso previo a cualquier
despliegue.
# Sin credenciales debe dar 401:
curl -i -X POST http://localhost:3000/api/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
# Con el secreto, para ver las tools registradas:
curl -s -X POST http://localhost:3000/api/mcp \
-H "Authorization: Bearer $MCP_AUTH_SECRET" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
Otros comandos: npm run typecheck, npm run build.
https://<tu-deploy>.vercel.app/api/mcp (o en el dominio que condifures)Notas sueltas:
El servidor acepta dos formas de autenticarse, ambas contra la misma
MCP_AUTH_SECRET. Cualquier cliente MCP genérico (Streamable HTTP) puede
usar la primera:
{
"mcpServers": {
"sinergiacrm": {
"url": "https://<tu-deploy>.vercel.app/api/mcp",
"headers": { "Authorization": "Bearer <MCP_AUTH_SECRET>" }
}
}
}
Esto vale para Claude Code, MCP Inspector, y cualquier agente que permita configurar cabeceras personalizadas en sus conectores MCP.
Claude Code:
claude mcp add --transport http sinergiacrm https://<tu-deploy>.vercel.app/api/mcp \
--header "Authorization: Bearer <MCP_AUTH_SECRET>"
Clientes que solo hablan stdio (vía mcp-remote):
{
"mcpServers": {
"sinergiacrm": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://<tu-deploy>.vercel.app/api/mcp",
"--header", "Authorization: Bearer <MCP_AUTH_SECRET>"
]
}
}
}
Claude en la web (claude.ai): no permite cabeceras personalizadas en conectores, solo OAuth — por eso el servidor incluye uno mínimo.
/api/mcp al
final. Deja Client ID y Client Secret vacíos: se registra solo.MCP_AUTH_SECRET.Si cambias MCP_AUTH_SECRET, todas las sesiones OAuth dejan de valer y hay
que reconectar (es la forma de revocar acceso de golpe).
Por defecto solo lectura, a propósito:
Editar solo en los módulos
que toque (el CRM manda, aunque el flag esté activo).ALLOW_WRITES=true en Vercel y redeploy.tools/list ahora incluye create_entry, update_entry y
set_relationship.Con ALLOW_WRITES=false esas tools ni se registran: el modelo no las ve,
no puede intentar llamarlas. Para volver atrás, ALLOW_WRITES=false y
redeploy.
app/
api/mcp/route.ts Endpoint MCP + verificación del token de entrada
oauth/register/ Dynamic Client Registration (RFC 7591)
oauth/authorize/ Pantalla de consentimiento + emisión del código (PKCE)
oauth/token/ Canje del código y refresco, con rotación
.well-known/… Metadatos RFC 9728 y RFC 8414
lib/
config.ts Lectura y validación de variables de entorno
mcp/auth.ts Comparación en tiempo constante contra MCP_AUTH_SECRET
mcp/tools.ts Definición y registro de las tools (flag de escritura)
mcp/docs-tools.ts Tools de documentación (docs_index, docs_search, docs_read)
mcp/sql-tools.ts Tools SQL de solo lectura (sql_schema, sql_query)
mcp/guide-tools.ts Tool crm_fields_guide
fields-guide.ts Lectura, caché y troceado por módulo de la guía de campos
db/client.ts Conexión MySQL, validación de solo lectura y límites
field-chunk.ts Lectura troceada de campos largos y HTML → texto
oauth/tokens.ts Firma y verificación HMAC de los artefactos OAuth
sinergia/auth.ts OAuth2 contra el CRM: login, refresh y caché de token
sinergia/client.ts Cliente HTTP de la API V8, errores y reintento en 401
sinergia/query.ts Filtros estructurados, orden, paginación, validaciones
sinergia/flatten.ts Aplanado de JSON:API a respuestas compactas
wiki/client.ts Cliente de la API MediaWiki y errores de "wiki no accesible"
wiki/wikitext.ts Índice del manual, wikitexto → markdown, secciones
wiki/manual.ts Descarga por lotes, caché, búsqueda y lectura de páginas
app/api/docs-health/ Estado de la conexión con la wiki
scripts/test-auth.ts Smoke test de credenciales
scripts/test-docs.ts Smoke test de la documentación
scripts/test-db.ts Smoke test de la conexión a la BBDD
scripts/test-guide.ts Comprueba la guía de campos (fuente, módulos, forma)
scripts/generate-fields-guide.ts Borrador de la guía desde la API V8
guia/README.md Cómo montar la guía de campos de tu organización
Referencias usadas: SuiteCRM V8 JSON:API y SinergiaTIC/SinergiaCRM-API-Examples.
| Síntoma | Causa probable |
|---|---|
404 al pedir el token | SINERGIA_URL mal puesta, o incluye /legacy o /Api |
Respuesta no-JSON de …/Api/access_token | La URL apunta a la interfaz web, no a la API; o la API V8 no está habilitada |
500 al pedir el token | Faltan o tienen mal los permisos las claves RSA de Api/V8/OAuth2/ |
invalid_client | Client ID/Secret mal, o el cliente OAuth2 tiene otro grant configurado |
invalid_credentials | Usuario o contraseña incorrectos, o usuario desactivado |
Todas las peticiones MCP dan 401 | Falta MCP_AUTH_SECRET, o el cliente manda otro valor |
Filter field X in Y module is not found | El campo no existe en ese módulo: compruébalo con get_module_fields |
| No aparecen las tools de escritura | ALLOW_WRITES no es true, o falta el redeploy |
Un módulo no aparece en get_available_modules | El rol del usuario API no le da acceso |
| Claude web: "No se pudo conectar" | La URL no acaba en /api/mcp, o pusiste algo en Client ID/Secret |
| Claude web: "Cliente no válido" al autorizar | Cambiaste MCP_AUTH_SECRET: borra el conector y vuelve a añadirlo |
docs_*: "No se puede acceder a la wiki de SinergiaTIC" | La wiki está caída o bloquea las IPs de Vercel. Mira /api/docs-health |
docs_search no encuentra algo que está en la wiki | Solo busca en las páginas enlazadas desde los dos índices del manual; prueba docs_read con el título |
| Claude web pide la clave una y otra vez | La clave tecleada no coincide con MCP_AUTH_SECRET (ojo a espacios al copiar) |
Movimiento Consolación para el Mundo · Equipo de desarrollo · [email protected]
Primera versión / prototipo, hecho para explorar cómo montar un servidor MCP sobre la API de SuiteCRM/SinergiaCRM. No es un producto cerrado ni la única forma de hacerlo — revísalo, adáptalo y rómpelo si hace falta.