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: Mavryn
Преимущества
Преимущество | Как помогает |
🔒 Защита 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 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 Connectors
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Guarded MCP server for agent-readable business truth, provenance, readiness, and discovery.
MCP Gateway: wrap any MCP server with cold-start retries, uptime SLA, and per-execution MPP billing.
Remote MCP server exposing SMI Aware tools, resources, and skills over Streamable HTTP.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceSelf-hosted MCP proxy and aggregation platform. Register multiple upstream MCP servers and expose them through a single unified endpoint with namespace routing, multi-transport support (HTTP/SSE, stdio, OpenAPI→MCP), per-tool overrides, and a web admin UI.16MIT
- AlicenseNot gradedqualityBmaintenanceCentralized MCP control plane that proxies multiple upstream MCP servers with tool namespacing, filtering, policy enforcement, audit logging, and health checks.16MIT
- AlicenseNot gradedqualityAmaintenanceAn authorizing reverse proxy for MCP servers that enforces per-call policy rules on tool arguments with audit logging, dry-run, and rate limiting.Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables serving multiple MCP toolkits behind one server with capability-based access control, so different callers see and can call only the tools they are authorized for, over stdio or streamable HTTP with bearer-token auth.MIT
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/DawidNowak/mcp-proxy'
If you have feedback or need assistance with the MCP directory API, please join our Discord server