SynaMCPs

Fornece um gateway universal para ferramentas de IA corporativas, oferecendo armazenamento de conhecimento, controle de acesso e proxy de fontes externas de IA.

Documentação

Synamcps (SynaMCPs) — Gateway MCP + Armazenamento de Conhecimento

Synamcps é um servidor que fornece:

  • Um servidor MCP para clientes de LLM (Cursor / Claude Desktop / Claude Code / etc.)
  • Uma API HTTP para criar/buscar/ler itens de conhecimento
  • Uma UI de Administração Web para gerenciar usuários/grupos/armazenamentos/tokens, visualizar status e fazer diagnósticos básicos
  • Acesso baseado em token aos armazenamentos (tokens apenas restringem permissões), com ACL/RBAC, limite de taxa e uso/métricas

O servidor suporta múltiplos métodos de autenticação (OIDC/Keycloak/Google/Teleport Proxy JWT) e um login interno para a UI de Administração.


Início rápido (Docker Compose)

Pré-requisitos:

  • Docker + Docker Compose

Execute:

cp .env.example .env
make compose-up

Abra:

  • Login: http://localhost:8080/login
  • UI de Administração: http://localhost:8080/admin
  • Aplicativo do usuário: http://localhost:8080/app
  • Endpoint MCP (streamable): http://localhost:8080/mcp
  • API HTTP: http://localhost:8080/api/*

Pare:

make compose-down

Execução local (sem Docker)

Pré-requisitos:

  • Go 1.23+
  • Postgres, Redis, S3/MinIO disponíveis (ou use Docker Compose como sua infraestrutura)
export CONFIG_PATH=configs/config.local.yaml
go run ./cmd/server

Recursos (visão geral)

Armazenamentos, ACL e tokens

  • Armazenamento é uma entidade lógica vinculada a:
    • registros no catálogo de metadados (Postgres)
    • um prefixo S3 (storage.S3Prefix)
    • escopo de busca (backend vetorial: pgvector/qdrant)
  • Vínculos de ACL definem acesso de usuário/grupo a um armazenamento (leitura/escrita/admin/proprietário).
  • Tokens de acesso:
    • pertencem a um usuário (proprietário)
    • não expandem permissões — eles restringem o acesso do proprietário (interseção entre a ACL do usuário e os escopos do token)
    • podem restringir: storageIds, maxMode (leitura/leitura_escrita), toolAllowlist, limites de taxa.
  • Visibilidade de documentos (personal/group/public) é aplicada além do acesso ao armazenamento: conseguir ler um armazenamento é necessário, mas não suficiente — um documento personal é visível apenas para seu proprietário, um documento group apenas para seu proprietário ou membros de seus grupos.

Itens de conhecimento

  • Você pode adicionar um item como:
    • Texto (via o padrão POST /api/knowledge)
    • Arquivo (upload → conteúdo bruto armazenado no S3 → extração com melhor esforço → resumo+embeddings → item salvo no armazenamento)
    • Link (download → conteúdo bruto armazenado no S3 → extração → resumo+embeddings → item salvo no armazenamento)
  • A ingestão de Link aceita apenas URLs http/https e recusa buscar endereços internos (loopback, link-local, incluindo metadados de nuvem 169.254.169.254, e faixas privadas) — proteção SSRF que também se aplica a redirecionamentos.

MCP

  • O MCP expõe um tools/list dinâmico baseado no token bearer (apenas ferramentas/armazenamentos permitidos são visíveis).
  • Suporta transporte Streamable HTTP (/mcp) e SSE legado opcional.
  • Os nomes das ferramentas MCP usam _ (underscore) para evitar filtragem/avisos em alguns clientes.
  • Proxy MCP: registre servidores MCP HTTP/SSE upstream na UI de Administração (aba MCP Servers), descubra ferramentas/recursos/prompts, restrinja por ACL e escopos por token. Identificadores com proxy:
    • ferramentas/prompts: {slug}__{upstream_name}
    • recursos: syna-mcp/{slug}/{upstream_uri}
    • o slug é gerado automaticamente a partir do nome do servidor (sem entrada manual).
  • Segredos de autenticação upstream são armazenados criptografados no Postgres (MCP_PROXY_SECRETS_KEY em .env).

Uso / Limite de taxa / Métricas

  • Limite de taxa por token (minuto/hora/dia + rajada), aplicado tanto para chamadas MCP quanto para a API REST (429 Too Many Requests quando excedido).
  • Corpos de requisição são limitados por limits.max_upload_bytes (413 quando excedido).
  • Eventos de uso (e status/erros) podem ser gravados no Redis TimeSeries (quando habilitado).
  • /metrics expõe métricas em formato Prometheus (valores de rótulos são sanitizados e a cardinalidade das séries é limitada).

UI de Administração Web

A UI de administração integrada (HTML renderizado no servidor) permite:

  • Usuários / Grupos / Membros de grupos
  • Armazenamentos + detalhes do armazenamento (ACL, chaves/tokens, lista de itens)
  • Tokens + assistente MCP Connect + exclusão
  • Adicionar item (Texto/Arquivo/Link)
  • Busca (por token / por armazenamento)
  • Status (Postgres/Redis/S3/LLMs + contadores de erros)

Os formulários selecionam entidades a partir de menus suspensos de nomes (armazenamentos/grupos/usuários/tokens/servidores MCP) com botões de atualização em vez de digitar IDs brutos. Slugs não são mais inseridos manualmente — um armazenamento usa seu id como slug padrão e um servidor MCP deriva um slug único a partir do seu nome.


Configuração

Config padrão: configs/config.example.yaml
Substituição: CONFIG_PATH=/path/to/config.yaml

Seções principais:

  • web.default_admin: nome de usuário/senha da UI de Administração (senha via referência de env)
  • oauth.providers: provedores OIDC (issuer/audience/jwks_url; client_id opcional para login AS)
  • oauth_as: Servidor de Autorização MCP OAuth 2.1 para Claude Desktop Connectors
  • teleport: Teleport Proxy JWT (issuer/audience)
  • redis: sessões + uso/séries temporais (quando habilitado)
  • s3: endpoint/bucket + limite de documentos grandes
  • embedding, summarization: LLMs (provider/model/api/api_key_env_ref)
  • vector_backend.active: pgvector ou qdrant
  • metadata_catalog.dsn: DSN do Postgres
  • api.allowed_origins: lista de permissão CORS estrita
  • usage: contabilidade e séries temporais, retenção, exportadores

Exemplo de .env para desenvolvimento local: .env.example.


API HTTP

Autenticação:

  • cookieAuth: sessão da UI web (cookie session_id)
  • bearerAuth: Authorization: Bearer <token>

Códigos de erro comuns:

  • 401 — token/sessão ausente
  • 403 — proibido (permissões insuficientes para o armazenamento/operação)
  • 404 — não encontrado
  • 413 — corpo da requisição excede limits.max_upload_bytes
  • 422 — requisição inválida
  • 429 — limite de taxa excedido (limites por token)

API de Conhecimento

GET /api/knowledge

Lista itens com paginação e filtros.

Parâmetros de consulta:

  • page (int)
  • pageSize (int)
  • storageId (string) — limitar a um armazenamento específico
  • source (string) — correspondência exata
  • sourceUrl (string)
  • sourceUrlMode (exact | partial) — partial funciona apenas quando search.filters.source_url.allow_partial_match=true

Resposta: models.PaginatedKnowledgeList (itens + total + hasNext + page/pageSize).

POST /api/knowledge

Cria um item a partir de texto.

Corpo:

{
  "storageId": "storage-id-optional",
  "title": "Runbook",
  "text": "Long knowledge text...",
  "mimeType": "text/plain",
  "visibility": "personal",
  "groupIds": [],
  "source": "api",
  "sourceUrl": "https://docs.example.com/runbook"
}

Observações:

  • se storageId estiver vazio e o serviço de acesso estiver habilitado, o servidor usa/cria o armazenamento pessoal do usuário
  • visibility assume o padrão personal
  • groupIds deve ser um array (não null)

GET /api/knowledge/{docId}

Retorna um único documento.

DELETE /api/knowledge/{docId}

Exclui um documento (e os embeddings associados no armazenamento vetorial).

POST /api/knowledge/search

Busca baseada em embeddings.

Corpo:

{
  "query": "kubernetes ingress timeout",
  "topK": 10,
  "filters": {
    "storageId": "storage-id-optional",
    "source": "api",
    "sourceUrl": "https://...",
    "sourceUrlMode": "exact"
  }
}

Resposta: um array de resultados de busca (incluindo snippet/título/fonte/sourceUrl).

API de Ingestão (Arquivo/Link)

POST /api/knowledge/ingest/file (multipart)

Envia um arquivo como item:

  • o conteúdo bruto é armazenado no S3
  • a extração de texto com melhor esforço é realizada
  • o pipeline produz resumo + embeddings
  • o resultado final é salvo como um item de conhecimento normal

Campos multipart:

  • storageId (string, opcional)
  • title (string, opcional)
  • visibility (personal|group|public, opcional)
  • source (string, opcional)
  • sourceUrl (string, opcional)
  • mimeType (string, opcional)
  • file (obrigatório)

Exemplo:

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)

Baixa uma URL, armazena o conteúdo bruto no S3, extrai texto e salva um item.

Corpo:

{
  "storageId": "storage-id-optional",
  "title": "Optional title",
  "url": "https://example.com/docs",
  "visibility": "personal",
  "source": "link"
}

API de Administração (/api/admin/*)

Todos os endpoints exigem autenticação (cookie ou bearer), e muitos exigem platform_admin.

Usuários

  • GET /api/admin/me
  • GET /api/admin/users (platform_admin)
  • POST /api/admin/users (platform_admin)
  • GET /api/admin/users/{id} (admin ou o próprio usuário)
  • PATCH /api/admin/users/{id} (admin ou o próprio usuário)
  • POST /api/admin/users/{id}/password (admin ou o próprio usuário)
  • 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)

Armazenamentos

  • GET /api/admin/storages (armazenamentos disponíveis para o usuário atual)
  • POST /api/admin/storages
  • DELETE /api/admin/storages/{id} (exige storage.delete: proprietário/admin do armazenamento ou platform_admin)
  • GET /api/admin/storages/{id} (detalhes do armazenamento: storage + acl + tokens; exige acesso de leitura)
  • GET /api/admin/storages/{id}/acl (exige acl.manage)
  • PUT /api/admin/storages/{id}/acl (exige acl.manage)

Tokens

Endpoints de mutação de tokens exigem que o chamador seja o proprietário do token ou platform_admin; GET /api/admin/tokens lista apenas os tokens do próprio chamador (platform_admin vê todos).

  • GET /api/admin/tokens
  • POST /api/admin/tokens
  • DELETE /api/admin/tokens/{id} (proprietário/platform_admin)
  • PATCH /api/admin/tokens/{id}/rate-limit (proprietário/platform_admin)
  • POST /api/admin/tokens/{id}/revoke (proprietário/platform_admin)
  • POST /api/admin/tokens/{id}/rotate (proprietário/platform_admin)
  • PATCH /api/admin/tokens/{id}/mcp-scopes (proprietário/platform_admin)
  • GET/POST /api/admin/tokens/{id}/connect-options (assistente para clientes MCP)

Uso / Status

  • GET /api/admin/usage/series
  • GET /api/admin/usage/summary
  • GET /api/admin/status — status dos componentes + contadores de erros (Redis TimeSeries)

MCP

Transporte

  • Streamable HTTP: POST /mcp (JSON-RPC) + GET /mcp (stream SSE via Mcp-Session-Id)
  • SSE legado (se habilitado): /sse + /messages

Fluxo mínimo (streamable)

  1. Obtenha um token bearer (OIDC/Teleport ou interno)
  2. 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":{}}'
  1. Salve Mcp-Session-Id dos cabeçalhos/resposta (os clientes fazem isso automaticamente)
  2. Abra o stream (o token bearer é obrigatório e deve corresponder ao proprietário da sessão):
curl -N http://localhost:8080/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "Mcp-Session-Id: <session_id>"

GET /mcp e DELETE /mcp são autenticados; uma sessão só pode ser lida/encerrada pelo principal que a criou.

tools/list dinâmico

tools/list retorna apenas as ferramentas permitidas pelo token bearer atual e seus escopos de armazenamento.

tools/call

tools/call roteia chamadas para os métodos internos correspondentes (knowledge_* etc.).

MCP Connect (UI Web)

A UI de Administração (/admin) inclui uma página MCP Connect que gera:

  • nome do arquivo de configuração
  • configBody (JSON)
  • instruções passo a passo

Você pode copiar configBody clicando nele.


Instalação e operações

Docker Compose

cp .env.example .env
make compose-up

Útil:

  • make compose-down
  • make seed-dev (se usado no seu ambiente)

Configuração e segredos

  • configs/config.example.yaml — exemplo de configuração
  • configs/config.local.yaml — configuração compose local (usada em docker-compose.yml)
  • .env — segredos (senhas/chaves), exemplo em .env.example

CORS

api.allowed_origins é uma lista de permissão estrita. Origens desconhecidas são rejeitadas. As rotas da UI Web (/, /login, /logout, /app*, /admin*) ignoram a verificação de origem.


Solução de problemas

  • Origem não permitida:
    • adicione a origem a api.allowed_origins
    • certifique-se de abrir a UI Web via /login (rotas web ignoram a verificação de origem)
  • Credenciais inválidas:
    • verifique se .env está carregado (no compose ele é conectado via env_file: .env)
    • verifique web.default_admin.password_env_ref
  • Séries temporais não são criadas:
    • você precisa de Redis com o módulo RedisTimeSeries (TS.ADD deve ser suportado)
    • ou desabilite usage.redis_timeseries
  • Aviso de ferramentas MCP "filtradas":
    • os nomes das ferramentas já usam _ em vez de .

Documentação neste repositório

  • docs/marketing.md — visão geral do produto + cenários de RAG corporativo / pipeline agêntico
  • docs/setup.md — instalação e execução
  • docs/api.md — endpoints básicos de conhecimento
  • docs/mcp-connection.md — conexão MCP
  • docs/auth-setup.md — provedores de autenticação
  • docs/openapi.yaml — stub OpenAPI de base