Введение
С развитием AI-агентов и Model Context Protocol (MCP) актуальной становится проблема безопасности при работе с различными инструментами. Что если ваш AI-агент случайно прочитает конфиденциальный файл с токенами доступа и "случайно" поделится ими с вами в своем ответе, а учитывая логирование ваших запросов, он точно попадет на сервер провайдера вашего агента, а возможно еще и IDE, в которой этот агент и обитает, это создаст ряд неприятностей.
Ярким примером тулы, которая выполняет поиск по вашим локальным файлом является mcp-filesystem. Один шаг не туда и вам придется бежать за ревоком токена, а если он еще и от организации, ваши админы явно не скажут вам спасибо.
Не забываем про то, как у XAI токен просто лежал на github пару месяцев: статья
Для решения этой проблемы команда Lasso Security разработала MCP Gateway — прокси-сервер, который встает между AI-агентом и MCP-тулами, обеспечивая санитизацию чувствительной информации.
В этой статье я поделюсь результатами тестирования Lasso MCP Gateway в двух сценариях: интеграция с Cursor IDE и локальная реализация с собственным агентом на PydanticAI.
Что умеет Lasso MCP Gateway
1) Перехватывает вызовы всех MCP-инструментов перед их выполнением
2) Маскирует в basic
вариации (бесплатной):
azure client secret
github tokens
github oauth
gcp api key
aws access token
jwt token
gitlab session cookie
huggingface access token
microsoft teams webhook
slack app token\
Есть интеграция с presidio и самим lasso. Позволяет логировать вызовы через xetrack
Общий принцип работы
Идея MCP-gateway заключается в том, что он становится неким прокси между агентом и тулами. То есть он подключается не просто как отдельный MCP тул, а как MCP-тул, под которым работают другие тулы. То есть любой вызов плагина по типу read_file
create_file
и тд будет происходит после того как mcp-gateway
дал условное одобрение и провел санитизацию.

Эксперименты
Тестирование в Cursor
Настройка и установка Следуя примеру из readme.md
я подключил mcp-gateway
к cursor
, добавил путь к логированию через xetrack
. Как выглядит:
{
"mcpServers": {
"mcp-gateway": {
"command": "uvx",
"args": [
"mcp-gateway",
"--mcp-json-path",
"/home/user/.cursor/mcp.json",
"--plugin",
"basic",
"--plugin",
"xetrack"
],
"env": {
"XETRACK_LOGS_PATH": "logs/",
"XETRACK_DB_PATH": "tracing.db"
},
"servers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"."
]
}
}
}
}
}
Эксперимент 1: Чтение файла с HuggingFace токеном
Создал файл
echo 'HF_TOKEN = "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"' > tokens.txt
в нем теперь хранится выдуманный токен из Huggingface
Следуя снова же примеру из readme.md
пишу запрос

Cursor получил уже очищенный контекст и вернул мне <HUGGINGFACE_TOKEN>
.
Подробнее:
в тулу передался параметр:
{
"path": "~/lasso-mcp/sts/tokens.txt"
}
внутри tokens.txt
хранится мой выдуманный hf token
тула произвела очистку:
{ "_meta": null, "content": [
{ "type": "text", "text": "HF_TOKEN = \"<HUGGINGFACE_TOKEN>\n", "annotations": null } ], "isError": false }
затем тула вернула респанс cursor агенту и он вернул очищенный токен:
<HUGGINGFACE_TOKEN>
Эксперимент 2: Обход через контекст агента
Интересный нюанс: когда я попросил агента создать файл с кодом, передав токен непосредственно в чате, MCP Gateway не смог заблокировать эту операцию. Санитизация происходит только при чтении файлов, но не при генерации/обработке моего запроса
(что в принципе и не удивительно).


Когда я позже попросил прочитать созданный файл, MCP Gateway снова сработал корректно. Однако Cursor "помнил" о токене из контекста беседы и просто обновил информацию, вернув мой токен мне же обратно.



Эксперимент 3
Создаем .env
файл с выдуманным ключом от OpenAI: ключ:
OPENAIKEY=sk-qwejxakdjruewhjnsvasdj
Результат работы агента с явным вызовом mcp-gateway через наш запрос:

В общем говоря, если ваш токен не находится в списке токенов из начала, не надейтесь, что вам повезет и он не будет слит.
Local Implementation
Тестовые данные
Для локальной реализации был создан файл с 5-ю вариациями токенов:
HF_TOKEN = "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"
OPENAI_TOKEN = "sk-proj-SDASDFASDSDDSFDFSDADSdfsdfaHAJKDD5JASMDBXAHEG4123XDLKCXCDFSDFSAZXCN"
JWT_TOKEN ="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik..."
GITHUB_TOKEN_CLASSIC = "ghp_mS5asdaSJASHDqwDSAD4KD"
GITHUB_TOKEN = "github_pat_33SDSDJKJXKCJXIHFGAdfdsfkS4333Nwqe13weqeirurururrrr"
MCP
Не забудьте установить mcp-gateway
через uv/pip
или используйте uvx
Для запуска MCP-Gateway ему обязательно нужен mcp.json
{
"mcpServers": {
"mcp-gateway": {
"command": "uv",
"args": [
"run",
"mcp-gateway",
"-p",
"basic",
"-p",
"xetrack"
],
"env": {
"XETRACK_DB_PATH": "tracing.db",
"XETRACK_LOGS_PATH": "logs/"
},
"servers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"."
]
}
}
}
}
}
Агент
Реализовал простого агента через pydantic AI, с подключением к Lasso MCP-Gateway в базовой реализации (бесплатной) и логированием через xetrack
from typing import Optional
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIModel
from pydantic_ai.providers.openai import OpenAIProvider
from pydantic_ai.mcp import MCPServerStdio
import os
from dotenv import load_dotenv
import asyncio
load_dotenv()
async def get_custom_openai_provider_response(
model_name: str,
query: str,
provider_url: Optional[str] = None,
system: Optional[str] = "",
api_key: Optional[str] = None,
) -> str:
"""
Func to call custom open ai provider, using `provider_url` if needed
"""
lasso_gateway = MCPServerStdio(
command="uv",
args=["run","mcp-gateway","--mcp-json-path", "./mcp.json","--plugin","basic", "--plugin", "xetrack"],
env={"XETRACK_DB_PATH": "tracing.db", "XETRACK_LOGS_PATH": "logs/"}
)
model = OpenAIModel(
model_name=model_name,
provider=OpenAIProvider(base_url=provider_url, api_key=api_key),
)
agent = Agent(
model=model,
retries=5,
system_prompt=system,
mcp_servers=[lasso_gateway]
)
async with agent.run_mcp_servers():
result = await agent.run(query)
return result
async def main():
test_keys = [
"HF_TOKEN",
"OPENAI_TOKEN",
"JWT_TOKEN",
"GITHUB_TOKEN_CLASSIC",
"GITHUB_TOKEN"
]
for key in test_keys:
query = f"Use your tools to read the ./tokens.txt and return the {key}"
print(f"Testing key: {key}")
response = await get_custom_openai_provider_response(
model_name="gpt-4.1-nano-2025-04-14",
system="Use all available tools to help user with his response, be always helpful",
query=query,
api_key=os.getenv("OPENAI_API_KEY")
)
print("#############")
print(f"Response for {key}: {response}\n")
print("#############")
if __name__ == "__main__":
asyncio.run(main())
результат тестирования:
HF_TOKEN = "<HUGGINGFACE_TOKEN>
OPENAI_TOKEN = "sk-proj-..."
JWT_TOKEN = "<JWT_TOKEN>
GITHUB_TOKEN_CLASSIC = "ghp_mS5asdaSJASHDqwDSAD4KD"
GITHUB_TOKEN = "github_pat_33..
Обещанный github-токен даже не был санитизирован что в классической, что в новой версии.
Что логирует xetrack
Среди полезной информации, которую сохраняет xetrack:
capability_name: название инструмента (например,
read_file
)path: путь к файлу (
./tokens.txt
)content_text: содержимое после санитизации (
HF_TOKEN = "<HUGGINGFACE_TOKEN>"
)pattern: шаблоны поиска (если применимо)
и не только.
Важно: xetrack НЕ логирует запросы пользователя и финальные ответы агента — только вызовы MCP-серверов, которые с ним явно связаны в вашем mcp.json
.
Вывод
Summary
Достаточно игрушечное решение, которое вроде бы работает на уровне mcp/tool-call и выступает прослойкой между всеми вашими mcp-тулами. Не позволяет агенту прочитать токен доступа из существующих файлов/источников, просто санитизируя ответ перед добавлением в контекст агента.
Для кого
Может помочь: в разработке, чтобы не скормить случайно агенту ключ из условного .env и не делать revoke, однако поддерживает только ограниченное число вариаций этих ключей. Бесполезен если: Вы явно отдали агенту ключ или ваш вид ключа не поддерживается mcp-gateway, e.g. OpenAIApiKey.
Что работает хорошо:
Простая интеграция — настройка в клиент в виде Cursor, Windsurf, VS Code, Claude-desktop и другие, а также в ваш проект с агентом под капотом.
Полезное логирование — xetrack предоставляет информацию о результатах работы.
Работает как прокси — не нужно модифицировать существующие MCP-серверы, просто перетащить их в серверы
mcp-gateway
Санитизация - MCP Gateway маскирует почти все поддерживаемые типы токенов при чтении файлов
Что работает плохо:
Не защищает от контекста — если токен передан в чате, а не прочитан из файла, защита не сработает, поскольку ваш агент его запомнит.
Ограниченный набор паттернов — базовая версия поддерживает только ограниченные типы токенов
Lasso MCP Gateway как идея мне кажется достаточно интересным, поскольку я сам пользуюсь умными помощниками, которые иногда получают доступ к тем файлам, к которым мне не хотелось бы, особенно это актуально для IDE-агентов, которые сами могут пройтись по проекту и собрать контекст. Это не очень приятно и базовое предложение делать .gitignore
не всегда работает.
Но по факту это достаточно сырое решение, которое покрывает ограниченное количество кейсов, а в тех случаях когда вам критично хранение отдельных секретов, пока что, проще сделать свое решение.