AuthProvider is the abstract base class for OAuth integrations. Implement it to let users connect their accounts.
Abstract methods
from mcp.gumstack import AuthProvider, TokenResponse
class MyProvider(AuthProvider):
name = "my-provider" # Unique identifier
def get_url(self, redirect_uri: str, state: str) -> str:
"""Return the OAuth authorization URL."""
...
async def exchange(self, code: str, redirect_uri: str) -> TokenResponse:
"""Exchange authorization code for tokens."""
...
async def refresh(self, refresh_token: str) -> TokenResponse:
"""Refresh an expired access token."""
...
async def get_nickname(self, access_token: str) -> str:
"""Return display name for the connected account."""
...
name
Unique identifier for this provider. Used in routes and credential storage.
get_url
Generate the OAuth authorization URL.
Callback URL where the provider redirects after authorization. Gumstack’s callback URL is https://api.gumstack.com/auth/callback — use this when registering your OAuth app with the third-party service.
Opaque value for CSRF protection. Include in the URL.
exchange
Exchange an authorization code for tokens.
Authorization code from the OAuth callback.
Same redirect URI used in get_url().
Returns TokenResponse with at least access_token.
refresh
Refresh an expired access token.
Refresh token from a previous exchange.
Returns TokenResponse with new access_token.
get_nickname
Return a display name for the connected account (shown in UI).
Return something recognizable: email, username, or organization name.
Optional methods
revoke
Revoke an access token when user disconnects.
async def revoke(self, access_token: str) -> bool:
"""Revoke the token. Return True if successful."""
# Default: returns True (no-op)
TokenResponse
from mcp.gumstack import TokenResponse
TokenResponse(
access_token: str, # Required
refresh_token: str | None, # For token refresh
expires_in: int | None, # Seconds until expiry
token_type: str = "Bearer",
scope: str | None = None
)
Example: Linear
import os
import httpx
from mcp.gumstack import AuthProvider, TokenResponse
class LinearAuthProvider(AuthProvider):
name = "linear"
def get_url(self, redirect_uri: str, state: str) -> str:
return (
f"https://linear.app/oauth/authorize"
f"?client_id={os.environ['LINEAR_CLIENT_ID']}"
f"&redirect_uri={redirect_uri}"
f"&state={state}"
f"&response_type=code"
f"&scope=read,write"
)
async def exchange(self, code: str, redirect_uri: str) -> TokenResponse:
async with httpx.AsyncClient() as client:
resp = await client.post(
"https://api.linear.app/oauth/token",
data={
"grant_type": "authorization_code",
"client_id": os.environ["LINEAR_CLIENT_ID"],
"client_secret": os.environ["LINEAR_CLIENT_SECRET"],
"redirect_uri": redirect_uri,
"code": code,
}
)
data = resp.json()
return TokenResponse(
access_token=data["access_token"],
expires_in=data.get("expires_in")
)
async def refresh(self, refresh_token: str) -> TokenResponse:
# Linear tokens don't expire, but implement if needed
raise NotImplementedError("Linear tokens don't expire")
async def get_nickname(self, access_token: str) -> str:
async with httpx.AsyncClient() as client:
resp = await client.post(
"https://api.linear.app/graphql",
headers={"Authorization": f"Bearer {access_token}"},
json={"query": "{ viewer { email } }"}
)
return resp.json()["data"]["viewer"]["email"]