Kimlik beyanı
Makine çevirisi
Bu sayfa İngilizce dokümantasyondan otomatik olarak çevrildi; esas alınması gereken sürüm İngilizce sayfadır. Yanlış görünen bir şey varsa, nasıl bildireceğinizi Çeviriler sayfası açıklar.
Sıradan bir OAuth sağlayıcısı (OAuth istemcileri) işe MCP sunucusuna bir soru sorarak başlar: hangi yetkilendirme sunucusuna güveniyorsun? Yanıt nereyi gösteriyorsa oraya gider; ardından ya bir kişi oturum açar ya da önceden paylaşılmış bir gizli anahtar onun yerini tutar.
Bir kurum ise bunların hiçbirinin sunucu başına kararlaştırılmasını istemez. Zaten bir kimlik sağlayıcısı işletir (Okta, Microsoft Entra ID, kendi yazdığınız); kullanıcı ona bu sabah zaten oturum açmıştır ve güvenlik ekibinin kimin neye erişebileceğine karar vermek istediği tek yer orasıdır. Enterprise-Managed Authorization uzantısı olan SEP-990, kararı oraya taşır. IdP kısa ömürlü bir JWT imzalar: bir Identity Assertion JWT Authorization Grant, kısaca ID-JAG. Bu, şu kullanıcının, şu istemci aracılığıyla şu MCP sunucusuna erişebileceğini söyleyen bir beyandır. İstemci onu sıradan bir erişim token'ıyla takas eder. Tarayıcı yok, onay ekranı yok, dinamik kayıt yok.
Bu sayfa o takasın iki ucunu da anlatır. MCP sunucusunun kendisi hiç değişmez: hâlâ Yetkilendirme sayfasındaki kaynak sunucusudur ve önüne hangi token gelirse onu denetler.
İki token isteği
İşin içinde iki farklı otorite var ve bu sayfayı anlamanın büyük kısmı ikisini ayrı adlarla anmaktan geçer. Kurumsal IdP, kuruluşunuzun kimlik sağlayıcısıdır: çalışanın kim olduğunu bilir, politikanın bulunduğu yerdir ve ID-JAG'i o düzenler. SDK onunla hiç konuşmaz. MCP yetkilendirme sunucusu ise Yetkilendirme sayfasındaki aynı taraftır: MCP sunucusunun meta verisinde adı geçen issuer, o MCP sunucusunun kabul ettiği token'ları basan şey. Sıradan bir OAuth akışında bu iki rol genellikle tek bir kutudur. Burada iki ayrı kutudur ve grant'in tamamı, ikincisinin birincisine güvenmeyi kabul etmesinden ibarettir.
İstemci her birine birer token isteği gönderir.
- Kurumsal IdP'ye. İstemci, kullanıcının oturum açma bilgisini (OpenID Connect ID token'ını) ID-JAG ile takas eder. Bu bir RFC 8693 token takasıdır, tamamen IdP'nizin API'sidir ve bu isteği SDK yapmaz. Siz yaparsınız, tek bir asenkron callback'in içinde. Politika kararı da burada verilir: hayır diyen bir IdP ID-JAG'i hiç düzenlemez ve ortada sunulacak bir şey kalmaz.
- MCP yetkilendirme sunucusuna. İstemci ID-JAG'i RFC 7523
jwt-bearergrant'i kapsamında sunar (grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer, ID-JAG deassertionolarak) ve erişim token'ını alır. SDK'nın yaptığı istek budur ve bu sayfanın bir yetkilendirme sunucusuna eklediği tek şey de onu kabul etmektir.
Aşağıdaki her şey ikinci istekle ilgilidir: onu gönderen istemci ve yanıtlayan yetkilendirme sunucusu.
İstemci
IdentityAssertionOAuthProvider, mcp.client.auth.extensions.identity_assertion modülünde bulunur. OAuth istemcileri sayfasındaki her sağlayıcı gibi o da bir httpx2.Auth nesnesidir: bir tane oluşturun, auth= parametresine verin, httpx2.AsyncClient'ı aktarıma teslim edin.
import time
import uuid
import httpx2
import jwt
from mcp import Client
from mcp.client.auth.extensions.identity_assertion import IdentityAssertionOAuthProvider
from mcp.client.streamable_http import streamable_http_client
from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
IDP_SIGNING_KEY = "the-enterprise-idp-signing-key-for-this-demo"
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
def idp_issue_id_jag(subject: str, audience: str, resource: str) -> str:
now = int(time.time())
claims = {
"iss": "https://idp.example.com",
"sub": subject,
"aud": audience,
"client_id": "finance-agent",
"resource": resource,
"scope": "notes:read",
"jti": str(uuid.uuid4()),
"iat": now,
"exp": now + 300,
}
return jwt.encode(claims, IDP_SIGNING_KEY, algorithm="HS256", headers={"typ": "oauth-id-jag+jwt"})
async def fetch_id_jag(audience: str, resource: str) -> str:
return idp_issue_id_jag("alice@example.com", audience, resource)
oauth = IdentityAssertionOAuthProvider(
server_url="http://localhost:8001/mcp",
storage=InMemoryTokenStorage(),
client_id="finance-agent",
client_secret="finance-agent-secret",
issuer="https://auth.example.com/",
assertion_provider=fetch_id_jag,
scope="notes:read",
)
async def main() -> None:
async with httpx2.AsyncClient(auth=oauth, follow_redirects=True) 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])
Aşağıdan yukarıya okuyun.
main(), standart OAuth istemcisimain()'idir (OAuth istemcileri), satırı satırına aynı. Mesele de bu: sağlayıcı bir kez var olduktan sonra, akışın devamındaki hiçbir şey token'ı hangi grant'in ürettiğini bilmez.- Sağlayıcı, diğer sağlayıcıların keşfedemeyeceği şeyleri alır: birinin yetkilendirme sunucusuna önceden kaydettirdiği bir
client_idveclient_secret, o yetkilendirme sunucusununissuer'ı veassertion_provider, yani istendiğinde taze bir ID-JAG döndüren asenkron bir callback. storageaynıTokenStorageprotokolüdür. Yalnızca iki token metodu çağrılır; burada dinamik kayıt olmadığından hatırlanacak birclient_infoda yoktur.
Beyan sağlayıcı
Yazdığınız tek kod fetch_id_jag(audience, resource) fonksiyonudur. Her token takasında bir kez await edilir; oluşturma sırasında asla, ve ancak yetkilendirme sunucusunun meta verisi alınıp doğrulandıktan sonra. Böylece yanlış yapılandırılmış bir issuer hiçbir zaman bir beyan sızdırmaz. İki argümanı, ID-JAG'in basılırken taşıması gereken claim'lerden ikisidir: audience yetkilendirme sunucusunun issuer'ıdır (ID-JAG'deki aud), resource ise MCP sunucusunun kanonik tanımlayıcısıdır (ID-JAG'deki resource). Üçüncüsü zaten elinizde: ID-JAG'in client_id claim'i, sağlayıcıya verdiğiniz client_id'yi göstermelidir; yoksa yetkilendirme sunucusu takası reddeder.
Onun üstündeki idp_issue_id_jag sizin kodunuz değildir. Kimlik sağlayıcısının yerini tutar; dosya eksiksiz olsun ve bir ID-JAG'in taşıdığı her claim'i okuyabilesiniz diye beyanı süreç içinde imzalar. Gerçek bir fetch_id_jag ise bunun yerine önceki bölümdeki ilk token isteğini yapar: IdP'nize karşı bir RFC 8693 token takası. Bu takası, SEP-990 belgesinin profil olarak daralttığı Identity Assertion JWT Authorization Grant taslağı tanımlar. Oturum açmış kullanıcının ID token'ı subject_token olarak girer, requested_token_type ID-JAG'in kendi URN'idir (urn:ietf:params:oauth:token-type:id-jag), audience ve resource olduğu gibi aktarılır ve yanıt ID-JAG'i taşır. IdP'nizin belgelerinde aramanız gereken şey, bu adlarla anılan bu takastır.
Tip
Her takas için taze bir ID-JAG istenir ve amaç da budur: tek kullanımlık, ömrü dakikalarla ölçülen bir grant'tir ve bu sayfadaki yetkilendirme sunucusu aynısını ikinci kez kabul etmez. Onu önbelleğe almayın. Yeniden kullanılan şey, size kazandırdığı erişim token'ıdır.
Yapılandırma olarak issuer
Tersine çevirme işte burada. OAuthClientProvider, kaynak sunucusuna hangi yetkilendirme sunucusunu kullanacağını sorar ve yanıt nereyi gösteriyorsa oraya gider. Bu sağlayıcı bunu reddeder: issuer zorunludur, RFC 8414 meta verisi o issuer'ın kendi well-known yolundan alınır, token endpoint'i o issuer'ın origin'inde olmalıdır ve kaynak sunucusuna hiçbir şey sorulmaz.
Uzantı bunu şart koşmaz; bu, bilerek yapılmış daha katı bir tercihtir. Bu istemci çalınmaya değer iki şey taşır: önceden kaydedilmiş bir gizli anahtar ve audience'a bağlı bir beyan. Ele geçirilmiş bir MCP sunucusunun kendisini saldırganın yetkilendirme sunucusuna yönlendirmesine izin veren bir istemci, ikisini de oraya gönderirdi. Oluşturma sırasında issuer'ı sabitlemek bu konuşmayı ortadan kaldırır.
Warning
Yapılandırılan issuer, meta veri belgesinin issuer alanıyla RFC 8414 §3.3'teki basit dize
karşılaştırmasıyla karşılaştırılır: karakter karakter, sondaki eğik çizgi dahil, normalleştirme
olmadan. Tahmin etmeyin. Yetkilendirme sunucunuzdan /.well-known/oauth-authorization-server
belgesini alın ve döndürdüğü issuer değerini kopyalayın. Bu sayfadaki yetkilendirme sunucusu
için bu değer, eğik çizgisiyle birlikte https://auth.example.com/ adresidir; çünkü issuer'ı
bir pydantic URL nesnesinden oluşturulmuştur. Bir uyuşmazlık, tek bir kimlik bilgisi ya da
beyan gönderilmeden akışı OAuthFlowError: Authorization server metadata issuer
mismatch hatasında durdurur.
Gizli istemci
client_secret zorunludur; yapıcı onsuz ValueError fırlatır. SEP-990 belgesinin dayandığı IETF profili bu grant'i gizli istemcilere ayırır, SEP-990 istemcinin kimliğini doğrulamasını şart koşar ve bu SDK, paylaşılan bir gizli anahtarda ısrar ederek ikisini de uygular. token_endpoint_auth_method, anahtarın nereden gideceğini seçer: client_secret_post (varsayılan, form gövdesinde) veya client_secret_basic (bir HTTP Basic başlığı). Profil private_key_jwt yöntemine de izin verir; bu sağlayıcı onu desteklemez.
Tip
client_secret'ı ortam değişkenlerinden veya bir gizli anahtar yöneticisinden okuyun, asla
kaynak kod deposundan değil.
Sağlayıcının sizin için yaptıkları
İlk istek kimlik doğrulaması olmadan gider ve sunucunun 401 yanıtı akışı başlatır.
- Keşif. Yetkilendirme sunucusu meta verisini yapılandırılan issuer'ın RFC 8414 well-known yolundan alır, belgenin
issueralanının eşleştiğini denetler ve token endpoint'inin issuer'ın origin'inde olduğunu denetler. - Beyan.
assertion_provider'ınızı await eder. - Takas.
jwt-bearergrant'ini token endpoint'ine POST eder,OAuthToken'ı saklar ve özgün isteğiniziAuthorization: Bearer ...ile yeniden gönderir.
WWW-Authenticate başlığında insufficient_scope geçen bir 403, 2. ve 3. adımları sizin scope'unuz ile yanıtın istediği kapsamın birleşimiyle yeniden çalıştırır. (scope yalnızca bir istektir; bu sayfadaki yetkilendirme sunucusu ID-JAG ne diyorsa onu verir, başka bir şey değil.) Bunun hiçbir yerinde yenileme token'ı yoktur: erişim token'ının süresi dolduğunda bir sonraki 401 taze bir ID-JAG bastırır ve takas yeniden yapılır; IdP'nin elinde tuttuğu kaldıraç işte budur. Hatalar, OAuth istemcileri sayfasının geri kalanındaki aynı iki istisnadır: keşif ve doğrulama için OAuthFlowError, token endpoint'i hayır dediğinde onun alt sınıfı OAuthTokenError.
Yetkilendirme sunucusu
Çoğu zaman burada durursunuz. MCP yetkilendirme sunucusu başkasının ürünüdür, ID-JAG kabul etmek o ürünün açılacak bir yapılandırmasıdır ve SDK'nın SEP-990 içindeki payı yukarıdaki istemcidir.
SDK yetkilendirme sunucusunun kendisi de olabilir: create_auth_routes, yetkilendirme sunucusunun route'larını herhangi bir Starlette uygulamasının bağlayabileceği bir liste olarak döndürür; depodaki examples/servers/simple-auth/ da bir tanesini böyle çalıştırır. SEP-990 bu yüzeye bir bayrak ve bir metot ekler:
import secrets
import time
import jwt
from pydantic import AnyHttpUrl
from starlette.applications import Starlette
from mcp.server.auth.provider import (
AccessToken,
AuthorizationCode,
AuthorizationParams,
AuthorizeError,
IdentityAssertionParams,
OAuthAuthorizationServerProvider,
RefreshToken,
TokenError,
)
from mcp.server.auth.routes import create_auth_routes
from mcp.shared.auth import JWT_BEARER_GRANT_TYPE, OAuthClientInformationFull, OAuthToken
ISSUER = "https://auth.example.com/"
MCP_SERVER = "http://localhost:8001/mcp"
IDP_ISSUER = "https://idp.example.com"
IDP_SIGNING_KEY = "the-enterprise-idp-signing-key-for-this-demo"
REGISTERED_CLIENTS = {
"finance-agent": OAuthClientInformationFull(
client_id="finance-agent",
client_secret="finance-agent-secret",
redirect_uris=None,
grant_types=[JWT_BEARER_GRANT_TYPE],
token_endpoint_auth_method="client_secret_post",
)
}
class EnterpriseAuthorizationServer(OAuthAuthorizationServerProvider[AuthorizationCode, RefreshToken, AccessToken]):
def __init__(self) -> None:
self.access_tokens: dict[str, AccessToken] = {}
self.seen_jtis: set[str] = set()
async def get_client(self, client_id: str) -> OAuthClientInformationFull | None:
return REGISTERED_CLIENTS.get(client_id)
async def load_access_token(self, token: str) -> AccessToken | None:
return self.access_tokens.get(token)
async def exchange_identity_assertion(
self, client: OAuthClientInformationFull, params: IdentityAssertionParams
) -> OAuthToken:
try:
header = jwt.get_unverified_header(params.assertion)
claims = jwt.decode(
params.assertion,
IDP_SIGNING_KEY,
algorithms=["HS256"],
issuer=IDP_ISSUER,
audience=ISSUER,
options={"require": ["iss", "sub", "aud", "exp", "iat", "jti", "client_id", "resource", "scope"]},
)
except jwt.InvalidTokenError as error:
raise TokenError("invalid_grant", "the assertion did not verify") from error
if header.get("typ") != "oauth-id-jag+jwt":
raise TokenError("invalid_grant", "the assertion is not an ID-JAG")
if claims["client_id"] != client.client_id:
raise TokenError("invalid_grant", "the assertion was issued to a different client")
if claims["resource"] != MCP_SERVER:
raise TokenError("invalid_target", "the assertion is for a resource this server does not serve")
if claims["jti"] in self.seen_jtis:
raise TokenError("invalid_grant", "the assertion has already been used")
self.seen_jtis.add(claims["jti"])
scopes = claims["scope"].split()
access_token = f"mcp_{secrets.token_hex(16)}"
self.access_tokens[access_token] = AccessToken(
token=access_token,
client_id=claims["client_id"],
scopes=scopes,
expires_at=int(time.time()) + 300,
resource=claims["resource"],
subject=claims["sub"],
)
return OAuthToken(access_token=access_token, token_type="Bearer", expires_in=300, scope=" ".join(scopes))
async def authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str:
raise AuthorizeError("unauthorized_client", "this authorization server only accepts ID-JAGs")
async def load_authorization_code(self, client: OAuthClientInformationFull, authorization_code: str) -> None:
return None
async def exchange_authorization_code(
self, client: OAuthClientInformationFull, authorization_code: AuthorizationCode
) -> OAuthToken:
raise TokenError("invalid_grant", "this authorization server only accepts ID-JAGs")
async def load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) -> None:
return None
async def exchange_refresh_token(
self, client: OAuthClientInformationFull, refresh_token: RefreshToken, scopes: list[str]
) -> OAuthToken:
raise TokenError("invalid_grant", "this authorization server only accepts ID-JAGs")
provider = EnterpriseAuthorizationServer()
auth_app = Starlette(
routes=create_auth_routes(provider, issuer_url=AnyHttpUrl(ISSUER), identity_assertion_enabled=True)
)
identity_assertion_enabled=Trueher şeyin kapısıdır. Kapalıyken (varsayılan budur)/token, hook'u uygulamış olsanız bile bu grant'eunsupported_grant_typeile yanıt verir ve meta veri ondan söz etmez. Açıkken meta verijwt-bearergrant türünü kazanır ve uzantının desteği duyurmak için kullandığı alan olanauthorization_grant_profiles_supportediçindeurn:ietf:params:oauth:grant-profile:id-jagdeğerini listeler. (Bu SDK'nın istemcisi onu hiç okumaz: tek bir issuer için hazırlanmıştır ve doğrudan sorar.)exchange_identity_assertionhook'un kendisidir. O çalışmadan önce SDK istemcinin kimliğini doğrulamış, açık (public) istemcileri reddetmiş ve kaydında bu grant'in listelenmediği istemcileri reddetmiştir. Size birIdentityAssertionParamsgelir (hamassertion, istenenscopesveresource) ve düz birOAuthTokendöndürürsünüz.- Dinamik istemci kaydı bu grant'i koşulsuz reddeder; bu yüzden buradaki
get_clientelle hazırlanmış bir istemci sunar. Bir ID-JAG istemcisi kendi kendini kaydederek var olamaz. - Sınıfın yarısı retlerden oluşur.
OAuthAuthorizationServerProvideryetkilendirme sunucusunun tamamıdır, bu yüzden yetkilendirme kodu akışını da ister; kullanıcılara oturum da açtıran bir sunucu onları gerçekten uygular, bunun ise tam olarak tek bir kapısı var.
Warning
SDK beyanın kodunu hiçbir zaman çözmez: hangi IdP'ye güvendiğini ve o IdP'nin hangi
anahtarları yayımladığını yalnızca sizin dağıtımınız bilir; bu yüzden
exchange_identity_assertion içindeki her şey yük taşır. İmzayı IdP'nin yayımladığı
anahtarlara karşı (JWKS'i; buradaki paylaşılan gizli anahtar demoya aittir), iss ve exp
değerlerini de RFC 7523 §3 uyarınca doğrulayın. JWT başlığındaki typ değerinin
oauth-id-jag+jwt olmasını şart koşun; bu, profilin başka bir JWT'nin grant olarak yeniden
oynatılmasına karşı koyduğu korumadır. aud değerinin kendi issuer'ınız olmasını şart koşun.
ID-JAG'in client_id claim'inin işleyicinin kimliğini doğruladığı istemciye eşit olmasını,
resource claim'inin de gerçekten sunduğunuz bir kaynağı göstermesini şart koşun. Beyan
yalnızca bir kez kabul edilsin diye jti değerini beyanın exp süresine kadar takip edin.
Verilen kapsamları ve her şeyden önce düzenlenen token'ın resource değerini istekten değil,
doğrulanmış ID-JAG'den alın: params.resource istemci ne yazdıysa odur. İşleme kurallarının
tamamı Enterprise-Managed Authorization spesifikasyonunda yer alır.
Kötü bir beyanı TokenError("invalid_grant", ...) ile reddedin. Bu akıştaki diğer hata kodu invalid_target'tır: sunmadığınız bir kaynağı gösteren bir ID-JAG onunla reddedilir; bu sunucunun başkasının kaynağı için token basmasını engelleyen de budur. Verilen kapsamlar ise ID-JAG'in scope claim'inden gelir (bu claim'i olmayan bir beyan da reddedilir); sizinki bunun yerine kullanıcının gruplarını eşleyebilir.
Döndürülen OAuthToken'ın ne taşımadığına da dikkat edin: bir yenileme token'ı. IdP, bir sonraki ID-JAG'i düzenleyip düzenlemeyeceğine karar vererek bu kullanıcının erişimi ne kadar süre koruyacağına karar verir. Burada basılacak bir yenileme token'ı o kararı sessizce geri teslim ederdi.
Info
Yetkilendirme sunucusunu hâlâ auth_server_provider= ile içine gömen bir sunucu, aynı koda
AuthSettings(identity_assertion_enabled=True) üzerinden ulaşır. Yeni sunucuların neden oradan
başlamaması gerektiğini Yetkilendirme sayfası açıklar.
Check
Bu sayfadaki iki dosyayı birbirine bağlayın; grant'in tamamı tek bir POST /token olur:
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
assertion=eyJhbGciOiJIUzI1NiIsInR5cCI6Im9hdXRoLWlkLWphZytqd3QifQ...
client_id=finance-agent
resource=http://localhost:8001/mcp
scope=notes:read
client_secret=finance-agent-secret
HTTP/1.1 200 OK
{"access_token": "mcp_...", "token_type": "Bearer", "expires_in": 300, "scope": "notes:read"}
/authorize yok, /register yok, korumalı kaynak meta verisi isteği yok. Ağ üzerindeki tek
istekler 401'i çeken istek, well-known isteği, bu takas ve ardından bearer eklenmiş sıradan
MCP trafiğidir. Doğrulayıcınızın ID-JAG'den okuduğu sub da bir aracın içinde
get_access_token().subject'in bildirdiği değerin ta kendisidir.
Deneyin
SDK deposundaki examples/stories/identity_assertion/, bu sayfanın gerçekten çalışan hâlidir: aynı exchange_identity_assertion doğrulayıcısı, onun token'larıyla korunan bir MCP sunucusu, yerine geçen bir IdP ve istemci; hepsi kendi kendini denetleyen tek bir programda. uv run python -m stories.identity_assertion.client --http takasın tamamını çalıştırır ve IdP'nin adını verdiği kullanıcının, aracın gördüğü kullanıcı olduğunu doğrular.
Özet
- SEP-990, bir istemcinin hangi MCP sunucularına erişebileceğine son kullanıcının değil, kurumsal kimlik sağlayıcısının karar vermesini sağlar. IdP o kararı imzalayıp bir ID-JAG içine koyar.
- ID-JAG'i elde etmek IdP'nize karşı yapılan bir RFC 8693 token takasıdır ve SDK bunu yapmaz. Onu MCP yetkilendirme sunucusuna sunmak RFC 7523
jwt-bearergrant'idir ve SDK bunun iki tarafını da üstlenir. IdentityAssertionOAuthProviderbir başkahttpx2.Authnesnesidir: önceden kaydedilmiş gizli bir istemci, sabitlenmiş birissuerve tek birassertion_provider(audience, resource)callback'i. Tarayıcı yok, kayıt yok, yenileme token'ı yok.- Yetkilendirme sunucusu hiçbir zaman kaynak sunucusundan keşfedilmez.
issuer'ı, meta veri belgesinin sunduğu dizenin tıpatıp aynısı olarak yapılandırın; karşılaştırma karakter karakter yapılır. - Sunucu tarafında
identity_assertion_enabled=Trueartıexchange_identity_assertion. SDK istemcinin kimliğini doğrular ve grant'in kapısını tutar; ID-JAG'i doğrulamak tamamen size aittir ve düzenlenen token isteğin değil, ID-JAG'inresourcedeğerine bağlanır.
Bu sayfanın hiç dokunmadığı tek taraf MCP sunucusudur. Az önce bastığınız token'la ne yapıyorsa, onu Yetkilendirme sayfasında zaten yapıyordu.