Skip to main content
Glama

Portcall

Небольшой шлюз плагинов, который обслуживает локальные MCP-серверы по HTTP.

Название — морской каламбур: port callport (гавань / сетевой порт) + 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 никогда не срабатывает, и каждый успешный запрос приводит к утечке процесса — очищаются только неудачные запросы. Его режим с сохранением состояния не даёт утечек (таймер сессии закрывает транспорт), но вместо этого он удерживает долгоживущий поток SSE GET, с которым некоторые прокси справляются плохо.

Обёртывание команды в npx усугубляет ситуацию: npx порождает настоящий сервер, поэтому при убийстве дочернего процесса убивается обёртка, а внучатый процесс остаётся сиротой.

Ответ Portcall — не порождать ничего, когда в этом нет необходимости.

Адаптеры

Адаптер

Для чего

Как

inProcess

Серверы, которые экспортируют фабрику как библиотеку

Вызывает фабрику в том же процессе. Дочернего процесса не существует, поэтому собирать некого.

stdio

Сторонние серверы, которые говорят только на stdio

Пока не реализовано. Когда появится, должен освобождать дочерний процесс при нормальном завершении, а не только при ошибке, и обрабатывать завершение групп процессов для команд-обёрток.

inProcess — интересный случай, который охватывает серверы, заслуживающие самостоятельного хостинга. Например, @bitbonsai/mcpvault экспортирует createServer(vaultPath, options), возвращающий Server из MCP SDK v2; его запись 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; уменьшите его, если впередистоящий прокси буферизует.

Конфигурация

Все значения, зависящие от хоста, берутся из окружения.

Переменная

По умолчанию

Назначение

PORTCALL_VAULT_PATH

(обязательно)

Абсолютный путь к хранилищу Obsidian для обслуживания

PORTCALL_PORT

7100

TCP-порт

PORTCALL_HOST

127.0.0.1

Интерфейс привязки

PORTCALL_TOKEN

(не задан)

Статический bearer-токен. Если не задан — аутентификация не требуется

PORTCALL_ALIAS_ROOT_MCP

(не задан)

Также смонтировать указанный плагин в /mcp

PORTCALL_PATH_PREFIX

(не задан)

Обслуживать все точки монтирования в /<prefix>/…

PORTCALL_KEEPALIVE_MS

15000

Интервал keepalive SSE; 0 отключает

PORTCALL_MODERN_ONLY

false

Отклонять запросы эпохи 2025 вместо их обслуживания

PORTCALL_TOKEN защищает каждую точку монтирования с помощью Authorization: Bearer <token>. Обратите внимание, что некоторые MCP-клиенты — в том числе пользовательский интерфейс коннектора Claude — не позволяют задать заголовок запроса, поэтому для них токен придётся проверять выше по потоку (или вообще не использовать, контролируя доступ на сетевом уровне).

PORTCALL_PATH_PREFIX — это запасной вариант именно для таких клиентов: он перемещает все точки монтирования в выбранный вами сегмент, так что /vault/mcp становится /<prefix>/vault/mcp, и сам URL несёт в себе секрет. Из этого следует два момента, и сервер обеспечивает оба:

  • Ответы 404 содержат только not_found. Они никогда не перечисляют, что смонтировано.

  • Список точек монтирования перемещается из публичного /healthz в /<prefix>/healthz. Голый /healthz по-прежнему отвечает, поэтому проверки живости продолжают работать, но не раскрывают никаких путей.

Относитесь к префиксу пути как к более слабой защите, чем заголовок. URL-адреса попадают в журналы доступа прокси, отчёты о сбоях и в любые записи, фиксирующие назначение, а утёкший префикс даёт тот же доступ, что и утёкший токен. Это поднимает планку — но это не аутентификация.

Какие плагины монтируются и где, объявляется в plugins.config.ts.

Запуск

Требуется Node 24 (см. .nvmrc).

npm install
npm run build
cp .env.example .env    # then set PORTCALL_VAULT_PATH
npm start

И npm 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, а TypeScript загружает tsx (он уже нужен для npm run dev).

Интеграционные тесты — «чёрный ящик». Они запускают собранный сервер с одноразовым хранилищем на эфемерном порту и обращаются к нему по реальному 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

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • A
    license
    Not graded
    quality
    C
    maintenance
    A universal MCP server that acts as a unified gateway for dynamically connecting and managing multiple MCP servers via a single HTTP endpoint.
    10
    6
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Unifies multiple MCP servers behind a single endpoint with lazy loading, auto-cleanup, Python plugins, and role-based filtering.
    2
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides LocalServer and RemoteServer implementations for running MCP servers locally via stdio or remotely via HTTP/SSE, with simple and advanced deployment options.
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    This 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

View all related MCP servers

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

View all MCP Connectors

Latest Blog Posts

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