Portcall
Portcall
Ein kleiner Plugin-Gateway, der lokale MCP-Server über HTTP bereitstellt.
Der Name ist ein nautisches Wortspiel: port call — port (Hafen / Netzwerk-Port) + call (ein Schiffsstopp / eine Anfrage).
Was es ist
Portcall lauscht auf einem HTTP-Port und mountet einen oder mehrere MCP-Server unter separaten Pfaden:
/vault/mcp → mcpvault (Obsidian vault)
/healthz → liveness + mount listJeder Mount ist ein unabhängiger MCP-Endpunkt. Clients registrieren sie separat – es gibt keine Tool-Aggregation, also keine Namenskollisionen und kein Namespacing-Schema, das gepflegt werden müsste.
Das Freigeben des Ports über localhost hinaus ist bewusst nicht im Scope. Setzen Sie einen Tunnel, einen Reverse-Proxy oder gar nichts davor; Portcall bindet standardmäßig an 127.0.0.1 und kümmert sich nicht darum, was vorgelagert ist.
Related MCP server: mcp-unify
Warum keine stdio-Brücke
Der naheliegende Weg, einen stdio-MCP-Server auf HTTP zu bringen, ist eine generische Brücke wie supergateway. Das funktioniert, hat aber ein strukturelles Problem: Jede Anfrage oder Sitzung erzeugt einen Kindprozess, und das Einsammeln dieser Kinder ist leicht falsch zu machen.
In supergateway im Speziellen wird das Kind nur über transport.onclose oder transport.onerror zurückgeholt. Nichts ruft transport.close() bei einer normal abgeschlossenen zustandslosen Anfrage auf, also feuert onclose nie und jede erfolgreiche Anfrage leakt einen Prozess – nur fehlgeschlagene Anfragen werden aufgeräumt. Der zustandsbehaftete Modus leakt nicht (ein Sitzungstimer schließt den Transport), hält aber stattdessen einen langlebigen GET-SSE-Stream, den einige Proxys schlecht behandeln.
Das Verpacken des Befehls in npx macht es schlimmer: npx forkt den echten Server, also tötet das Töten des Kindes den Wrapper und verwaist das Enkelkind.
Portcalls Antwort ist, nichts zu spawnen, wenn es nicht nötig ist.
Adapter
Adapter | Für | Wie |
| Server, die eine Factory als Bibliothek exportieren | Ruft die Factory im Prozess auf. Es existiert kein Kindprozess, also gibt es nichts einzusammeln. |
| Drittanbieter-Server, die nur stdio sprechen | Noch nicht implementiert. Wenn es kommt, muss es das Kind bei normalem Abschluss einsammeln, nicht nur bei Fehlern, und Prozessgruppen-Kills für Wrapper-Befehle behandeln. |
inProcess ist der interessante Fall und deckt die Server ab, die sich lohnen, selbst zu hosten. @bitbonsai/mcpvault exportiert zum Beispiel createServer(vaultPath, options), das einen MCP-SDK-v2-Server zurückgibt; sein bin-Eintrag ist im Wesentlichen serveStdio(() => createServer(...)). Portcall ruft dieselbe Funktion direkt auf und überspringt stdio vollständig.
Das SDK baut pro Anfrage eine frische Serverinstanz und entsorgt sie mit der Anfrage, also gibt es keinen Sitzungszustand, der ausläuft, und keine sich ansammelnden Handles.
Protokollversionen
Portcall basiert auf @modelcontextprotocol/server v2, das zwei Protokoll-Ären aus einem einzigen Handler bedient:
Modern (
2026-07-28) – Pro-Anfrage-Umschlag. Anfragen tragenMCP-Protocol-Version,Mcp-Methodund (für Tool-Aufrufe)Mcp-Name-Header plus einenparams._meta-Block. Es gibt keineninitialize-Handshake und keine langlebige Sitzung; Discovery istserver/discover.Legacy (2025-Ära) – standardmäßig zustandslos bedient.
GETundDELETE(2025-Sitzungsoperationen) antworten mit405. Setzen SiePORTCALL_MODERN_ONLY=true, um Legacy-Verkehr komplett abzulehnen.
Da die moderne Ära pro Anfrage ist, gibt es keinen dauerhaften SSE-Stream, der offen gehalten werden muss. Das umgeht eine Klasse von Proxy-Problemen: Einige Reverse-Proxys halten Antwort-Header zurück, bis das erste Body-Byte ankommt, was einen gerade geöffneten, aber stillen SSE-Stream unbegrenzt blockiert. Für die Streams, die doch vorkommen, steuert PORTCALL_KEEPALIVE_MS das SSE-Kommentar-Frame-Intervall; senken Sie es, wenn ein vorgelagerter Proxy puffert.
Konfiguration
Alle host-spezifischen Werte kommen aus der Umgebung.
Variable | Standard | Bedeutung |
| (erforderlich) | Absoluter Pfad zum Obsidian-Vault, das bedient werden soll |
|
| TCP-Port |
|
| Bind-Interface |
| (nicht gesetzt) | Statisches Bearer-Token. Nicht gesetzt bedeutet keine Authentifizierung |
| (nicht gesetzt) | Mountet das benannte Plugin zusätzlich unter |
| (nicht gesetzt) | Bedient jeden Mount unter |
|
| SSE-Keepalive-Intervall; |
|
| Lehnt 2025-Ära-Anfragen ab, statt sie zu bedienen |
PORTCALL_TOKEN schützt jeden Mount mit Authorization: Bearer <token>. Beachten Sie, dass einige MCP-Clients – darunter Clauses benutzerdefinierte Connector-UI – keine Möglichkeit bieten, einen Request-Header zu setzen, also muss das Token für diese stattdessen vorgelagert erzwungen werden (oder weggelassen werden, mit Zugriffskontrolle auf Netzwerkebene).
PORTCALL_PATH_PREFIX ist der Fallback genau für diese Clients: Es verschiebt jeden Mount unter ein Segment Ihrer Wahl, sodass /vault/mcp zu /<prefix>/vault/mcp wird und die URL selbst das Geheimnis trägt. Zwei Dinge folgen daraus, und der Server erzwingt beide:
404-Antworten sagen nurnot_found. Sie listen nie auf, was gemountet ist.Die Mount-Liste wandert aus dem öffentlichen
/healthzin/<prefix>/healthz. Das nackte/healthzantwortet weiterhin, damit Liveness-Probes funktionieren, gibt aber keine Pfade preis.
Behandeln Sie ein Pfad-Präfix als schwächer als einen Header. URLs erreichen Proxy-Zugriffsprotokolle, Absturzberichte und alles, was ein Ziel aufzeichnet, und ein geleaktes gewährt denselben Zugriff wie ein geleaktes Token. Es erhöht die Hürde – es ist keine Authentifizierung.
Welche Plugins gemountet sind und wo, wird in plugins.config.ts deklariert.
Ausführen
Erfordert Node 24 (siehe .nvmrc).
npm install
npm run build
cp .env.example .env # then set PORTCALL_VAULT_PATH
npm startSowohl npm start als auch npm run dev laden .env, falls vorhanden, und starten ohne, falls nicht, sodass ein Daemon die Umgebung direkt injizieren kann. Bereits in der Umgebung gesetzte Variablen werden nicht überschrieben.
npm run dev führt den Einstiegspunkt über tsx mit Watch aus. Ein Daemon sollte das gebaute Ergebnis ausführen, nicht tsx.
Prüfen Sie, ob es läuft:
curl -s localhost:7100/healthzTests
npm test # builds, then runs unit and integration tests
npm run typecheckKeine Test-Abhängigkeiten: Der Runner ist node:test, und tsx (bereits für npm run dev benötigt) lädt das TypeScript.
Die Integrationstests sind Black-Box. Sie starten den gebauten Server gegen ein Wegwerf-Vault auf einem ephemeren Port und treiben ihn über echtes HTTP, sodass sie dasselbe Artefakt testen, das ein Daemon ausführt – Routing, den /mcp-Alias, Bearer-Auth und beide Protokoll-Ären. Die Unit-Tests decken Mount-Auflösung und den Bearer-Check ab, wo eine stille Regression wie ein toter Client aussehen würde, nicht wie ein Fehler.
Layout
src/
server.ts HTTP entry point, wiring, health, shutdown
routes.ts mount resolution and URL normalisation
auth.ts bearer token check
config.ts environment parsing
log.ts structured logging
types.ts the Plugin interface
adapters/
inProcess.ts library-factory adapter
plugins/
vault.ts mcpvault
plugins.config.ts which plugins mount at which paths
test/
integration.test.ts black-box tests against the built server
routes.test.ts mount resolution
auth.test.ts bearer token check
helpers.ts server harness and MCP request buildersLizenz
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Stateless MCP gateway and OTel span-streaming bridge for hosted MCP servers.
The MCP server that finds MCP servers. Aggregates Official Registry, Glama, and Smithery.
MCP Gateway: wrap any MCP server with cold-start retries, uptime SLA, and per-execution MPP billing.
Create, deploy, and operate MCP servers directly from your GitHub repositories.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA universal MCP server that acts as a unified gateway for dynamically connecting and managing multiple MCP servers via a single HTTP endpoint.7 npm6MIT
- AlicenseNot gradedqualityCmaintenanceUnifies multiple MCP servers behind a single endpoint with lazy loading, auto-cleanup, Python plugins, and role-based filtering.2MIT

FlowMCP Serverofficial
AlicenseNot gradedqualityDmaintenanceProvides LocalServer and RemoteServer implementations for running MCP servers locally via stdio or remotely via HTTP/SSE, with simple and advanced deployment options.1MIT- FlicenseNot gradedqualityDmaintenanceA configurable MCP gateway that runs multiple Streamable HTTP MCP servers and exposes all their tools through a single endpoint, enabling tool aggregation and routing for MCP clients.1-