MCP Fusion/Protocol and runtime/Runtime-Architektur

Runtime-Architektur

Frag die KI über Vinkius

Wie MCP Fusion tatsächlich ausgeführt wird: Build-Time-Kompilierung, O(1)-Request-Routing, Context pro Request, die Result-Monade, die Execution-Pipeline und die Transports.

Diese Seite beschreibt die Engine unterhalb der in Tools dokumentierten Fluent-API. Sie ist die technische Tiefenreferenz für den Request-Lebenszyklus, kompiliert in packages/core.

Zwei Ebenen: einmal bauen, viele bedienen

MCP Fusion trennt die Arbeit, die zur Build-Zeit gehört, von der Arbeit, die bei jedem Request passieren muss.

Build time. registry.register(builder) ruft sofort builder.buildToolDefinition() auf. Dieser Kompilierungsschritt erzeugt alles, was der Server für die Lebensdauer des Prozesses braucht:

  • das zusammengeführte Input-Schema, mit dem Diskriminatorfeld und den pro Action erforderlichen Annotationen
  • die <tool_error>-bereiten Validierungsschemas, pro Action ein strict Zod-Schema
  • die vorkompilierte Middleware-Kette, pro Action ein Closure, globales Middleware äußerstem und Action-Middleware innerstem
  • die Action-Map, eine O(1)-Map vom Action-Key zum kompilierten Kontext

Nach buildToolDefinition() ist der Builder eingefroren: Object.freeze auf den Actions und eine Absicherung auf jeder verändernden Methode. Sie können ein gebautes Tool nicht versehentlich ändern. mergeActions() ist der einzige erlaubte Weg, Actions hinzuzufügen, und er taut gezielt auf, um neu zu bauen.

Request time. Der Runtime-Pfad ist festgelegt und hat keinen Assembly-Schritt:

contextFactory → discriminator parse → action resolve (Map)
  → arg validation (cached strict schema) → middleware chain (precompiled)
  → handler → Presenter (postProcess) → guards → response

Jeder Schritt liest aus einer vorkompilierten Struktur. Auf dem Request-Pfad wird kein Schema neu gebaut, keine Kette komponiert, keine Suche durchlaufen.

Registry, Tool-Namen und Actions

Ein gepunkteter Builder-Name ist kein flacher Tool-Name. f.query('billing.get_invoice') registriert die Action get_invoice auf dem Tool billing. Zwei Namen mit Punkt zur Build-Zeit sind ein Fehler; Verschachtelung nutzt f.router('support.tickets'). Builder, die sich einen Namespace teilen, werden zusammengeführt: drei Dateien, die compliance.scan, compliance.report und compliance.status exportieren, werden ein compliance-Tool mit drei Actions, sodass Sie einen Connector nach Verzeichnissen gliedern können und der Agent trotzdem eine zusammenhängende Oberfläche sieht.

contextFactory: der Einstieg pro Request

attachToServer(server, { contextFactory }) führt contextFactory(extra) einmal pro Request aus, vor dem Routing. extra ist der rohe MCP-SDK-Request-Kontext: die Session-ID, das _meta-Objekt (Fortschritts-Token), das AbortSignal für Abbruch und der sendRequest-Kanal für Elicitation. Geben Sie ein frisches Objekt zurück; das Framework verändert es an Ort und Stelle, während die Middleware es anreichert. Hier gehen Datenbank-Handles, Tenant-IDs und das Abbruch-Signal in ctx ein, sodass eine stateless Bereitstellung konstruktionsbedingt kein Request-übergreifendes Leck hat.

Wenn Sie contextFactory weglassen, wirft der Zugriff auf ctx einen expliziten Fehler, statt mysteriös zu scheitern.

Die Transports

startServer() unterstützt drei Transports:

TransportSessionVerwendung
stdiokeinelokale Clients, die Desktop-Apps, mcpfusion dev
httpUUID-Session-ID, TTL-Reaper, Body-Limit, Token-Bucket pro Sessionein persistenter HTTP-MCP-Server
statelesskeineMCP-2.0-Modus: ein frisches Server pro Request, hinter jedem Load Balancer

Stateless ist die Skalierungs-Voreinstellung: kein initialize-Handschlag, Requests werden über die Header Mcp-Method und Mcp-Name geroutet, und die Vercel- und Cloudflare-Adapter liefern JSON-Antworten ohne SSE und ohne Session-State. Der Vinkius Edge nutzt dieses Modell: Ihr StartServerOptions.state wird serialisiert, bevor die Isolate verworfen wird, und beim nächsten Cold Request transparent wiederhergestellt.

Result-Monade: Steuerungsfluss ohne Exceptions

Intern basiert die Pipeline auf Result: Success oder Failure, jeder Schritt bricht bei Failure ab. Handler sehen Result nicht; sie geben Daten zurück oder succeed()/toolError(). Was Result bringt, ist Berechenbarkeit: Validierung, Diskriminator-Parsing und Dispatch geben Werte zurück, keine geworfenen Exceptions, so geht nichts in einem Stack Trace verloren. Ein geworfenes ToolResponse passiert unverändert (damit funktioniert throw toolError('NOT_FOUND', ...)); jedes andere Werfen wird als INTERNAL_ERROR verpackt, mit einer „nicht blind erneut versuchen“-Erholungsanweisung.

Guards um den Handler

Drei unabhängige Guards umgeben die Ausführung und sind kostenfrei, wenn sie nicht konfiguriert sind:

  • Parallelität: ein Semaphor mit begrenzter Warteschlange. Über der Kapazität gibt es SERVER_BUSY mit einem Retry-Hinweis zurück, statt zu degradieren.
  • Serialisierung von Mutationen: eine destruktive Action bekommt automatisch einen FIFO-Mutex pro Action, damit zwei Agenten nicht um denselben Write konkurrieren.
  • Egress-Limit: ein Byte-Budget auf der Antwort. Überlanger Text wird mit einer Systemnachricht gekürzt, die das Modell zum Paginieren auffordert. Structured structuredContent bleibt erhalten; isError wird nie gekippt.

Siehe Security pipeline für die Guards auf der Eingangsseite und State sync für Cache- und Staleness-Signale.

Observability-Hooks

registry.enableDebug(observer), enableTracing(tracer) und enableTelemetry(sink) pflanzen sich über dieselbe duck-typed Schnittstelle in jeden Builder fort. Jeder Hook ist inaktiv, bis er gesetzt ist: kein Timer, kein Wrapper, keine Zuweisung, wenn ein Subsystem still ist. Dieselben drei Senken speisen mcpfusion inspect, die OTel-Pipeline und den Middleware-Abdeckungsnachweis in der Lockfile.

Nächste Schritte