Portcall
Portcall
一个通过 HTTP 提供本地 MCP 服务器的小型插件网关。
这个名字是一个航海双关语:port call — port(港口 / 网络端口)+ call(船舶停靠 / 请求)。
它是什么
Portcall 监听一个 HTTP 端口,并将一个或多个 MCP 服务器挂载到不同的路径上:
/vault/mcp → mcpvault (Obsidian vault)
/healthz → liveness + mount list每个挂载点都是一个独立的 MCP 端点。客户端分别注册它们——没有工具聚合,因此不会产生名称冲突,也无需维护命名空间方案。
刻意不将端口暴露到 localhost 之外。在它前面放一个隧道、反向代理,或者什么都不放;Portcall 默认绑定 127.0.0.1,并不关心上游是什么。
Related MCP server: mcp-unify
为什么不用 stdio 桥接
要把 stdio MCP 服务器放到 HTTP 上,显而易见的做法是使用一个通用桥接,比如 supergateway。它能工作,但有一个结构性问题:每个请求或会话都会派生一个子进程,而回收这些子进程很容易出错。
具体到 supergateway,子进程只会在 transport.onclose 或 transport.onerror 中被回收。对于正常完成的无状态请求,没有任何代码调用 transport.close(),因此 onclose 永远不会触发,每个成功的请求都会泄漏一个进程——只有失败的请求才会被清理。它的有状态模式不会泄漏(会话计时器会关闭 transport),但取而代之的是它持有一条长时间存活的 GET SSE 流,有些代理对这种情况处理得很糟糕。
用 npx 包装命令会让情况更糟:npx 会 fork 出真正的服务器,因此杀掉子进程会连带杀掉包装进程,并使孙进程成为孤儿。
Portcall 的答案是:在不需要的时候不派生任何东西。
适配器
适配器 | 适用对象 | 工作方式 |
| 以库的形式导出工厂函数的服务器 | 在进程内调用工厂函数。不存在子进程,因此没有需要回收的东西。 |
| 只支持 stdio 的第三方服务器 | 尚未实现。落地时,它必须在正常完成时回收子进程,而不只是在出错时,并且要处理包装命令的进程组终止。 |
inProcess 是值得关注的情况,涵盖了那些值得自托管的服务器。例如,@bitbonsai/mcpvault 导出一个 createServer(vaultPath, options),它返回一个 MCP SDK v2 的 Server;它的 bin 入口本质上就是 serveStdio(() => createServer(...))。Portcall 直接调用同一个函数,完全跳过 stdio。
SDK 为每个请求构建一个全新的服务器实例,并随请求一起销毁,因此既没有需要超时的会话状态,也不会累积句柄。
协议版本
Portcall 基于 @modelcontextprotocol/server v2 构建,它用同一个处理器服务两个协议时代:
现代(
2026-07-28)——逐请求封装。请求携带MCP-Protocol-Version、Mcp-Method头,以及(对于工具调用)Mcp-Name头,外加一个params._meta块。没有initialize握手,也没有长期存活的会话;发现通过server/discover完成。旧版(2025 时代)——默认以无状态方式提供服务。
GET和DELETE(2025 会话操作)返回405。设置PORTCALL_MODERN_ONLY=true可直接拒绝旧版流量。
由于现代时代是每请求模式,没有需要保持打开的常驻 SSE 流。这规避了一类代理问题:有些反向代理会扣住响应头不放,直到第一个响应体字节到达,这会让一条刚刚打开但尚无数据的 SSE 流无限期停滞。对于确实会出现的流,PORTCALL_KEEPALIVE_MS 控制 SSE 注释帧的间隔;如果前面的代理在缓冲,就调低它。
配置
所有与主机相关的值都来自环境变量。
变量 | 默认值 | 含义 |
| (必填) | 要服务的 Obsidian 仓库的绝对路径 |
|
| TCP 端口 |
|
| 绑定接口 |
| (未设置) | 静态 Bearer 令牌。未设置表示不进行身份验证 |
| (未设置) | 同时将指定插件挂载到 |
| (未设置) | 在 |
|
| SSE 保活间隔; |
|
| 拒绝 2025 时代的请求,而非为其提供服务 |
PORTCALL_TOKEN 用 Authorization: Bearer <token> 为每个挂载点把关。请注意,某些 MCP 客户端——其中包括 Claude 的自定义连接器 UI——无法设置请求头,因此对于这些客户端,令牌只能在上游强制执行(或者干脆不设置,而在网络层控制访问)。
PORTCALL_PATH_PREFIX 正是为这些客户端准备的回退方案:它把所有挂载点都移到你选择的一个路径段之下,于是 /vault/mcp 变成 /<prefix>/vault/mcp,URL 本身就携带了秘密。由此产生两点,服务器都会强制执行:
404响应只会返回not_found。它们永远不会列出已挂载的内容。挂载列表从公开的
/healthz移到了/<prefix>/healthz。裸的/healthz仍然会应答,因此存活探针继续有效,但它不会泄露任何路径。
应该把路径前缀视为比请求头更弱的保护。URL 会到达代理访问日志、崩溃报告以及任何记录目标地址的地方,而泄露的 URL 与泄露的令牌具有同等的访问权限。它提高了门槛——但它不是身份验证。
哪些插件被挂载、挂载在哪里,在 plugins.config.ts 中声明。
运行
需要 Node 24(参见 .nvmrc)。
npm install
npm run build
cp .env.example .env # then set PORTCALL_VAULT_PATH
npm startnpm start 和 npm run dev 都会在存在 .env 时加载它,不存在时则不带它启动,这样守护进程就可以直接注入环境变量。已在环境中设置过的变量不会被覆盖。
npm run dev 通过 tsx 以 watch 模式运行入口点。守护进程应运行构建产物,而不是 tsx。
检查它是否已启动:
curl -s localhost:7100/healthz测试
npm test # builds, then runs unit and integration tests
npm run typecheck没有测试依赖:运行器是 node:test,由 tsx(npm run dev 本来就需要它)加载 TypeScript。
集成测试是黑盒测试。它们在临时端口上针对一个一次性的仓库启动构建后的服务器,并通过真实 HTTP 驱动它,因此它们测试的正是守护进程所运行的同一产物——路由、/mcp 别名、Bearer 认证,以及两个协议时代。单元测试覆盖挂载解析和 Bearer 检查;在这些地方,静默回归看起来会像客户端挂了,而不是报错。
目录结构
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 builders许可证
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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.106MIT
- 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- AlicenseNot gradedqualityCmaintenanceThis server bridges a stdio MCP server to HTTP, allowing MCP clients that communicate over HTTP to use the server's tools. It includes a per-tool allow/deny filter for security.MIT
Related MCP Connectors
A basic MCP server to operate on the Postman API.
A MCP server built for developers enabling Git based project management with project and personal…
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/pizza6899-crypto/portcall'
If you have feedback or need assistance with the MCP directory API, please join our Discord server