Descope
The Descope plugin provides authentication for your MCP server using Descope's OAuth 2.1 agentic identity system with Dynamic Client Registration.
For the complete documentation index, see llms.txt. Markdown variants of every page are available by appending .md to the URL.Installation
Install the Descope plugin:
pnpm i @xmcp-dev/descopeDescope Setup
MCP clients use Dynamic Client Registration (DCR) and OAuth 2.1 to authenticate. Configure your Descope project so your xmcp server can participate in the agentic OAuth flow.
Create an MCP Server Resource
- Go to Descope Console → Agentic Identity Hub → Resources
- Click Create Resource → MCP Server
- Set a name and your server's base URL (e.g.,
http://127.0.0.1:3001for local development) - Copy the Issuer URL
The issuer URL identifies your resource and contains your project ID. The plugin parses both from it, so you only need this one value.
Environment Variables
Configure the following environment variables in your .env file:
For production, set BASE_URL to your deployed server URL and update the base URL on your MCP Server record in the Descope Console to match.
Create a Management Key (optional)
Required only when using getUser() or getManagementClient()
A management key is required to call getUser() or getManagementClient(). If you only need session data from the token, you can skip this step.
- Go to Descope Console → Company → Management Keys
- Click + Management Key
- Copy and save the key to your
.envfile since it is only shown once
Set up the Provider
Create a middleware.ts file in your xmcp app's src directory:
To enable user profile lookups, add the management key:
Configuration Options
issuerURL: Issuer URL from your MCP Server resource in the Descope Console (required). Contains your project ID — no separate project ID field needed.baseURL: Base URL of your MCP server (required). Must match the URL configured on the resource in Descope.managementKey: (Optional) Descope management key. Required to usegetUser()orgetManagementClient().scopesSupported: (Optional) Array of OAuth scopes advertised in the resource metadata. Defaults to["openid", "profile", "email"].
Get a user session
Access the authenticated user's session in your tools using getSession().
Example: Return the current user's identity
The session object contains the following fields:
session.userId: The Descope user ID.session.email: Email address from the JWT claims.session.token: The raw bearer token from the request.session.loginIds: Login identifiers associated with the user (email, phone, etc.).session.permissions: Array of permissions granted to this session.session.roles: Roles assigned to the user.session.tenants: Tenant memberships, each with per-tenantpermissionsandroles.session.expiresAt: Token expiry as aDate.session.issuedAt: Token issue time as aDate.session.claims: Raw JWT claims object.
Do not call getSession() at module load time. Only call it inside tool handler functions where the middleware context is active.
Example: Read custom JWT claims
Any claims added to Descope JWTs via JWT templates are available on session.claims:
session.claims is typed as Record<string, unknown>, so cast each value to the type you expect after reading it.
Get user profile
Use getUser() to fetch full user details from the Descope Management API. This requires managementKey to be set in your provider configuration.
Example: Return full user profile
Access the SDK clients
getClient()
Returns the full Descope Node SDK client, giving you access to all Descope features from within your tool handlers.
getManagementClient()
Returns the management namespace of the Descope SDK. Requires managementKey to be configured.
Fetch a connection token
Map your MCP server scopes to any corresponding connection scopes that are necessary to fetch from the Descope Connections Vault.
Use fetchConnectionToken() to retrieve a stored OAuth access token from a Descope connection. This uses the MCP Server's access token (no Management Key required).
Troubleshooting
These are the most common errors you may encounter when using the Descope plugin:
unauthorized: The request is missing theAuthorizationheader. The MCP client must complete the OAuth flow before accessing protected routes.token_expired: The access token has expired. MCP clients should automatically refresh tokens; users can disconnect and reconnect to get fresh tokens.invalid_token: Token verification failed. Check thatDESCOPE_ISSUER_URLcontains the correct project ID that issued the token.
OAuth Init Failed
If an MCP client fails to initialize the OAuth flow:
- Verify your resource exists in Descope Console → Agentic Identity Hub → Resources
- Confirm
DESCOPE_ISSUER_URLmatches the issuer URL shown in the console - Ensure
BASE_URLmatches the base URL configured on your resource
Session Not Initialized
If getSession() throws "getSession() called outside of Descope middleware":
- Ensure
descopeProvideris exported as default frommiddleware.ts - Ensure the tool is called on a route under
/mcp/* - Do not call
getSession()at module load time, only inside tool handlers
Management Key Errors
If getUser() or getManagementClient() throws an error about the management key:
- Set
DESCOPE_MANAGEMENT_KEYin your environment - Pass
managementKey: process.env.DESCOPE_MANAGEMENT_KEYin yourdescopeProviderconfig - Verify the key is valid in Descope Console → Company → Management Keys
Token Expired Errors
Access tokens are short-lived. If you see token_expired errors:
- MCP clients should automatically refresh tokens using the refresh token flow
- Users can disconnect and reconnect to get fresh tokens
Invalid Token Errors
If token verification consistently fails:
- Verify
DESCOPE_ISSUER_URLcontains the correct project ID - Ensure the MCP client is sending tokens issued by the correct Descope project