Zurück zur Dokumentation
Guide 07

MCP Marketplace

Der MCP Marketplace ermöglicht die Installation, Aktivierung und das Scoping externer MCP-Server aus einem kuratierten Katalog. MCP-Server stellen Tools bereit, die der ReAct-Agent während der Chats aufrufen kann, wodurch die Fähigkeiten ohne Code erweit…

4 Min. Lesezeit

Der MCP Marketplace ermöglicht die Installation, Aktivierung und das Scoping externer MCP-Server aus einem kuratierten Katalog. MCP-Server stellen Tools bereit, die der ReAct-Agent während der Chats aufrufen kann, wodurch die Fähigkeiten ohne Code erweitert werden.

Das System umspannt: Prisma-Modelle (McpCatalogEntry, MCPConnection, ChatMCPPin), Routes (marketplace.ts, mcp.ts), Agent-Skill-Registry (skills.ts), Services (Health-Check-Scheduler, DLP-Filter, Audit-Logging).

Konzepte

  • `McpCatalogEntry` - Katalog-Template (27 geseedete Einträge: 12 Platzhalter + 15 kuratierte). Felder: 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` - Workspace-scoped Verbindung. source (manual/marketplace), catalogEntryId (FK wenn aus dem Marketplace), enabled, headers (JSON-String), workspaceId/projectId.
  • `ChatMCPPin` - Scoping pro Chat. @@unique([chatId, connectionId]). Hard-Deleted beim Unpin.

Katalog (Browse + Install)

Das Browse (Liste und Detail) erfordert nur JWT-Authentifizierung; Schreiboperationen erfordern authMiddleware + requireAdmin. Das Frontend filtert Search/Category clientseitig.

MethodePathAuthHinweis
GET/api/mcp-marketplaceJWTListe; Query ?workspaceId (ergänzt isInstalled)
GET/api/mcp-marketplace/:entryIdJWTEntry-Detail
POST/api/mcp-marketplaceJWT + adminKatalog-Eintrag erstellen
POST/api/mcp-marketplace/:entryId/installJWT + adminIm Workspace installieren
POST/api/mcp-marketplace/:entryId/uninstallJWT + adminDeinstallieren

Install Flow (marketplace.ts:165)

  1. Validate installMcpServerSchema ({ workspaceId: uuid, name?, headers? }); Override-Header werden write-seitig mit mcpHeadersSchema validiert (D-12: Hop-by-Hop-Blocklist, Name-Regex, Size Limits).
  2. Check Entry existiert (404), nicht bereits installiert (409, Filter source: "marketplace" - eine manuelle Verbindung mit derselben catalogEntryId blockiert nicht).
  3. Erstellt MCPConnection: source="marketplace", catalogEntryId, url/transportType aus dem Katalog kopiert, enabled=true, name = override || catalog.name.
  4. logEvent("mcp_connection", ..., "mcp.installed", ...).
  5. Fire-and-forget connectMCPServer(connection.id) - Fehler wird geloggt, nicht propagiert (201 wird trotzdem zurückgegeben).

Uninstall Flow (marketplace.ts:291)

  1. Findet MCPConnection über { catalogEntryId, workspaceId, source: "marketplace" } (404 wenn nicht).
  2. disconnectMCPServer + unregisterSkillsForConnection (entfernt Skills mit Präfix mcp_<conn>_).
  3. Hard Delete MCPConnection (kein Soft Delete).
  4. logEvent("mcp.uninstalled").

Re-Install = Install Flow erneut ausführen (neue MCPConnection mit neuer ID).

Manuelle Verbindungen (CRUD)

/api/mcp-connections (mcp.ts, authMiddleware + requireAdmin): GET, GET /statuses (live aus der activeConnections Map, nicht der DB), POST erstellt, PUT /:id (Auto-Disconnect/Reconnect wenn enabled), DELETE /:id, POST /:id/toggle, POST /:id/test (10s Timeout, gibt { success, toolCount?, error? } zurück).

Marketplace-installierte Verbindungen sind auf CRUD-Ebene nicht von manuellen zu unterscheiden - das Feld source differenziert die Herkunft.

MCP Client (agent/mcpClient.ts)

  • connectMCPServer(connectionId) (:233): ensureConnected Cache-Hit → Read-Side-Header-Validierung (mcpHeadersSchema D-12), Transport-Fallback D-09 (streamable-http primär → 4xx → SSE-Fallback; sse deklariert = nur SSE; 5xx triggert keinen Fallback), Client.listTools() → registriert jedes Tool als Skill mcp_<connectionId>_<toolName> (UUID-Präfix kollisionsfrei).
  • Per-Connection-Mutex withConnectionLock.
  • activeConnections: Map<connectionId, MCPConnectionState> (nicht persistiert, Rebuild beim Restart; der scope Workspace/Project wird zur Connect-Zeit erfasst für den D-14-Filter).
  • disconnectMCPServer (:363): delete-first + unregister + client.close().
  • shutdownMCPConnections für den Graceful Shutdown.
  • resolveMcpSourceName(toolName) → human-readable Name für mcpSources SSE.

Per-Chat-Pinning

Die Pins erlauben es, die MCP-Verbindungen einzuschränken, die einer bestimmten Chat Tools bereitstellen.

/api/chats/:chatId/pins (mcpPins.ts): GET, POST (createMcpPinSchema), DELETE /:pinId.

Auflösung (agent/skills.ts:136 resolveSkillsForChat):

  • Kein Pin → alle MCP-Verbindungen enabled des Workspace stellen Skills bereit.
  • Pins vorhanden → filtert Verbindungen pinned AND enabled AND derselbe Workspace (globale beide-null inklusive; project-scoped ausgeschlossen).
  • Alle gepinnten deaktiviert/out-of-scope → Fallback Workspace-Defaults + Warn-Log.
  • Union MCP-02 (strict, D-04): Builtin + Pinned-MCP-Skills + Union mit den aktiven workspace-scoped Tools (Pins entfernen nie Tools - sie fügen nur hinzu).

Health Check

services/mcpHealthCheckJob.ts - pg-boss Cron 30-min (initMCPHealthCheckScheduler() aus src/index.ts; */30 * * * *, kein Fallback-Timer wenn pg-boss unavailable - D-02). Loopt über die installierten + enabled Verbindungen mit catalogEntryId: Ping url mit exponentiellem Retry (1s/2s/4s, Timeout 10s), aktualisiert healthStatus (healthy / stale beim 1.-2. Fehler / down ab dem 3.), lastHealthCheck, lastHealthError, consecutiveFailures. Nicht blockierend: Stale/Down-Einträge bleiben mit Warning-Badge sichtbar; der Reset beginnt bei einem erfolgreichen Ping.

Trust Layer (Verification Tier)

verificationTier ist Seed-Data-Only (nicht zur Runtime geschrieben):

  • official - vom Projekt Simmetric Chat gepflegt.
  • verified_community - von den Maintainern freigegeben.
  • unverified - Community, nicht reviewt.

lastCommitDate = Aktualitätsindikator. Die Tiers sind informativ (sie blockieren die Installation nicht), werden aber als Badges gerendert. Die Installation von unverified/down-Einträgen erfolgt auf Risiko des Admins.

Audit Logging

Alle Operationen loggen via eventLogService.logEvent mit entityType = "mcp_connection":

AktionEvent Action
Install aus Marketplacemcp.installed
Uninstall aus Marketplacemcp.uninstalled
Enable Connectionmcp.enabled
Disable Connectionmcp.disabled
Delete Connectionmcp.deleted

In Community laufen die Events über logEvent (Schreib-No-op ohne das Enterprise-Plugin, Webhook-Dispatch trotzdem aktiv); mit audit_log_immutable (Enterprise) werden die Events in das unveränderliche Audit Log geschrieben. Filtere mit entityType = "mcp_connection" für den vollständigen Audit Trail.

mcpSources im SSE

Das SSE done enthält mcpSources: string[] - Namen der MCP-Verbindungen, deren Tools aufgerufen wurden. Abgeleitet aus dem Präfix des Tool Calls (mcp_<conn>_<tool>) via resolveMcpSourceName. Das Frontend zeigt die Source-Chips an der Nachricht; der Widget-Proxy relayt das Event byte-verbatim, sodass die Daten clientseitig verfügbar bleiben.

Verwendung (Admin)

  1. Settings → MCP Connections (Unter-Sektion Advanced) oder /mcp-marketplace (Admin/Superuser).
  2. Katalog durchstöbern (Search, Category Filter, Tier-/Health-Badges - Client-seitige Filter).
  3. In einem Workspace installieren → "Installed"-Badge + Kontextmenü Uninstall.
  4. (Optional) Spezifische Tools an eine Chat pinnen via McpPinnerPopover im Chat-Header.
  5. Verbindungstest (POST /:id/test) zum Prüfen des Live-toolCount.

Erweitern

  • Neuer Catalog Entry: POST /api/mcp-marketplace (Admin) oder direktes Seed in Prisma (seedCatalogEntries, idempotenter Upsert - Runtime-Felder wie installCount werden beim Re-Seed nie überschrieben).
  • Neuer Transport: buildTransport erweitern (mcpClient.ts:136) + TransportKind.
  • Custom Scope: getMCPToolsForWorkspace(workspaceId) filtert nach scope.workspaceId (global inklusive).