SOLFIND
Web Lens
Portal home

OAuth-клиенты - MCP Python SDK

https://py.sdk.modelcontextprotocol.io/ru/client/oauth-clients/ • 90 KB fetched
Open original page


OAuth-клиенты - MCP Python SDK Перейти к содержанию MCP Python SDK OAuth-клиенты * en - English * de - Deutsch * es - español * fr - français * hi - हिन्दी * ja - 日本語 * ko - 한국어 * pt - português (Brasil) * ru - русский язык * tr - Türkçe * uk - українська мова * zh - 简体中文 * zh-hant - 繁體中文 Поиск modelcontextprotocol/python-sdk MCP Python SDK modelcontextprotocol/python-sdk * MCP Python SDK * Что нового в v2 * Начало работы Начало работы * Установка * Первые шаги * Подключение к настоящему хосту * Тестирование * Серверы Серверы * Инструменты * Структурированный вывод * Ресурсы * Шаблоны URI и безопасность путей * Промпты * Автодополнение * Медиа * Обработка ошибок * Внутри обработчика Внутри обработчика * Объект Context * Зависимости * Жизненный цикл * Элицитация * Многораундовые запросы (multi-round-trip) * Сэмплирование и корневые каталоги * Ход выполнения * Логирование * Подписки * Запуск сервера Запуск сервера * Добавление в существующее приложение * Развёртывание и масштабирование * Авторизация * OpenTelemetry * Обслуживание клиентов старого поколения * Объект Client Объект Client * Колбэки клиента * Клиентские транспорты * OAuth-клиенты OAuth-клиенты Содержание * Провайдер * Метаданные клиента * Хранилище токенов * Два обработчика * Подключение к Client * Что провайдер делает за вас * Попробуйте сами * Client ID Metadata Documents * Межмашинное взаимодействие * Когда возникает ошибка * Итоги * Утверждение идентичности * Группы сессий * Подписки * Подсказки по кэшированию * Версии протокола * Устаревшие возможности * Продвинутые темы Продвинутые темы * Низкоуровневый Server * Пагинация * Middleware * Расширения * MCP Apps * Устранение неполадок * Переводы * Migration Guide: v1 to v2 * API Reference Содержание * Провайдер * Метаданные клиента * Хранилище токенов * Два обработчика * Подключение к Client * Что провайдер делает за вас * Попробуйте сами * Client ID Metadata Documents * Межмашинное взаимодействие * Когда возникает ошибка * Итоги * MCP Python SDK * Объект Client OAuth-клиенты Машинный перевод Эта страница переведена с английской документации автоматически, и основной версией остаётся английская страница . Если что-то читается неправильно, на странице Переводы объясняется, как об этом сообщить. Некоторые MCP-серверы защищены. Отправьте им запрос без токена — и в ответ придёт 401 Unauthorized . OAuthClientProvider — это способ получить токен. Это вовсе не объект MCP. Это httpx2.Auth , стандартный хук httpx2 для задачи «сделать что-то с каждым запросом». Его подключают к httpx2.AsyncClient , передают этот клиент транспорту Streamable HTTP — и больше о нём не думают. Эта страница — о клиентской стороне. Как заставить собственный сервер требовать токен, описано на странице Авторизация . Провайдер client.py from urllib.parse import parse_qs , urlparse import httpx2 from pydantic import AnyUrl from mcp import Client from mcp.client.auth import AuthorizationCodeResult , OAuthClientProvider from mcp.client.streamable_http import streamable_http_client from mcp.shared.auth import OAuthClientInformationFull , OAuthClientMetadata , OAuthToken class InMemoryTokenStorage : def __init__ ( self ) -> None : self . tokens : OAuthToken | None = None self . client_info : OAuthClientInformationFull | None = None async def get_tokens ( self ) -> OAuthToken | None : return self . tokens async def set_tokens ( self , tokens : OAuthToken ) -> None : self . tokens = tokens async def get_client_info ( self ) -> OAuthClientInformationFull | None : return self . client_info async def set_client_info ( self , client_info : OAuthClientInformationFull ) -> None : self . client_info = client_info async def open_browser ( authorization_url : str ) -> None : print ( f "Visit: { authorization_url } " ) async def wait_for_callback () -> AuthorizationCodeResult : redirect_url = input ( "Paste the URL you were redirected to: " ) params = parse_qs ( urlparse ( redirect_url ) . query ) return AuthorizationCodeResult ( code = params [ "code" ][ 0 ], state = params [ "state" ][ 0 ], iss = params [ "iss" ][ 0 ] if "iss" in params else None , ) oauth = OAuthClientProvider ( server_url = "http://localhost:8001/mcp" , client_metadata = OAuthClientMetadata ( client_name = "Bookshop Agent" , redirect_uris = [ AnyUrl ( "http://localhost:3030/callback" )], scope = "user" , ), storage = InMemoryTokenStorage (), redirect_handler = open_browser , callback_handler = wait_for_callback , ) async def main () -> None : async with httpx2 . AsyncClient ( auth = oauth ) as http_client : transport = streamable_http_client ( "http://localhost:8001/mcp" , http_client = http_client ) async with Client ( transport ) as client : result = await client . list_tools () print ([ tool . name for tool in result . tools ]) Ему передают четыре вещи: * server_url : конечная точка MCP, к которой вы подключаетесь. Всё остальное провайдер выясняет по ней сам. * client_metadata : то, что вы ввели бы в форму «зарегистрировать приложение» на сервере авторизации. * storage : место, где токены хранятся между запусками. * redirect_handler и callback_handler : два момента, когда участвует человек. Больше нигде в файле OAuth не упоминается. main() токена не видит вовсе. Метаданные клиента OAuthClientMetadata — это настоящий регистрационный документ из RFC 7591 , оформленный как модель Pydantic. Вы задаёте три поля. Остальное заполняют значения по умолчанию: grant_types уже равно ["authorization_code", "refresh_token"] , а response_types — ["code"] , и это ровно тот сценарий, который выполняет провайдер. Check Поскольку это модель Pydantic, она проверяется ещё до того, как хоть один байт уйдёт в сеть . Опустите redirect_uris — и создание объекта тут же завершится ошибкой ValidationError , в которой названо поле: redirect_uris Field required [type=missing, input_value={'client_name': 'Bookshop Agent'}, input_type=dict] Браузер не открылся, и на сервере авторизации не осталось недоделанной регистрации. Хранилище токенов TokenStorage — это Protocol с четырьмя асинхронными методами. Наследоваться ни от чего не нужно: напишите эти методы — и любой класс станет хранилищем токенов: * get_tokens / set_tokens хранят OAuthToken : токен доступа, токен обновления, срок действия, область доступа (scope). * get_client_info / set_client_info хранят OAuthClientInformationFull , который сервер авторизации выдал, когда провайдер вас зарегистрировал, — включая ваш client_id . Версия в памяти из примера выше работает. Но при завершении процесса она всё забывает, так что при следующем запуске вся процедура повторяется с начала. Сохраняйте данные в файл или в связку ключей вашей платформы — и следующий запуск пройдёт тихо. Tip Сохраняйте client_info , а не только токены. Провайдер проходит динамическую регистрацию в первый раз, когда не находит сохранённого client_info . Выбросьте его — и при каждом запуске будет создаваться новая регистрация. Два обработчика Сценарию с кодом авторизации человек нужен ровно один раз: кто-то должен войти в систему и нажать «разрешить». * redirect_handler асинхронно вызывается с полностью собранным URL авторизации. client_id , redirect_uri , state и PKCE challenge в нём уже есть. Ваша единственная задача — открыть его в браузере. Настольное приложение вызывает webbrowser.open ; в этом файле URL просто печатается. * callback_handler вызывается следующим. Он ждёт, пока пользователь вернётся на ваш redirect_uri , и возвращает параметры запроса из этого перенаправления в виде AuthorizationCodeResult . Настоящий клиент вместо вызова input() поднимает небольшой локальный HTTP-сервер на URI перенаправления. Схема та же: принять перенаправление, вернуть code , state и iss . Warning Передавайте state и iss ровно в том виде, в каком они пришли. Провайдер сравнивает state со значением, которое сгенерировал сам, а iss — с издателем, которого обнаружил, и при несовпадении отказывает. Это защита от CSRF и от подмены сервера (mix-up). Подключение к Client Посмотрите на main() . Провайдер подключается к клиенту httpx2 , клиент httpx2 передаётся в streamable_http_client(url, http_client=...) , а этот транспорт — в Client . У streamable_http_client нет именованного параметра auth= . Всё, что относится к уровню HTTP (аутентификация, заголовки, тайм-ауты, прокси), настраивается на httpx2.AsyncClient , который вы приносите сами. Об этом разделении уровней — на странице Транспорты клиента . Что провайдер делает за вас Когда Client отправляет первый запрос, сервер отвечает 401 . Дальше действует провайдер: * Обнаружение. Он читает заголовок WWW-Authenticate , загружает метаданные защищённого ресурса (Protected Resource Metadata) сервера с /.well-known/oauth-protected-resource , узнаёт, какой сервер авторизации защищает этот ресурс, и загружает метаданные уже того сервера. (У сервера постарше, который не публикует метаданные ресурса, вместо этого запрашиваются метаданные сервера авторизации по его собственному origin.) В любом случае метаданные должны называть в поле issuer тот сервер, для которого они были загружены; всё остальное отклоняется. * Регистрация. В хранилище пусто? Он динамически регистрирует вас с вашим OAuthClientMetadata и сохраняет результат. * Авторизация. Он генерирует пару PKCE и state , собирает URL авторизации, ждёт ваш redirect_handler , а затем ждёт от callback_handler код. * Обмен. Он обменивает код на OAuthToken , сохраняет его и повторяет исходный запрос уже с Authorization: Bearer ... . После этого он работает незаметно. Токены берутся из хранилища, истёкший токен доступа обновляется с помощью токена обновления, и только когда ничего из этого не срабатывает, сценарий запускается заново. Ко всем этим запросам применяется одно транспортное правило: как и MCP-запрос, внутри которого они выполняются, они следуют перенаправлению только тогда, когда оно остаётся на том же origin и сохраняет метод (скажем, 307/308 на завершающий слеш), а любое другое перенаправление считают тем, что этот URL не отвечает. Ничего из этого вы не писали. Остаются два именованных аргумента ( client_metadata_url и validate_resource_url ), и этому файлу не нужен ни один из них. О client_metadata_url стоит знать — ему посвящён отдельный раздел ниже. Попробуйте сами Client(server) в памяти, которым пользуются ваши тесты, здесь не поможет: весь смысл сценария в HTTP-ответе 401 , а между клиентом в памяти и его сервером никакого HTTP нет. В репозитории есть живая версия. examples/servers/simple-auth/ запускает отдельный сервер авторизации и защищённый MCP-сервер; examples/clients/simple-auth-client/ — это клиент с этой страницы, выросший в небольшой CLI. В его README — две команды: запустите серверы, запустите клиент, подключив его к ним, — и наблюдайте, как проходят все четыре шага. Client ID Metadata Documents Ревизия спецификации 2026-07-28 объявляет динамическую регистрацию клиентов устаревшей в пользу Client ID Metadata Documents (CIMD). Вместо того чтобы отправлять POST-запросом новую регистрацию каждому встреченному серверу авторизации, клиент публикует один JSON-документ о себе по стабильному HTTPS URL — и этот URL и есть его client_id . Документ загружает сервер авторизации; провайдер его вообще не трогает. SDK это уже умеет: передайте URL как client_metadata_url= при создании провайдера. Если метаданные сервера авторизации объявляют client_id_metadata_document_supported: true , провайдер полностью пропускает запрос /register : URL идёт в сценарий как client_id , а client_secret нет вовсе. Если сервер этого не объявляет (большинство пока не объявляет) или URL не передан, провайдер молча откатывается к динамической регистрации, и всё описанное выше работает ровно так, как описано. Сохранённый client_info по-прежнему имеет приоритет над обоими вариантами. URL должен быть HTTPS и с некорневым путём; всё остальное — ValueError при создании, до любого обращения к сети. Поставляемый пример examples/clients/simple-auth-client/ принимает его в переменной окружения MCP_CLIENT_METADATA_URL . Межмашинное взаимодействие Ночное задание, шаг CI, другой сервис. Браузера нет, и нажать «разрешить» некому. Это грант client credentials : client_id и client_secret у вас уже есть, а весь сценарий сводится к конечной точке токенов. ClientCredentialsOAuthProvider — тот же httpx2.Auth , только без человека: client.py import httpx2 from mcp import Client from mcp.client.auth.extensions.client_credentials import ClientCredentialsOAuthProvider from mcp.client.streamable_http import streamable_http_client from mcp.shared.auth import OAuthClientInformationFull , OAuthToken class InMemoryTokenStorage : def __init__ ( self ) -> None : self . tokens : OAuthToken | None = None self . client_info : OAuthClientInformationFull | None = None async def get_tokens ( self ) -> OAuthToken | None : return self . tokens async def set_tokens ( self , tokens : OAuthToken ) -> None : self . tokens = tokens async def get_client_info ( self ) -> OAuthClientInformationFull | None : return self . client_info async def set_client_info ( self , client_info : OAuthClientInformationFull ) -> None : self . client_info = client_info oauth = ClientCredentialsOAuthProvider ( server_url = "http://localhost:8001/mcp" , storage = InMemoryTokenStorage (), client_id = "reporting-agent" , client_secret = "..." , scope = "user" , issuer = "http://localhost:9000" , ) async def main () -> None : async with httpx2 . AsyncClient ( auth = oauth ) as http_client : transport = streamable_http_client ( "http://localhost:8001/mcp" , http_client = http_client ) async with Client ( transport ) as client : result = await client . list_tools () print ([ tool . name for tool in result . tools ]) Что изменилось: * Нет OAuthClientMetadata , нет обработчиков. Вы передаёте client_id и client_secret ; провайдер строит вокруг них минимальную регистрацию client_credentials и полностью пропускает динамическую регистрацию. * issuer называет сервер авторизации, который выдал эти учётные данные; используйте значение issuer , которое возвращает его документ /.well-known/oauth-authorization-server . Обнаружение по-прежнему проходит, как описано выше, но запросы токена строятся только по метаданным этого издателя; если MCP-сервер указывает куда-то ещё, сценарий останавливается с OAuthFlowError . Не указывать его — устаревший вариант, и в 3.0 параметр станет обязательным (см. Устаревшие возможности ); до тех пор провайдер выдаёт предупреждение и использует тот сервер авторизации, который найдёт обнаружение. * scope — строка с разделением пробелами, формат OAuth для передачи по сети. * Всё дальше по цепочке идентично: тот же TokenStorage , тот же httpx2.AsyncClient(auth=...) , тот же streamable_http_client . По умолчанию секрет передаётся в запросе токена через HTTP Basic auth ( client_secret_basic ). Передайте token_endpoint_auth_method="client_secret_post" , чтобы вместо этого поместить его в тело формы. Некоторые серверы авторизации принимают только один из двух способов. Tip Читайте client_secret из окружения или менеджера секретов, никогда не из системы контроля версий. Info Ещё один провайдер находится в mcp.client.auth.extensions.client_credentials : PrivateKeyJWTOAuthProvider — для клиентов, которые аутентифицируются с помощью JWT вместо общего секрета ( private_key_jwt , вариант с парой ключей и workload identity). Схема та же: создайте экземпляр (он принимает тот же необязательный issuer ) и передайте его в auth= . В том же модуле есть SignedJWTParameters и static_assertion_provider — два вспомогательных средства, которые собирают для него утверждение (assertion). Есть ещё одна ситуация без человека: клиент принадлежит организации, чей провайдер удостоверений, а не пользователь, решает, к каким MCP-серверам он может обращаться. Это другой грант со своей моделью доверия и своей страницей — Подтверждение идентичности . Когда возникает ошибка Когда сценарий OAuth идёт не так, провайдер выбрасывает OAuthFlowError из mcp.client.auth . У него два подкласса. OAuthRegistrationError означает, что регистрация не дала пригодного клиента: сервер авторизации отказал в регистрации или всё же зарегистрировал вас, но с учётными данными, которые этот сценарий использовать не может (например, с методом аутентификации, который он не реализует). OAuthTokenError означает, что получить токен не удалось: конечная точка токенов ответила отказом, или в сохранённой записи клиента указан метод аутентификации, который этот клиент применить не может, — об этом сообщается при сборке запроса токена, а не после его отправки. Один except OAuthFlowError: охватывает обнаружение, регистрацию, авторизацию и обмен. Не всё — ошибка сценария. Сеть по-прежнему может подвести; это обычные исключения httpx2 , и они проходят насквозь без изменений. Итоги * OAuthClientProvider — это httpx2.Auth . Подключите его к httpx2.AsyncClient , передайте тот в streamable_http_client(url, http_client=...) — и Client так и не узнает, что был OAuth. * От вас нужны четыре вещи: URL сервера, OAuthClientMetadata , TokenStorage и пара обработчиков redirect/callback. * TokenStorage — это Protocol : четыре асинхронных метода, без базового класса. Сохраняйте client_info наряду с токенами. * Обнаружение, регистрация (динамическая или через Client ID Metadata Document ), PKCE, проверки state и iss и обновление токенов — забота провайдера, а не ваша. * ClientCredentialsOAuthProvider — версия без человека: client_id + client_secret , без обработчиков, без браузера. * Любой сбой OAuth — это OAuthFlowError ; OAuthRegistrationError и OAuthTokenError — его подклассы. Вторая половина этого рукопожатия — как заставить ваш сервер требовать токен — на странице Авторизация . К началу Назад Клиентские транспорты Вперед Утверждение идентичности Made with Zensical

Links found on this page

  1. Перейти к содержанию [direct]
  2. en - English [direct]
  3. de - Deutsch [direct]
  4. es - español [direct]
  5. fr - français [direct]
  6. hi - हिन्दी [direct]
  7. ja - 日本語 [direct]
  8. ko - 한국어 [direct]
  9. pt - português (Brasil) [direct]
  10. ru - русский язык [direct]
  11. tr - Türkçe [direct]
  12. uk - українська мова [direct]
  13. zh - 简体中文 [direct]
  14. zh-hant - 繁體中文 [direct]
  15. modelcontextprotocol/python-sdk [direct]
  16. Что нового в v2 [direct]
  17. Начало работы [direct]
  18. Установка [direct]
  19. Первые шаги [direct]
  20. Подключение к настоящему хосту [direct]
  21. Тестирование [direct]
  22. Серверы [direct]
  23. Инструменты [direct]
  24. Структурированный вывод [direct]
  25. Ресурсы [direct]
  26. Шаблоны URI и безопасность путей [direct]
  27. Промпты [direct]
  28. Автодополнение [direct]
  29. Медиа [direct]
  30. Обработка ошибок [direct]
  31. Внутри обработчика [direct]
  32. Объект Context [direct]
  33. Зависимости [direct]
  34. Жизненный цикл [direct]
  35. Элицитация [direct]
  36. Многораундовые запросы (multi-round-trip) [direct]
  37. Сэмплирование и корневые каталоги [direct]
  38. Ход выполнения [direct]
  39. Логирование [direct]
  40. Подписки [direct]
  41. Запуск сервера [direct]
  42. Добавление в существующее приложение [direct]
  43. Развёртывание и масштабирование [direct]
  44. Авторизация [direct]
  45. OpenTelemetry [direct]
  46. Обслуживание клиентов старого поколения [direct]
  47. Объект Client [direct]
  48. Колбэки клиента [direct]
  49. Клиентские транспорты [direct]
  50. Утверждение идентичности [direct]
  51. Группы сессий [direct]
  52. Подписки [direct]
  53. Подсказки по кэшированию [direct]
  54. Версии протокола [direct]
  55. Устаревшие возможности [direct]
  56. Продвинутые темы [direct]
  57. Низкоуровневый Server [direct]
  58. Пагинация [direct]
  59. Middleware [direct]
  60. Расширения [direct]
  61. MCP Apps [direct]
  62. Устранение неполадок [direct]
  63. Переводы [direct]
  64. Migration Guide: v1 to v2 [direct]
  65. API Reference [direct]
  66. английская страница [direct]
  67. RFC 7591 [direct]
  68. Zensical [direct]