This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
oauth-mcp-proxy is an OAuth 2.1 authentication library for Go MCP servers. It provides server-side OAuth integration with minimal code (3-line integration via WithOAuth()), supporting multiple providers (HMAC, Okta, Google, Azure AD).
Version: v1.0.0 (Supports both mark3labs/mcp-go and official modelcontextprotocol/go-sdk)
# Run tests
make test
# Run tests with verbose output
make test-verbose
# Run tests with coverage report (generates coverage.html)
make test-coverage
# Run linters (same as CI - checks go.mod tidy + golangci-lint)
make lint
# Format code
make fmt
# Clean build artifacts and caches
make clean
# Install/download dependencies
make install
# Check for security vulnerabilities
make vulnoauth-mcp-proxy/
├── [core package - SDK-agnostic]
│ ├── oauth.go - Server type, NewServer, ValidateTokenCached
│ ├── config.go - Configuration validation and provider setup
│ ├── cache.go - Token cache with 5-minute TTL
│ ├── context.go - Context utilities (WithOAuthToken, GetUserFromContext, etc.)
│ ├── handlers.go - OAuth HTTP endpoints (/.well-known/*, /oauth/*)
│ ├── middleware.go - CreateHTTPContextFunc for token extraction
│ ├── logger.go - Logger interface
│ ├── metadata.go - OAuth metadata structures
│ └── provider/ - Token validators (HMAC, OIDC)
│
├── mark3labs/ - Adapter for mark3labs/mcp-go SDK
│ ├── oauth.go - WithOAuth → ServerOption
│ └── middleware.go - Middleware for mark3labs types
│
└── mcp/ - Adapter for official modelcontextprotocol/go-sdk
└── oauth.go - WithOAuth → http.Handler
Core Package (SDK-agnostic):
- oauth.go -
Servertype,NewServer(),ValidateTokenCached()(used by adapters) - config.go - Configuration validation and provider setup
- cache.go - Token caching logic (
TokenCache,CachedToken) - context.go - Context utilities (
WithOAuthToken,GetOAuthToken,WithUser,GetUserFromContext) - handlers.go - OAuth HTTP endpoints
- provider/provider.go - Token validators (HMACValidator, OIDCValidator)
Adapters (SDK-specific):
- mark3labs/ - Middleware adapter for
mark3labs/mcp-go - mcp/ - HTTP handler wrapper for official SDK
- OpenTelemetry Pattern: Core logic is SDK-agnostic; adapters provide SDK-specific integration
- Instance-scoped: Each
Serverinstance has its own token cache and validator (no globals) - Provider abstraction:
TokenValidatorinterface supports multiple OAuth providers - Caching strategy: Tokens cached for 5 minutes using SHA-256 hash as key
- Context propagation: OAuth token extracted from HTTP header → stored in context → validated → user added to context
mark3labs SDK:
1. HTTP request with "Authorization: Bearer <token>" header
2. CreateHTTPContextFunc() extracts token → adds to context via WithOAuthToken()
3. mark3labs middleware validates token:
- Calls Server.ValidateTokenCached() (checks cache first)
- If not cached, validates via provider (HMAC or OIDC)
- Caches result (5-minute TTL)
4. Adds authenticated User to context via WithUser()
5. Tool handler accesses user via GetUserFromContext(ctx)
Official SDK:
1. HTTP request with "Authorization: Bearer <token>" header
2. mcp adapter's HTTP handler intercepts request
3. Validates token via Server.ValidateTokenCached():
- Checks cache first (5-minute TTL)
- If not cached, validates via provider
- Caches result
4. Adds token and user to context (WithOAuthToken, WithUser)
5. Passes request to official SDK's StreamableHTTPHandler
6. Tool handler accesses user via GetUserFromContext(ctx)
- HMAC: Validates JWT tokens with shared secret (testing/dev)
- OIDC: Validates tokens via JWKS/OIDC discovery (Okta/Google/Azure)
- All validation happens in
provider/provider.go - Validators implement
TokenValidatorinterface
The codebase has extensive test coverage across multiple scenarios:
- api_test.go - Core API functionality tests
- integration_test.go - End-to-end integration tests
- security_test.go - Security validation tests
- attack_scenarios_test.go - Security attack scenario tests
- middleware_compatibility_test.go - Middleware compatibility tests
- provider/provider_test.go - Token validator tests
Run single test:
go test -v -run TestName ./...Tests use table-driven subtests with t.Run():
tests := []struct {
name string
// test fields
}{...}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// test body
})
}Mock validators implement TokenValidator interface. Use httptest.NewRecorder() for HTTP handler tests.
Use ConfigBuilder for production code instead of direct Config structs:
cfg, _ := oauth.NewConfigBuilder().
WithProvider("okta").
WithIssuer("https://company.okta.com").
WithAudience("api://my-server").
WithHost(host).WithPort(port).
Build()Build() validates config and auto-constructs ServerURL if not set.
- OIDC validation: 10 seconds
- Provider initialization: 30 seconds
- Redirect URI validation: All URIs must be in explicit allowlist
- State parameter HMAC: OAuth states are HMAC-signed to prevent CSRF
- Audience validation: Both HMAC and OIDC validators explicitly check
audclaim - No raw token logging: Only log
fmt.Sprintf("%x", sha256.Sum256([]byte(token)))[:16] - TLS in production: Always warn if
useTLS=falseinLogStartup()
- User Context: Always use
GetUserFromContext(ctx)in tool handlers to access authenticated user - Token Caching: Tokens cached for 5 minutes - design for this TTL in testing. Cache uses
sync.RWMutexwith background cleanup viadeleteExpiredToken()goroutine - Logging: Config.Logger is optional. If nil, uses default logger (log.Printf with level prefixes)
- Modes: Library supports "native" (token validation only) and "proxy" (OAuth flow proxy) modes. Auto-detected based on
ClientIDpresence - Adapter Pattern:
WithOAuth()is in adapter packages (mark3labs.WithOAuth()ormcp.WithOAuth()) for SDK-specific integration
- SDK Imports: Adapter code (
mark3labs/,mcp/) can import SDKs. Core package cannot - keep it SDK-agnostic - Context Propagation: Always extract user via
GetUserFromContext(ctx)in tool handlers - Cache Expiry: Background cleanup runs in goroutine to avoid lock contention
- Mode Detection: Config auto-detects "native" vs "proxy" based on
ClientIDpresence - Logger Fallback: If
cfg.Logger == nil, usesdefaultLogger{}withlog.Printf
- Core logic:
oauth.go,config.go,cache.go,context.go,handlers.go,middleware.go - Tests:
*_test.go(e.g.,security_test.go,integration_test.go) - Adapters:
mark3labs/oauth.go,mcp/oauth.go(not*_adapter.go) - Provider:
provider/provider.go(single file, multiple validators)
import (
oauth "github.com/tuannvm/oauth-mcp-proxy"
"github.com/tuannvm/oauth-mcp-proxy/mark3labs"
)
oauthServer, oauthOption, _ := mark3labs.WithOAuth(mux, &oauth.Config{...})
mcpServer := server.NewMCPServer("name", "1.0.0", oauthOption)
streamableServer := server.NewStreamableHTTPServer(mcpServer, ...)
mux.HandleFunc("/mcp", oauthServer.WrapMCPEndpoint(streamableServer))Note: WrapMCPEndpoint() provides automatic 401 handling with proper WWW-Authenticate headers when Bearer token is missing. It also passes through OPTIONS requests (CORS) and non-Bearer auth schemes.
import (
oauth "github.com/tuannvm/oauth-mcp-proxy"
mcpoauth "github.com/tuannvm/oauth-mcp-proxy/mcp"
)
mcpServer := mcp.NewServer(&mcp.Implementation{...}, nil)
_, handler, _ := mcpoauth.WithOAuth(mux, &oauth.Config{...}, mcpServer)
http.ListenAndServe(":8080", handler) // 401 handling automaticNote: Official SDK adapter includes automatic 401 handling in the returned handler.
- Add validator to
provider/provider.goimplementingTokenValidatorinterface - Update
createValidator()switch inconfig.go - Add provider documentation in
docs/providers/
- Create
<sdk>/oauth.gowithWithOAuth()function - Follow pattern: create
oauth.Server, register handlers, return SDK-specific middleware/option - Never import MCP SDKs in core package
- Add handler method to
OAuth2Handlerinhandlers.go - Register in
RegisterHandlers()inoauth.go
examples/README.md- Complete setup guide with Okta configurationexamples/mark3labs/andexamples/official/- Working examples (simple + advanced)docs/providers/*.md- Provider-specific setup (OKTA.md, GOOGLE.md, AZURE.md, HMAC.md)docs/CONFIGURATION.md- All configuration optionsdocs/SECURITY.md- Production best practicesdocs/TROUBLESHOOTING.md- Common issues and solutions