Skip to content

Commit a73ccba

Browse files
docs: add zero-config vs explicit OAuth examples, improve auth status output
1 parent 66d4a43 commit a73ccba

2 files changed

Lines changed: 90 additions & 25 deletions

File tree

README.md

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,11 @@ docker ps -a --filter label=com.mcpproxy.managed=true -q | xargs docker rm -f
559559

560560
## OAuth Authentication Support
561561

562-
MCPProxy provides **seamless OAuth 2.1 authentication** for MCP servers that require user authorization (like Cloudflare AutoRAG, GitHub, etc.):
562+
MCPProxy provides **seamless OAuth 2.1 authentication** for MCP servers that require user authorization (like Cloudflare AutoRAG, Runlayer, GitHub, etc.):
563563

564564
### **Key Features**
565+
- **Zero-Config OAuth**: Automatic detection and configuration for most OAuth servers
566+
- **RFC 8707 Resource Auto-Detection**: Automatically discovers resource parameters from server metadata
565567
- **RFC 8252 Compliant**: Dynamic port allocation for secure callback handling
566568
- **PKCE Security**: Proof Key for Code Exchange for enhanced security
567569
- **Auto Browser Launch**: Opens your default browser for authentication
@@ -570,38 +572,88 @@ MCPProxy provides **seamless OAuth 2.1 authentication** for MCP servers that req
570572

571573
### 🔄 **How It Works**
572574
1. **Add OAuth Server**: Configure an OAuth-enabled MCP server in your config
573-
2. **Auto Authentication**: MCPProxy detects when OAuth is required (401 response)
575+
2. **Auto Detection**: MCPProxy detects OAuth requirements and auto-configures parameters
574576
3. **Browser Opens**: Your default browser opens to the OAuth provider's login page
575577
4. **Dynamic Callback**: MCPProxy starts a local callback server on a random port
576578
5. **Token Exchange**: Authorization code is automatically exchanged for access tokens
577579
6. **Ready to Use**: Server becomes available for tool calls immediately
578580

579-
### 📝 **OAuth Server Configuration**
581+
### 📝 **OAuth Configuration Examples**
580582

581-
> **Note**: The `"oauth"` configuration is **optional**. MCPProxy will automatically detect when OAuth is required and use sensible defaults in most cases. You only need to specify OAuth settings if you want to customize scopes or have pre-registered client credentials.
583+
#### Zero-Config OAuth (Recommended)
582584

583-
```jsonc
585+
For most OAuth servers (including Runlayer, Cloudflare, etc.), **no OAuth configuration is needed**. MCPProxy automatically:
586+
- Detects OAuth requirements via 401 responses
587+
- Discovers Protected Resource Metadata (RFC 9728)
588+
- Injects the RFC 8707 `resource` parameter automatically
589+
590+
```json
584591
{
585592
"mcpServers": [
593+
{
594+
"name": "runlayer-slack",
595+
"url": "https://oauth.runlayer.com/api/v1/proxy/YOUR-UUID/mcp"
596+
},
586597
{
587598
"name": "cloudflare_autorag",
588599
"url": "https://autorag.mcp.cloudflare.com/mcp",
589-
"protocol": "streamable-http",
590-
"enabled": true,
600+
"protocol": "streamable-http"
601+
}
602+
]
603+
}
604+
```
605+
606+
That's it - **no `oauth` block needed**. MCPProxy handles everything automatically.
607+
608+
#### Explicit OAuth Configuration
609+
610+
Use explicit configuration when you need to:
611+
- Customize OAuth scopes
612+
- Override auto-detected parameters
613+
- Use pre-registered client credentials
614+
- Support providers with non-standard requirements
615+
616+
```json
617+
{
618+
"mcpServers": [
619+
{
620+
"name": "github-enterprise",
621+
"url": "https://github.example.com/mcp",
622+
"protocol": "http",
591623
"oauth": {
592-
"scopes": ["mcp.read", "mcp.write"],
593-
"pkce_enabled": true
624+
"scopes": ["repo", "user:email", "read:org"],
625+
"pkce_enabled": true,
626+
"client_id": "your-registered-client-id",
627+
"extra_params": {
628+
"resource": "https://api.github.example.com",
629+
"audience": "github-enterprise-api"
630+
}
594631
}
595632
}
596633
]
597634
}
598635
```
599636

600637
**OAuth Configuration Options** (all optional):
601-
- `scopes`: OAuth scopes to request (default: `["mcp.read", "mcp.write"]`)
638+
- `scopes`: OAuth scopes to request (auto-discovered if not specified)
602639
- `pkce_enabled`: Enable PKCE for security (default: `true`, recommended)
603-
- `client_id`: Pre-registered client ID (optional, uses Dynamic Client Registration if empty)
640+
- `client_id`: Pre-registered client ID (uses Dynamic Client Registration if empty)
604641
- `client_secret`: Client secret (optional, for confidential clients)
642+
- `extra_params`: Additional OAuth parameters (override auto-detected values)
643+
644+
### 🔍 **OAuth Diagnostics**
645+
646+
Check OAuth status and auto-detected parameters:
647+
```bash
648+
# View OAuth status for a specific server
649+
mcpproxy auth status --server=runlayer-slack
650+
651+
# View all OAuth-enabled servers
652+
mcpproxy auth status --all
653+
654+
# Run health diagnostics including OAuth issues
655+
mcpproxy doctor
656+
```
605657

606658
### 🔧 **OAuth Debugging**
607659

cmd/mcpproxy/auth_cmd.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -322,39 +322,52 @@ func runAuthStatusClientMode(ctx context.Context, dataDir, serverName string, al
322322
}
323323

324324
// Handle both map[string]string (from runtime) and map[string]interface{} (from conversions)
325-
var hasManualResource bool
325+
var manualResource string
326+
var otherParams []string
326327
if extraParams, ok := oauth["extra_params"].(map[string]string); ok && len(extraParams) > 0 {
327-
paramParts := make([]string, 0, len(extraParams))
328328
for key, val := range extraParams {
329-
paramParts = append(paramParts, fmt.Sprintf("%s=%v", key, val))
330329
if key == "resource" {
331-
hasManualResource = true
330+
manualResource = val
331+
} else {
332+
otherParams = append(otherParams, fmt.Sprintf("%s=%v", key, val))
332333
}
333334
}
334-
fmt.Printf(" Extra Params: %s\n", strings.Join(paramParts, ", "))
335335
} else if extraParams, ok := oauth["extra_params"].(map[string]interface{}); ok && len(extraParams) > 0 {
336-
paramParts := make([]string, 0, len(extraParams))
337336
for key, val := range extraParams {
338-
paramParts = append(paramParts, fmt.Sprintf("%s=%v", key, val))
339337
if key == "resource" {
340-
hasManualResource = true
338+
manualResource = fmt.Sprintf("%v", val)
339+
} else {
340+
otherParams = append(otherParams, fmt.Sprintf("%s=%v", key, val))
341341
}
342342
}
343-
fmt.Printf(" Extra Params: %s\n", strings.Join(paramParts, ", "))
344343
}
345344

346-
// Show RFC 8707 resource parameter status
347-
if hasManualResource {
348-
fmt.Printf(" Resource: Configured (manual override)\n")
349-
} else {
345+
// Show non-resource extra params
346+
if len(otherParams) > 0 {
347+
fmt.Printf(" Extra Params: %s\n", strings.Join(otherParams, ", "))
348+
}
349+
350+
// Show RFC 8707 resource parameter with actual URL
351+
serverURL, _ := srv["url"].(string)
352+
if manualResource != "" {
353+
fmt.Printf(" Resource: %s (manual)\n", manualResource)
354+
} else if serverURL != "" {
350355
// Resource will be auto-detected from RFC 9728 Protected Resource Metadata
356+
// Show server URL as the expected value (fallback if metadata unavailable)
357+
fmt.Printf(" Resource: Auto-detect → %s\n", serverURL)
358+
} else {
351359
fmt.Printf(" Resource: Auto-detect (RFC 9728)\n")
352360
}
353361
} else {
354362
// Server requires OAuth but has no explicit config (discovery/DCR)
355363
fmt.Printf(" OAuth: Discovered via Dynamic Client Registration\n")
356364
// RFC 8707 resource will be auto-detected for zero-config OAuth servers
357-
fmt.Printf(" Resource: Auto-detect (RFC 9728)\n")
365+
serverURL, _ := srv["url"].(string)
366+
if serverURL != "" {
367+
fmt.Printf(" Resource: Auto-detect → %s\n", serverURL)
368+
} else {
369+
fmt.Printf(" Resource: Auto-detect (RFC 9728)\n")
370+
}
358371
}
359372

360373
// Display token expiration if available

0 commit comments

Comments
 (0)