docs/v2/integrations/discord.mdx
import { VersionBadge } from "/snippets/version-badge.mdx"
<VersionBadge version="2.13.2" />This guide shows you how to secure your FastMCP server using Discord OAuth. Since Discord doesn't support Dynamic Client Registration, this integration uses the OAuth Proxy pattern to bridge Discord's traditional OAuth with MCP's authentication requirements.
Before you begin, you will need:
http://localhost:8000)Create an application in the Discord Developer Portal to get the credentials needed for authentication:
<Steps> <Step title="Navigate to Discord Developer Portal"> Go to the [Discord Developer Portal](https://discord.com/developers/applications).Click **"New Application"** and give it a name users will recognize (e.g., "My FastMCP Server").
In the **Redirects** section, click **"Add Redirect"** and enter your callback URL:
- For development: `http://localhost:8000/auth/callback`
- For production: `https://your-domain.com/auth/callback`
<Warning>
The redirect URL must match exactly. The default path is `/auth/callback`, but you can customize it using the `redirect_path` parameter. Discord allows `http://localhost` URLs for development. For production, use HTTPS.
</Warning>
- **Client ID**: A numeric string like `12345`
- **Client Secret**: Click "Reset Secret" to generate one
<Tip>
Store these credentials securely. Never commit them to version control. Use environment variables or a secrets manager in production.
</Tip>
Create your FastMCP server using the DiscordProvider, which handles Discord's OAuth flow automatically:
from fastmcp import FastMCP
from fastmcp.server.auth.providers.discord import DiscordProvider
auth_provider = DiscordProvider(
client_id="12345", # Your Discord Application Client ID
client_secret="your-client-secret", # Your Discord OAuth Client Secret
base_url="http://localhost:8000", # Must match your OAuth configuration
)
mcp = FastMCP(name="Discord Secured App", auth=auth_provider)
@mcp.tool
async def get_user_info() -> dict:
"""Returns information about the authenticated Discord user."""
from fastmcp.server.dependencies import get_access_token
token = get_access_token()
return {
"discord_id": token.claims.get("sub"),
"username": token.claims.get("username"),
"avatar": token.claims.get("avatar"),
}
Start your FastMCP server with HTTP transport to enable OAuth flows:
fastmcp run server.py --transport http --port 8000
Your server is now running and protected by Discord OAuth authentication.
Create a test client that authenticates with your Discord-protected server:
from fastmcp import Client
import asyncio
async def main():
async with Client("http://localhost:8000/mcp", auth="oauth") as client:
print("✓ Authenticated with Discord!")
result = await client.call_tool("get_user_info")
print(f"Discord user: {result['username']}")
if __name__ == "__main__":
asyncio.run(main())
When you run the client for the first time:
Discord OAuth supports several scopes for accessing different types of user data:
| Scope | Description |
|---|---|
identify | Access username, avatar, and discriminator (default) |
email | Access the user's email address |
guilds | Access the user's list of servers |
guilds.join | Ability to add the user to a server |
To request additional scopes:
auth_provider = DiscordProvider(
client_id="...",
client_secret="...",
base_url="http://localhost:8000",
required_scopes=["identify", "email"],
)
For production deployments with persistent token management across server restarts, configure jwt_signing_key and client_storage:
import os
from fastmcp import FastMCP
from fastmcp.server.auth.providers.discord import DiscordProvider
from key_value.aio.stores.redis import RedisStore
from key_value.aio.wrappers.encryption import FernetEncryptionWrapper
from cryptography.fernet import Fernet
auth_provider = DiscordProvider(
client_id="12345",
client_secret=os.environ["DISCORD_CLIENT_SECRET"],
base_url="https://your-production-domain.com",
jwt_signing_key=os.environ["JWT_SIGNING_KEY"],
client_storage=FernetEncryptionWrapper(
key_value=RedisStore(
host=os.environ["REDIS_HOST"],
port=int(os.environ["REDIS_PORT"])
),
fernet=Fernet(os.environ["STORAGE_ENCRYPTION_KEY"])
)
)
mcp = FastMCP(name="Production Discord App", auth=auth_provider)
For complete details on these parameters, see the OAuth Proxy documentation. </Note>
For production deployments, use environment variables instead of hardcoding credentials.
Setting this environment variable allows the Discord provider to be used automatically without explicitly instantiating it in code.
<Card> <ParamField path="FASTMCP_SERVER_AUTH" default="Not set"> Set to `fastmcp.server.auth.providers.discord.DiscordProvider` to use Discord authentication. </ParamField> </Card>These environment variables provide default values for the Discord provider, whether it's instantiated manually or configured via FASTMCP_SERVER_AUTH.
Example .env file:
FASTMCP_SERVER_AUTH=fastmcp.server.auth.providers.discord.DiscordProvider
FASTMCP_SERVER_AUTH_DISCORD_CLIENT_ID=12345
FASTMCP_SERVER_AUTH_DISCORD_CLIENT_SECRET=your-client-secret
FASTMCP_SERVER_AUTH_DISCORD_BASE_URL=https://your-server.com
FASTMCP_SERVER_AUTH_DISCORD_REQUIRED_SCOPES=identify,email
With environment variables set, your server code simplifies to:
from fastmcp import FastMCP
mcp = FastMCP(name="Discord Secured App")
@mcp.tool
async def protected_tool(query: str) -> str:
"""A tool that requires Discord authentication to access."""
return f"Processing authenticated request: {query}"