mcp-proxy
mcp-proxy
Усиливающий безопасность MCP-прокси, который располагается перед одним или несколькими вышестоящими MCP-серверами и открывает только те инструменты, которые указанный профиль может видеть и вызывать.
Один конфигурационный файл описывает ваши реальные серверы (GitHub, filesystem, Slack, …) и профили (reviewer, implementer, ci-bot, …). Каждый агент запускает собственную копию прокси с флагом --profile <name> — или, в режиме serve, один общий HTTP-сервер сопоставляет каждое подключение с профилем через аутентификацию — и получает отфильтрованное и принудительно ограниченное представление этих серверов, экономя токены контекста и предотвращая опасные вызовы инструментов по построению.
Зачем mcp-proxy?
Официальные MCP-серверы предоставляют все свои инструменты каждому агенту. Клиент получает tools/list и вставляет схему каждого инструмента в промпт на каждом ходу, сжигая токены контекста. А видимый инструмент — это инструмент, который можно вызвать; жёсткой границы нет.
mcp-proxy решает обе проблемы одновременно:
Экономия токенов — профиль рекламирует только те инструменты, которые вы явно разрешили, поэтому в контекст агента попадают лишь эти схемы.
Жёсткое ограждение — инструмент, который не разрешён, не отображается в списке и не доступен для вызова: даже галлюцинированный вызов отклоняется во время выполнения, а не просто скрывается из меню.
Related MCP server: sovereign-mcp-gateway
Преимущества
Преимущество | Как помогает |
🔒 Защита fail-closed | Правило |
📉 Экономия токенов | Отфильтрованный |
👥 Один конфиг, много агентов | Reviewer, implementer и CI-бот используют один и тот же блок |
🧩 Агрегация нескольких серверов | Объединение нескольких вышестоящих серверов (stdio + HTTP) за одной MCP-конечной точкой. |
🔐 Секреты не попадают в репозиторий | Плейсхолдеры |
♻️ Устойчивость | Автопереподключение с экспоненциальной задержкой; живые обновления |
🛡️ Проверка аргументов | Аргументы |
📊 Наблюдаемость |
|
🌐 Режим общего сервера |
|
🏷️ Безопасность при коллизиях | Инструменты с одинаковыми именами на разных серверах автоматически получают префикс ( |
Как это работает
Архитектура
flowchart TB
subgraph agents["🤖 Agents (MCP clients)"]
direction LR
A1["reviewer agent<br/><code>--profile reviewer</code>"]
A2["implementer agent<br/><code>--profile implementer</code>"]
end
subgraph proxy["mcp-proxy — one stdio process per agent"]
direction TB
D1["stdio transport"]
D2["tool filter<br/>(allow/block · globs + regex)"]
D3["call-time guardrail<br/>+ argument validation"]
D4["upstream registry<br/>(discovery · reconnect · list_changed)"]
end
subgraph up["Upstream MCP servers"]
direction LR
U1["filesystem<br/>(stdio)"]
U2["github<br/>(HTTP)"]
U3["slack<br/>(HTTP)"]
end
A1 -->|"stdin/stdout"| D1
A2 -->|"stdin/stdout"| D1
D1 --> D2 --> D3 --> D4
D4 -->|"spawn"| U1
D4 -->|"connect"| U2
D4 -->|"connect"| U3Каждый агент запускает прокси как дочерний процесс через stdio. Прокси подключается ко всем вышестоящим серверам, перечисленным в выбранном профиле, получает каждый tools/list, применяет правила разрешения/блокировки профиля и повторно предоставляет только выжившие инструменты.
Поток запросов
sequenceDiagram
autonumber
participant A as Agent
participant P as mcp-proxy
participant U as Upstream MCP server
A->>P: tools/list
P->>U: tools/list (every upstream in profile)
U-->>P: full tool set
P->>P: filter + collision resolve
P-->>A: allowed tools only
A->>P: tools/call (allowed tool)
P->>P: guardrail re-check<br/>+ schema validation
P->>U: forward call
U-->>P: result
P-->>A: result
A->>P: tools/call (blocked tool)
P-->>A: ❌ rejected with error
U-->>P: notifications/tools/list_changed
P->>U: re-fetch tools/list
P->>P: re-filter
P-->>A: notifications/tools/list_changedРешение о фильтрации
Инструмент разрешён, только если он проходит следующую цепочку приоритетов:
flowchart LR
T["tool name"] --> B{"matches a<br/><code>block</code> pattern?"}
B -- "yes" --> DENY["🔒 DENY"]
B -- "no" --> A{"matches an<br/><code>allow</code> pattern?"}
A -- "yes" --> OK["✅ ALLOW"]
A -- "no" --> D["fallback:<br/>server <code>default</code><br/>→ profile <code>default</code><br/>→ <code>block</code>"]
D --> F{"fallback is <code>allow</code>?"}
F -- "yes" --> OK
F -- "no" --> DENYblock всегда побеждает. Шаблоны — это glob-выражения (read_*, {get,list}_*) или регулярные выражения (/.*delete.*/i). Сервер, опущенный в профиле, не показывает ни одного своего инструмента.
Пример: три профиля с замером в реальном времени
Один и тот же прокси, запущенный с тремя профилями, был проверен на реальном вышестоящем сервере @modelcontextprotocol/server-filesystem (14 инструментов). Второй экземпляр filesystem выступил в роли HTTP-сервера GitHub, чтобы демонстрации не требовался токен — фильтрация на уровне сервера ведёт себя одинаково для любого вышестоящего сервера.
# mcp-proxy.yaml (demo)
version: 1
servers:
filesystem:
type: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "C:/data"]
github: # HTTP in real life; filesystem stand-in in this demo
type: http
url: https://api.github.com/mcp
headers: { Authorization: "${GITHUB_TOKEN}" }
profiles:
reviewer:
default: block
servers:
filesystem:
allow: ["read_file", "list_directory", "search_files", "directory_tree", "get_file_info"]
github:
block: ["**"] # GitHub fully disabled for this agent
implementer:
default: allow
servers:
filesystem:
block: ["/.*delete.*/i", "remove_*", "edit_file", "write_file"]
github: {} # all GitHub tools allowed
noTools:
default: block
servers:
filesystem: { block: ["**"] }
github: { block: ["**"] }Измерения проводились на живом рукопожатии tools/list:
Профиль | Инструментов доступно | Полезная нагрузка | ~токенов |
| 5 | 2,926 символов | ~732 |
| 26 | 15,762 символов | ~3,941 |
| 0 | 2 символа | ~1 |
Токены оцениваются по эвристике ~4 символа на токен; реальная экономия — это поверхность схем, которую агент заново загружает в контекст на каждом ходу.
Инструменты, которые каждый профиль реально получил:
reviewer(только чтение, GitHub заблокирован):read_file,list_directory,directory_tree,search_files,get_file_infoimplementer(список запретов, GitHub разрешён):filesystem__read_file,github__read_file,filesystem__read_text_file,github__read_text_file,filesystem__read_media_file,github__read_media_file,filesystem__read_multiple_files,github__read_multiple_files,filesystem__create_directory,github__create_directory,filesystem__list_directory,github__list_directory,filesystem__list_directory_with_sizes,github__list_directory_with_sizes,filesystem__directory_tree,github__directory_tree,filesystem__move_file,github__move_file,filesystem__search_files,github__search_files,filesystem__get_file_info,github__get_file_info,filesystem__list_allowed_directories,github__list_allowed_directories,write_file,edit_filenoTools(всё заблокировано): (нет)
Два момента, на которые стоит обратить внимание:
Автопрефикс при коллизиях —
read_fileсуществует на обоих серверах, поэтому превращается вfilesystem__read_fileиgithub__read_file. Ноwrite_file/edit_fileсохраняют простые имена, потому что они заблокированы наfilesystem, иgithubостаётся единственным источником.Пустое представление профиля допустимо —
noTools(или любой профиль сblock: ["**"], или просто опущенный сервер) не показывает ни одного инструмента; агент всё равно подключается, просто ему нечего вызывать.
Быстрый старт
1. Установка и сборка
npm install
npm run build # compiles TypeScript to dist/2. Поместите секреты в .env (никогда в конфиг)
cp .env.example .env # then fill in your tokens3. Напишите mcp-proxy.yaml
version: 1
servers:
filesystem:
type: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "C:/repo"]
env:
ROOT: "C:/repo"
github:
type: http
url: https://api.github.com/mcp
headers:
Authorization: "${GITHUB_TOKEN}" # env-var reference, not a literal secret
profiles:
reviewer: # read-only, fail-closed
description: "Read-only agent"
default: block
servers:
filesystem:
allow: ["read_file", "list_directory", "directory_tree", "get_file_info"]
github:
allow: ["get_*", "list_*", "search_*"]
implementer: # deny-list, fail-open minus dangerous ops
description: "Full access minus destructive ops"
default: allow
servers:
filesystem:
block: ["/.*delete.*/i", "edit_file", "write_file"]
github:
block: ["merge_pull_request", "delete_*"]
defaultProfile: reviewer4. Запуск
node dist/cli/index.js --profile reviewer
# add --verbose for structured debug logging
node dist/cli/index.js --profile reviewer --verboseПриоритет профиля: --profile > MCP_PROFILE > defaultProfile.
Справочник по конфигурации
servers — вышестоящие MCP-серверы
stdio (запускается как дочерний процесс):
filesystem:
type: stdio
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "C:/repo"]
env: { ROOT: "C:/repo" }
prefix: fs__ # optional: override collision-prefix namespacehttp (Streamable HTTP):
github:
type: http
url: https://api.github.com/mcp
headers:
Authorization: "${GITHUB_TOKEN}"
prefix: gh__ # optionalprofiles — именованные представления инструментов
profiles:
my-profile:
description: "..." # optional
default: allow # allow | block (fallback when no rule matches)
servers:
github:
allow: ["get_*"] # optional allow-list
block: ["delete_*"] # optional block-list (always wins)
default: block # optional per-server fallback override
# filesystem omitted → none of its tools are exposedhttp — Streamable HTTP для клиента (режим serve)
Необязательный блок верхнего уровня, который превращает прокси в общий HTTP-сервер, обслуживающий множество агентов из одного процесса. См. Общий сервер (HTTP).
http:
host: 0.0.0.0 # default 127.0.0.1
port: 3000 # default 3000
path: /mcp # MCP endpoint (default /mcp)
metricsPath: /metrics # Prometheus metrics (default /metrics)
healthPath: /health # liveness (default /health)
readyPath: /ready # readiness (default /ready)
auth:
header: authorization # selector header (default authorization)
scheme: Bearer # optional prefix to strip
tokens: # token -> profile map (values may use ${VAR})
tok-reviewer: reviewer
tok-impl: implementer
defaultProfile: reviewer # optional fallback (fail-closed without it)Когда задан tokens, значение заголовка без схемы ищется в карте. Без tokens значение заголовка без схемы используется напрямую как имя профиля. Отсутствующий или неизвестный селектор откатывается к defaultProfile, а затем отклоняется (401/403), если ни один не подходит.
Секреты
Плейсхолдеры ${VAR} подставляются из окружения (или .env) при загрузке. В YAML хранится только имя переменной, поэтому его безопасно коммитить. Отсутствующая переменная заставляет загрузчик быстро завершаться с ошибкой — никаких молча пустых заголовков.
Подключите его к своему агенту
Прокси является MCP-сервером через stdio. Направьте своего агента на точку входа прокси вместо реального сервера, передав флаг профиля.
// .mcp.json — reviewer agent
{
"mcpServers": {
"proxy": {
"command": "node",
"args": ["C:/Dev/mcp-proxy/dist/cli/index.js", "--profile", "reviewer"]
}
}
}// .mcp.json — implementer agent (same proxy, different profile)
{
"mcpServers": {
"proxy": {
"command": "node",
"args": ["C:/Dev/mcp-proxy/dist/cli/index.js", "--profile", "implementer"]
}
}
}Каждый агент получает собственный stdio-процесс, поэтому профили полностью изолированы для каждого агента, а учётные данные никогда не пересекают границы процесса.
Общий сервер (HTTP)
Для централизованного развёртывания запустите serve, чтобы предоставить один Streamable HTTP-сервер, который используют многие агенты. Каждое подключение сопоставляется с профилем по его заголовку аутентификации:
node dist/cli/index.js serve --config mcp-proxy.yaml
# options: --host, --port (override http.host/http.port)Конечные точки:
Путь | Назначение |
| Конечная точка MCP Streamable HTTP (сессия на каждое подключение) |
| Живучесть — всегда |
| Готовность — |
| Текстовые метрики Prometheus (перечисленные/вызванные/заблокированные инструменты, задержка, состояние вышестоящих серверов) |
Разрешение профиля на каждое подключение работает по принципу fail-closed: подключение без пригодного селектора отклоняется (401), если не задан http.auth.defaultProfile, а селектор, ведущий к неизвестному профилю, отклоняется (403).
Конфигурация клиента для общего развёртывания (любой клиент, поддерживающий Streamable HTTP):
// .mcp.json — reviewer agent (token maps to the `reviewer` profile)
{
"mcpServers": {
"proxy": {
"type": "http",
"url": "https://proxy.example.com/mcp",
"headers": { "Authorization": "Bearer ${PROXY_TOKEN}" }
}
}
}// .mcp.json — implementer agent (same server, different token/profile)
{
"mcpServers": {
"proxy": {
"type": "http",
"url": "https://proxy.example.com/mcp",
"headers": { "Authorization": "Bearer ${PROXY_TOKEN_IMPL}" }
}
}
}Кодирующий агент Copilot читает .mcp.json репозитория; для других агентов используйте их собственное поле MCP-сервера (см. context/AGENT-SETUP.md и context/VENDOR-AGENTS.md).
Наблюдаемость
Запустите с --verbose, чтобы выводить структурированные журналы JSON-lines в stderr (сохраняя канал MCP stdio на stdout чистым):
{"timestamp":"2026-08-23T17:22:26.976Z","level":"info","message":"connected to upstream","server":"filesystem","tools":14}
{"timestamp":"2026-08-23T17:22:26.980Z","level":"debug","message":"tools/call","correlationId":"42","tool":"read_file","server":"filesystem"}Каждая запись tools/list и tools/call несёт correlationId MCP-запроса, поэтому отдельный запрос можно проследить через прокси и его вышестоящие серверы.
Чтобы оценить стоимость профиля в контексте, сравните количество инструментов и размер полезной нагрузки tools/list между профилями (см. пример с замерами выше): меньше рекламируемых инструментов — меньше схем попадает в промпт на каждом ходу.
В режиме serve считывайте /metrics для счётчиков, датчиков и гистограмм Prometheus: mcp_proxy_tools_listed_total, mcp_proxy_tools_called_total, mcp_proxy_tools_blocked_total, mcp_proxy_tool_call_duration_seconds и mcp_proxy_upstream_connections (все с метками profile/server/tool).
Устойчивость
Автопереподключение — если вышестоящий сервер (особенно запущенный stdio-процесс) умирает, прокси переподключается с экспоненциальной задержкой (500 мс → максимум 15 с, бесконечные попытки).
Живые обновления инструментов — когда вышестоящий сервер отправляет
notifications/tools/list_changed, прокси заново получает, повторно фильтрует и передаёт изменение дальше, поэтому агенты всегда видят точный список инструментов.Проверка аргументов — аргументы
tools/callпроверяются поinputSchemaвышестоящего сервера перед пересылкой; недопустимые вызовы отклоняются локально.
Разработка
npm run typecheck # tsc --noEmit
npm test # vitest (unit + integration + filesystem smoke)
npm run build # tsc → dist/Дополнительно
context/DESIGN.md — полный дизайн, решения и компромиссы.
context/SETUP.md — пошаговая настройка реальных серверов stdio + HTTP.
context/ROADMAP.md — v1.0 выпущена (HTTP-канал для клиента, профили на каждое подключение, наблюдаемость, упаковка).
mcp-proxy.yaml— рабочий пример конфигурации..env.example— шаблон переменных окружения.
This server cannot be deployed
Maintenance
Related MCP Connectors
Stateless MCP gateway and OTel span-streaming bridge for hosted MCP servers.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Streamable HTTP MCP gateway for NV oOS fleets — one API key, namespaced tools, quotas and policy.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceCentralized MCP control plane that proxies multiple upstream MCP servers with tool namespacing, filtering, policy enforcement, audit logging, and health checks.6 npmMIT
- FlicenseNot gradedqualityBmaintenanceActs as a gating proxy for MCP servers, merging tools and applying policy, verification, and audit layers.40 PyPI-
- AlicenseNot gradedqualityBmaintenanceProvides a governance proxy layer for MCP servers, enforcing per-tool allowlists, human approval for write operations, quotas, secret redaction, and a hash-chained audit log of all calls.MIT
- AlicenseNot gradedqualityCmaintenanceFronts many downstream MCP servers through one session, presenting a unified tool namespace with collision resolution, fleet-wide allow/deny/hide policy, per-tool rate limits, centralized audit logging, Prometheus metrics, and bearer-key auth at the edge. Supports both stdio drop-in and Streamable HTTP deployment so agents point at a single endpoint instead of N servers.MIT