I enabled DCR but when i do ```curl <https://api...
# ask-a-descoper
r
I enabled DCR but when i do
Copy code
curl <https://api.descope.com/PROJECT_ID/.well-known/openid-configuration>
I dont see the registtration endpoint. what am i missing
✅ 1
👀 1
d
DCR Configuration Issue The
registration_endpoint
only appears in the OpenID configuration when DCR is properly enabled for your MCP server . Check Your MCP Server Settings 1. Go to your Descope Console → Agentic Identity Hub → MCP Servers 2. Select your MCP server 3. Navigate to the "MCP Client Registration" section 4. Verify that "Dynamic Client Registration (DCR)" is enabled Verify the Configuration After enabling DCR, the discovery document should include:
Copy code
{
  "registration_endpoint": "<https://api.descope.com/v1/mgmt/mcp/client/PROJECT_ID/MCP_SERVER_ID/register>"
}
Common Issues • DCR must be explicitly enabled in your MCP server configuration • Changes may take a few minutes to propagate to the discovery endpoint • Ensure you're using the correct PROJECT_ID in your curl command If DCR is enabled but the endpoint still doesn't appear, try refreshing your MCP server configuration or check if there are any validation errors in the console. OAuth Discovery Endpoints | Descope Documentation MCP Server Settings | Descope Documentation
a
Hey Roopak, The correct discovery URL format is: https://api.descope.com/v1/apps/agentic/<Project ID>/<MCP Server ID>/.well-known/openid-configuration The registration_endpoint appears only in the MCP server-specific discovery endpoint, not the project-level one.
r
got it, that worked!
👍 1
on our backend what method is best to verify the token?
Copy code
await descopeSdk.validateSession(token)
did not work
a
For MCP/OIDC access tokens, use JWKS-based JWT validation (signature, issuer, audience, expiry) rather than
validateSession()
, which is designed for Descope session tokens from authentication flows.
r
Yeah thatw as hat i was trying to do, but the example code in the agentic identity hub suggestd suing validateSession
Copy code
// /app/.well-known/oauth-protected-resource/route.ts

import {
  protectedResourceHandler,
  metadataCorsOptionsRequestHandler,
} from "mcp-handler";

const DESCOPE_PROJECT_ID = process.env.NEXT_PUBLIC_DESCOPE_PROJECT_ID;
const DESCOPE_BASE_URL =
  process.env.NEXT_PUBLIC_DESCOPE_BASE_URL || "<https://api.descope.com>";

const handler = protectedResourceHandler({
  authServerUrls: [`${DESCOPE_BASE_URL}/v1/apps/${DESCOPE_PROJECT_ID}`],
});

export const GET = handler;
export const OPTIONS = metadataCorsOptionsRequestHandler();

// /app/api/[transport]/route.ts

import { createMcpHandler, withMcpAuth } from "mcp-handler";
import DescopeClient from "@descope/node-sdk";

const descopeClient = DescopeClient({
  projectId: DESCOPE_PROJECT_ID,
  baseUrl: DESCOPE_BASE_URL,
});

const verifyToken = async (req: Request, bearerToken?: string) => {
  if (!bearerToken) return undefined;

  try {
    const authInfo = await descopeClient.validateSession(bearerToken);

    const scopeString = authInfo.token.scope as string;
    const scopes = scopeString ? scopeString.split(" ") : [];
    const clientId = authInfo.token.azp as string;

    return {
      token: bearerToken,
      scopes: scopes,
      clientId: clientId,
      extra: {
        // Optional extra information
        userId: authInfo.token.sub,
      },
    };
  } catch (error) {
    console.error("Token validation failed:", error);
    return undefined;
  }
};

const authHandler = withMcpAuth(handler, verifyToken, {
  required: true,
  resourceMetadataPath: "/.well-known/oauth-protected-resource",
});
a
Got it. validateSession() is provided as a convenience via the SDK. JWKS-based validation is the more reliable approach for MCP tokens.
r
yeah i guess my question is why doesnt the helper work if it was in the guide
My bigger worry is if I am doing something wrong beyond just using JWKS
a
Hey Roopak, you’re not doing anything wrong.
validateSession()
is meant for Descope session tokens, not MCP/OIDC access tokens. In MCP, the tokens are standard JWT access tokens, so they should be validated using JWKS (issuer, audience, signature, expiry). The guide shows validateSession() as a convenience, but for MCP flows JWKS validation is the correct approach.
r
i guess thwe guide showing validateSession() is wrong, and confusing is what i am coming to. the code literally does not work. it cant be for convenience 😛
a
Understood Roopak. I will check internally if the docs should be updated accordingly. Thanks for bringing this up. Please let me know if you need help with anything else.
@Roopak Venkatakrishnan which sdk are u using for backend?
r
@Aditya Arakal @descope/node-sdk, the sme one in the code snippet
r
Hey @Roopak Venkatakrishnan, Hey Roopak, thanks for confirming you're on
@descope/node-sdk
. I did some digging and actually
validateSession()
should work for MCP tokens. Our official
@descope/mcp-express
SDK uses
validateSession()
internally as its token validation method, and both the project-level and MCP-server-level JWKS endpoints use the same signing keys. So the issue is likely in your setup rather than
validateSession()
itself. Could you share: 1. The exact error message you're getting from
validateSession()
2. Your
@descope/node-sdk
version (
npm ls @descope/node-sdk
) 3. How you're initializing the DescopeClient (the
projectId
and
baseUrl
values) That said, for building Express-based MCP servers, I'd recommend our dedicated SDK
@descope/mcp-express
— it handles token validation, scope enforcement, and metadata endpoints out of the box:
Copy code
npm i @descope/mcp-express
Here's a full guide on setting this up: https://www.descope.com/blog/post/auth-remote-mcp It only needs
DESCOPE_PROJECT_ID
,
SERVER_URL
, and optionally
DESCOPE_MANAGEMENT_KEY
(if using DCR) in your
.env
.
r
•
unexpected "iss" claim value
(from here) • 1.10.0
Copy code
client = DescopeClient({
      projectId: config.get('descope.projectId'),
      managementKey: config.get('descope.managementKey'),
    });
But I also tried a few other ways. The backend that is handling sessions for our API is also handing auth for the web app so mcp-express might not be the best thing> cc @Rahul Rijhwani
a
Hi Roopak, The error you’re seeing (
unexpected "iss" claim value
) indicates that the issuer (
iss
) in the token doesn’t match what the SDK expects for your project. This usually happens if there’s a mismatch between the token issuer and the configured
projectId
/
baseUrl
.
r
yeah but i checked, and the projectId and baseUrl were not the iss token for the dynamic MCP
iss was the MCP server id
a
Since the
iss
in your token is the MCP server ID (and not the project issuer),
validateSession()
will fail with the
unexpected "iss"
error. This is expected in MCP flows. For MCP/OIDC tokens, we recommend validating using standard JWKS-based JWT validation (signature, issuer, audience, expiry) instead of
validateSession()
. You’re on the right track here — nothing wrong with your implementation We’re also reviewing the docs internally to improve clarity around this.
r
@Amol Thite but the sample code says to use validateSession. ovrall what your guide says does not match what happens when i run it
a
Hi Roopak, You’re right — the current guide is confusing here and doesn’t fully align with the actual behavior. In MCP flows, since the token issuer (
iss
) can be the MCP server ID,
validateSession()
may fail due to this mismatch, even though the sample code suggests using it. So your observation is correct. For now, the reliable approach is to use JWKS-based validation for these tokens. We’ve raised this internally to get the docs corrected and aligned with the expected behavior, so this confusion is addressed going forward.