Skip to main contentArrow Right

Table of Contents

Model Context Protocol (MCP) servers are quickly becoming the control plane for AI systems–the place where models, users, tools, tenants, and real-world systems intersect. With that potential comes a hard requirement: you must know who is calling your server, what they’re allowed to do, and which external systems they can access.

That’s why we built the Descope Python MCP SDK–a simple way to integrate Descope authentication and authorization with MCP servers built in Python. This SDK adds to our suite of capabilities in the Agentic Identity Hub to help MCP server developers securely connect their MCP servers with MCP clients.

The SDK is not just a token validator. It’s a bridge between modern identity, policy-driven authorization, and agent infrastructure.

And if you’re already using Descope as your customer identity provider, it’s the fastest way to bring your existing roles, permissions, SSO integrations, and security policies directly into your MCP servers. Visit the SDK docs to get started, or keep reading to learn more about what the Python MCP SDK provides!

The authorization model for MCP with Descope

In an MCP-based system:

  • Descope acts as a hosted Authorization Server (AS)

  • MCP clients register with Descope and obtain access tokens

  • MCP servers validate those access tokens

  • MCP servers use validated access tokens to retrieve external tokens when needed

Descope is the system of record for identity, policy, and credentials. MCP servers never create or manage external credentials themselves.

What the Descope Python MCP SDK provides

The Descope Python MCP SDK is designed to sit directly on the security boundary of your MCP server. It gives you three core capabilities that every production MCP service needs:

  1. Validate who is calling

  2. Enforce what they’re allowed to do

  3. Safely access external systems on their behalf

Instead of building custom auth logic into every tool, you use the SDK as a single, consistent authorization layer.

Function

What it does

Why it matters

validate_token()

Verifies the MCP access token using Descope’s keys and config

Ensures the caller is real, trusted, and targeting the right MCP server

require_scopes()

Enforces fine-grained permissions and raises MCP-spec errors

Prevents unauthorized tool execution

get_connection_token()

Exchanges the MCP access token for an external app token

Gives you secure, policy-approved access to third-party APIs

Together, these functions let your MCP server act on behalf of a user without ever storing secrets or hardcoding permissions.

See the full setup instructions, including MCP server configuration and .well-known URLs in the Descope Python MCP SDK Documentation.

The SDK is intentionally focused. It does not replace MCP frameworks or tool execution layers. Instead, it concentrates on the authorization primitives every MCP server needs in order to safely access external systems.

Token validation with scopes and audience enforcement

The SDK provides first-class validation of Descope-issued access tokens. MCP servers can validate that an access token:

  • Is authentic and unexpired

  • Contains a required subset of scopes

  • Is issued for the correct audience

The audience is configured when the SDK is initialized and ensures that:

  • Tokens are bound to a specific MCP server or deployment

  • Cross-server token replay is prevented

  • Authorization decisions remain context-aware

User-level and tenant-level external tokens

The SDK supports retrieving external application tokens at the two levels required by real MCP deployments:

  • User-level external tokens, optionally scoped and tenant-aware

  • Tenant-level external tokens, representing tenant-wide access

External tokens can be requested:

  • With explicit scope requirements

  • With refresh or force-refresh semantics

  • As fully typed responses including expiration and metadata

This enables MCP servers to safely power both interactive user tools and automated workflows without ever handling raw credential storage.

Policy-enforced access to external tokens

A core guarantee of the SDK is: When an MCP access token is exchanged for an external application token, Descope policies are enforced. More precisely:

  • External tokens are stored and managed by Descope in the Connections vault

  • MCP servers request access to those tokens using an MCP access token

  • Descope evaluates user, tenant, application, and scope policies at request time

  • Only policy-approved external tokens are returned

  • Revocations and policy changes take effect immediately

The MCP server acts as a policy-enforced accessor, not merely a token issuer.

Why this matters for MCP servers

MCP servers are security boundaries.

They frequently execute actions with real-world impact, call external services on behalf of users, and return sensitive data. Without strong guarantees around how external tokens are accessed, authorization flaws become inevitable.

The Descope Python MCP SDK ensures that:

  • External credentials are never embedded in tools

  • Access to external tokens is always policy-checked

  • Authorization logic is centralized and auditable

  • User identity, tenant identity, and client identity remain clearly separated

A complete example

Below is a real MCP server tool that:

  1. Validates the caller

  2. Enforces the calendar.read scope

  3. Retrieves a Google Calendar OAuth token from Descope

  4. Calls the Google Calendar API on the user’s behalf

from mcp.server import FastMCP
from mcp_descope import DescopeMCP, validate_token, require_scopes, get_connection_token, InsufficientScopeError
import requests

# Initialize SDK
DescopeMCP(
    well_known_url="https://api.descope.com/v1/apps/agentic/<Descope Project ID>/<Descope MCP Server URL>/.well-known/openid-configuration",
    mcp_server_url="https://your-mcp-server.com"
)

mcp = FastMCP("calendar-server")

@mcp.tool()
async def get_calendar_events(
    max_results: int = 5,
    mcp_access_token: str = None
) -> str:
    """
    Get upcoming calendar events for the authenticated user.
    Requires 'calendar.read' scope.
    """
    if not mcp_access_token:
        return {"error": "Authentication required"}
    
    try:
        # 1. Validate the MCP access token
        token_result = validate_token(mcp_access_token)
        
        # 2. Enforce required scopes
        require_scopes(token_result, ["calendar.read"])
        
        # 3. Resolve the user identity
        user_id = token_result.get("sub") or token_result.get("userId")
        
        # 4. Retrieve a policy-approved Google token
        google_token = get_connection_token(
            user_id=user_id,
            app_id="google-calendar",
            scopes=["https://www.googleapis.com/auth/calendar.readonly"],
            access_token=mcp_access_token
        )
        
        # 5. Call Google Calendar API
        from datetime import datetime
        now = datetime.utcnow().isoformat() + "Z"
        url = "https://www.googleapis.com/calendar/v3/calendars/primary/events"
        params = {
            "timeMin": now,
            "maxResults": max_results,
            "orderBy": "startTime",
            "singleEvents": True
        }
        
        response = requests.get(
            url,
            headers={"Authorization": f"Bearer {google_token}"},
            params=params
        )
        
        if response.status_code != 200:
            return {"error": f"Google API error: {response.status_code}"}
        
        return response.json()
        
    except InsufficientScopeError as e:
        # MCP spec-compliant error response
        return e.to_json()
    except Exception as e:
        return {"error": str(e)}

How this complements FastMCP

FastMCP already enforces tool-level access controls. The Descope Python MCP SDK extends that boundary by adding:

  • Secure access to external credentials

  • Policy-backed scope enforcement

Together, FastMCP handles how tools run, while Descope handles who is allowed to act and what they can access. That separation is what makes MCP servers safe to use in production.

For Express and TypeScript teams: Descope Express MCP SDK

Not building your MCP server in Python? You don’t have to wait for the same security and policy guarantees.

Descope already provides an Express MCP SDK for teams building MCP servers in Node.js and TypeScript. It brings the same core principles of the Python MCP SDK to the JavaScript ecosystem:

  • Policy-enforced access to external tokens

  • User- and tenant-level credentials

  • Scope and audience validation

  • Compatibility with MCP servers built on Express and the generic MCP SDK

This means you can standardize on Descope as your identity and authorization layer across both Python and Node-based MCP servers while keeping a single source of truth for roles, permissions, scopes, and policies.

Together, the Python MCP SDK and the Express MCP SDK give you a consistent, cross-language security foundation for your entire MCP deployment.

Closing thoughts

MCP servers don’t just execute tools — they represent users, tenants, and businesses in the real world. That means your authorization model is not an implementation detail. It is your security boundary.

With Descope, your MCP server inherits everything you already trust:

  • Enterprise SSO

  • Role and permission models

  • Policy enforcement

  • Secure credential governance

  • Auditable access control

The Descope Python MCP SDK makes it effortless to bring that identity foundation into the MCP world — so your agents can act with power, and your security team can still sleep at night. Sign up for a free account and visit the SDK docs to get started. Have questions about our platform? Book a demo with our team.