Skip to content

07 - OAuth Tools

Learn how to create tools that require OAuth

Prerequisites

Before running this example, you need to authenticate with Arcade:

# Install the Arcade CLI (if not already installed)
uv pip install arcade-mcp

# Login to Arcade
arcade login

Running the Example

  • Run HTTP: uv run examples/07_auth.py
  • Run stdio: uv run examples/07_auth.py stdio

Source Code

#!/usr/bin/env python3
"""
07_auth.py - Using tools with OAuth

This example demonstrates how to create and use tools that require OAuth.
Tools that require auth will automatically prompt users to authorize the action when called.

Prerequisites:
    1. Install arcade-mcp: uv pip install arcade-mcp
    2. Login to Arcade: arcade login
    3. Run this server: uv run 07_auth.py

To run:
    uv run examples/07_auth.py
    uv run examples/07_auth.py stdio  # For Claude Desktop
"""

import sys
from typing import Annotated

import httpx
from arcade_mcp_server import Context, MCPApp
from arcade_mcp_server.auth import Reddit

# Create the app
app = MCPApp(name="auth_example", version="1.0.0", log_level="DEBUG")


# To use this tool, you need to use the Arcade CLI (uv pip install arcade-mcp)
# and run 'arcade login' to authenticate.
@app.tool(requires_auth=Reddit(scopes=["read"]))
async def get_posts_in_subreddit(
    context: Context, subreddit: Annotated[str, "The name of the subreddit"]
) -> dict:
    """Get posts from a specific subreddit"""
    # Normalize the subreddit name
    subreddit = subreddit.lower().replace("r/", "").replace(" ", "")

    # Prepare the httpx request
    # OAuth token is injected into the context at runtime.
    # LLMs and MCP clients cannot see or access your OAuth tokens.
    oauth_token = context.get_auth_token_or_empty()
    headers = {
        "Authorization": f"Bearer {oauth_token}",
        "User-Agent": "mcp_server-mcp-server",
    }
    params = {"limit": 5}
    url = f"https://oauth.reddit.com/r/{subreddit}/hot"

    # Make the request
    async with httpx.AsyncClient() as client:
        response = await client.get(url, headers=headers, params=params)
        response.raise_for_status()

        # Return the response
        return response.json()


# Run with specific transport
if __name__ == "__main__":
    # Get transport from command line argument, default to "http"
    transport = sys.argv[1] if len(sys.argv) > 1 else "http"

    print(f"Starting auth example server with {transport} transport")
    print("Prerequisites:")
    print("  1. Install: uv pip install arcade-mcp")
    print("  2. Login: arcade login")
    print("")

    # Run the server
    # - "http" (default): HTTP streaming for Cursor, VS Code, etc.
    # - "stdio": Standard I/O for Claude Desktop, CLI tools, etc.
    app.run(transport=transport, host="127.0.0.1", port=8000)

OAuth Authentication Features

1. Requiring Authentication

Use the requires_auth parameter with an auth provider to require OAuth:

from arcade_mcp_server.auth import Reddit

@app.tool(requires_auth=Reddit(scopes=["read"]))
async def get_posts_in_subreddit(
    context: Context,
    subreddit: Annotated[str, "The name of the subreddit"]
) -> dict:
    """Get posts from a specific subreddit."""
    # OAuth token is automatically injected into context
    oauth_token = context.get_auth_token_or_empty()
    # Use the token to make authenticated API requests

2. Specifying OAuth Scopes

Different tools may require different scopes:

# Read-only access
@app.tool(requires_auth=Reddit(scopes=["read"]))
async def read_only_tool(context: Context) -> dict:
    """Tool that only reads data."""
    pass

# Multiple scopes for more permissions
@app.tool(requires_auth=Reddit(scopes=["read", "identity"]))
async def identity_tool(context: Context) -> dict:
    """Tool that accesses user identity."""
    pass

3. Accessing OAuth Tokens

OAuth tokens are securely injected into the context at runtime:

# Get the token (returns empty string if not authenticated)
oauth_token = context.get_auth_token_or_empty()

# Use token in API requests
headers = {
    "Authorization": f"Bearer {oauth_token}",
    "User-Agent": "my-app",
}

4. Making Authenticated API Requests

Use the OAuth token with httpx or other HTTP clients:

import httpx

async with httpx.AsyncClient() as client:
    response = await client.get(
        "https://oauth.reddit.com/api/endpoint",
        headers={"Authorization": f"Bearer {oauth_token}"}
    )
    response.raise_for_status()
    return response.json()

Available Auth Providers

The arcade_mcp_server.auth module provides several OAuth providers:

Authentication Flow

  1. User runs arcade login: Authenticates with Arcade and stores credentials
  2. Tool is called: MCP client calls a tool that requires authentication
  3. Authorization Required: If the user has not authorized the required scopes, then they are prompted to go through an OAuth flow
  4. Token injection: Arcade injects the OAuth token into the tool's context
  5. API request: Tool uses the token to make authenticated API requests
  6. Response: Tool returns data to the MCP client

The LLM and MCP clients never see the OAuth tokens - they are securely injected server-side.

Security Best Practices

  1. Never log tokens: OAuth tokens should never be logged or exposed
  2. Use appropriate scopes: Request only the scopes your tool actually needs

Key Concepts

  • OAuth Integration: Arcade handles OAuth flows and token management
  • Secure Token Injection: Tokens are injected into context at runtime
  • Scope Management: Specify exactly which permissions your tool needs
  • Provider Support: Multiple OAuth providers available out of the box
  • User Privacy: LLMs and MCP clients never see OAuth tokens