1. MCP Jam jako laboratorium do autoryzacji
MCP Jam — to nie „jeszcze jedno dziwne narzędzie”, lecz twój stanowisko laboratoryjne, które potrafi grać rolę klienta MCP. W istocie to emulator zachowania ChatGPT w pracy z serwerem MCP: umie czytać .well-known/oauth-protected-resource, uruchamiać OAuth‑flow, dołączać tokeny do żądań i pokazywać, co dokładnie poszło nie tak.
Bardzo ważny aspekt praktyczny: jeśli osiągniesz udany Default OAuth-flow w MCP Jam, jesteś mniej więcej w 80 % gotowy do integracji z prawdziwym ChatGPT App. Wszystko, co robi ChatGPT przy podpinaniu konta (linkowaniu), Jam już potrafi — tylko z bardziej przejrzystymi logami i przyciskami.
W poprzednim wykładzie skonfigurowaliśmy podstawową autoryzację dla naszego ćwiczebnego serwera MCP GiftGenius: wybraliśmy wariant weryfikacji tokena (JWT lub introspection), zaimplementowaliśmy .well-known/oauth-protected-resource i middleware, który chroni narzędzia. Teraz zobaczymy, jak to wszystko zachowuje się w MCP Jam w różnych trybach autoryzacji.
Naszym celem w tym wykładzie jest nauczyć się:
- świadomie przełączać tryby autoryzacji w Jam (None, Bearer, OAuth with credentials, Default OAuth);
- rozumieć, co dokładnie Jam wysyła do serwera MCP w każdym trybie;
- diagnozować, która część systemu się zepsuła: MCP Server, Auth Server czy metadane;
- sprawdzić, że chronione narzędzia działają tylko z tokenem, a otwarte — również bez niego.
2. Nasz ćwiczebny serwer MCP: co testujemy
Aby nie mówić abstrakcyjnie, krótko przypomnijmy kontekst. Kontynuujemy historię z naszym ćwiczebnym aplikacją GiftGenius — to ChatGPT‑App, który pomaga dobierać prezenty i pokazuje użytkownikowi jego zamówienia oraz listy życzeń.
Po stronie serwera MCP mamy już:
- otwarte narzędzie, np. search_gifts — można je wywoływać anonimowo;
- chronione narzędzie, np. list_user_orders — powinno działać tylko dla uwierzytelnionego użytkownika i wymagać scope mcp:tools.
Serwer potrafi:
- publikować .well-known/oauth-protected-resource;
- weryfikować token (JWT lub przez introspection — wybrałeś jedno podejście w poprzednim wykładzie);
- wyciągać z tokena sub (user id), scope, aud i przekazywać je do handlerów narzędzi.
Typowy middleware weryfikujący token w Node.js/TypeScript może wyglądać tak:
// middleware/auth.ts
export function requireScope(requiredScope: string) {
return async (req: any, res: any, next: () => void) => {
const header = req.headers["authorization"];
if (!header?.startsWith("Bearer ")) {
res
.status(401)
.set(
"WWW-Authenticate",
`Bearer realm="mcp", resource_metadata="${process.env.BASE_URL}/.well-known/oauth-protected-resource", scope="${requiredScope}"`
)
.json({ error: "unauthorized" });
return;
}
// tutaj weryfikujesz token (podpis, exp, aud, scope...)
// i zapisujesz wynik w req.user
next();
};
}
Ten middleware będzie używany przed chronionymi narzędziami MCP. Jeśli brakuje tokena — zwracamy 401 i poprawny WWW-Authenticate z resource_metadata, jak wymaga specyfikacja MCP Authorization. Szczegółową analizę weryfikacji tokena i implementacji funkcji pomocniczych robiłeś już w poprzednim wykładzie — tutaj traktujemy to jako daną.
3. Tryby autoryzacji w MCP Jam: przegląd
W MCP Jam istnieje kilka trybów autoryzacji do podłączenia z serwerem MCP. Odpowiadają one typowym wzorcom OAuth — od całkowitego braku tokena po pełny Authorization Code + PKCE.
Krótko wymieńmy:
- None (No Auth) — Jam w ogóle nie dodaje nagłówka Authorization. To dostęp anonimowy. Nadaje się do otwartych serwerów MCP i sprawdzenia, że zasoby zamknięte poprawnie odmawiają z 401 i WWW-Authenticate.
- Bearer Token — Jam dodaje Authorization: Bearer <token>, który wprowadzasz ręcznie w interfejsie. Nadaje się do szybkich testów: token został już gdzieś pozyskany (curl, UI Keycloak/IdP), a ty chcesz sprawdzić zachowanie zasobu MCP.
- OAuth with credentials (Client Credentials) — Jam sam pozyskuje token przez client_credentials u serwera autoryzacji, używając podanych Client ID i Secret. To tryb „klienta poufnego”, bardziej przypominający autoryzację serwer‑serwer bez udziału użytkownika.
- Default OAuth (Authorization Code + PKCE) — główny tryb dla klientów podobnych do ChatGPT (public client bez sekretu). Jam sam czyta resource_metadata, znajduje serwer autoryzacji, uruchamia przeglądarkę z /authorize, przeprowadza PKCE‑flow i uzyskuje token użytkownika.
Dla przejrzystości zbierzmy to w tabelę.
| Tryb w Jam | Co wysyła Jam | Kto pozyskuje token | Typowy scenariusz |
|---|---|---|---|
| None | Brak Authorization | Nikt | Anonimowe narzędzia, sprawdzenie 401 |
| Bearer Token | Bearer <ręczny> | Ty (curl, interfejs IdP) | Testowanie logiki serwera zasobów (Resource Server) |
| OAuth with cred. | Bearer <client token> | Jam poprzez client_credentials | Narzędzia serwisowe/administracyjne |
| Default OAuth | Bearer <user token> | Jam przez Authorization Code+PKCE | Logowanie użytkownika jak w ChatGPT |
Teraz przejdziemy przez każdy tryb i zobaczymy, jak przepuścić przez niego nasz serwer MCP GiftGenius.
4. Tryb None: sprawdzamy, że serwer poprawnie odmawia
Zacznijmy od najbardziej prymitywnego trybu: żadnej autoryzacji.
W MCP Jam wybierasz swój serwer (np. http://localhost:4000/mcp) i w ustawieniach połączenia ustawiasz tryb autoryzacji None.
Co się wtedy dzieje:
- Jam ustanawia połączenie MCP;
- przy wywołaniu narzędzia nie dodaje nagłówka Authorization;
- możesz wywoływać dowolne otwarte narzędzia (np. search_gifts);
- przy wywołaniu narzędzia chronionego (np. list_user_orders) twój serwer powinien odpowiedzieć 401 Unauthorized.
Ważne, aby serwer w tej odpowiedzi 401 dodał poprawny WWW-Authenticate. Przykład odpowiedzi z dodatkowymi polami realm i scope, zbliżony do zalecanego przez OpenAI i spec MCP Authorization:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
resource_metadata="https://giftgenius.example.com/.well-known/oauth-protected-resource",
scope="mcp:tools"
Content-Type: application/json
{"error": "unauthorized"}
Jam, widząc taką odpowiedź, rozumie: zasób jest chroniony, stąd ma brać metadane (resource_metadata) i jakich scopes się oczekuje. W trybie None po prostu pokaże błąd, lecz w trybie Default OAuth automatycznie pójdzie pod wskazane resource_metadata i uruchomi OAuth‑flow.
Z punktu widzenia debugowania, w trybie None sprawdzasz:
- że otwarte narzędzia działają zupełnie bez tokena;
- że chronione narzędzia nigdy nie wykonują się anonimowo;
- że nagłówek WWW-Authenticate jest zgodny ze specyfikacją (zawiera Bearer i resource_metadata).
Brzmi jak trywialny test, ale ogrom problemów zaczyna się od tego, że 401 jest zwracany bez WWW-Authenticate lub z błędnym parametrem (np. przestarzałe resource_metadata_uri zamiast aktualnego resource_metadata).
5. Tryb Bearer Token: szybki test logiki Resource Server
Kolejny krok — tryb, w którym masz już ważny token (pozyskany poza Jam) i chcesz sprawdzić właśnie logikę Resource Server: czy poprawnie przyjmuje/odrzuca ten token, czy właściwie działa ze scope i audience oraz wiąże sub z użytkownikiem twojej usługi.
W MCP Jam przełączasz tryb na Bearer Token i wklejasz w pole tokenu na przykład:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Teraz Jam będzie dodawał do każdego żądania MCP nagłówek:
Authorization: Bearer eyJhbGciOi...
Twój serwer MCP przyjmuje żądanie, przechodzi przez middleware requireScope("mcp:tools"), dekoduje JWT i weryfikuje claims. Typowy kod weryfikacji można w uproszczeniu zapisać tak:
// auth/verifyToken.ts
import jwt from "jsonwebtoken";
export function verifyToken(header: string) {
const token = header.replace("Bearer ", "");
const payload = jwt.verify(token, process.env.JWT_PUBLIC_KEY!);
// tutaj możesz sprawdzić aud, scope itd.
return payload as { sub: string; scope?: string };
}
I użyć go w middleware:
// wewnątrz requireScope
const payload = verifyToken(header);
if (!payload.scope?.includes(requiredScope)) {
res.status(403).json({ error: "insufficient_scope" });
return;
}
(req as any).user = { id: payload.sub };
next();
W trybie Bearer możesz eksperymentować:
- wkleić token bez wymaganego scope i upewnić się, że serwer odpowiada 403/401;
- wkleić token z błędnym aud i zobaczyć, że serwer go odrzuca;
- wkleić token przeterminowany, aby sprawdzić błąd invalid_token.
To tryb lokalnego „testu uderzeniowego” logiki Resource Server bez udziału logowania przez UI i PKCE. Wszystko, co tu sprawdzisz, później 1:1 stosuje się do tokenów, które ChatGPT lub Jam uzyskają w trybie Default OAuth.
6. Tryb OAuth with credentials (Client Credentials): token „w imieniu aplikacji”
Teraz — rzadszy, ale pożyteczny dla zrozumienia tryb: OAuth with credentials, czyli grant client_credentials. W Jam podajesz:
- Client ID
- Client Secret
- wymagane scopes (np. mcp:tools)
Jam wykonuje żądanie do token_endpoint twojego serwera autoryzacji mniej więcej takiej postaci:
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=<ID>&
client_secret=<SECRET>&
scope=mcp:tools
Serwer autoryzacji wydaje token, w którym sub zwykle oznacza sam klient (np. sub = "mcp-jam-test-client"), a nie konkretnego użytkownika. Jam zaczyna używać tego tokena jak zwykłego Bearera.
Do czego to może być przydatne w świecie MCP:
- narzędzia serwisowe/administracyjne, niepowiązane z konkretnym użytkownikiem (np. eksport logów, health‑check, support);
- sprawdzenie, że serwer MCP potrafi rozróżniać tokeny użytkowników i tokeny „klienta”, jeśli twoja logika biznesowa to uwzględnia.
W kontekście ChatGPT Apps ten tryb zwykle nie jest używany, ponieważ ChatGPT jako publiczny klient nie przechowuje sekretów (a public client z definicji nie powinien mieć client_secret). Ale w Jam pomaga zobaczyć różnicę między:
- „Po prostu podsunąłem gotowy token” (tryb Bearer);
- „Jam sam poszedł po token na podstawie danych klienta” (OAuth with credentials).
Na ćwiczebnym serwerze możesz np. zrobić specjalny MCP‑tool admin_list_all_orders, dostępny tylko z tokenem o grant_type=client_credentials i odpowiedniej roli. To nie jest obowiązkowa część dzisiejszego wykładu, ale pożyteczny eksperyment.
7. Tryb Default OAuth: pełny Authorization Code + PKCE, jak w ChatGPT
Teraz — główna gwiazda programu: Default OAuth. Właśnie ten tryb jest najbliższy temu, co robi ChatGPT przy linkowaniu konta twojej aplikacji. Klient czyta resource_metadata, idzie do serwera autoryzacji, otwiera użytkownikowi stronę logowania, otrzymuje authorization code i wymienia go na access token według schematu Authorization Code + PKCE S256.
Rozłóżmy sekwencję kroków. Dla przejrzystości — diagram.
sequenceDiagram
participant Jam as MCP Jam (Client)
participant RS as MCP Server (Resource)
participant PRM as /.well-known/oauth-protected-resource
participant AS as Auth Server (Keycloak/Auth0)
Jam->>RS: Wywołanie chronionego narzędzia (bez tokenu)
RS-->>Jam: 401 + WWW-Authenticate (resource_metadata=PRM)
Jam->>PRM: GET /.well-known/oauth-protected-resource
PRM-->>Jam: JSON z resource, authorization_servers, scopes_supported...
Jam->>AS: GET /authorize?client_id=...&code_challenge=...&scope=...
Note right of AS: Użytkownik loguje się i wyraża zgodę
AS-->>Jam: redirect z authorization_code
Jam->>AS: POST /token (code + code_verifier)
AS-->>Jam: { access_token, scope, expires_in, ... }
Jam->>RS: Wywołanie narzędzia z Authorization: Bearer <access_token>
RS-->>Jam: Pomyślny wynik narzędzia
Co warto sprawdzić w tym trybie:
- Poprawną odpowiedź 401/WWW-Authenticate z serwera MCP. Jeśli serwer nie zwraca resource_metadata albo zwraca błędny URL, Jam nie będzie w stanie odczytać PRM i uruchomić OAuth‑flow.
- Ważny dokument .well-known/oauth-protected-resource. Powinny w nim być poprawne resource, authorization_servers, scopes_supported itd., aby Jam zrozumiał, dokąd iść po tokeny i jakich scopes żądać.
- Prawidłową konfigurację serwera autoryzacji.
- Włączony Authorization Code Flow z PKCE S256.
- Client ID odpowiada temu, czego oczekuje PRM (lub jest rejestrowany przez DCR — Dynamic Client Registration).
- Redirect URI w serwerze autoryzacji dokładnie pokrywa się z tym, którego używa Jam.
- PKCE S256. Jam formuje code_challenge i oczekuje, że serwer autoryzacji wspiera metodę S256. Jeśli PKCE jest wyłączone lub wspierane tylko „plain”, flow się rozpadnie.
- Scopes i audience. Serwer autoryzacji powinien wydawać token z właściwym aud i żądanymi scopes (mcp:tools itd.), a serwer MCP — je weryfikować.
W wyniku udanego Default OAuth otrzymasz:
- w Jam — połączenie z serwerem MCP, w którym chronione narzędzie list_user_orders zwraca poprawne dane właśnie dla użytkownika, na którego zalogowałeś się w serwerze autoryzacji;
- w logach serwera autoryzacji — udany authorize + wymianę tokenu;
- w logach serwera MCP — udaną walidację tokena i wyciągnięcie sub.
Do debugowania często pomaga dodać prosty logger w handlerze narzędzia, aby upewnić się, że rzeczywiście widzisz userId z tokena:
// wewnątrz handlera narzędzia MCP list_user_orders
export async function listUserOrders(args: any, context: any) {
const user = context.user as { id: string };
console.log("[MCP] listUserOrders for user", user.id);
// dalej zwracasz zamówienia tego użytkownika
}
8. Gdzie co się psuje: diagnostyka według trybów
Porozmawiajmy teraz, jak na podstawie symptomów w MCP Jam rozpoznać, gdzie dokładnie leży problem: w serwerze MCP, w serwerze autoryzacji czy w metadanych. Ta sekcja to coś w rodzaju checklisty diagnostycznej według trybów.
Jeśli w trybie None:
Wywołujesz narzędzie chronione, serwer zwraca:
- 200 OK i wykonuje działanie nawet bez tokena — znaczy, że nie masz weryfikacji tokena przed tym narzędziem. Trzeba dodać middleware lub sprawdzanie scopes.
- 401, ale bez WWW-Authenticate lub z błędnym resource_metadata — Jam nie dowie się, skąd wziąć metadane, i nie uruchomi Default OAuth. Popraw nagłówek według przykładu powyżej.
Jeśli w trybie Bearer Token:
- Jam uparcie otrzymuje 401/403 nawet z tokenem, o którym wiesz, że jest ważny przy wywołaniu bezpośrednio (przez curl lub Postman). Najpewniej coś jest nie tak w logice Resource Server: niepoprawne sprawdzanie aud/scope lub zły klucz publiczny do podpisu JWT.
- Jeśli token Bearer działa w Jam, ale potem nie działa w Default OAuth — problem nie leży w serwerze MCP, lecz w serwerze autoryzacji lub PRM: token uzyskany przez Default OAuth różni się po scope/aud od tego, który testowałeś ręcznie.
Jeśli w trybie OAuth with credentials:
- Jeśli Jam nie może uzyskać tokena (błąd na kroku /token) — szukaj przyczyny w ustawieniach klienta na serwerze autoryzacji (zły secret, niedozwolony client_credentials lub zabroniony scope).
- Jeśli token jest, ale serwer MCP go odrzuca — możliwe, że twój serwer oczekuje użytkownikowego sub (email/ID użytkownika), a w tokenie jest tylko identyfikator klienta. Albo aud/scope nie pokrywają się z oczekiwaniami.
Jeśli w trybie Default OAuth:
To scenariusz najbogatszy w rafy. Częste problemy:
- Nieprawidłowe redirect URI. Serwer autoryzacji zgłasza invalid_redirect_uri lub po prostu nie wydaje kodu. Upewnij się, że URI Jam jest wpisany w ustawieniach klienta IdP bez zbędnych ukośników i literówek.
- Brak lub niewspierany PKCE. Jeśli serwer autoryzacji wymaga PKCE, a Jam (lub jego stara wersja) nie wysyła code_challenge, albo odwrotnie — Jam wysyła S256, a IdP nie wspiera tej metody, zobaczysz invalid_request.
- Niedopasowane scopes. W PRM zadeklarowałeś mcp:tools, a klient w IdP ma dozwolone tylko openid, albo odwrotnie — Jam prosi o więcej scope, niż IdP jest gotów wydać.
- Niewłaściwy audience (aud). Token jest wydawany z aud innym niż oczekuje serwer MCP (np. URL innego zasobu). Serwer zasadnie go odrzuci.
Bardzo ważne jest, aby umieć patrzeć w logi trzech miejsc:
- MCP Jam — błędy przy parsowaniu PRM i przy żądaniach HTTP do serwera autoryzacji;
- Serwer autoryzacji — logi /authorize i /token podpowiedzą, czego odmawia;
- Serwer MCP — przyczyny odrzucenia tokena (invalid_token, insufficient_scope, wrong_audience).
9. Jak to się ma do prawdziwego ChatGPT App
Dlaczego tak dużo czasu spędzamy z Jam, zamiast od razu biec do Developer Mode ChatGPT? Bo Jam to właśnie stanowisko laboratoryjne: daje ci w ręce sterowanie trybami autoryzacji i wyświetla całą wewnętrzną kuchnię flow.
Gdy uruchamiasz Default OAuth w Jam i doprowadzasz go do sukcesu, w praktyce potwierdzasz:
- .well-known/oauth-protected-resource u serwera MCP jest poprawny;
- Serwer autoryzacji (Keycloak/Auth0/…) jest poprawnie skonfigurowany;
- role, scopes, audience i claims odpowiadają oczekiwaniom;
- Serwer MCP potrafi weryfikować token i wiązać go z użytkownikiem.
ChatGPT, podłączony do tego samego serwera MCP, zrobi to samo: odczyta PRM, pójdzie do serwera autoryzacji, uzyska token i zacznie wywoływać narzędzia z Authorization: Bearer.
Różnica w tym, że w ChatGPT widzisz tylko wynik końcowy („konto zostało pomyślnie zlinkowane” albo „coś poszło nie tak”), a w Jam — widzisz cały protokół i możesz krok po kroku zrozumieć, gdzie dokładnie było „nie tak”.
10. Mini‑praktyka: sekwencyjne testowanie naszego serwera MCP GiftGenius
Zbierzmy wszystko w prosty scenariusz sekwencyjny, który możesz powtórzyć w swoim projekcie.
Najpierw uruchamiasz swój serwer MCP (np. pnpm dev:mcp), upewnij się, że:
- nasłuchuje pod http://localhost:4000/mcp (lub twoim URL);
- endpoint /.well-known/oauth-protected-resource zwraca poprawny JSON;
- Serwer autoryzacji (Keycloak) działa i ma skonfigurowanego public‑client dla Jam/ChatGPT.
Dalej:
- Tryb None.
Podłączasz Jam do serwera MCP bez autoryzacji. Sprawdzasz, że:- search_gifts działa;
- list_user_orders zwraca 401 z poprawnym WWW-Authenticate.
- Tryb Bearer Token.
Pobierasz access token przez Keycloak (przez UI lub curl). Podstawiasz w Jam, wywołujesz list_user_orders i upewniasz się, że:- przy ważnym tokenie narzędzie działa i zwraca zamówienia konkretnego użytkownika;
- przy tokenie bez mcp:tools lub z innym aud — serwer zwraca błąd.
- Tryb OAuth with credentials.
Jeśli masz klienta poufnego: podajesz client_id i client_secret w Jam, ustawiasz wymagany scope, wywołujesz narzędzie techniczne (np. admin_list_all_orders) i sprawdzasz, że działa tylko z takim serwisowym tokenem. - Tryb Default OAuth.
Włączasz Default OAuth, wywołujesz list_user_orders. Jam sam:- otrzyma 401 + WWW-Authenticate,
- odczyta PRM,
- otworzy przeglądarkę, gdzie zalogujesz się do Keycloak,
- uzyska token przez Authorization Code + PKCE,
- wywoła narzędzie MCP z tokenem, po czym zobaczysz swoje zamówienia w odpowiedzi.
Jeśli wszystkie cztery tryby zadziałały zgodnie z oczekiwaniami — gratulacje, nie tylko „coś tam ustawiłeś w Keycloak”, ale naprawdę rozumiesz, jak sprawdzać i debugować cały flow autoryzacji.
11. Typowe błędy przy pracy z MCP Jam i testowaniu autoryzacji
W praktyce te problemy często objawiają się w postaci powtarzalnych wzorców błędów. Poniżej kilka typowych scenariuszy „jak nie robić”, abyś mógł rozpoznawać je po objawach.
Błąd nr 1: oczekiwanie, że chronione narzędzie zadziała w trybie None.
Czasem developer włącza Jam w trybie None, wywołuje list_user_orders i dziwi się 401, a potem „na wszelki wypadek” usuwa weryfikację tokena z serwera. W rezultacie narzędzie MCP zaczyna działać anonimowo, co w przypadku danych osobistych i scenariuszy commerce jest kategorycznie niedopuszczalne. Tryb None służy temu, aby sprawdzić, że serwer poprawnie odmawia bez tokena i zwraca WWW-Authenticate z resource_metadata.
Błąd nr 2: zapomniany lub niepoprawny nagłówek WWW-Authenticate.
Bardzo częsty przypadek: serwer zwraca 401 bez WWW-Authenticate lub ze starym parametrem resource_metadata_uri. Jam (tak jak ChatGPT) w takim przypadku nie rozumie, skąd pobrać Protected Resource Metadata, i Default OAuth po prostu nie startuje. Minimalny wariant wystarczający — WWW-Authenticate: Bearer resource_metadata="https://.../.well-known/oauth-protected-resource". Pola realm i scope pozostają opcjonalne; najważniejsze — nie zapomnieć o samym resource_metadata.
Błąd nr 3: testowanie tylko trybu Bearer i ignorowanie Default OAuth.
Developer ręcznie zdobywa token, wstawia go do Jam, widzi, że wszystko działa, i uznaje zadanie za rozwiązane. A gdy przychodzi czas podłączać realny ChatGPT, okazuje się, że .well-known jest niepoprawny, PKCE nie jest wspierane, redirect URI nie pokrywa się i linkowanie się wywraca. Test trybu Bearer — konieczny, ale niewystarczający krok. Default OAuth trzeba koniecznie przeprowadzić, inaczej nie sprawdzisz połowy kluczowych ustawień serwera autoryzacji i PRM.
Błąd nr 4: próba użycia client_credentials tam, gdzie potrzebny jest token użytkownika.
Czasem w desperacji developer włącza w Jam tryb OAuth with credentials i zaczyna uzyskiwać tokeny przez client_credentials, a potem używa ich do narzędzi użytkownikowych, jak list_user_orders. W rezultacie sub w tokenie to client_id, a nie realny użytkownik, i logika biznesowa zachowuje się dziwnie (np. pokazuje „ogólne” dane lub pada przy próbie znalezienia użytkownika o takim ID). Dla scenariuszy ChatGPT z realnymi użytkownikami potrzebny jest Authorization Code + PKCE (Default OAuth), a client_credentials nadaje się tylko do zadań serwisowych.
Błąd nr 5: niespójność scopes i audience między PRM, serwerem autoryzacji i serwerem MCP.
W .well-known/oauth-protected-resource zadeklarowałeś, że resource to https://giftgenius.example.com, a wspierane scopes to ["mcp:tools"]. W serwerze autoryzacji klientowi wydano token bez aud, a serwer MCP przy weryfikacji tokena oczekuje ściśle aud = "https://giftgenius.example.com" i obecności mcp:tools. W rezultacie token uzyskany przez Default OAuth serwer MCP odrzuca, a ty tracisz pół dnia na szukanie „magii”. Zawsze sprawdzaj, że PRM, konfiguracja klienta w IdP i weryfikacja w middleware serwera MCP są zgodne co do audience i scope.
Błąd nr 6: używanie starej wersji MCP Jam.
Specyfikacja MCP Authorization szybko się rozwija, pojawiają się nowe pola (resource_metadata, ulepszony PKCE‑flow, narzędzia do debugowania). Jeśli masz starą wersję Jam, może nie rozumieć świeżych pól albo działać ze starymi nazwami parametrów. To prowadzi do surrealistycznych bugów: wszystko skonfigurowałeś zgodnie z ostatnim RFC, a Jam po prostu nie wie, co z tym zrobić. Zanim popadniesz w rozpacz, upewnij się, że Jam jest zaktualizowany do bieżącej wersji.
GO TO FULL VERSION