plugins/plugin-dev/skills/mcp-integration/references/authentication.md
Complete guide to authentication methods for MCP servers in Claude Code plugins.
MCP servers support multiple authentication methods depending on the server type and service requirements. Choose the method that best matches your use case and security requirements.
Claude Code automatically handles the complete OAuth 2.0 flow for SSE and HTTP servers:
{
"service": {
"type": "sse",
"url": "https://mcp.example.com/sse"
}
}
No additional auth configuration needed! Claude Code handles everything.
Known OAuth-enabled MCP servers:
https://mcp.asana.com/sseOAuth scopes are determined by the MCP server. Users see required scopes during the consent flow.
Document required scopes in your README:
## Authentication
This plugin requires the following Asana permissions:
- Read tasks and projects
- Create and update tasks
- Access workspace data
Tokens are stored securely by Claude Code:
Authentication loop:
Scope issues:
Token expiration:
Most common for HTTP and WebSocket servers.
Configuration:
{
"api": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
}
}
}
Environment variable:
export API_TOKEN="your-secret-token-here"
Alternative to Bearer tokens, often in custom headers.
Configuration:
{
"api": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"X-API-Key": "${API_KEY}",
"X-API-Secret": "${API_SECRET}"
}
}
}
Services may use custom authentication headers.
Configuration:
{
"service": {
"type": "sse",
"url": "https://mcp.example.com/sse",
"headers": {
"X-Auth-Token": "${AUTH_TOKEN}",
"X-User-ID": "${USER_ID}",
"X-Tenant-ID": "${TENANT_ID}"
}
}
}
Always document in your README:
## Setup
### Required Environment Variables
Set these environment variables before using the plugin:
\`\`\`bash
export API_TOKEN="your-token-here"
export API_SECRET="your-secret-here"
\`\`\`
### Obtaining Tokens
1. Visit https://api.example.com/tokens
2. Create a new API token
3. Copy the token and secret
4. Set environment variables as shown above
### Token Permissions
The API token needs the following permissions:
- Read access to resources
- Write access for creating items
- Delete access (optional, for cleanup operations)
\`\`\`
For stdio servers, pass credentials via environment variables:
{
"database": {
"command": "python",
"args": ["-m", "mcp_server_db"],
"env": {
"DATABASE_URL": "${DATABASE_URL}",
"DB_USER": "${DB_USER}",
"DB_PASSWORD": "${DB_PASSWORD}"
}
}
}
# User sets these in their shell
export DATABASE_URL="postgresql://localhost/mydb"
export DB_USER="myuser"
export DB_PASSWORD="mypassword"
## Database Configuration
Set these environment variables:
\`\`\`bash
export DATABASE_URL="postgresql://host:port/database"
export DB_USER="username"
export DB_PASSWORD="password"
\`\`\`
Or create a `.env` file (add to `.gitignore`):
\`\`\`
DATABASE_URL=postgresql://localhost:5432/mydb
DB_USER=myuser
DB_PASSWORD=mypassword
\`\`\`
Load with: \`source .env\` or \`export $(cat .env | xargs)\`
\`\`\`
For tokens that change or expire, use a helper script:
{
"api": {
"type": "sse",
"url": "https://api.example.com",
"headersHelper": "${CLAUDE_PLUGIN_ROOT}/scripts/get-headers.sh"
}
}
Script (get-headers.sh):
#!/bin/bash
# Generate dynamic authentication headers
# Fetch fresh token
TOKEN=$(get-fresh-token-from-somewhere)
# Output JSON headers
cat <<EOF
{
"Authorization": "Bearer $TOKEN",
"X-Timestamp": "$(date -Iseconds)"
}
EOF
✅ Use environment variables:
{
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
}
}
✅ Document required variables in README
✅ Use HTTPS/WSS always
✅ Implement token rotation
✅ Store tokens securely (env vars, not files)
✅ Let OAuth handle authentication when available
❌ Hardcode tokens:
{
"headers": {
"Authorization": "Bearer sk-abc123..." // NEVER!
}
}
❌ Commit tokens to git
❌ Share tokens in documentation
❌ Use HTTP instead of HTTPS
❌ Store tokens in plugin files
❌ Log tokens or sensitive headers
Via environment variable:
{
"api": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer ${API_TOKEN}",
"X-Workspace-ID": "${WORKSPACE_ID}"
}
}
}
Via URL:
{
"api": {
"type": "http",
"url": "https://${TENANT_ID}.api.example.com/mcp"
}
}
Users set their own workspace:
export WORKSPACE_ID="my-workspace-123"
export TENANT_ID="my-company"
401 Unauthorized:
403 Forbidden:
Token not found:
# Check environment variable is set
echo $API_TOKEN
# If empty, set it
export API_TOKEN="your-token"
Token in wrong format:
// Correct
"Authorization": "Bearer sk-abc123"
// Wrong
"Authorization": "sk-abc123"
Enable debug mode:
claude --debug
Look for:
Test authentication separately:
# Test HTTP endpoint
curl -H "Authorization: Bearer $API_TOKEN" \
https://api.example.com/mcp/health
# Should return 200 OK
Before:
{
"headers": {
"Authorization": "Bearer sk-hardcoded-token"
}
}
After:
{
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
}
}
Migration steps:
Before:
{
"headers": {
"Authorization": "Basic ${BASE64_CREDENTIALS}"
}
}
After:
{
"type": "sse",
"url": "https://mcp.example.com/sse"
}
Benefits:
Some enterprise services require client certificates.
Not directly supported in MCP configuration.
Workaround: Wrap in stdio server that handles mTLS:
{
"secure-api": {
"command": "${CLAUDE_PLUGIN_ROOT}/servers/mtls-wrapper",
"args": ["--cert", "${CLIENT_CERT}", "--key", "${CLIENT_KEY}"],
"env": {
"API_URL": "https://secure.example.com"
}
}
}
Generate JWT tokens dynamically with headers helper:
#!/bin/bash
# generate-jwt.sh
# Generate JWT (using library or API call)
JWT=$(generate-jwt-token)
echo "{\"Authorization\": \"Bearer $JWT\"}"
{
"headersHelper": "${CLAUDE_PLUGIN_ROOT}/scripts/generate-jwt.sh"
}
For APIs requiring request signing:
#!/bin/bash
# generate-hmac.sh
TIMESTAMP=$(date -Iseconds)
SIGNATURE=$(echo -n "$TIMESTAMP" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2)
cat <<EOF
{
"X-Timestamp": "$TIMESTAMP",
"X-Signature": "$SIGNATURE",
"X-API-Key": "$API_KEY"
}
EOF
Choose the authentication method that matches your MCP server's requirements:
Always prioritize security and provide clear setup documentation for users.