Co serwer odbiera i musi zweryfikować
Serwer nigdy nie loguje użytkowników. Robi to bramka, a potem wywołuje serwer własnym, krótkotrwałym tokenem i, dla systemu docelowego, poświadczeniem użytkownika. Jeśli używasz startera (mcp-platform-starter), wszystkie poniższe kontrole wykonuje on za ciebie.
Klient AI do bramki
Dział zatytułowany „Klient AI do bramki”Klienty AI używają OAuth z dostawcą tożsamości platformy (Keycloak). Bramka jest chronionym zasobem w rozumieniu RFC 9728:
| Co | Gdzie |
|---|---|
Metadane endpointu /mcp |
https://<platform>/.well-known/oauth-protected-resource/mcp |
| Metadane trasy jednego serwera | https://<platform>/.well-known/oauth-protected-resource/s/mcp-<system>/mcp |
| Klucze publiczne, którymi serwery weryfikują bramkę | https://<platform>/.well-known/mcp-jwks.json |
Żądanie bez ważnego tokenu dostaje 401 z WWW-Authenticate: Bearer resource_metadata="..." wskazującym te metadane. Klient idzie za wskazaniem i rozpoczyna logowanie. Klient obsługujący RFC 9728 nie potrzebuje innej konfiguracji. Gdy zestaw kluczy dostawcy tożsamości jest niedostępny, bramka odpowiada 503 z Retry-After i bez WWW-Authenticate, żeby klient nie rozpoczynał logowania, które nic nie da.
Bramka do serwera: wewnętrzny JWT (C2)
Dział zatytułowany „Bramka do serwera: wewnętrzny JWT (C2)”Bramka podpisuje nowy token dla każdego żądania do serwera (ES256). Serwer weryfikuje go zestawem kluczy spod MCP_GATEWAY_JWKS_URI.
| Claim | Znaczenie |
|---|---|
iss |
Wystawca bramki, domyślnie https://mcp-gateway.internal |
aud |
Identyfikator serwera odbierającego, mcp-<system>. Jeden ciąg znaków, nigdy lista |
sub |
sub użytkownika od dostawcy tożsamości; system:mcp-gateway w wariancie serwisowym |
usr |
preferred_username użytkownika |
grp |
Grupy użytkownika z prefiksem mcp- |
azp |
Identyfikator klienta OAuth klienta AI |
tool |
Nazwa narzędzia po stronie serwera. Obecne dokładnie przy tools/call i równe params.name |
use |
Cel: call dla endpointu MCP, policy-data dla wysyłki danych polityk |
rid |
Identyfikator żądania, musi być równy nagłówkowi X-Request-Id |
prv |
Rewizja danych polityk bramki |
mfa |
Sekundy od ostatniego logowania wieloskładnikowego, jeśli znane |
iat, exp, jti |
Czas wystawienia, wygaśnięcie i unikalny identyfikator |
Serwer musi odrzucić żądanie z 401, chyba że spełnione jest wszystko poniżej:
- podpis jest poprawny,
algto ES256, akidjest w zestawie kluczy (kidzaczynający się odtest-lubdev-tylko w trybie dev); audto identyfikator tego serwera;exp - iatwynosi najwyżej 60 sekund, aexpiiatmieszczą się w 30 sekundach od zegara serwera;jtinie był wcześniej użyty (każdy token jest jednorazowy; domyślnie pamięć, Redis przy wielu instancjach);ridjest równeX-Request-Id;useodpowiada celowi endpointu: tokenpolicy-dataprzytools/calljest odrzucany jakowrong_purpose, i odwrotnie;tooljest obecne przytools/calli nieobecne przy każdym innym żądaniu;- wariant serwisowy (bez użytkownika i bez
tool) pojawia się tylko przyinitialize,server/discover,tools/list,pingi wysyłce danych polityk.
GET /.well-known/mcp-manifest nie wymaga tokenu, a /actuator/health to jedyna inna otwarta ścieżka. Serwer nigdy nie czyta nagłówka Host i nie żąda certyfikatu klienta.
Wektory w mcp-contracts (test-vectors/jwt/) podają oczekiwany wynik każdej reguły, zobacz Kontrakty.
Co robi starter
Dział zatytułowany „Co robi starter”InternalJwtFilter wykonuje wszystkie powyższe kontrole przed jakimkolwiek kodem narzędzia. Odrzucenie to 401 z WWW-Authenticate: Bearer; powód trafia do logu (event=internal_jwt_rejected reason=<code>) i audytu, nie do wywołującego. Niedostępny zestaw kluczy lub Redis daje 503, nigdy przepuszczenie. Ustawiasz tylko adres zestawu kluczy i identyfikator serwera:
aiwcorpo.mcp.platform: server-id: mcp-notes internal-jwt: jwks-uri: ${MCP_GATEWAY_JWKS_URI}W narzędziu CallerContext.current() zwraca zweryfikowane subject, username, groups, client, tool, requestId, policyRevision i mfaAgeSeconds. Serwer nie decyduje, kto może wywołać narzędzie; robi to bramka. Serwer sprawdza zasoby, które narzędzie nazywa, zobacz Napisz serwer z szablonu.
Poświadczenie do systemu docelowego (C3)
Dział zatytułowany „Poświadczenie do systemu docelowego (C3)”Bramka pobiera poświadczenie użytkownika od brokera poświadczeń i wysyła je z każdym wywołaniem w dwóch nagłówkach: X-MCP-Credential (wartość) i X-MCP-Credential-Type. Typ to jeden z user-oauth, user-secret, token-exchange i service-account. Typ musi należeć do accepted-credential-types serwera; w przeciwnym razie serwer odpowiada internal_error, zanim dotknie polityki lub systemu.
BackendCredential credential = CredentialContext.require("notes");restClient.get() .uri("/api/records/{id}", id) .headers(credential::applyTo) .retrieve() .body(Record.class);applyTo ustawia Authorization: <scheme> <value>. CredentialContext.require rzuca wyjątek, gdy z żądaniem nie przyszło poświadczenie, a starter zamienia to na credential_required z linkiem do portalu z MCP_CONNECT_URL. Po odczytaniu starter ukrywa Authorization, X-MCP-Credential i X-PAT-* przed resztą żądania i usuwa wartość z każdego komunikatu i wpisu logu. Serwer nigdy nie przechowuje poświadczenia.
