Skip to content

Conversation

@tuannvm
Copy link
Owner

@tuannvm tuannvm commented Oct 22, 2025

Summary by CodeRabbit

  • New Features

    • Dual-SDK support: works with both the official SDK and a mark3labs adapter.
    • Token caching to speed authenticated requests.
    • New runnable examples demonstrating OAuth-protected MCP servers for both SDKs.
  • Breaking Changes

    • OAuth setup helper moved into SDK-specific adapter packages (see migration notes).
  • Documentation

    • Expanded architecture, migration guidance, provider guides, examples, troubleshooting, and quick-starts.

Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
…support

Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Refactors the project into an SDK-agnostic oauth core and two SDK-specific adapters (mark3labs and official mcp), adds a thread-safe token cache and context helpers, moves WithOAuth into adapter packages, updates middleware to use cached validation, and expands docs and examples for dual-SDK usage.

Changes

Cohort / File(s) Change Summary
Documentation & Guides
CLAUDE.md, README.md, docs/generic-implementation.md, docs/generic-plan.md, docs/MIGRATION.md (removed), docs/TROUBLESHOOTING.md, docs/CLIENT-SETUP.md, docs/providers/*, examples/README.md
Added v2.0.0 architecture and migration notes, dual-SDK usage/examples (mark3labs & official), expanded refactor plan, removed old MIGRATION.md, updated troubleshooting and provider docs, and added examples README.
Core oauth package (new/modified)
cache.go, context.go, oauth.go
Added thread-safe token cache types (TokenCache, CachedToken) with TTL and locking; added context helpers (WithOAuthToken, GetOAuthToken, WithUser, GetUserFromContext); implemented Server.ValidateTokenCached (SHA-256 token hash, cache lookup, provider validation, 5m TTL) and integrated cached validation into request flow.
Removed legacy middleware scaffolding
middleware.go
Removed previous in-file re-exports, legacy token-cache/context scaffolding, and related helpers; responsibilities relocated to oauth core and adapters.
mark3labs adapter
mark3labs/oauth.go, mark3labs/middleware.go
Added mark3labs.WithOAuth(...) that constructs/registers OAuth handlers and returns (*oauth.Server, mcpserver.ServerOption, error); added NewMiddleware that uses ValidateTokenCached, injects User into context, and wraps tool handlers.
mcp (official SDK) adapter
mcp/oauth.go
Added mcp.WithOAuth(...) that creates an OAuth server, registers endpoints, returns (*oauth.Server, http.Handler, error), and supplies a protected HTTP handler which extracts Bearer tokens, calls ValidateTokenCached, and injects token/user into request context.
Examples & build
examples/simple/main.go, examples/mark3labs/*, examples/official/*, examples/README.md, .github/workflows/test.yml
Updated examples to call adapter WithOAuth() (mark3labs or official), added new runnable examples (mark3labs and official) with env-driven configs and user-context usage; CI builds all examples/**/main.go.
Dependencies
go.mod
Added github.com/modelcontextprotocol/go-sdk v1.0.0 (direct) and github.com/google/jsonschema-go v0.3.0 (indirect).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (mark3labs)
    participant MCP as MCP Server
    participant Middleware as mark3labs Middleware
    participant OAuth as oauth Core
    participant Cache as Token Cache

    Client->>MCP: Tool request (token in context)
    MCP->>Middleware: Invoke handler
    Middleware->>OAuth: ValidateTokenCached(token)
    OAuth->>Cache: Check cache (token hash)
    alt Cache hit
        Cache-->>OAuth: Cached user
    else Cache miss
        OAuth->>OAuth: Validate token with provider
        OAuth->>Cache: Store user (5m TTL)
    end
    OAuth-->>Middleware: User
    Middleware->>MCP: Call tool with user in context
    MCP-->>Client: Response
Loading
sequenceDiagram
    participant Client as Client (HTTP)
    participant HTTP as mcp.WithOAuth handler
    participant OAuth as oauth Core
    participant Cache as Token Cache
    participant MCP as MCP Stream Handler

    Client->>HTTP: HTTP Request + Authorization: Bearer <token>
    HTTP->>HTTP: Extract Bearer token
    HTTP->>OAuth: ValidateTokenCached(token)
    OAuth->>Cache: Check cache
    alt Cache hit
        Cache-->>OAuth: Cached user
    else Cache miss
        OAuth->>OAuth: Validate with provider
        OAuth->>Cache: Store user (5m)
    end
    HTTP->>HTTP: Attach token/user to request context
    HTTP->>MCP: Delegate to MCP handler
    MCP-->>Client: Response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Feat/generic sdk #4 — Overlapping multi-SDK refactor (core cache/context extraction, adapters, WithOAuth relocation); likely the most directly related.
  • Refactor/user friendliness #2 — Prior changes to WithOAuth wiring and return types; directly connected to adapter WithOAuth signatures.
  • Feat/init #1 — Earlier refactor extracting middleware, token-cache, and context helpers; shares core design and migration steps.

Poem

🐰
I hopped through code with tiny paws,
Two adapters grew to share the cause,
Cache keeps tokens snug and bright,
Context carries users into light,
OAuth wired up — we take flight!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The PR title "Feat/generic sdk" is vague and does not clearly communicate the primary purpose of the changeset. While the title is loosely related to the changes (which involve refactoring to support multiple MCP SDKs), the term "generic sdk" is ambiguous and could mean several different things. A developer scanning the git history would not clearly understand that this PR adds dual SDK support (mark3labs/mcp-go and the official modelcontextprotocol/go-sdk) with a reorganized architecture separating SDK-agnostic core from SDK-specific adapters. The title also appears to use branch naming convention format rather than a proper, descriptive PR title. Consider revising the PR title to be more specific and descriptive about the main change. A clearer title would explicitly mention the dual SDK support, such as "Support both mark3labs and official MCP SDKs" or "Refactor for multi-SDK support with adapter pattern" or "Enable support for both mark3labs/mcp-go and official SDK". This would help teammates quickly understand the purpose of this significant refactoring when reviewing commit history.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5437063 and 17e9c2c.

📒 Files selected for processing (1)
  • examples/official/advanced/main.go (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
README.md (1)

124-134: Remove raw token preview logging in middleware.go
In middleware.go at line 135, log.Printf("OAuth: Invalid Authorization header format: %s", preview) exposes parts of the bearer token. Remove or mask this log to prevent token leakage.

go.mod (1)

3-3: Fix invalid Go version directive.

go.mod’s go directive must use major.minor only. Apply:

-go 1.24.0
+go 1.24

Then run:

go mod tidy
go mod verify

and confirm any indirect modules (e.g., github.com/google/jsonschema-go) are still required.

oauth.go (1)

239-242: Avoid logging raw Authorization header in CreateHTTPContextFunc
In middleware.go, the invalid‐header branch logs a raw header preview (first 30 chars), which can expose un-hashed tokens. Change this to log only a hashed preview or token length per the “no raw token logs” rule. [middleware.go:122–132]

🧹 Nitpick comments (16)
cache.go (2)

35-39: Avoid spawning a goroutine per expired read.

Deleting synchronously is simpler and avoids goroutine churn.

Apply this diff:

- if time.Now().After(cached.ExpiresAt) {
-   tc.mu.RUnlock()
-   go tc.deleteExpiredToken(tokenHash)
-   return nil, false
- }
+ if time.Now().After(cached.ExpiresAt) {
+   tc.mu.RUnlock()
+   tc.deleteExpiredToken(tokenHash)
+   return nil, false
+ }

Also applies to: 45-53


25-43: Avoid mutating the returned CachedToken

getCachedToken returns a pointer to an internal CachedToken after unlocking; callers (middleware.go :45–47, oauth.go :115–117) must treat it as read-only or return a defensive copy to prevent data races.

docs/generic-plan.md (2)

25-45: Add language identifiers to fenced code blocks.

Helps readability and satisfies MD040.

Example fix:

-```
+```text
 oauth-mcp-proxy/
 ├── [core package - SDK-agnostic]
 ...

And for import examples:

```diff
-```
+```go
 import (
   "github.com/modelcontextprotocol/go-sdk/mcp"
   mcpoauth "github.com/tuannvm/oauth-mcp-proxy/mcp"
 )


Also applies to: 51-56

---

`212-216`: **Use headings instead of emphasis for section titles.**

Convert emphasized lines to proper `###` headings (MD036).

Example fix:

```diff
-**Overall Complexity: MEDIUM**
+### Overall Complexity: MEDIUM

-**Total Estimated Effort: 1 day of focused work**
+### Total Estimated Effort

-**Option 1: Quick Update (mark3labs users)**
+### Option 1: Quick Update (mark3labs users)

Also applies to: 229-237

README.md (2)

12-16: Fix heading levels (MD001).

Increment by one: use ## for section heads.

Apply:

-### mark3labs/mcp-go
+## mark3labs/mcp-go
...
-### Official SDK
+## Official SDK
...
-### Using mark3labs/mcp-go
+## Using mark3labs/mcp-go
...
-### Using Official SDK
+## Using Official SDK

Also applies to: 25-33, 84-86, 138-146


34-35: Hyphenate “2‑line change.”

Minor copy edit.

-> **📢 Migrating from v1.x?** See [MIGRATION-V2.md](./MIGRATION-V2.md) (2 line change, ~5 min)
+> **📢 Migrating from v1.x?** See [MIGRATION-V2.md](./MIGRATION-V2.md) (2-line change, ~5 min)
docs/generic-implementation.md (1)

31-36: Add language to fenced blocks.

Specify text, bash, or go as appropriate (MD040).

Examples:

-```
+```text
=== RUN   TestOfficialSDKContextPropagation
...
-```bash
+```bash
go build ./...
go test ./... -v
-```go
+```go
// ValidateTokenCached validates a token with caching support.

Also applies to: 101-106, 150-156, 371-377, 781-805

context.go (1)

8-27: LGTM. Context helpers are minimal and type‑safe.

Good unexported keys and clear API. Add a short caution to not log tokens retrieved via GetOAuthToken to align with project policy.

Add to comments:

// Do not log raw tokens; prefer hashed previews if absolutely necessary.

As per coding guidelines

Also applies to: 29-45

examples/advanced/main.go (1)

94-112: Unify TLS detection logic.

You set WithTLS based on cert presence only (Line 24), but later require both cert and key (Line 94). This can drift config and runtime.

Use the same condition in both places:

- WithTLS(getEnv("HTTPS_CERT_FILE", "") != "").
+ WithTLS(getEnv("HTTPS_CERT_FILE", "") != "" && getEnv("HTTPS_KEY_FILE", "") != "")

And keep the same check for useTLS.

Also applies to: 16-25

oauth.go (2)

120-126: Avoid leaking validator internals; add hashed token context to error log.

Log a hashed preview to correlate failures without exposing internals; keep client message generic.

- s.logger.Error("Token validation failed: %v", err)
+ s.logger.Error("Token validation failed (hash: %s...): %v", tokenHash[:16], err)

102-133: Concurrency-safe TTL enforcement in TokenCache verified. TokenCache uses sync.RWMutex for safe concurrent access and deletes expired entries on reads. Optionally, if the provider.User struct exposes its own expiration, cap the cache TTL to the shorter duration.

mark3labs/oauth.go (1)

11-39: Doc note: clarify root oauth.WithOAuth deprecation/back-compat.

Docs say WithOAuth moved; add a brief note that root oauth.WithOAuth remains for back-compat or mark deprecated to avoid confusion.

mcp/oauth.go (1)

52-76: Consider reusing Server.WrapHandler to avoid duplication.

Once WrapHandler validates tokens, wrap mcpHandler with it for a single source of truth.

- wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-   ...
-   mcpHandler.ServeHTTP(w, r)
- })
- return oauthServer, wrappedHandler, nil
+ return oauthServer, s.WrapHandler(mcpHandler), nil
mark3labs/middleware.go (1)

25-28: Return a typed auth error for better handling (optional).

Define a sentinel error (e.g., ErrUnauthenticated) to allow callers to map errors uniformly.

CLAUDE.md (2)

41-62: Add fenced code languages to satisfy markdownlint and improve rendering.

Use “text” for trees/flows and keep “go” for snippets.

-```
+```text
 oauth-mcp-proxy/
@@
-```text
+```text
 1. HTTP request with "Authorization: Bearer <token>" header
@@
-```text
+```text
 1. HTTP request with "Authorization: Bearer <token>" header

Also applies to: 66-77, 88-111, 147-170


145-146: Clarify WithOAuth move vs. back-compat.

State that root oauth.WithOAuth remains for backward compatibility or is deprecated, to prevent confusion with the new adapter entry points.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41ddb4c and a41ba57.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • CLAUDE.md (3 hunks)
  • README.md (5 hunks)
  • cache.go (1 hunks)
  • context.go (1 hunks)
  • docs/generic-implementation.md (1 hunks)
  • docs/generic-plan.md (1 hunks)
  • examples/advanced/main.go (2 hunks)
  • examples/simple/main.go (1 hunks)
  • go.mod (1 hunks)
  • mark3labs/middleware.go (1 hunks)
  • mark3labs/oauth.go (1 hunks)
  • mcp/oauth.go (1 hunks)
  • middleware.go (0 hunks)
  • oauth.go (2 hunks)
💤 Files with no reviewable changes (1)
  • middleware.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Never log raw OAuth tokens; only log hashed previews (e.g., truncated SHA-256)

Files:

  • mark3labs/oauth.go
  • mark3labs/middleware.go
  • examples/simple/main.go
  • context.go
  • oauth.go
  • mcp/oauth.go
  • examples/advanced/main.go
  • cache.go
{oauth.go,middleware.go,provider/**/*.go}

📄 CodeRabbit inference engine (CLAUDE.md)

Keep the server instance-scoped: no package-level globals for token caches or validators

Files:

  • oauth.go
🧠 Learnings (3)
📚 Learning: 2025-10-20T05:43:12.537Z
Learnt from: CR
PR: tuannvm/oauth-mcp-proxy#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-20T05:43:12.537Z
Learning: Always use GetUserFromContext(ctx) in tool handlers to access the authenticated user

Applied to files:

  • README.md
📚 Learning: 2025-10-20T05:43:12.537Z
Learnt from: CR
PR: tuannvm/oauth-mcp-proxy#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-20T05:43:12.537Z
Learning: Applies to middleware.go : Use a 5-minute token cache with SHA-256 of the token as the cache key

Applied to files:

  • oauth.go
📚 Learning: 2025-10-20T05:43:12.537Z
Learnt from: CR
PR: tuannvm/oauth-mcp-proxy#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-20T05:43:12.537Z
Learning: Applies to {oauth.go,middleware.go,provider/**/*.go} : Keep the server instance-scoped: no package-level globals for token caches or validators

Applied to files:

  • CLAUDE.md
  • cache.go
🧬 Code graph analysis (7)
mark3labs/oauth.go (2)
oauth.go (3)
  • WithOAuth (318-327)
  • Server (21-27)
  • NewServer (43-76)
mark3labs/middleware.go (1)
  • NewMiddleware (22-40)
mark3labs/middleware.go (2)
oauth.go (1)
  • Server (21-27)
context.go (2)
  • GetOAuthToken (19-22)
  • WithUser (25-27)
examples/simple/main.go (2)
oauth.go (1)
  • WithOAuth (318-327)
config.go (1)
  • Config (10-34)
context.go (1)
cache.go (1)
  • User (11-11)
oauth.go (2)
cache.go (1)
  • User (11-11)
provider/provider.go (1)
  • User (17-21)
mcp/oauth.go (3)
mark3labs/oauth.go (1)
  • WithOAuth (39-48)
oauth.go (3)
  • WithOAuth (318-327)
  • Server (21-27)
  • NewServer (43-76)
context.go (2)
  • WithOAuthToken (14-16)
  • WithUser (25-27)
examples/advanced/main.go (1)
oauth.go (1)
  • WithOAuth (318-327)
🪛 LanguageTool
README.md

[grammar] ~34-~34: Use a hyphen to join words.
Context: ... MIGRATION-V2.md (2 line change, ~5 min) [![GitHub Workflow...

(QB_NEW_EN_HYPHEN)

docs/generic-implementation.md

[style] ~25-~25: Consider using a different adverb to strengthen your wording.
Context: .... If context doesn't propagate, we need a completely different approach. Test Created: ...

(COMPLETELY_ENTIRELY)


[style] ~454-~454: To form a complete sentence, be sure to include a subject.
Context: ...Status*: Skipped for initial release. Can be added later. Files to Create: -...

(MISSING_IT_THERE)

🪛 markdownlint-cli2 (0.18.1)
README.md

12-12: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

docs/generic-plan.md

25-25: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


51-51: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


167-167: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


212-212: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


229-229: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

CLAUDE.md

43-43: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/generic-implementation.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (8)
examples/advanced/main.go (3)

35-36: Adapter switch looks correct.

mark3labs.WithOAuth usage matches the new adapter pattern.


82-91: LGTM: user access via context.

Correctly uses oauth.GetUserFromContext(ctx) in tool handler.


64-66: No changes required WrapHandlerFunc and GetHTTPServerOptions remain defined on oauth.Server.

examples/simple/main.go (2)

20-26: Adapter switch looks good.

mark3labs.WithOAuth replaces root oauth.WithOAuth as intended.


50-55: Confirm CreateHTTPContextFunc remains exported in oauth/middleware.go (line 120); no import or usage updates required.

oauth.go (1)

61-75: Instance-scoped cache/validator: LGTM.

No package-level globals; aligns with server instance scope. Based on learnings.

mark3labs/oauth.go (1)

39-48: Adapter wiring looks correct.

Creates server, registers handlers, returns middleware via NewMiddleware; matches intended flow.

mark3labs/middleware.go (1)

22-39: Middleware flow: LGTM.

Reads token from context, validates via cache, attaches user, calls next. Clean.

Comment on lines +55 to +64
// setCachedToken stores a token validation result
func (tc *TokenCache) setCachedToken(tokenHash string, user *User, expiresAt time.Time) {
tc.mu.Lock()
defer tc.mu.Unlock()

tc.cache[tokenHash] = &CachedToken{
User: user,
ExpiresAt: expiresAt,
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make TokenCache zero‑value safe (prevent map panic).

tc.cache may be nil, leading to a panic on first write.

Apply this diff:

 func (tc *TokenCache) setCachedToken(tokenHash string, user *User, expiresAt time.Time) {
   tc.mu.Lock()
   defer tc.mu.Unlock()

+  if tc.cache == nil {
+    tc.cache = make(map[string]*CachedToken)
+  }
   tc.cache[tokenHash] = &CachedToken{
     User:      user,
     ExpiresAt: expiresAt,
   }
 }

Optionally add:

// NewTokenCache constructs a ready-to-use cache.
func NewTokenCache() *TokenCache { return &TokenCache{cache: make(map[string]*CachedToken)} }
🤖 Prompt for AI Agents
In cache.go around lines 55 to 64, the setCachedToken method writes to tc.cache
without ensuring the map is initialized, which can panic if tc is a zero value;
modify setCachedToken to check if tc.cache == nil and initialize it with
make(map[string]*CachedToken) before assigning, and keep the mutex semantics
(lock/unlock) as-is; optionally add a NewTokenCache constructor that returns
&TokenCache{cache: make(map[string]*CachedToken)} so callers can obtain a
ready-to-use cache.

Comment on lines +57 to +61
authHeader := r.Header.Get("Authorization")
if authHeader == "" || len(authHeader) < 7 || authHeader[:7] != "Bearer " {
http.Error(w, "Missing or invalid Authorization header", http.StatusUnauthorized)
return
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return RFC 6750-compliant 401s and avoid exposing internal errors.

Add WWW-Authenticate with resource metadata and JSON body; keep client message generic.

@@
-    if authHeader == "" || len(authHeader) < 7 || authHeader[:7] != "Bearer " {
-      http.Error(w, "Missing or invalid Authorization header", http.StatusUnauthorized)
-      return
-    }
+    if authHeader == "" || len(authHeader) < 7 || authHeader[:7] != "Bearer " {
+      metadataURL := oauthServer.GetProtectedResourceMetadataURL()
+      w.Header().Add("WWW-Authenticate", `Bearer realm="OAuth", error="invalid_token", error_description="Missing or invalid access token"`)
+      w.Header().Add("WWW-Authenticate", fmt.Sprintf(`resource_metadata="%s"`, metadataURL))
+      w.Header().Set("Content-Type", "application/json")
+      w.WriteHeader(http.StatusUnauthorized)
+      _ = json.NewEncoder(w).Encode(map[string]string{
+        "error":             "invalid_token",
+        "error_description": "Missing or invalid access token",
+      })
+      return
+    }
@@
-    user, err := oauthServer.ValidateTokenCached(r.Context(), token)
-    if err != nil {
-      http.Error(w, fmt.Sprintf("Authentication failed: %v", err), http.StatusUnauthorized)
-      return
-    }
+    user, err := oauthServer.ValidateTokenCached(r.Context(), token)
+    if err != nil {
+      metadataURL := oauthServer.GetProtectedResourceMetadataURL()
+      w.Header().Add("WWW-Authenticate", `Bearer realm="OAuth", error="invalid_token", error_description="Authentication failed"`)
+      w.Header().Add("WWW-Authenticate", fmt.Sprintf(`resource_metadata="%s"`, metadataURL))
+      w.Header().Set("Content-Type", "application/json")
+      w.WriteHeader(http.StatusUnauthorized)
+      _ = json.NewEncoder(w).Encode(map[string]string{
+        "error":             "invalid_token",
+        "error_description": "Authentication failed",
+      })
+      return
+    }

Note: add imports for encoding/json if not present.

Also applies to: 65-69

Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
examples/mark3labs/simple/main.go (1)

78-83: LGTM! Simple and effective helper.

The getEnv helper function is clean and serves its purpose well for this example.

Minor note on implementation variance: The advanced example uses os.LookupEnv (lines 122-127 in advanced/main.go) while this uses os.Getenv. Both are functionally equivalent for this use case, but LookupEnv is slightly more idiomatic as it distinguishes between unset and empty values. For consistency across examples, consider aligning the implementations.

examples/official/advanced/main.go (3)

18-21: Prefer full Okta issuer URL over bare domain.

Okta issuers are typically https://{domain}/oauth2/{authServerId} (often /default). Using only the domain commonly fails discovery/validation.

Apply this diff:

-	//   export OKTA_DOMAIN="dev-12345.okta.com"
-	//   export OKTA_AUDIENCE="api://my-mcp-server"
+	//   export OKTA_ISSUER="https://dev-12345.okta.com/oauth2/default"
+	//   export OKTA_AUDIENCE="api://my-mcp-server"
-		WithIssuer(fmt.Sprintf("https://%s", getEnv("OKTA_DOMAIN", "dev-12345.okta.com"))).
+		WithIssuer(getEnv("OKTA_ISSUER", "https://dev-12345.okta.com/oauth2/default")).
-	log.Println("\nMake sure to set your Okta environment variables:")
-	log.Println("  export OKTA_DOMAIN=dev-12345.okta.com")
+	log.Println("\nMake sure to set your Okta environment variables:")
+	log.Println("  export OKTA_ISSUER=https://dev-12345.okta.com/oauth2/default")
 	log.Println("  export OKTA_AUDIENCE=api://my-mcp-server")

Please confirm your proxy’s Okta adapter accepts a full issuer and whether WithProvider("okta") has any special domain handling. If it mandates domain input, we can adapt accordingly.

Also applies to: 23-24, 134-138


52-52: Avoid logging PII in production.

You log usernames. For examples it’s fine; in real deployments, gate behind a DEBUG flag or redact.

Also applies to: 77-77, 100-100


129-142: Graceful shutdown and timeouts.

Consider http.Server with ReadHeaderTimeout/IdleTimeout and OS signal handling for clean shutdown.

Minimal sketch:

srv := &http.Server{
	Addr:         addr,
	Handler:      handler,
	ReadTimeout:  10 * time.Second,
	WriteTimeout: 10 * time.Second,
	IdleTimeout:  60 * time.Second,
}
go func() { log.Fatal(srv.ListenAndServe()) }()
/* handle SIGINT/SIGTERM, then: srv.Shutdown(ctx) */
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a41ba57 and dad9dc7.

📒 Files selected for processing (5)
  • examples/README.md (1 hunks)
  • examples/mark3labs/advanced/main.go (2 hunks)
  • examples/mark3labs/simple/main.go (1 hunks)
  • examples/official/advanced/main.go (1 hunks)
  • examples/official/simple/main.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Never log raw OAuth tokens; only log hashed previews (e.g., truncated SHA-256)

Files:

  • examples/mark3labs/simple/main.go
  • examples/mark3labs/advanced/main.go
  • examples/official/advanced/main.go
  • examples/official/simple/main.go
🧬 Code graph analysis (4)
examples/mark3labs/simple/main.go (2)
context.go (1)
  • GetUserFromContext (42-45)
middleware.go (1)
  • CreateHTTPContextFunc (120-139)
examples/mark3labs/advanced/main.go (1)
config.go (1)
  • NewConfigBuilder (164-170)
examples/official/advanced/main.go (3)
config.go (1)
  • NewConfigBuilder (164-170)
oauth.go (1)
  • NewServer (43-76)
context.go (1)
  • GetUserFromContext (42-45)
examples/official/simple/main.go (2)
oauth.go (1)
  • NewServer (43-76)
context.go (1)
  • GetUserFromContext (42-45)
🪛 Gitleaks (8.28.0)
examples/README.md

[high] 298-298: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🪛 markdownlint-cli2 (0.18.1)
examples/README.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


69-69: Bare URL used

(MD034, no-bare-urls)


88-88: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


98-98: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


365-365: Bare URL used

(MD034, no-bare-urls)


366-366: Bare URL used

(MD034, no-bare-urls)

🔇 Additional comments (14)
examples/official/simple/main.go (4)

1-13: LGTM! Clean import structure.

The imports are well-organized with standard library packages followed by external dependencies.


15-45: LGTM! Proper OAuth context usage.

The example correctly demonstrates:

  • MCP server initialization with official SDK
  • Tool registration with OAuth-protected context
  • Proper user extraction from context with error handling
  • Returning user information to the authenticated user

47-83: LGTM! OAuth configuration follows best practices.

The example demonstrates:

  • Proper Okta configuration with environment variables
  • Comprehensive error handling for OAuth setup
  • Helpful startup logging without exposing sensitive data (complies with coding guidelines)
  • Clear instructions for users to set required environment variables

86-91: LGTM! Clean environment variable helper.

Simple and effective helper function for retrieving environment variables with default fallbacks.

examples/mark3labs/simple/main.go (5)

3-14: LGTM! Well-organized imports.

The imports are cleanly organized and include all necessary packages for the OAuth-enabled MCP server example.


16-33: LGTM! Clear OAuth configuration with good documentation.

The OAuth setup is well-structured with helpful comments documenting the required environment variables. The use of getEnv with example defaults makes the code runnable out-of-the-box for demonstration purposes while clearly indicating where real values should be provided.


35-52: LGTM! Proper OAuth context usage in tool handler.

The tool correctly retrieves the authenticated user from context using oauth.GetUserFromContext and handles the unauthenticated case appropriately. This is a clear example of how to access user information in OAuth-protected tools.


54-60: LGTM! Correct HTTP context integration.

The endpoint setup properly integrates OAuth by using CreateHTTPContextFunc() to extract and propagate the OAuth token through the request context. The implementation adheres to security guidelines by not logging raw tokens.


62-76: LGTM! Helpful startup logging and guidance.

The server startup includes clear logging of configuration details and helpful guidance on required environment variables. This makes the example easy to follow and debug.

examples/mark3labs/advanced/main.go (4)

13-13: LGTM! Necessary import for mark3labs adapter.

The addition of the mark3labs adapter package import is correct and supports the refactoring to use SDK-specific adapters.


18-24: LGTM! Improved configuration flexibility.

The refactoring to use environment variables for OAuth configuration makes the example more flexible and production-ready. The updated comments clearly document the required variables.


38-38: LGTM! Correct adapter usage.

The change from oauth.WithOAuth to mark3labs.WithOAuth properly uses the SDK-specific adapter, which aligns with this PR's objective of supporting multiple SDKs through adapter packages.


122-127: LGTM! Idiomatic environment variable helper.

The use of os.LookupEnv is the more idiomatic approach as it distinguishes between unset and empty values. This implementation is clean and appropriate.

examples/official/advanced/main.go (1)

42-59: Verify AddTool API; handle errors if the SDK returns them.

If mcp.AddTool returns an error in this SDK version, capture and handle it; otherwise ignore this.

Example if it does return error:

if err := mcp.AddTool(mcpServer, tool, handler); err != nil {
	log.Fatalf("register tool %q: %v", tool.Name, err)
}

Also applies to: 63-85, 88-107

Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
examples/official/simple/main.go (1)

68-87: Server startup and logging are well-structured.

The startup sequence correctly logs configuration without exposing sensitive data. The detailed comments about PORT vs SERVER_URL (lines 69-72) are helpful for users deploying behind reverse proxies. No raw OAuth tokens are logged, complying with coding guidelines.

Optional: Consider extracting the issuer URL construction.

The issuer URL is constructed twice (lines 54 and 78). For slightly improved maintainability, you could extract it to a variable:

+	issuer := fmt.Sprintf("https://%s", getEnv("OKTA_DOMAIN", "dev-12345.okta.com"))
 	cfg := &oauth.Config{
 		Provider:  "okta",
-		Issuer:    fmt.Sprintf("https://%s", getEnv("OKTA_DOMAIN", "dev-12345.okta.com")),
+		Issuer:    issuer,
 		Audience:  getEnv("OKTA_AUDIENCE", "api://my-mcp-server"),
 		ServerURL: getEnv("SERVER_URL", "http://localhost:8080"),
 	}
 	
 	// ... later in logging ...
 	
-	log.Printf("Issuer: https://%s", getEnv("OKTA_DOMAIN", "dev-12345.okta.com"))
+	log.Printf("Issuer: %s", issuer)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb5ae40 and a114747.

📒 Files selected for processing (3)
  • examples/mark3labs/simple/main.go (1 hunks)
  • examples/official/advanced/main.go (1 hunks)
  • examples/official/simple/main.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/official/advanced/main.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Never log raw OAuth tokens; only log hashed previews (e.g., truncated SHA-256)

Files:

  • examples/official/simple/main.go
  • examples/mark3labs/simple/main.go
🧬 Code graph analysis (2)
examples/official/simple/main.go (1)
context.go (1)
  • GetUserFromContext (42-45)
examples/mark3labs/simple/main.go (2)
context.go (1)
  • GetUserFromContext (42-45)
middleware.go (1)
  • CreateHTTPContextFunc (120-139)
🔇 Additional comments (4)
examples/official/simple/main.go (3)

25-45: LGTM! Clean example of context-based user retrieval.

The tool correctly uses oauth.GetUserFromContext(ctx) to retrieve authenticated user data and handles the case where authentication is missing. The implementation aligns well with the new context helpers introduced in the PR.


47-57: Configuration setup looks good.

The OAuth configuration correctly uses environment variables with sensible placeholder defaults for an example. The issuer URL construction from OKTA_DOMAIN is appropriate.


90-95: Standard helper implementation.

The getEnv function follows a common pattern for environment variable access with defaults. Implementation is correct.

examples/mark3labs/simple/main.go (1)

1-87: LGTM! Well-structured OAuth integration example.

This example file effectively demonstrates OAuth integration with the mark3labs MCP SDK. The code is correct, secure, and compliant with the coding guidelines:

  • No raw OAuth tokens are logged (only token length in the middleware, which is compliant)
  • Environment variable configuration with sensible defaults
  • Clear error handling for OAuth setup and server startup
  • Proper authentication checks in the tool handler
  • Comprehensive user guidance with environment setup instructions
  • Helpful comments explaining PORT vs SERVER_URL relationship

The example values (dev-12345.okta.com) are clearly placeholders, and the startup logs provide excellent guidance for users to configure their environment.

Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
examples/README.md (1)

325-325: Replace non-existent Go version in Dockerfile.

Go 1.24 doesn't exist yet. Use an available version like golang:1.23 or golang:latest instead.

-FROM golang:1.24 AS builder
+FROM golang:1.23 AS builder
🧹 Nitpick comments (2)
examples/README.md (1)

296-299: Add clarification to curl example about token placeholder.

The curl example shows $TOKEN which will expand to a real token value. Add a comment clarifying this should be replaced with an actual test token from the setup steps above.

# Save your Okta token
TOKEN="<your-okta-access-token>"

# Test with curl
+# Replace TOKEN with an actual token from the setup steps above
curl -H "Authorization: Bearer $TOKEN" \
docs/generic-implementation.md (1)

1-50: Implementation tracking documentation is well-structured and comprehensive.

The phases are clearly organized with verification checkpoints and status indicators. Test results are properly documented. This serves as a good implementation guide.

Minor style notes (from linters):

  • Lines 25, 462: Minor grammar suggestions (optional)
  • Line 31: Consider adding language to code fence for markdown compliance (optional)

These are documentation style issues, not blockers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a114747 and 5437063.

📒 Files selected for processing (11)
  • .github/workflows/test.yml (1 hunks)
  • README.md (5 hunks)
  • docs/CLIENT-SETUP.md (3 hunks)
  • docs/MIGRATION.md (0 hunks)
  • docs/TROUBLESHOOTING.md (1 hunks)
  • docs/generic-implementation.md (1 hunks)
  • docs/providers/AZURE.md (1 hunks)
  • docs/providers/GOOGLE.md (1 hunks)
  • docs/providers/HMAC.md (4 hunks)
  • docs/providers/OKTA.md (6 hunks)
  • examples/README.md (1 hunks)
💤 Files with no reviewable changes (1)
  • docs/MIGRATION.md
✅ Files skipped from review due to trivial changes (2)
  • docs/providers/AZURE.md
  • docs/providers/GOOGLE.md
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-20T05:43:12.537Z
Learnt from: CR
PR: tuannvm/oauth-mcp-proxy#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-20T05:43:12.537Z
Learning: Always use GetUserFromContext(ctx) in tool handlers to access the authenticated user

Applied to files:

  • README.md
🪛 Gitleaks (8.28.0)
examples/README.md

[high] 298-298: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🪛 LanguageTool
docs/generic-implementation.md

[style] ~25-~25: Consider using a different adverb to strengthen your wording.
Context: .... If context doesn't propagate, we need a completely different approach. Test Created: ...

(COMPLETELY_ENTIRELY)


[style] ~462-~462: To form a complete sentence, be sure to include a subject.
Context: ...Status*: Skipped for initial release. Can be added later. Files to Create: -...

(MISSING_IT_THERE)

🪛 markdownlint-cli2 (0.18.1)
README.md

12-12: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

docs/generic-implementation.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (6)
docs/TROUBLESHOOTING.md (1)

477-477: Documentation correctly reflects dual-SDK example structure.

The update from single example path to dual SDK examples is accurate and helps users understand both pathways are available.

.github/workflows/test.yml (1)

50-54: Improved build approach for dynamic example discovery.

Using find to discover examples is better than hardcoded paths. This scales automatically as new examples are added to the directory structure.

docs/CLIENT-SETUP.md (1)

163-240: Dual-SDK client configuration documentation is clear and consistent.

Both mark3labs and official SDK paths are properly documented with parallel examples for Native and Proxy modes. Import paths and initialization sequences are correct.

docs/providers/HMAC.md (1)

29-69: HMAC provider configuration correctly documents both SDKs.

The parallel examples for mark3labs and official SDKs are clear, imports are accurate, and the configuration patterns are consistent. Token generation guidance is appropriately provider-agnostic.

docs/providers/OKTA.md (1)

80-142: Okta provider configuration correctly documents both SDKs.

Native and Proxy modes are clearly documented with parallel examples for both mark3labs and official SDKs. Import paths and configuration patterns are consistent and accurate.

README.md (1)

5-32: Excellent dual-SDK documentation in README.

The introduction clearly highlights support for both SDKs, and the parallel code examples immediately show how each SDK integrates differently. Migration guidance link at line 34 is helpful for existing users. The examples table and quick start sections effectively demonstrate both pathways.

**One-time setup:** Configure provider + add `WithOAuth()` to your server.
**Result:** All tools automatically protected with token validation and caching.

### mark3labs/mcp-go
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix heading hierarchy: change h3 to h2.

Line 12 is an h3 heading (###) but should be h2 (##) as a direct child of the "Quick Start" h2 section. Heading levels should increment by one level at a time per markdown linting rules.

-### Using mark3labs/mcp-go
+## Using mark3labs/mcp-go

And similarly for the "Using Official SDK" section at line 138.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

12-12: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🤖 Prompt for AI Agents
In README.md around lines 12 and 138, the headings use h3 (###) but must be h2
(##) to maintain proper incremental hierarchy under the "Quick Start" h2 section
and the "Using Official SDK" section; change the two headings from `###` to `##`
so each is a direct child one level below their parent h2, and verify adjacent
headings follow the same incremental rule.

…ver startup

Signed-off-by: Tommy Nguyen <tuannvm@hotmail.com>
@tuannvm tuannvm merged commit ed55729 into main Oct 23, 2025
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants