SynaMCPs
Proporciona una puerta de enlace universal para herramientas de IA corporativas, ofreciendo almacenamiento de conocimiento, control de acceso y proxy de fuentes de IA externas.
Documentación
Synamcps (SynaMCPs) — Puerta de enlace de MCP + Almacenamiento de Conocimiento
Synamcps es un servidor que proporciona:
- Un servidor MCP para clientes LLM (Cursor / Claude Desktop / Claude Code / etc.)
- Una API HTTP para crear/buscar/leer elementos de conocimiento
- Una interfaz de administración web para gestionar usuarios/grupos/almacenamientos/tokens, ver estado y realizar diagnósticos básicos
- Acceso a almacenamientos basado en tokens (los tokens solo restringen permisos), con ACL/RBAC, limitación de velocidad y uso/métricas
El servidor admite múltiples métodos de autenticación (OIDC/Keycloak/Google/Teleport Proxy JWT) y un inicio de sesión interno para la interfaz de administración.
Inicio rápido (Docker Compose)
Requisitos:
- Docker + Docker Compose
Ejecutar:
cp .env.example .env
make compose-up
Abrir:
- Inicio de sesión:
http://localhost:8080/login - Interfaz de administración:
http://localhost:8080/admin - Aplicación de usuario:
http://localhost:8080/app - Endpoint MCP (transmisible):
http://localhost:8080/mcp - API HTTP:
http://localhost:8080/api/*
Detener:
make compose-down
Ejecución local (sin Docker)
Requisitos:
- Go 1.23+
- Postgres, Redis, S3/MinIO disponibles (o usar Docker Compose como infraestructura)
export CONFIG_PATH=configs/config.local.yaml
go run ./cmd/server
Características (nivel alto)
Almacenamientos, ACL y tokens
- Almacenamiento es una entidad lógica vinculada a:
- registros en el catálogo de metadatos (Postgres)
- un prefijo S3 (
storage.S3Prefix) - alcance de búsqueda (backend vectorial: pgvector/qdrant)
- Vinculaciones ACL definen el acceso de usuario/grupo a un almacenamiento (lectura/escritura/administración/propietario).
- Tokens de acceso:
- pertenecen a un usuario (propietario)
- no amplían permisos — solo restringen el acceso del propietario (intersección de ACL de usuario y alcances del token)
- pueden restringir:
storageIds,maxMode(lectura/lectura_escritura),toolAllowlist, límites de velocidad.
- Visibilidad de documentos (
personal/group/public) se aplica además del acceso al almacenamiento: poder leer un almacenamiento es necesario pero no suficiente — un documentopersonales visible solo para su propietario, un documentogroupsolo para su propietario o miembros de sus grupos.
Elementos de conocimiento
- Puede agregar un elemento como:
- Texto (mediante el estándar
POST /api/knowledge) - Archivo (subir → contenido sin procesar almacenado en S3 → extracción de mejor esfuerzo → resumen+incrustaciones → elemento guardado en el almacenamiento)
- Enlace (descargar → contenido sin procesar almacenado en S3 → extracción → resumen+incrustaciones → elemento guardado en el almacenamiento)
- Texto (mediante el estándar
- La ingesta de enlaces solo acepta URLs
http/httpsy se niega a obtener direcciones internas (loopback, link-local incl. metadatos de nube169.254.169.254, y rangos privados) — protección SSRF que también se aplica a través de redirecciones.
MCP
- MCP expone un
tools/listdinámico basado en el token portador (solo se muestran herramientas/almacenamientos permitidos). - Admite transporte HTTP transmisible (
/mcp) y SSE heredado opcional. - Los nombres de herramientas MCP usan
_(guion bajo) para evitar filtrados/avisos en algunos clientes. - Proxy MCP: registre servidores MCP HTTP/SSE ascendentes en la interfaz de administración (pestaña
MCP Servers), descubra herramientas/recursos/indicaciones, restrinja por ACL y alcances por token. Identificadores proxy:- herramientas/indicaciones:
{slug}__{upstream_name} - recursos:
syna-mcp/{slug}/{upstream_uri} - el
slugse genera automáticamente a partir del nombre del servidor (sin entrada manual).
- herramientas/indicaciones:
- Los secretos de autenticación ascendentes se almacenan cifrados en Postgres (
MCP_PROXY_SECRETS_KEYen.env).
Uso / Límite de velocidad / Métricas
- Limitación de velocidad por token (minuto/hora/día + ráfaga), aplicada tanto para llamadas MCP como para la API REST (
429 Too Many Requestscuando se excede). - Los cuerpos de solicitud están limitados por
limits.max_upload_bytes(413cuando se excede). - Los eventos de uso (y estado/errores) pueden escribirse en Redis TimeSeries (cuando está habilitado).
/metricsexpone métricas en formato Prometheus (los valores de etiquetas se sanean y la cardinalidad de series está limitada).
Interfaz de administración web
La interfaz de administración integrada (HTML renderizado en servidor) le permite:
- Usuarios / Grupos / Miembros de grupo
- Almacenamientos + detalles de almacenamiento (ACL, claves/tokens, lista de elementos)
- Tokens + asistente de conexión MCP + eliminar
- Agregar elemento (Texto/Archivo/Enlace)
- Búsqueda (por token / por almacenamiento)
- Estado (Postgres/Redis/S3/LLMs + contadores de errores)
Los formularios seleccionan entidades de listas desplegables de nombres (almacenamientos/grupos/usuarios/tokens/servidores MCP) con botones de actualización en lugar de escribir IDs sin procesar. Los slugs ya no se ingresan manualmente — un almacenamiento establece su slug por defecto a su id y un servidor MCP deriva un slug único de su nombre.
Configuración
Configuración predeterminada: configs/config.example.yaml
Anulación: CONFIG_PATH=/path/to/config.yaml
Secciones clave:
web.default_admin: nombre de usuario/contraseña de la interfaz de administración (contraseña mediante referencia de entorno)oauth.providers: proveedores OIDC (emisor/audiencia/jwks_url; client_id opcional para inicio de sesión AS)oauth_as: servidor de autorización MCP OAuth 2.1 para conectores de Claude Desktopteleport: Teleport Proxy JWT (emisor/audiencia)redis: sesiones + uso/series temporales (cuando está habilitado)s3: endpoint/contenedor + umbral de documentos grandesembedding,summarization: LLMs (proveedor/modelo/api/api_key_env_ref)vector_backend.active:pgvectoroqdrantmetadata_catalog.dsn: DSN de Postgresapi.allowed_origins: lista de permitidos CORS estrictausage: contabilidad y series temporales, retención, exportadores
Ejemplo .env para desarrollo local: .env.example.
API HTTP
Autenticación:
- cookieAuth: sesión de interfaz web (cookie
session_id) - bearerAuth:
Authorization: Bearer <token>
Códigos de error comunes:
401— token/sesión faltante403— prohibido (permisos insuficientes para el almacenamiento/operación)404— no encontrado413— el cuerpo de la solicitud excedelimits.max_upload_bytes422— solicitud no válida429— límite de velocidad excedido (límites por token)
API de conocimiento
GET /api/knowledge
Listar elementos con paginación y filtros.
Parámetros de consulta:
page(int)pageSize(int)storageId(cadena) — limitar a un almacenamiento específicosource(cadena) — coincidencia exactasourceUrl(cadena)sourceUrlMode(exact|partial) —partialfunciona solo cuandosearch.filters.source_url.allow_partial_match=true
Respuesta: models.PaginatedKnowledgeList (elementos + total + hasNext + página/tamañoDePágina).
POST /api/knowledge
Crear un elemento a partir de texto.
Cuerpo:
{
"storageId": "storage-id-optional",
"title": "Runbook",
"text": "Long knowledge text...",
"mimeType": "text/plain",
"visibility": "personal",
"groupIds": [],
"source": "api",
"sourceUrl": "https://docs.example.com/runbook"
}
Notas:
- si
storageIdestá vacío y el servicio de acceso está habilitado, el servidor usa/crea el almacenamiento personal del usuario visibilitypor defecto espersonalgroupIdsdebe ser una matriz (nonull)
GET /api/knowledge/{docId}
Devolver un documento individual.
DELETE /api/knowledge/{docId}
Eliminar un documento (y las incrustaciones asociadas en el almacén vectorial).
POST /api/knowledge/search
Búsqueda basada en incrustaciones.
Cuerpo:
{
"query": "kubernetes ingress timeout",
"topK": 10,
"filters": {
"storageId": "storage-id-optional",
"source": "api",
"sourceUrl": "https://...",
"sourceUrlMode": "exact"
}
}
Respuesta: una matriz de resultados de búsqueda (incluyendo fragmento/título/fuente/URL de fuente).
API de ingesta (Archivo/Enlace)
POST /api/knowledge/ingest/file (multipart)
Subir un archivo como elemento:
- el contenido sin procesar se almacena en S3
- se realiza extracción de texto de mejor esfuerzo
- el proceso produce resumen + incrustaciones
- el resultado final se guarda como un elemento de conocimiento normal
Campos multipart:
storageId(cadena, opcional)title(cadena, opcional)visibility(personal|group|public, opcional)source(cadena, opcional)sourceUrl(cadena, opcional)mimeType(cadena, opcional)file(obligatorio)
Ejemplo:
curl -X POST http://localhost:8080/api/knowledge/ingest/file \
-H "Authorization: Bearer $TOKEN" \
-F "storageId=..." \
-F "title=Spec" \
-F "visibility=personal" \
-F "file=@./spec.txt"
POST /api/knowledge/ingest/link (json)
Descarga una URL, almacena el contenido sin procesar en S3, extrae texto y guarda un elemento.
Cuerpo:
{
"storageId": "storage-id-optional",
"title": "Optional title",
"url": "https://example.com/docs",
"visibility": "personal",
"source": "link"
}
API de administración (/api/admin/*)
Todos los endpoints requieren autenticación (cookie o bearer), y muchos requieren platform_admin.
Usuarios
GET /api/admin/meGET /api/admin/users(platform_admin)POST /api/admin/users(platform_admin)GET /api/admin/users/{id}(admin o el propio usuario)PATCH /api/admin/users/{id}(admin o el propio usuario)POST /api/admin/users/{id}/password(admin o el propio usuario)DELETE /api/admin/users/{id}(platform_admin)
Grupos
GET /api/admin/groups(platform_admin)POST /api/admin/groups(platform_admin)DELETE /api/admin/groups/{id}(platform_admin)GET /api/admin/groups/{id}/members(platform_admin)PUT /api/admin/groups/{id}/members/{userId}(platform_admin)DELETE /api/admin/groups/{id}/members/{userId}(platform_admin)
Almacenamientos
GET /api/admin/storages(almacenamientos disponibles para el usuario actual)POST /api/admin/storagesDELETE /api/admin/storages/{id}(requierestorage.delete: propietario/administrador del almacenamiento o platform_admin)GET /api/admin/storages/{id}(detalles del almacenamiento: almacenamiento + acl + tokens; requiere acceso de lectura)GET /api/admin/storages/{id}/acl(requiereacl.manage)PUT /api/admin/storages/{id}/acl(requiereacl.manage)
Tokens
Los endpoints de mutación de tokens requieren que el llamante sea el propietario del token o platform_admin; GET /api/admin/tokens lista solo los tokens del propio llamante (platform_admin ve todos).
GET /api/admin/tokensPOST /api/admin/tokensDELETE /api/admin/tokens/{id}(propietario/platform_admin)PATCH /api/admin/tokens/{id}/rate-limit(propietario/platform_admin)POST /api/admin/tokens/{id}/revoke(propietario/platform_admin)POST /api/admin/tokens/{id}/rotate(propietario/platform_admin)PATCH /api/admin/tokens/{id}/mcp-scopes(propietario/platform_admin)GET/POST /api/admin/tokens/{id}/connect-options(asistente para clientes MCP)
Uso / Estado
GET /api/admin/usage/seriesGET /api/admin/usage/summaryGET /api/admin/status— estado de componentes + contadores de errores (Redis TimeSeries)
MCP
Transporte
- HTTP transmisible:
POST /mcp(JSON-RPC) +GET /mcp(flujo SSE porMcp-Session-Id) - SSE heredado (si está habilitado):
/sse+/messages
Flujo mínimo (transmisible)
- Obtener un token portador (OIDC/Teleport o interno)
initialize:
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"initialize","params":{}}'
- Guardar
Mcp-Session-Idde los encabezados/respuesta (los clientes lo hacen automáticamente) - Abrir el flujo (el token portador es obligatorio y debe coincidir con el propietario de la sesión):
curl -N http://localhost:8080/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Mcp-Session-Id: <session_id>"
GET /mcp y DELETE /mcp están autenticados; una sesión solo puede ser leída/cerrada por el principal que la creó.
Herramientas dinámicas/lista
tools/list devuelve solo las herramientas permitidas por el token portador actual y sus alcances de almacenamiento.
herramientas/llamada
tools/call enruta llamadas a los métodos internos correspondientes (knowledge_* etc.).
Conexión MCP (interfaz web)
La interfaz de administración (/admin) incluye una página de Conexión MCP que genera:
- nombre de archivo de configuración
configBody(JSON)- instrucciones paso a paso
Puede copiar configBody haciendo clic en él.
Instalación y operaciones
Docker Compose
cp .env.example .env
make compose-up
Útil:
make compose-downmake seed-dev(si se usa en su entorno)
Configuración y secretos
configs/config.example.yaml— configuración de ejemploconfigs/config.local.yaml— configuración de compose local (usada endocker-compose.yml).env— secretos (contraseñas/claves), ejemplo en.env.example
CORS
api.allowed_origins es una lista de permitidos estricta. Los orígenes desconocidos se rechazan.
Las rutas de la interfaz web (/, /login, /logout, /app*, /admin*) omiten la verificación de origen.
Solución de problemas
- Origen no permitido:
- agregue el origen a
api.allowed_origins - asegúrese de abrir la interfaz web mediante
/login(las rutas web omiten la verificación de origen)
- agregue el origen a
- Credenciales no válidas:
- verifique que
.envesté cargado (en compose está conectado medianteenv_file: .env) - verifique
web.default_admin.password_env_ref
- verifique que
- Las series temporales no se crean:
- necesita Redis con el módulo RedisTimeSeries (
TS.ADDdebe ser compatible) - o deshabilite
usage.redis_timeseries
- necesita Redis con el módulo RedisTimeSeries (
- Aviso de herramientas MCP "filtradas":
- los nombres de herramientas ya usan
_en lugar de.
- los nombres de herramientas ya usan
Documentación en este repositorio
docs/marketing.md— descripción general del producto + escenarios de RAG corporativo / proceso agénticodocs/setup.md— instalación y ejecucióndocs/api.md— endpoints básicos de conocimientodocs/mcp-connection.md— conexión MCPdocs/auth-setup.md— proveedores de autenticacióndocs/openapi.yaml— stub OpenAPI de referencia