Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.alterauth.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Alter Python SDK turns any FastMCP server into a multi-user MCP server with built-in OAuth 2.0. Each user authenticates with an identity provider, and the SDK resolves their OAuth grants automatically — no shared credentials, no per-user configuration, no mcp-remote shim. One server, many users, each with their own OAuth tokens.

Prerequisites

  • Alter Vault API key (from the Developer Portal)
  • OAuth provider configured in the Developer Portal (e.g., Google, GitHub, Slack)
  • Identity provider configured (e.g., Auth0, Clerk) for user authentication
  • Python 3.11+

Install Dependencies

pip install 'alter-sdk[fastapi,mcp]' fastapi uvicorn

Build the Server

Create a file called server.py:
import os
import uvicorn
from fastapi import FastAPI
from fastmcp import FastMCP
from alter_sdk.fastapi import AlterFastAPI
from alter_sdk.mcp import AlterMCP, AlterContext

# --- SDK setup ---

ALTER_API_KEY = os.environ["ALTER_API_KEY"]
BASE_URL = os.environ.get("BASE_URL", "http://localhost:8000/mcp")

alter = AlterFastAPI(api_key=ALTER_API_KEY, caller="my-mcp-server")
vault = alter.vault

# Create the MCP auth provider -- a full OAuth 2.0 Authorization Server.
# MCP clients discover it automatically via RFC 8414 metadata.
alter_mcp = AlterMCP(vault)
auth = alter.auth_provider(
    base_url=BASE_URL,
    providers={"google": ["gmail.readonly"]},
)
mcp = FastMCP("my-server", auth=auth)

# --- MCP tools ---

@mcp.tool()
@alter_mcp.tool(provider="google")
async def list_emails(ctx: AlterContext, max_results: int = 10) -> list[dict]:
    """List recent emails from the inbox."""
    max_results = max(1, min(max_results, 50))
    resp = await ctx.request(
        "GET",
        "https://gmail.googleapis.com/gmail/v1/users/me/messages",
        query_params={"maxResults": str(max_results)},
    )
    return resp.json().get("messages", [])

# --- App setup ---

mcp_app = mcp.http_app(path="/")
app = FastAPI(lifespan=mcp_app.lifespan)
app.mount("/mcp", mcp_app)

# Mount OAuth discovery routes at the application root (RFC 8414).
for route in auth.get_well_known_routes():
    app.routes.insert(0, route)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
The base_url parameter must match the URL where the MCP server is mounted (e.g., http://localhost:8000/mcp). The SDK uses this as the OAuth issuer and advertises endpoint URLs relative to it.

Run the Server

export ALTER_API_KEY="alter_key_..."
python server.py
The server starts on http://localhost:8000 with the MCP endpoint at /mcp.

How Multi-User Auth Works

When any MCP client connects, the server handles the full OAuth 2.0 flow automatically:
1

Discovery

The client fetches /.well-known/oauth-authorization-server to discover OAuth endpoints (authorization, token, registration).
2

Client Registration

The client registers itself via the /register endpoint (dynamic client registration per RFC 7591).
3

User Login

A browser window opens for IDP login (e.g., Auth0, Clerk). The user authenticates with existing credentials.
4

Token Exchange

The SDK exchanges the authorization code for access and refresh tokens (with PKCE). The client stores these locally.
5

Identity Resolution

On every tool call, the SDK maps the authenticated token to the user’s identity, then resolves the correct OAuth grant for that user. Different users get different grants — true multi-tenancy.
On subsequent connections, clients reuse stored tokens (refreshing as needed) — no re-login required until the refresh token expires.

Connecting MCP Clients

Claude Code

Add the MCP server to the project configuration. Create or edit .claude.json in the project root:
{
  "mcpServers": {
    "my-server": {
      "type": "http",
      "url": "http://localhost:8000/mcp"
    }
  }
}
Or use the CLI:
claude mcp add my-server --type http --url http://localhost:8000/mcp

Claude Desktop

Add the same configuration to the Claude Desktop config file:
  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "my-server": {
      "type": "http",
      "url": "http://localhost:8000/mcp"
    }
  }
}
Restart Claude Desktop after saving the config.

Any MCP Client

Any MCP client that supports the HTTP transport with OAuth 2.0 (RFC 8414 discovery + PKCE) can connect using the same URL. No client-specific configuration is needed on the server side.

Adding More Tools

Add additional tools by stacking the @mcp.tool() and @alter_mcp.tool() decorators:
@mcp.tool()
@alter_mcp.tool(provider="google")
async def search_emails(
    ctx: AlterContext, query: str, max_results: int = 10, account: str = ""
) -> list[dict]:
    """Search emails using Gmail search syntax.

    Args:
        query: Gmail search query (e.g., "from:alice subject:meeting").
        max_results: Number of results (1-50, default 10).
        account: Email address of the Google account to use (required if
            multiple accounts are connected).
    """
    max_results = max(1, min(max_results, 50))
    acct = account.strip() or None
    resp = await ctx.request(
        "GET",
        "https://gmail.googleapis.com/gmail/v1/users/me/messages",
        account=acct,
        query_params={"q": query, "maxResults": str(max_results)},
    )
    return resp.json().get("messages", [])
The account parameter enables multi-account support. When a user has connected multiple accounts for the same provider, the LLM can pass the email address to select which account to use.

Next Steps

MCP Tool API

Detailed @alter.tool() decorator reference, error recovery, nested tools, and AlterContext API.

FastAPI Integration

Build combined REST + MCP servers with shared identity resolution.

LangChain Integration

Use @alter_tool with LangChain agents and LangGraph workflows.

Python SDK Quickstart

Full SDK setup, configuration options, and error handling.