Авторизація
Машинний переклад
Цю сторінку перекладено автоматично з англомовної документації, і основною версією є англомовна сторінка. Якщо щось читається неправильно, на сторінці Переклади пояснено, як про це повідомити.
Через Streamable HTTP ваш MCP-сервер — це звичайний вебсервіс, і захищати його слід так само, як будь-який інший вебсервіс: bearer-токенами OAuth 2.1.
У термінах OAuth ваш сервер — це сервер ресурсів (resource server). Він ніколи нікого не автентифікує й ніколи не видає токенів. Він робить одне: дивиться на заголовок Authorization у кожному запиті й вирішує, чи придатний токен у ньому.
Ця сторінка — про серверний бік. Клієнт, який знаходить ваш сервер авторизації й отримує токен, описано на сторінці Клієнти OAuth.
Три сторони
- Сервер авторизації автентифікує людей і видає токени доступу. Ви його не пишете. Це ваш постачальник ідентичності (Auth0, Keycloak, Entra, ваш власний).
- Сервер ресурсів — це ваш MCP-сервер. Він перевіряє токен у кожному запиті.
- Клієнт з'ясовує, якому серверу авторизації ви довіряєте, отримує від нього токен і надсилає його вам як
Authorization: Bearer <token>.
Оце й увесь трикутник. Усе на цій сторінці стосується середнього пункту.
Верифікатор токенів
SDK не має власної думки про те, який вигляд має дійсний токен. Це визначаєте ви, реалізувавши TokenVerifier:
from pydantic import AnyHttpUrl
from mcp.server import MCPServer
from mcp.server.auth.provider import AccessToken, TokenVerifier
from mcp.server.auth.settings import AuthSettings
KNOWN_TOKENS = {
"alice-token": AccessToken(token="alice-token", client_id="alice", scopes=["notes:read"]),
}
class StaticTokenVerifier(TokenVerifier):
async def verify_token(self, token: str) -> AccessToken | None:
return KNOWN_TOKENS.get(token)
mcp = MCPServer(
"Notes",
token_verifier=StaticTokenVerifier(),
auth=AuthSettings(
issuer_url=AnyHttpUrl("https://auth.example.com"),
resource_server_url=AnyHttpUrl("http://127.0.0.1:8000/mcp"),
required_scopes=["notes:read"],
),
)
@mcp.tool()
def list_notes() -> list[str]:
"""List every note in the notebook."""
return ["Buy milk", "Ship the release"]
TokenVerifier— це протокол з одним асинхронним методом.verify_tokenотримує сирий токен із заголовкаAuthorizationі повертаєAccessToken, якщо він дійсний, абоNone, якщо ні. Більше нічого реалізовувати не треба.- Цей верифікатор шукає токен у таблиці. Справжній перевіряє підпис JWT або звертається до кінцевої точки інтроспекції токенів сервера авторизації. Цей код — ваш; SDK лише викликає його.
token_verifier=іauth=завжди йдуть разом. Передайте одне без іншого — іMCPServer(...)викинеValueErrorще до того, як обслужить хоч один запит.
AuthSettings — це публічне обличчя вашого сервера ресурсів:
issuer_url: сервер авторизації, що видає ваші токени.resource_server_url: публічний URL цієї MCP-кінцевої точки. Він указує, для якого ресурсу призначено токен, і саме тут розміщено документ виявлення.required_scopes: кожен токен мусить містити їх усі.
Tip
examples/servers/simple-auth/ у репозиторії SDK містить IntrospectionTokenVerifier, який звертається
до кінцевої точки RFC 7662 справжнього сервера авторизації. Саме таку форму мають більшість продакшн-верифікаторів.
Що з'являється через HTTP
Авторизація живе в HTTP-заголовках, тож існує лише на HTTP-транспортах. Запустіть сервер на тому, який розгортаєте: mcp.run(transport="streamable-http") розміщує його на http://127.0.0.1:8000/mcp, а решта — на сторінці Запуск сервера. Тепер застосунок має два маршрути:
/mcp
/.well-known/oauth-protected-resource/mcp
Ви зареєстрували один інструмент. Другий маршрут належить SDK.
Виявлення
Зробіть GET на цей well-known-шлях — і отримаєте RFC 9728 Protected Resource Metadata, побудовані безпосередньо з вашого AuthSettings:
{
"resource": "http://127.0.0.1:8000/mcp",
"authorization_servers": ["https://auth.example.com/"],
"scopes_supported": ["notes:read"],
"bearer_methods_supported": ["header"]
}
Саме завдяки цьому документу клієнт, який ніколи не чув про ваш сервер, знаходить дорогу: він читає authorization_servers і йде туди по токен. Ви не написали з нього жодного рядка.
Check
Зверніться до /mcp без токена (або з таким, для якого ваш верифікатор повернув None) — і запит
зупинять на порозі:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", error_description="Authentication required", resource_metadata="http://127.0.0.1:8000/.well-known/oauth-protected-resource/mcp"
{"error": "invalid_token", "error_description": "Authentication required"}
Нічого не було розібрано, і жоден інструмент не виконався. А вказівник resource_metadata у WWW-Authenticate —
саме те, що робить виявлення автоматичним: 401 -> документ метаданих -> сервер авторизації -> токен -> повторна спроба.
Warning
Ніщо з цього не захищає stdio. Канал не має заголовка Authorization, тож token_verifier там ніколи
не викликається. Межа безпеки stdio-сервера — це процес, який його запустив. Те саме
стосується Client(mcp) у пам'яті, який ви використовуєте в тестах: він під'єднується безпосередньо до об'єкта сервера
й оминає HTTP-рівень, разом з авторизацією.
Ідентичність того, хто викликає
Усередині будь-якого обробника get_access_token() — це AccessToken, який ваш верифікатор повернув для поточного запиту:
from pydantic import AnyHttpUrl
from mcp.server import MCPServer
from mcp.server.auth.middleware.auth_context import get_access_token
from mcp.server.auth.provider import AccessToken, TokenVerifier
from mcp.server.auth.settings import AuthSettings
KNOWN_TOKENS = {
"alice-token": AccessToken(token="alice-token", client_id="alice", scopes=["notes:read"]),
}
class StaticTokenVerifier(TokenVerifier):
async def verify_token(self, token: str) -> AccessToken | None:
return KNOWN_TOKENS.get(token)
mcp = MCPServer(
"Notes",
token_verifier=StaticTokenVerifier(),
auth=AuthSettings(
issuer_url=AnyHttpUrl("https://auth.example.com"),
resource_server_url=AnyHttpUrl("http://127.0.0.1:8000/mcp"),
required_scopes=["notes:read"],
),
)
@mcp.tool()
def whoami() -> str:
"""Report which OAuth client is calling."""
token = get_access_token()
if token is None:
return "anonymous"
return f"{token.client_id} (scopes: {', '.join(token.scopes)})"
- Це працює в інструментах, ресурсах і промптах, і нічого нікуди передавати не треба: middleware авторизації зберігає його в контекстній змінній для кожного запиту.
- Повертається той самий об'єкт, який побудував ваш верифікатор:
client_id,scopes,subject,expires_atі будь-які додатковіclaims, які ви додали. Це й є зачіпка для правил на рівні окремих інструментів: прочитайте scopes і відмовте. - Поза автентифікованим HTTP-запитом він повертає
None. У пам'яті й черезstdioце завждиNone.
Викличте whoami з Authorization: Bearer alice-token — і модель прочитає:
alice (scopes: notes:read)
Половина, якої SDK не робить
SDK дає вам половину сервера ресурсів: перевірити, оголосити, відмовити. Він не дає сторінки входу, екрана згоди чи токена.
Щоб побачити всі три сторони в русі, запустіть examples/servers/simple-auth/ з репозиторію SDK (невеликий сервер авторизації та сервер ресурсів, налаштований точно як на цій сторінці), а потім спрямуйте на нього examples/clients/simple-auth-client/, щоб пройти повний шлях виявлення й отримання токена.
Info
Є другий аргумент конструктора, auth_server_provider=, який вбудовує повноцінний сервер
авторизації всередину вашого MCP-сервера. Він з'явився ще до розділення AS/RS, навколо якого
побудовано специфікацію авторизації MCP. Новим серверам не слід до нього вдаватися.
Сервер авторизації також може прийняти підписане твердження корпоративного постачальника ідентичності замість того, щоб користувач проходив екран згоди, і SDK підтримує обидва боки цього обміну. Про цей grant і клієнта, що його пред'являє, — на сторінці Твердження ідентичності.
Підсумки
- Через Streamable HTTP ваш сервер — це сервер ресурсів OAuth 2.1: він перевіряє токени й ніколи їх не видає.
TokenVerifier— це вся поверхня інтеграції: один асинхронний метод, на вході токен, на виходіAccessToken | None.token_verifier=іauth=AuthSettings(issuer_url=..., resource_server_url=..., required_scopes=[...])завжди йдуть разом.- SDK публікує RFC 9728 Protected Resource Metadata за адресою
/.well-known/oauth-protected-resource/...і відповідає на неавтентифіковані запити кодом 401, чий заголовокWWW-Authenticateвказує на них. Оце й уся історія виявлення. get_access_token()у будь-якому обробнику — це той, хто викликає.- Авторизація — справа HTTP.
stdioта клієнт у пам'яті ніколи її не бачать.
Клієнтська половина (виявлення вашого сервера авторизації й отримання токена за вас) — на сторінці Клієнти OAuth. А клієнт, який стверджує ідентичність замість того, щоб запитувати її в користувача, — на сторінці Твердження ідентичності.