AI Extensions: MCP SDK 2.3.0 для Python и TypeScript затягивает границы
Сервер, который молча принимает перенаправление на другой хост или неограниченный массив аргументов, доверяет больше, чем, возможно, задумал автор.

Сервер, который молча принимает перенаправление на другой хост или неограниченный массив аргументов, доверяет больше, чем, возможно, задумал автор. SDK MCP для Python и TypeScript выпустили 2.3.0 2 октября, и часть изменений касается именно этих границ.
Коротко
Официальные проекты: modelcontextprotocol/python-sdk (MIT) и modelcontextprotocol/typescript-sdk (лицензия переходит с MIT на Apache-2.0). Оба выпустили v2.3.0 2 октября. Наблюдение 3 октября: Python SDK - 24 468 звёзд, 4006 форков, последний push 2 октября в 22:02:27 UTC; TypeScript SDK - 13 504 звезды, 2248 форков, последний push 2 октября в 19:24:26 UTC. Звёзды показывают внимание, а не качество или безопасность. Я прочитал релизы и не устанавливал и не запускал SDK. ↗
Python SDK 2.3.0
Опубликован 2 октября в 22:02:27 UTC, установка pip install -U mcp по примечаниям к релизу. Сначала в примечаниях перечислены изменения поведения: нужен httpx2 версии 2.10.0 или новее; инструмент с неверной аннотацией x-mcp-header теперь не регистрируется, а не пропадает молча из списка клиента; пустые _meta и params больше не отправляются на старых соединениях; интерактивный вход OAuth больше не засчитывается во времени запроса. Новые параметры: max_sse_event_size (по умолчанию 1 МиБ на событие SSE) и MCPServer(subscriptions=False). ↗
TypeScript SDK 2.3.0
Опубликован 2 октября в 17:55:03 UTC. По заметкам об обновлении, экземпляр сервера теперь отклоняет connect(), если уже подключён, поэтому серверы HTTP без состояния должны создавать сервер и транспорт на каждый запрос. HTTP-транспорты клиента следуют перенаправлению только в пределах того же origin, если redirectPolicy не установлен в follow. Новые необязательные параметры: maxToolInputElements ограничивает число элементов массивов и членов объектов в аргументах инструмента, а expectedResource при проверке bearer-токена принимает только токены, выпущенные для этого сервера. Сопровождающие сообщают, что результат инструмента в 100 МБ одним событием ускорился с примерно минуты и 1,7 ГБ пиковой памяти до менее секунды и около 0,5 ГБ. Это их тест, не мой. ↗
Перед установкой: лицензии, поддержка, настройка
Файл LICENSE у Python SDK - MIT. В LICENSE у TypeScript SDK сказано о переходе с MIT на Apache-2.0: новый код под Apache-2.0, прежние вклады без согласия на смену лицензии остаются MIT, документация кроме спецификаций - CC-BY-4.0, так что называть его просто MIT или просто Apache-2.0 неточно. Оба репозитория получили push в течение суток до моей проверки и выпустили релизы в один день: это активность, а не доказательство пригодности для вашего сервера. Для пробы зафиксируйте версию, прочитайте заметки об обновлении для вашего транспорта и сначала проверьте на тестовом сервере.
Где проходят границы разрешений
Мой анализ: три изменения сужают то, что принимает сервер или клиент. Правило перенаправления в пределах origin ограничивает, куда может попасть запрос, предел элементов аргументов - сколько может нести вызов, проверка аудитории привязывает токен к одному серверу. Все три отключены или настраиваются, и ни одно не заменяет аутентификацию, авторизацию каждого инструмента и отношение к результатам инструментов как к недоверенному содержимому. Увеличение max_sse_event_size или redirectPolicy в значение follow сознательно ослабляет ограничение, поэтому записывайте причину.
Чек-лист, который я написал
Код ниже - упрощённое правило перенаправления с выдуманными адресами, выполненное локально. Оно разрешает тот же origin и переход с http на https на том же хосте и отклоняет другой хост или порт. Это не реализация SDK: прочитайте параметр транспорта и проверьте настоящее поведение перенаправления на тестовой точке.
Чек-лист в коде
from urllib.parse import urlsplit
def origin(url):
u = urlsplit(url)
port = u.port or {"http": 80, "https": 443}[u.scheme]
return (u.scheme, u.hostname, port)
def follow(src, dst):
a, b = origin(src), origin(dst)
if a == b:
return True
# http to https on the same host and default ports is allowed
return a[1] == b[1] and (a[0], b[0]) == ("http", "https") and a[2] == 80 and b[2] == 443
assert follow("https://api.example.test/mcp", "https://api.example.test/v2/mcp")
assert follow("http://api.example.test/mcp", "https://api.example.test/mcp")
assert not follow("https://api.example.test/mcp", "https://other.example.test/mcp")
assert not follow("https://api.example.test/mcp", "https://api.example.test:8443/mcp")
print("redirect policy checks passed")
# Invented URLs and a simplified rule, not the SDK's implementation.
Источники и ограничения
- https://github.com/modelcontextprotocol/python-sdk
- https://github.com/modelcontextprotocol/python-sdk/releases/tag/v2.3.0
- https://github.com/modelcontextprotocol/python-sdk/blob/main/LICENSE
- https://github.com/modelcontextprotocol/typescript-sdk
- https://github.com/modelcontextprotocol/typescript-sdk/releases/tag/v2.3.0
- https://github.com/modelcontextprotocol/typescript-sdk/blob/main/LICENSE