MCP Marketplace
Il MCP Marketplace permette di installare, abilitare e scoping MCP server esterni da un catalogo curato. Gli MCP server espongono tool che l'agente ReAct può invocare durante le chat, estendendo le capacità senza codice.
Il MCP Marketplace permette di installare, abilitare e scoping MCP server esterni da un catalogo curato. Gli MCP server espongono tool che l'agente ReAct può invocare durante le chat, estendendo le capacità senza codice.
MCP bidirezionale (F-MCP-1): oltre alla direzione connect (MCP esterni connesi come skill), la direzione expose è built-in: l'MCP server interno espone la ricerca RAG a client esterni.
Il sistema spanna: modelli Prisma (McpCatalogEntry, MCPConnection, ChatMCPPin), route (marketplace.ts, mcp.ts), agent skill registry (skills.ts), servizi (health-check scheduler, DLP filter, audit logging).
Lato expose (built-in)
packages/server/src/agent/mcpServer.ts espone la RAG via Model Context Protocol a client esterni (Cursor, VS Code e IDE): tool rag_query (ricerca documenti workspace) e list_workspaces. Transport: Streamable HTTP stateless primario (POST /api/mcp/mcp, core MCP 2026-07-28, risposte JSON-only) + SSE legacy (GET /api/mcp/sse + POST /api/mcp/message, deprecato in v2 con grace di un anno). Auth: con MCP_API_KEY impostata → Authorization: Bearer <MCP_API_KEY> (401 su miss/wrong); unset → fallback loopback-only (127.0.0.1/::1/^::ffff:127.0.0.1) - il remoto → 401.
Concetti
- `McpCatalogEntry` - template di catalogo (27 entry seedate: 12 placeholder + 15 curate). Campi:
name,url,transportType(sse/streamable-http),headers(JSON string),description,category,version,author,verificationTier(official/verified_community/unverified),healthStatus(healthy/stale/down),lastHealthCheck,lastHealthError,lastCommitDate,consecutiveFailures. - `MCPConnection` - connessione workspace-scoped.
source(manual/marketplace),catalogEntryId(FK se da marketplace),enabled,headers(JSON string),workspaceId/projectId. - `ChatMCPPin` - scoping per-chat.
@@unique([chatId, connectionId]). Hard-deleted on unpin.
Catalogo (browse + install)
La browse (lista e dettaglio) richiede solo autenticazione JWT; le operazioni di scrittura richiedono authMiddleware + requireAdmin. Il frontend filtra search/category lato client.
| Metodo | Path | Auth | Note |
|---|---|---|---|
| GET | /api/mcp-marketplace | JWT | Lista; query ?workspaceId (aggiunge isInstalled) |
| GET | /api/mcp-marketplace/:entryId | JWT | Dettaglio entry |
| POST | /api/mcp-marketplace | JWT + admin | Crea entry di catalogo |
| POST | /api/mcp-marketplace/:entryId/install | JWT + admin | Installa nel workspace |
| POST | /api/mcp-marketplace/:entryId/uninstall | JWT + admin | Disinstalla |
Install flow (marketplace.ts:165)
- Validate
installMcpServerSchema({ workspaceId: uuid, name?, headers? }); override headers validati lato write conmcpHeadersSchema(D-12: hop-by-hop blocklist, name regex, size limits). - Check entry esiste (404), non già installato (409, filtro
source: "marketplace"- una connessione manuale con lo stessocatalogEntryIdnon blocca). - Crea
MCPConnection:source="marketplace",catalogEntryId,url/transportTypecopiati dal catalogo,enabled=true,name = override || catalog.name. logEvent("mcp_connection", ..., "mcp.installed", ...).- Fire-and-forget
connectMCPServer(connection.id)- fallimento logged, non propagato (201 restituito comunque).
Uninstall flow (marketplace.ts:291)
- Trova
MCPConnectionper{ catalogEntryId, workspaceId, source: "marketplace" }(404 se no). disconnectMCPServer+unregisterSkillsForConnection(rimuove skill prefissomcp_<conn>_).- Hard delete
MCPConnection(no soft delete). logEvent("mcp.uninstalled").
Re-install = rifai l'install flow (nuovo MCPConnection con nuovo ID).
Connessioni manuali (CRUD)
/api/mcp-connections (mcp.ts, authMiddleware + requireAdmin): GET, GET /statuses (live da activeConnections Map, non DB), POST crea, PUT /:id (auto-disconnect/ reconnect se enabled), DELETE /:id, POST /:id/toggle, POST /:id/test (10s timeout, ritorna { success, toolCount?, error? }).
Le connessioni marketplace-installate sono indistinguibili da quelle manuali a livello CRUD - il campo source differenzia l'origine.
MCP client (agent/mcpClient.ts)
connectMCPServer(connectionId)(:233): ensureConnected cache-hit → read-side header validation (mcpHeadersSchemaD-12), transport fallback D-09 (streamable-httpprimario → 4xx → SSE fallback;ssedichiarato = solo SSE; 5xx non triggera fallback),Client.listTools()→ registra ogni tool come skillmcp_<connectionId>_<toolName>(UUID prefix collision-free).- Per-connection mutex
withConnectionLock. activeConnections: Map<connectionId, MCPConnectionState>(non persistito, rebuild al restart; loscopeworkspace/project è catturato a connect-time per il filtro D-14).disconnectMCPServer(:363): delete-first + unregister +client.close().shutdownMCPConnectionsper graceful shutdown.resolveMcpSourceName(toolName)→ nome human-readable permcpSourcesSSE.
Pinning per-chat
Le pin permettono di limitare quali connessioni MCP forniscono tool a una specifica chat.
/api/chats/:chatId/pins (mcpPins.ts): GET, POST (createMcpPinSchema), DELETE /:pinId.
Risoluzione (agent/skills.ts:136 resolveSkillsForChat):
- No pin → tutti i connection MCP enabled del workspace forniscono skill.
- Pin presenti → filtra connection pinned AND enabled AND stesso workspace (globali entrambi-null incluse; project-scoped escluse).
- Tutti pinned disabilitati/out-of-scope → fallback workspace defaults + warn log.
- Union MCP-02 (strict, D-04): builtin + pinned MCP skill + union con i tool workspace-scoped attivi (le pin non rimuovono mai tool - solo aggiungono).
Health check
services/mcpHealthCheckJob.ts - pg-boss cron 30-min (initMCPHealthCheckScheduler() da src/index.ts; */30 * * * *, no fallback timer quando pg-boss è unavailable - D-02). Cicla sulle connessioni installate + enabled con catalogEntryId: ping url con retry esponenziale (1s/2s/4s, timeout 10s), aggiorna healthStatus (healthy / stale al 1°-2° fail / down dal 3°), lastHealthCheck, lastHealthError, consecutiveFailures. Non bloccante: entry stale/down restano visibili con badge warning; il reset parte da un ping riuscito.
Trust layer (verification tier)
verificationTier è seed-data-only (non scritto a runtime):
- official - mantenuto dal progetto Simmetric Chat.
- verified_community - approvato dai maintainer.
- unverified - community, non revisionato.
lastCommitDate = indicatore di recenza. I tier sono informativi (non bloccano install) ma renderizzati come badge. L'installazione di entry unverified/down è a rischio dell'admin.
Audit logging
Tutte le operazioni loggano via eventLogService.logEvent con entityType = "mcp_connection":
| Azione | Event action |
|---|---|
| Install da marketplace | mcp.installed |
| Uninstall da marketplace | mcp.uninstalled |
| Enable connection | mcp.enabled |
| Disable connection | mcp.disabled |
| Delete connection | mcp.deleted |
In Community gli eventi passano da logEvent (no-op di scrittura senza il plugin Enterprise, dispatch webhook comunque attivo); con audit_log_immutable (Enterprise) gli eventi sono scritti nell'audit log immutabile. Filtra con entityType = "mcp_connection" per l'audit trail completo.
mcpSources in SSE
L'SSE done include mcpSources: string[] - nomi delle connessioni MCP i cui tool sono stati invocati. Derivato dal prefix del tool call (mcp_<conn>_<tool>) via resolveMcpSourceName. Il frontend mostra i chip delle fonti sul messaggio; il proxy widget rigola l'evento byte-verbatim, così il dato resta disponibile lato client.
Come si usa (admin)
- Settings → MCP Connections (sotto-sezione Avanzate) o /mcp-marketplace (admin/superuser).
- Sfoglia catalogo (search, category filter, badge tier/health - filtri lato client).
- Installa in un workspace → badge "Installed" + menu contestuale Uninstall.
- (Opzionale) Pin tool specifici a una chat via
McpPinnerPopovernell'header chat. - Test connessione (
POST /:id/test) per verificare toolCount live.
Come si estende
- Nuovo catalog entry:
POST /api/mcp-marketplace(admin) o seed diretto in Prisma (seedCatalogEntries, upsert idempotente - i campi runtimeinstallCount-like non vengono mai sovrascritti dal re-seed). - Nuovo transport: estendi
buildTransport(mcpClient.ts:136) +TransportKind. - Scope custom:
getMCPToolsForWorkspace(workspaceId)filtra perscope.workspaceId(global inclusi).
Provenienza v0.28.1
| Affermazione | Fonte |
|---|---|
| La direzione expose (tool rag_query/list_workspaces, transport streamable stateless + SSE legacy, gate MCP_API_KEY/loopback) | packages/server/src/agent/mcpServer.ts:1-100 (row-time probe 35-02) + README.md:19 - F-MCP-1 |
| Il marketplace = ruoli admin | docs/MCP_MARKETPLACE.md:3 (row-time) |
Riferimento operativo - Simmetric Chat v0.28.1 (docs aggiornate 2026-10-02, census SHA `aca5b8ff`).
Cross-link
- Endpoint: 04 - API Reference
- Agent loop: 05 - Feature Guide
- License/audit flag: 03 - Configuration / 08 - Administration