Levante OAuth provider for MCP servers (framework-agnostic core + adapters).
Project description
levante-mcp-sdk
OAuth provider for FastMCP servers deployed through
Levante. It wraps your MCP server with a RemoteAuthProvider
that validates Levante-issued tokens: it verifies the JWT locally against Levante's JWKS and
confirms the grant/membership via Levante's introspection endpoint
(/api/v1/mcp/introspect), with a short-lived in-memory cache.
Part of the
sdks-levanterepository. The TypeScript SDK (@levante/mcp-sdk) will live inpackages/mcp-sdk/of the same repo.
Install
pip install levante-mcp-sdk # núcleo (solo cliente de introspección)
pip install "levante-mcp-sdk[mcp]" # + adaptador SDK oficial de Anthropic (mcp)
pip install "levante-mcp-sdk[fastmcp]" # + adaptador FastMCP v2 (standalone)
pip install "levante-mcp-sdk[jwt]" # + verificación JWT local
Requires Python >=3.11. The core depends only on httpx; each framework is an optional extra.
Uso con el SDK oficial (mcp.server.fastmcp.FastMCP)
from mcp.server.auth.settings import AuthSettings
from mcp.server.fastmcp import FastMCP
from pydantic import AnyHttpUrl
from levante_mcp_sdk import LevanteConfig
from levante_mcp_sdk.integrations.mcp import LevanteTokenVerifier
cfg = LevanteConfig.from_env()
mcp = FastMCP(
"my-mcp",
token_verifier=LevanteTokenVerifier(cfg),
auth=AuthSettings(
issuer_url=AnyHttpUrl(cfg.issuer),
resource_server_url=AnyHttpUrl(cfg.resource),
),
)
Uso con FastMCP v2 (standalone)
from fastmcp import FastMCP
from levante_mcp_sdk import LevanteAuthProvider
mcp = FastMCP(name="my-mcp", auth=LevanteAuthProvider.from_env())
LevanteAuthProvider.from_env() reads its configuration from environment variables. When the
MCP is deployed through Levante with a levante.yaml, these are injected automatically; you can
also set them manually for local development.
Configuration (environment variables)
A typical deployment only needs two variables: LEVANTE_API_KEY and LEVANTE_MCP_RESOURCE.
The issuer defaults to the production platform URL, and the introspection endpoint is derived
from it ({issuer}/api/v1/mcp/introspect). The remaining variables exist as overrides.
| Variable | Required | Description |
|---|---|---|
LEVANTE_API_KEY |
yes | A real Levante API key (lv_sk_*) the MCP uses to authenticate against /introspect. |
LEVANTE_MCP_RESOURCE |
yes | Resource/audience for this MCP — its own public URL. |
LEVANTE_ISSUER |
no (defaults to https://platform.levanteapp.com) |
OAuth issuer / platform URL. Override for local/staging. |
LEVANTE_INTROSPECT_URL |
no (derived from issuer) | Override the introspection endpoint if it isn't {issuer}/api/v1/mcp/introspect. |
LEVANTE_JWKS_URI |
no (only [fastmcp]/[jwt]) |
JWKS URI for local JWT verification. Not used by the introspection-only path. |
LEVANTE_MCP_BASE_URL |
no (defaults to resource, only [fastmcp]) |
RemoteAuthProvider base URL. |
LEVANTE_REQUIRE_ORG_MEMBERSHIP |
no (true) |
If true, denies tokens whose user is no longer an org member. |
LEVANTE_FAIL_OPEN |
no (false) |
If true, allows requests when introspection is unreachable. Leave false in production. |
LEVANTE_CACHE_TTL_SECONDS |
no (45) |
TTL of the in-memory introspection cache. |
Minimal example:
LEVANTE_API_KEY=lv_sk_...
LEVANTE_MCP_RESOURCE=https://crm-conquer.example.run.app
# Optional override (defaults to https://platform.levanteapp.com):
# LEVANTE_ISSUER=https://staging.levanteapp.com
LEVANTE_API_KEY must be a real key registered in Levante; it cannot be an arbitrary string.
Future enhancement:
LEVANTE_JWKS_URIcould be auto-discovered viaGET {issuer}/.well-known/oauth-authorization-server(which returnsjwks_uri). This is deliberately not done at startup to avoid a network call; the introspection-only path needs no JWKS at all.
How it works
- JWT verification (local, offline): the bearer token is verified against
LEVANTE_JWKS_URIwith the expected issuer and audience. - Introspection (cached): the token is checked against
LEVANTE_INTROSPECT_URL, which returns whether the grant is active and whether the user is still an org member. - Membership gate: with
LEVANTE_REQUIRE_ORG_MEMBERSHIP=true, a non-member is denied even if the JWT is otherwise valid. - On success, Levante identity is attached to the token claims:
levante_user_id,levante_org_id,levante_mcp_slug.
Revocations may take up to LEVANTE_CACHE_TTL_SECONDS to take effect on each running instance.
Build
uv build
uv run twine check dist/*
Publishing to PyPI is automated via .github/workflows/publish-python.yml on GitHub release: it
checks the tag matches pyproject.toml's version and runs ruff + mypy + pytest + twine before
publishing.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file levante_mcp_sdk-0.3.0.tar.gz.
File metadata
- Download URL: levante_mcp_sdk-0.3.0.tar.gz
- Upload date:
- Size: 109.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee64f73144e86327f4b5cdb4ce593af7fe4c77e48171b32e53f3374bafb045a5
|
|
| MD5 |
4df1953b0898a8a07968b0a3dfbee95b
|
|
| BLAKE2b-256 |
9e029e4bcb4669f34c11312800d54aa37df1ee94b8620a1df99c82783ae4404d
|
Provenance
The following attestation bundles were made for levante_mcp_sdk-0.3.0.tar.gz:
Publisher:
publish-python.yml on levante-hub/SDKS-Levante
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
levante_mcp_sdk-0.3.0.tar.gz -
Subject digest:
ee64f73144e86327f4b5cdb4ce593af7fe4c77e48171b32e53f3374bafb045a5 - Sigstore transparency entry: 1694815890
- Sigstore integration time:
-
Permalink:
levante-hub/SDKS-Levante@eacb6ce373e87c7bce3fd6c4621272864c365245 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/levante-hub
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@eacb6ce373e87c7bce3fd6c4621272864c365245 -
Trigger Event:
release
-
Statement type:
File details
Details for the file levante_mcp_sdk-0.3.0-py3-none-any.whl.
File metadata
- Download URL: levante_mcp_sdk-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca4888194caea6b08aaf6bb4fb306a32c0c441ebb4abc371fece14d173e36975
|
|
| MD5 |
a14ce8165b16bbc157ee160a2f61996b
|
|
| BLAKE2b-256 |
f3e240ccfff43645f92e0e825e9a2f747ce617d62fd298bdd878a3c529cfac2a
|
Provenance
The following attestation bundles were made for levante_mcp_sdk-0.3.0-py3-none-any.whl:
Publisher:
publish-python.yml on levante-hub/SDKS-Levante
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
levante_mcp_sdk-0.3.0-py3-none-any.whl -
Subject digest:
ca4888194caea6b08aaf6bb4fb306a32c0c441ebb4abc371fece14d173e36975 - Sigstore transparency entry: 1694815945
- Sigstore integration time:
-
Permalink:
levante-hub/SDKS-Levante@eacb6ce373e87c7bce3fd6c4621272864c365245 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/levante-hub
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@eacb6ce373e87c7bce3fd6c4621272864c365245 -
Trigger Event:
release
-
Statement type: