Poprzednie artykuły skupiały się na tym co przepływa między klientem a serwerem MCP i jak. Ten artykuł pyta o coś innego: kto w ogóle ma prawo nawiązać to połączenie?
To jest pytanie o autoryzację — i ma dwie warstwy które łatwo pomylić.
Warstwa 1: kto może wywołać Twój serwer MCP? Warstwa 2: jakie akcje agent może wykonać w zewnętrznym serwisie przez Twój serwer?
Pierwsza warstwa to zabezpieczenie serwera. Druga to problem OAuth — jak agent działa w imieniu użytkownika w serwisach jak Gmail, GitHub czy Slack bez podawania mu hasła użytkownika.
Trzy poziomy autoryzacji serwera MCP
Zanim przejdziemy do OAuth — prostsza taksonomia. Serwery MCP dzielą się na trzy grupy pod kątem autoryzacji dostępu.
Publiczne — bez uwierzytelniania
Każdy klient MCP może się połączyć. Serwer nie sprawdza kto pyta.
Przykład: Webflux Słownik MCP. Słownik jest publiczny — definicje pojęć są dostępne bez logowania na stronie, więc serwer MCP też nie wymaga tokenu. Każdy agent który zna URL może odpytać słownik.
# Działa bez żadnego nagłówka auth
curl -X POST https://webflux.pl/wp-json/webflux/v1/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Kiedy sensowne: narzędzia do danych publicznie dostępnych, słowniki, kalkulatory, konwertery.
Chronione kluczem API
Serwer wymaga klucza API w nagłówku. Prosta kontrola dostępu — kto ma klucz, ten ma dostęp.
curl -X POST https://twoj-serwer-mcp.pl/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-abc123def456" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}'
PHP — weryfikacja po stronie serwera:
function handle_mcp_request(WP_REST_Request $request) {
$auth = $request->get_header('Authorization');
$token = str_replace('Bearer ', '', $auth ?? '');
if (!$token || !validate_api_key($token)) {
return new WP_REST_Response(
['jsonrpc' => '2.0', 'error' => ['code' => -32001, 'message' => 'Unauthorized']],
401
);
}
// Obsługa żądania...
}
Pułapka: klucz API nie powinien trafiać do kontekstu modelu językowego. Jeśli konfiguracja serwera MCP jest w system promptcie agenta lub w zmiennych które model widzi — klucz może zostać ujawniony przez credential leakage. Klucz API powinien być w zmiennych środowiskowych hosta, nie w treści dostępnej dla modelu.
Chronione OAuth
Serwer wymaga tokenu OAuth 2.0. Używane gdy serwer MCP działa w imieniu konkretnego użytkownika i musi weryfikować jego tożsamość. Najczęściej dla serwerów enterprise lub obsługujących dane wielu użytkowników.
Problem OAuth w świecie agentów
OAuth dla agentów to adaptacja standardu OAuth 2.0 do przypadku gdy autoryzację przeprowadza autonomiczny agent — nie człowiek klikający „Zezwól” w przeglądarce.
Zanim opiszę jak to działa — trzy problemy specyficzne dla agentów, których standardowy OAuth nie rozwiązuje.
Problem 1: Token w kontekście modelu
Standardowe podejście do autoryzacji: przekaż token do agenta w zmiennej środowiskowej lub w konfiguracji. Agent ma token, może działać.
Problem: cokolwiek trafia do kontekstu modelu językowego — może zostać z niego wyciągnięte. Agent który ma token OAuth w kontekście może go ujawnić gdy odpowie na pytanie „pokaż mi swoją konfigurację” albo gdy złośliwa instrukcja w przetwarzanym dokumencie poprosi go o wylistowanie zmiennych środowiskowych.
To jest credential leakage przez model — i jest realnym wektorem ataku na agenty z dostępem do zewnętrznych serwisów.
Rozwiązanie: token nigdy nie trafia do kontekstu modelu. Agent wywołuje wrapper który ma token — nie sam token.
Model językowy
│
│ wywołuje: send_email(to, subject, body)
│
▼
Warstwa serwera MCP
│
│ pobiera token z vault/env (nie z kontekstu modelu)
│ wysyła żądanie do Gmail API z tokenem
│
▼
Gmail API
Model widzi tylko wywołanie narzędzia z parametrami. Token jest niewidoczny.
Problem 2: Interactive OAuth flow
Standardowy OAuth wymaga że użytkownik zatwierdza dostęp w przeglądarce. Agent który działa autonomicznie o 3 w nocy nie może czekać na kliknięcie „Zezwól”.
Rozwiązanie: autoryzacja z wyprzedzeniem. Użytkownik przeprowadza OAuth flow raz — ręcznie lub przez interfejs aplikacji. Token jest zapisany bezpiecznie. Agent używa zapisanego tokenu i odświeża go automatycznie gdy wygasa, bez ponownej interakcji użytkownika.
[Etap 1 — jednorazowo, z udziałem użytkownika]
Użytkownik → klikam "Połącz z GitHubem" w aplikacji
Aplikacja → GitHub OAuth flow
GitHub → zwraca access_token + refresh_token
Aplikacja → zapisuje tokeny w vault
[Etap 2 — wielokrotnie, automatycznie]
Agent → chce wywołać GitHub API
Serwer MCP → pobiera access_token z vault
→ jeśli wygasł, używa refresh_token żeby dostać nowy
→ wywołuje GitHub API
Problem 3: Scope creep
Agent który dostał szeroki zakres OAuth może zrobić więcej niż użytkownik zamierzał. Token z scope repo na GitHubie pozwala na wszystkie operacje na repozytoriach — nie tylko te które agent akurat potrzebuje.
Rozwiązanie: principle of least privilege. Token powinien mieć minimalny zakres potrzebny do konkretnego zadania.
# Złe — szeroki scope "na wszelki wypadek"
scope: repo user admin:org
# Dobre — dokładnie to czego potrzebujesz
scope: repo:read issues:write
Jeszcze lepiej: serwer MCP tworzy różne tokeny dla różnych narzędzi. Tool list_repos używa tokenu tylko do odczytu. Tool create_issue używa tokenu z prawem zapisu do issues. Kompromitacja jednego toola nie daje dostępu do wszystkiego.
MCP OAuth extension — specyfikacja 2025-03-26
Specyfikacja MCP 2025-03-26 dodała oficjalne wsparcie dla OAuth — serwery MCP mogą teraz deklarować że wymagają autoryzacji OAuth i prowadzić klienta przez flow.
Discovery — serwer informuje o wymaganiu OAuth:
Gdy niezautoryzowany klient próbuje się połączyć, serwer zwraca HTTP 401 z nagłówkiem WWW-Authenticate:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mcp",
authorization_uri="https://api.github.com/login/oauth/authorize",
token_uri="https://github.com/login/oauth/access_token",
scopes="repo issues:write"
Klient MCP który obsługuje OAuth extension wie że musi przeprowadzić flow autoryzacji przed ponowną próbą połączenia.
Flow z PKCE:
MCP rekomenduje Authorization Code Flow z PKCE (Proof Key for Code Exchange) — bez client secret, odporne na przechwycenie kodu autoryzacyjnego.
1. Klient generuje code_verifier (losowy string)
code_challenge = BASE64(SHA256(code_verifier))
2. Klient otwiera URL autoryzacji z code_challenge:
https://github.com/login/oauth/authorize
?client_id=abc
&redirect_uri=http://localhost:PORT/callback
&code_challenge=xyz
&code_challenge_method=S256
&scope=repo
3. Użytkownik zatwierdza dostęp
GitHub przekierowuje na redirect_uri z ?code=AUTH_CODE
4. Klient wymienia kod na token, podając code_verifier:
POST https://github.com/login/oauth/access_token
code=AUTH_CODE
code_verifier=ORIGINAL_VERIFIER
5. GitHub weryfikuje że SHA256(code_verifier) == code_challenge
i zwraca access_token + refresh_token
Bez client secret — klucz do weryfikacji jest generowany jednorazowo przez klienta i nigdy nie opuszcza jego środowiska.
Używanie tokenu:
Po autoryzacji każde żądanie MCP zawiera token w nagłówku:
// Żądanie HTTP do serwera MCP
POST /mcp HTTP/1.1
Authorization: Bearer eyJhbGc...
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}
Implementacja: kontrola dostępu per tool
Zaawansowany wzorzec: różne narzędzia na tym samym serwerze MCP mają różne wymagania autoryzacji.
// Mapa uprawnień per tool
$tool_permissions = [
'list_repos' => ['scope' => 'repo:read', 'require_auth' => true],
'create_issue' => ['scope' => 'issues:write', 'require_auth' => true],
'get_public_info' => ['scope' => null, 'require_auth' => false],
];
function handle_tools_call($id, $params, $token) {
$tool_name = $params['name'] ?? '';
$permissions = $tool_permissions[$tool_name] ?? null;
if (!$permissions) {
return mcp_error($id, -32601, 'Tool not found');
}
// Sprawdź czy tool wymaga auth
if ($permissions['require_auth']) {
if (!$token) {
return new WP_REST_Response(
['error' => 'Unauthorized'],
401
);
}
// Sprawdź scope tokenu
$token_scopes = get_token_scopes($token);
if ($permissions['scope'] && !in_array($permissions['scope'], $token_scopes)) {
return new WP_REST_Response(
['error' => 'Insufficient scope'],
403
);
}
}
// Wywołaj tool z tokenem w warstwie biznesowej
return execute_tool($tool_name, $params['arguments'], $token);
}
Checklist autoryzacji przed wdrożeniem serwera MCP
Zanim Twój serwer MCP trafi na sieć:
- Klucze API i tokeny nigdy nie trafiają do kontekstu modelu — są w vault lub env, dostępne tylko dla warstwy serwera
- Każdy tool ma zdefiniowany minimalny scope — nie jeden szeroki token dla wszystkiego
- Tokeny mają czas wygaśnięcia — nie permanentne klucze bez TTL
- Refresh token jest przechowywany bezpiecznie — nie w tym samym miejscu co access token
- Logi autoryzacji są aktywne — kto wywołał które narzędzie i kiedy
- Rate limiting per token — jeden token nie może wywołać nieskończonej liczby requestów
W następnym artykule: mamy teorię, mamy protokół, mamy autoryzację. Czas zbudować własny serwer MCP od zera — praktyczny tutorial w PHP z WordPressem jako platformą.
Pojęcia ze słownika: OAuth dla agentów · MCP · Credential leakage · Streamable HTTP · Principal hierarchy











