diff --git a/.claude/conventions.yml b/.claude/conventions.yml new file mode 100644 index 0000000..8386f41 --- /dev/null +++ b/.claude/conventions.yml @@ -0,0 +1,63 @@ +# oauth-mcp-proxy conventions + +adapter_pattern: + description: "SDK-agnostic core with SDK-specific adapters" + rules: + - "Core package (root) contains all OAuth logic and MUST NOT import any MCP SDK" + - "mark3labs/ adapter imports github.com/mark3labs/mcp-go and wraps core as ServerOption" + - "mcp/ adapter imports github.com/modelcontextprotocol/go-sdk and wraps core as http.Handler" + - "Adapters call oauth.NewServer(), RegisterHandlers(), ValidateTokenCached() — never re-implement validation" + - "New SDK adapters go in their own top-level directory with a single WithOAuth() entry point" + +provider_abstraction: + description: "TokenValidator interface with pluggable provider implementations" + rules: + - "All providers implement provider.TokenValidator (ValidateToken + Initialize)" + - "HMACValidator for dev/testing — validates JWT with shared secret, no network I/O" + - "OIDCValidator for prod — validates via OIDC discovery and JWKS (Okta, Google, Azure)" + - "createValidator() in config.go is the only factory — switch on cfg.Provider" + - "New providers: add to provider/provider.go, update createValidator() switch, add docs/providers/*.md" + +context_propagation: + description: "Two-phase context: HTTP layer adds token, middleware validates and adds User" + rules: + - "Phase 1: CreateHTTPContextFunc() extracts Bearer token -> WithOAuthToken(ctx, token)" + - "Phase 2: Middleware calls GetOAuthToken(ctx) -> ValidateTokenCached() -> WithUser(ctx, user)" + - "Tool handlers access user via GetUserFromContext(ctx) — never parse tokens directly" + - "Context keys are unexported typed constants (contextKey string type)" + - "Never use context.WithValue with string keys — always use the typed helpers" + +token_caching: + description: "5-minute TTL cache with SHA-256 hash keys and sync.RWMutex" + rules: + - "Cache key is full SHA-256 hex of the raw token — never store raw tokens" + - "TTL is 5 minutes (hardcoded in ValidateTokenCached and Middleware)" + - "TokenCache uses sync.RWMutex — RLock for reads, Lock for writes" + - "Expired tokens cleaned lazily via goroutine in getCachedToken (deleteExpiredToken)" + - "Each Server instance has its own cache — no global shared state" + +configuration: + description: "Config struct with ConfigBuilder fluent API and FromEnv() factory" + rules: + - "Config struct for direct construction, ConfigBuilder for fluent API" + - "Build() validates config via Config.Validate() before returning" + - "FromEnv() reads env vars: OAUTH_MODE, OAUTH_PROVIDER, OIDC_ISSUER, OIDC_AUDIENCE, etc." + - "Mode auto-detected: ClientID present -> proxy, absent -> native" + - "Audience is always required regardless of provider" + +security: + description: "Security-first design for OAuth token handling" + rules: + - "Never log raw tokens — only log SHA-256 hash prefix (tokenHash[:16])" + - "401 responses follow RFC 6750: WWW-Authenticate Bearer with error codes" + - "Two error codes: invalid_request (missing token) and invalid_token (validation failed)" + - "Audience validation is explicit in both HMAC and OIDC validators" + - "Redirect URI validation requires explicit allowlist (RedirectURIs config)" + - "OIDC requires TLS 1.2+ (MinVersion: tls.VersionTLS12)" + +testing: + description: "oauth-mcp-proxy specific test organization" + rules: + - "Mock validators implement provider.TokenValidator interface" + - "Test files: api_test.go, security_test.go, integration_test.go, config_test.go" + - "Provider tests in provider/provider_test.go" diff --git a/.claude/knowledge/adrs/001-sdk-agnostic-core.md b/.claude/knowledge/adrs/001-sdk-agnostic-core.md new file mode 100644 index 0000000..559a23d --- /dev/null +++ b/.claude/knowledge/adrs/001-sdk-agnostic-core.md @@ -0,0 +1,47 @@ +--- +title: "ADR-001: SDK-Agnostic Core Package" +status: accepted +date: 2024-12-01 +--- + +## Context + +oauth-mcp-proxy needs to support multiple MCP SDK implementations: the community `mark3labs/mcp-go` +(v0.41.1) and the official `modelcontextprotocol/go-sdk` (v1.0.0). These SDKs have different APIs: +mark3labs uses `server.ToolHandlerFunc` middleware and `ServerOption`, while the official SDK uses +standard `http.Handler` wrapping with `mcp.NewStreamableHTTPHandler`. + +Coupling OAuth logic to a single SDK would require duplication when supporting the other, and would +create vendor lock-in that prevents users from migrating between SDKs. + +## Decision + +The core package (root `oauth` package) contains all OAuth logic and is SDK-agnostic. It must not +import any MCP SDK. SDK-specific integration lives in adapter packages: + +- **`mark3labs/`** — Imports `mark3labs/mcp-go`, provides `WithOAuth(mux, cfg) -> (*Server, ServerOption, error)`. + Uses `server.WithToolHandlerMiddleware()` to inject the authentication middleware. +- **`mcp/`** — Imports `modelcontextprotocol/go-sdk`, provides `WithOAuth(mux, cfg, mcpServer) -> (*Server, http.Handler, error)`. + Wraps `mcp.NewStreamableHTTPHandler` with Bearer token validation. + +Core types used by adapters: +- `oauth.Server` — Created via `NewServer(cfg)`, holds validator + cache + handler +- `oauth.ValidateTokenCached(ctx, token)` — Cache-aware token validation +- `oauth.WithOAuthToken(ctx, token)` / `oauth.WithUser(ctx, user)` — Context helpers +- `oauth.RegisterHandlers(mux)` — Registers OAuth HTTP endpoints + +Note: The root package `oauth.go` does import `mark3labs/mcp-go` for the legacy `WithOAuth()` and +`Middleware()` functions. The `mark3labs/` adapter is the recommended path for new code. + +## Consequences + +**Positive:** +- Users choose their SDK without changing OAuth config or learning a different API. +- Core logic is tested once, not per-SDK. Adapter tests focus on SDK wiring. +- Adding a third SDK adapter requires only a new directory, no core changes. + +**Negative:** +- Adapters duplicate some wiring code (Bearer extraction, 401 responses). The `mcp/oauth.go` adapter + re-implements Bearer validation inline rather than reusing `WrapHandler()` because it needs to + control the full HTTP lifecycle for the official SDK's handler. +- The root package still has a legacy mark3labs dependency via `Middleware()` and `GetHTTPServerOptions()`. diff --git a/.claude/knowledge/adrs/002-dual-mode-auth.md b/.claude/knowledge/adrs/002-dual-mode-auth.md new file mode 100644 index 0000000..677a53e --- /dev/null +++ b/.claude/knowledge/adrs/002-dual-mode-auth.md @@ -0,0 +1,60 @@ +--- +title: "ADR-002: Dual-Mode Authentication (Native vs Proxy)" +status: accepted +date: 2024-12-01 +--- + +## Context + +MCP clients have varying OAuth capabilities. Some clients (like Claude Desktop) can perform their own +OAuth flows and send Bearer tokens directly. Others lack built-in OAuth support and need the server to +act as an OAuth proxy, handling the authorization code flow on their behalf. + +The library needs to support both scenarios without requiring separate server configurations. + +## Decision + +Two authentication modes, auto-detected from configuration: + +**Native mode** (token validation only): +- Client obtains token from OAuth provider independently. +- Server validates Bearer token via configured provider (HMAC or OIDC). +- Auto-selected when `ClientID` is empty. +- Only `/.well-known/*` metadata endpoints are registered. + +**Proxy mode** (server-driven OAuth flow): +- Server proxies the full OAuth 2.1 authorization code flow. +- Registers additional endpoints: `/oauth/authorize`, `/oauth/callback`, `/oauth/token`, `/oauth/register`. +- Requires `ClientID`, `ClientSecret`, `ServerURL`, and `RedirectURIs` in config. +- Auto-selected when `ClientID` is present. + +Auto-detection in `Config.Validate()` (config.go:39-45): +```go +if c.Mode == "" { + if c.ClientID != "" { + c.Mode = "proxy" + } else { + c.Mode = "native" + } +} +``` + +Proxy mode validates additional requirements (config.go:77-87): ClientID, ServerURL, and RedirectURIs +must all be set. + +## Consequences + +**Positive:** +- Zero-config mode detection: users set credentials for their scenario and mode is inferred. +- Native mode has minimal surface area (no proxy endpoints exposed). +- Both modes share the same token validation and caching infrastructure. + +**Negative:** +- Mode auto-detection can surprise users who set ClientID for a different reason. +- Proxy mode requires more config validation, making error messages harder to understand. + +## Alternatives Considered + +- **Single mode only**: Rejected because it would force all clients to have OAuth capability. +- **Explicit mode required**: Considered, but auto-detection from ClientID is intuitive and reduces + configuration burden. diff --git a/.claude/knowledge/adrs/003-token-caching-strategy.md b/.claude/knowledge/adrs/003-token-caching-strategy.md new file mode 100644 index 0000000..45bfb74 --- /dev/null +++ b/.claude/knowledge/adrs/003-token-caching-strategy.md @@ -0,0 +1,67 @@ +--- +title: "ADR-003: Token Caching with 5-Minute TTL and SHA-256 Keys" +status: accepted +date: 2024-12-01 +--- + +## Context + +Token validation can be expensive: OIDC validation requires network calls to the provider's JWKS +endpoint (10-second timeout per call). In a high-request environment, validating the same token on +every request would create unacceptable latency and load on the identity provider. + +The cache must be thread-safe (concurrent HTTP requests), secure (no raw token storage), and +time-bounded (tokens should not be cached indefinitely after revocation). + +## Decision + +Each `Server` instance maintains its own `TokenCache` (cache.go). Key design choices: + +**SHA-256 hash keys**: Raw tokens are never stored. The cache key is the full hex-encoded SHA-256 +hash of the token (oauth.go:114): +```go +tokenHash := fmt.Sprintf("%x", sha256.Sum256([]byte(token))) +``` + +**5-minute TTL**: Cached tokens expire after 5 minutes (oauth.go:129): +```go +expiresAt := time.Now().Add(5 * time.Minute) +``` + +**sync.RWMutex**: Read-heavy workload (most requests hit cache) uses `RLock` for reads and `Lock` +for writes (cache.go:15-16): +```go +type TokenCache struct { + mu sync.RWMutex + cache map[string]*CachedToken +} +``` + +**Lazy cleanup**: Expired tokens are cleaned up asynchronously. When `getCachedToken` finds an expired +entry, it returns cache-miss and spawns a goroutine to delete it (cache.go:37-38): +```go +go tc.deleteExpiredToken(tokenHash) +``` +The `deleteExpiredToken` goroutine re-checks expiry under write lock to prevent race conditions. + +**Instance-scoped**: No global cache. Each `Server` creates its own cache in `NewServer()` (oauth.go:63-65). + +## Consequences + +**Positive:** +- OIDC validation cost amortized across requests with the same token. +- SHA-256 keys mean a cache dump does not leak tokens. +- RWMutex allows concurrent cache reads without contention. +- 5-minute TTL limits exposure window after token revocation. + +**Negative:** +- Revoked tokens remain valid for up to 5 minutes. +- No cache size limit — a large number of unique tokens could grow memory (acceptable for MCP servers + which typically have few concurrent users). +- Lazy cleanup means expired entries may persist briefly (not a correctness issue). + +## Alternatives Considered + +- **No cache**: Rejected because OIDC calls add 100-500ms latency per request. +- **Longer TTL (30min)**: Rejected because it widens the revocation window too much. +- **LRU cache with size limit**: Considered but over-engineered for MCP server use cases. diff --git a/.claude/knowledge/adrs/004-provider-abstraction.md b/.claude/knowledge/adrs/004-provider-abstraction.md new file mode 100644 index 0000000..539cb0f --- /dev/null +++ b/.claude/knowledge/adrs/004-provider-abstraction.md @@ -0,0 +1,77 @@ +--- +title: "ADR-004: TokenValidator Interface for Provider Abstraction" +status: accepted +date: 2024-12-01 +--- + +## Context + +The library needs to support multiple OAuth providers: HMAC (for development/testing), and OIDC-based +providers (Okta, Google, Azure AD) for production. Each provider has fundamentally different validation +mechanisms: HMAC uses local JWT signature verification, while OIDC requires network-based JWKS +discovery and verification. + +Tests also need to validate OAuth flows without real provider infrastructure. + +## Decision + +A `TokenValidator` interface in `provider/provider.go` abstracts all provider differences: + +```go +type TokenValidator interface { + ValidateToken(ctx context.Context, token string) (*User, error) + Initialize(cfg *Config) error +} +``` + +Two implementations: +- **`HMACValidator`** — Local JWT validation using `golang-jwt/jwt/v5`. Uses `sync.Once` for + initialization. No network I/O, making it fast and suitable for dev/testing. Validates HMAC-SHA256 + signature, expiry, not-before, issued-at, and audience claims. +- **`OIDCValidator`** — Network-based validation using `coreos/go-oidc/v3`. Initializes with 30-second + timeout for OIDC discovery. Validates tokens via JWKS with 10-second timeout. Supports RS256 and + ES256 signing algorithms. Enforces TLS 1.2+. + +Both validators explicitly check the `aud` (audience) claim — this is security-critical and not +delegated to the JWT library alone. + +Factory in `config.go` (createValidator, line 119): +```go +switch cfg.Provider { +case "hmac": + validator = &provider.HMACValidator{} +case "okta", "google", "azure": + validator = &provider.OIDCValidator{} +} +``` + +The `User` struct (provider/provider.go:17-21) is the common return type: +```go +type User struct { + Username string + Email string + Subject string +} +``` + +## Consequences + +**Positive:** +- Tests use `HMACValidator` or mock implementations without any OIDC infrastructure. +- Adding a new provider (e.g., Auth0) requires only implementing the interface and adding a case + to `createValidator()`. +- Core package and adapters depend on the interface, not concrete implementations. +- `User` struct is re-exported from root package via type alias (`type User = provider.User` in cache.go). + +**Negative:** +- `Initialize()` method means validators are not ready at construction time — must be called before use. +- The `ctx` parameter on `HMACValidator.ValidateToken` is unused (accepted for interface compliance). +- Provider-specific config (e.g., OIDC-specific TLS settings) is passed through the generic + `provider.Config` struct. + +## Alternatives Considered + +- **Single validator with config switches**: Rejected because HMAC and OIDC have fundamentally + different dependencies and initialization patterns. +- **Separate packages per provider**: Considered, but the current single-file approach + (`provider/provider.go`) is appropriate given only two implementations. diff --git a/.claude/knowledge/adrs/005-context-based-user-propagation.md b/.claude/knowledge/adrs/005-context-based-user-propagation.md new file mode 100644 index 0000000..9f29696 --- /dev/null +++ b/.claude/knowledge/adrs/005-context-based-user-propagation.md @@ -0,0 +1,75 @@ +--- +title: "ADR-005: Context-Based User Propagation" +status: accepted +date: 2024-12-01 +--- + +## Context + +Authenticated user information must flow from the HTTP layer (where Bearer tokens are extracted) +through the MCP middleware layer (where tokens are validated) to the tool handler (where the user +identity is consumed). Go's MCP SDKs use `context.Context` as the primary mechanism for passing +request-scoped data through handler chains. + +The library must avoid global state (multiple Server instances may coexist) and must work with both +mark3labs and official SDK handler signatures. + +## Decision + +Two-phase context propagation using typed context keys (context.go): + +**Phase 1 — Token extraction** (HTTP layer): +`CreateHTTPContextFunc()` (middleware.go:120-142) extracts the Bearer token from the Authorization +header and stores it in context: +```go +ctx = WithOAuthToken(ctx, token) // context.go:14-16 +``` + +**Phase 2 — Token validation and user injection** (middleware layer): +The SDK-specific middleware retrieves the token, validates it, and adds the user: +```go +tokenString, ok := GetOAuthToken(ctx) // context.go:19-22 +user, err := s.ValidateTokenCached(ctx, token) +ctx = WithUser(ctx, user) // context.go:25-27 +``` + +**Consumer access** (tool handler): +```go +user, ok := GetUserFromContext(ctx) // context.go:42-44 +``` + +Context keys are typed constants to prevent collisions (context.go:7-11): +```go +type contextKey string +const ( + oauthTokenKey contextKey = "oauth_token" + userContextKey contextKey = "user" +) +``` + +The mark3labs adapter (mark3labs/middleware.go) uses `server.ToolHandlerFunc` signature and calls +`oauth.GetOAuthToken` -> `ValidateTokenCached` -> `oauth.WithUser`. The official SDK adapter +(mcp/oauth.go) does the same but at the HTTP handler level before passing to +`mcp.NewStreamableHTTPHandler`. + +## Consequences + +**Positive:** +- Request-scoped: each request carries its own user, no global state or goroutine-local storage. +- Works identically with both SDK adapters since both use `context.Context`. +- Type-safe keys prevent collision with other context values. +- Tool handlers have a clean API: `GetUserFromContext(ctx)` returns `(*User, bool)`. + +**Negative:** +- Two-phase design means a missing Phase 1 (no `CreateHTTPContextFunc`) silently fails at Phase 2 + with "authentication required: missing OAuth token" — not always obvious to debug. +- Context values are not visible in type signatures, so the dependency on `WithOAuthToken` being + called before middleware is implicit. + +## Alternatives Considered + +- **Global user map keyed by request ID**: Rejected because it introduces global state and cleanup complexity. +- **Custom request struct wrapping context**: Rejected because it would require non-standard handler + signatures incompatible with both SDKs. +- **Single-phase (validate at HTTP level only)**: Rejected because mark3labs SDK needs middleware at + the tool handler level to access `mcp.CallToolRequest`. diff --git a/.claude/knowledge/computed/api-surface.json b/.claude/knowledge/computed/api-surface.json new file mode 100644 index 0000000..dc268a1 --- /dev/null +++ b/.claude/knowledge/computed/api-surface.json @@ -0,0 +1,380 @@ +{ + "generated": "2026-03-20T03:16:45Z", + "module": "github.com/Vungle/oauth-mcp-proxy", + "packages": [ + { + "path": "github.com/Vungle/oauth-mcp-proxy", + "rel_dir": ".", + "types": [ + { + "name": "CachedToken", + "kind": "struct" + }, + { + "name": "Config", + "kind": "struct", + "methods": [ + { + "name": "Validate", + "signature": "func() error" + } + ] + }, + { + "name": "ConfigBuilder", + "kind": "struct", + "methods": [ + { + "name": "WithMode", + "signature": "func(mode string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithProvider", + "signature": "func(provider string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithRedirectURIs", + "signature": "func(uris string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithIssuer", + "signature": "func(issuer string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithAudience", + "signature": "func(audience string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithClientID", + "signature": "func(clientID string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithClientSecret", + "signature": "func(secret string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithJWTSecret", + "signature": "func(secret []byte) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithLogger", + "signature": "func(logger github.com/Vungle/oauth-mcp-proxy.Logger) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithServerURL", + "signature": "func(url string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithHost", + "signature": "func(host string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithPort", + "signature": "func(port string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "WithTLS", + "signature": "func(useTLS bool) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "Build", + "signature": "func() (*github.com/Vungle/oauth-mcp-proxy.Config, error)" + } + ] + }, + { + "name": "Endpoint", + "kind": "struct" + }, + { + "name": "Logger", + "kind": "interface" + }, + { + "name": "OAuth2Config", + "kind": "struct" + }, + { + "name": "OAuth2Handler", + "kind": "struct", + "methods": [ + { + "name": "GetConfig", + "signature": "func() *github.com/Vungle/oauth-mcp-proxy.OAuth2Config" + }, + { + "name": "HandleJWKS", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleAuthorize", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleCallback", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleToken", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleMetadata", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleAuthorizationServerMetadata", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleProtectedResourceMetadata", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleRegister", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleCallbackRedirect", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "HandleOIDCDiscovery", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)" + }, + { + "name": "GetAuthorizationServerMetadata", + "signature": "func() map[string]interface{}" + } + ] + }, + { + "name": "Server", + "kind": "struct", + "methods": [ + { + "name": "Middleware", + "signature": "func() func(github.com/mark3labs/mcp-go/server.ToolHandlerFunc) github.com/mark3labs/mcp-go/server.ToolHandlerFunc" + }, + { + "name": "RegisterHandlers", + "signature": "func(mux *net/http.ServeMux)" + }, + { + "name": "ValidateTokenCached", + "signature": "func(ctx context.Context, token string) (*github.com/Vungle/oauth-mcp-proxy.User, error)" + }, + { + "name": "GetAuthorizationServerMetadataURL", + "signature": "func() string" + }, + { + "name": "GetProtectedResourceMetadataURL", + "signature": "func() string" + }, + { + "name": "GetOIDCDiscoveryURL", + "signature": "func() string" + }, + { + "name": "GetCallbackURL", + "signature": "func() string" + }, + { + "name": "GetAuthorizeURL", + "signature": "func() string" + }, + { + "name": "GetTokenURL", + "signature": "func() string" + }, + { + "name": "GetRegisterURL", + "signature": "func() string" + }, + { + "name": "GetAllEndpoints", + "signature": "func() []github.com/Vungle/oauth-mcp-proxy.Endpoint" + }, + { + "name": "LogStartup", + "signature": "func(useTLS bool)" + }, + { + "name": "GetStatusString", + "signature": "func(useTLS bool) string" + }, + { + "name": "GetHTTPServerOptions", + "signature": "func() []github.com/mark3labs/mcp-go/server.StreamableHTTPOption" + }, + { + "name": "WrapHandler", + "signature": "func(next net/http.Handler) net/http.Handler" + }, + { + "name": "WrapHandlerFunc", + "signature": "func(next net/http.HandlerFunc) net/http.HandlerFunc" + }, + { + "name": "WrapMCPEndpoint", + "signature": "func(handler net/http.Handler) net/http.HandlerFunc" + }, + { + "name": "Return401", + "signature": "func(w net/http.ResponseWriter)" + }, + { + "name": "Return401InvalidToken", + "signature": "func(w net/http.ResponseWriter)" + } + ] + }, + { + "name": "TokenCache", + "kind": "struct" + } + ], + "functions": [ + { + "name": "AutoDetectServerURL", + "signature": "func(host string, port string, useTLS bool) string" + }, + { + "name": "CreateHTTPContextFunc", + "signature": "func() func(context.Context, *net/http.Request) context.Context" + }, + { + "name": "CreateOAuth2Handler", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config, version string, logger github.com/Vungle/oauth-mcp-proxy.Logger) *github.com/Vungle/oauth-mcp-proxy.OAuth2Handler" + }, + { + "name": "CreateRequestAuthHook", + "signature": "func(validator provider.TokenValidator) func(context.Context, interface{}, interface{}) error" + }, + { + "name": "FromEnv", + "signature": "func() (*github.com/Vungle/oauth-mcp-proxy.Config, error)" + }, + { + "name": "GetOAuthToken", + "signature": "func(ctx context.Context) (string, bool)" + }, + { + "name": "GetUserFromContext", + "signature": "func(ctx context.Context) (*github.com/Vungle/oauth-mcp-proxy.User, bool)" + }, + { + "name": "NewConfigBuilder", + "signature": "func() *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder" + }, + { + "name": "NewOAuth2ConfigFromConfig", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config, version string) *github.com/Vungle/oauth-mcp-proxy.OAuth2Config" + }, + { + "name": "NewOAuth2Handler", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.OAuth2Config, logger github.com/Vungle/oauth-mcp-proxy.Logger) *github.com/Vungle/oauth-mcp-proxy.OAuth2Handler" + }, + { + "name": "NewServer", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config) (*github.com/Vungle/oauth-mcp-proxy.Server, error)" + }, + { + "name": "OAuthMiddleware", + "signature": "func(validator provider.TokenValidator, enabled bool) func(github.com/mark3labs/mcp-go/server.ToolHandlerFunc) github.com/mark3labs/mcp-go/server.ToolHandlerFunc" + }, + { + "name": "SetupOAuth", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config) (provider.TokenValidator, error)" + }, + { + "name": "WithOAuth", + "signature": "func(mux *net/http.ServeMux, cfg *github.com/Vungle/oauth-mcp-proxy.Config) (*github.com/Vungle/oauth-mcp-proxy.Server, github.com/mark3labs/mcp-go/server.ServerOption, error)" + }, + { + "name": "WithOAuthToken", + "signature": "func(ctx context.Context, token string) context.Context" + }, + { + "name": "WithUser", + "signature": "func(ctx context.Context, user *github.com/Vungle/oauth-mcp-proxy.User) context.Context" + } + ] + }, + { + "path": "github.com/Vungle/oauth-mcp-proxy/mark3labs", + "rel_dir": "mark3labs", + "functions": [ + { + "name": "NewMiddleware", + "signature": "func(s *github.com/Vungle/oauth-mcp-proxy.Server) func(github.com/mark3labs/mcp-go/server.ToolHandlerFunc) github.com/mark3labs/mcp-go/server.ToolHandlerFunc" + }, + { + "name": "WithOAuth", + "signature": "func(mux *net/http.ServeMux, cfg *github.com/Vungle/oauth-mcp-proxy.Config) (*github.com/Vungle/oauth-mcp-proxy.Server, github.com/mark3labs/mcp-go/server.ServerOption, error)" + } + ] + }, + { + "path": "github.com/Vungle/oauth-mcp-proxy/mcp", + "rel_dir": "mcp", + "functions": [ + { + "name": "WithOAuth", + "signature": "func(mux *net/http.ServeMux, cfg *github.com/Vungle/oauth-mcp-proxy.Config, mcpServer *github.com/modelcontextprotocol/go-sdk/mcp.Server) (*github.com/Vungle/oauth-mcp-proxy.Server, net/http.Handler, error)" + } + ] + }, + { + "path": "github.com/Vungle/oauth-mcp-proxy/provider", + "rel_dir": "provider", + "types": [ + { + "name": "Config", + "kind": "struct" + }, + { + "name": "HMACValidator", + "kind": "struct", + "methods": [ + { + "name": "Initialize", + "signature": "func(cfg *provider.Config) error" + }, + { + "name": "ValidateToken", + "signature": "func(ctx context.Context, tokenString string) (*provider.User, error)" + } + ] + }, + { + "name": "Logger", + "kind": "interface" + }, + { + "name": "OIDCValidator", + "kind": "struct", + "methods": [ + { + "name": "Initialize", + "signature": "func(cfg *provider.Config) error" + }, + { + "name": "ValidateToken", + "signature": "func(ctx context.Context, tokenString string) (*provider.User, error)" + } + ] + }, + { + "name": "TokenValidator", + "kind": "interface" + }, + { + "name": "User", + "kind": "struct" + } + ] + } + ] +} diff --git a/.claude/knowledge/computed/call-graph.json b/.claude/knowledge/computed/call-graph.json new file mode 100644 index 0000000..861ff25 --- /dev/null +++ b/.claude/knowledge/computed/call-graph.json @@ -0,0 +1,4277 @@ +{ + "generated": "2026-03-20T03:16:48Z", + "module": "github.com/Vungle/oauth-mcp-proxy", + "total_edges": 489, + "total_functions": 234, + "edges": [ + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.getEnv", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "os.LookupEnv", + "callee_pkg": "os", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 122 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/mark3labs/mcp-go/server.init", + "callee_pkg": "github.com/mark3labs/mcp-go/server" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "context.init", + "callee_pkg": "context" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "log.init", + "callee_pkg": "log" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "os.init", + "callee_pkg": "os" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.init", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "callee_pkg": "mark3labs" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/mark3labs/mcp-go/mcp.init", + "callee_pkg": "github.com/mark3labs/mcp-go/mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithIssuer", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/mark3labs/mcp-go/server.WithStateLess", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithAudience", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithHost", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithPort", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithTLS", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetHTTPServerOptions", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/mark3labs/mcp-go/server.NewMCPServer", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy/mark3labs.WithOAuth", + "callee_pkg": "mark3labs", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).Build", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "log.Fatalf", + "callee_pkg": "log", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "net/http.NewServeMux", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetCallbackURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.getEnv", + "callee_pkg": "examples/mark3labs/advanced", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithProvider", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewConfigBuilder", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/mark3labs/mcp-go/server.WithEndpointPath", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/mark3labs/mcp-go/server.NewStreamableHTTPServer", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).WrapMCPEndpoint", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "net/http.ListenAndServe", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "net/http.ListenAndServeTLS", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*net/http.ServeMux).HandleFunc", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/mark3labs/mcp-go/server.MCPServer).AddTool", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$1", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 54 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$1", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/mark3labs/mcp-go/server.StreamableHTTPServer).ServeHTTP", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 54 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$2", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 70 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$2", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "fmt.Fprintf", + "callee_pkg": "fmt", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 70 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$2", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetStatusString", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 70 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$2", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.getEnv", + "callee_pkg": "examples/mark3labs/advanced", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 70 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$3", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.GetUserFromContext", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 85 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$3", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 85 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$3", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 85 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$3", + "caller_pkg": "examples/mark3labs/advanced", + "callee": "github.com/mark3labs/mcp-go/mcp.NewToolResultText", + "callee_pkg": "github.com/mark3labs/mcp-go/mcp", + "caller_file": "examples/mark3labs/advanced/main.go", + "caller_line": 85 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.getEnv", + "caller_pkg": "examples/mark3labs/simple", + "callee": "os.Getenv", + "callee_pkg": "os", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 82 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "os.init", + "callee_pkg": "os" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "log.init", + "callee_pkg": "log" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "context.init", + "callee_pkg": "context" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/mark3labs/mcp-go/server.init", + "callee_pkg": "github.com/mark3labs/mcp-go/server" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/mark3labs/mcp-go/mcp.init", + "callee_pkg": "github.com/mark3labs/mcp-go/mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "callee_pkg": "mark3labs" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy.init", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/mark3labs/mcp-go/server.WithHTTPContextFunc", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "(*github.com/mark3labs/mcp-go/server.MCPServer).AddTool", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "(*net/http.ServeMux).HandleFunc", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).WrapMCPEndpoint", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/mark3labs/mcp-go/server.NewStreamableHTTPServer", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "net/http.ListenAndServe", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy/mark3labs.WithOAuth", + "callee_pkg": "mark3labs", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "net/http.NewServeMux", + "callee_pkg": "net/http", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/mark3labs/mcp-go/server.WithEndpointPath", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "log.Println", + "callee_pkg": "log", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "log.Fatalf", + "callee_pkg": "log", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/mark3labs/mcp-go/server.NewMCPServer", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.getEnv", + "callee_pkg": "examples/mark3labs/simple", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main$1", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy.GetUserFromContext", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main$1", + "caller_pkg": "examples/mark3labs/simple", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main$1", + "caller_pkg": "examples/mark3labs/simple", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main$1", + "caller_pkg": "examples/mark3labs/simple", + "callee": "github.com/mark3labs/mcp-go/mcp.NewToolResultText", + "callee_pkg": "github.com/mark3labs/mcp-go/mcp", + "caller_file": "examples/mark3labs/simple/main.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.getEnv", + "caller_pkg": "examples/official/advanced", + "callee": "os.Getenv", + "callee_pkg": "os", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 164 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "context.init", + "callee_pkg": "context" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy/mcp.init", + "callee_pkg": "mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.init", + "callee_pkg": "github.com/modelcontextprotocol/go-sdk/mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.init", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "time.init", + "callee_pkg": "time" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "os.init", + "callee_pkg": "os" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "log.init", + "callee_pkg": "log" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.AddTool[*github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.UserInfoParams any]", + "callee_pkg": "", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithHost", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithAudience", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithTLS", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).Build", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "log.Fatalf", + "callee_pkg": "log", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithIssuer", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.getEnv", + "callee_pkg": "examples/official/advanced", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewConfigBuilder", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithProvider", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.NewServer", + "callee_pkg": "github.com/modelcontextprotocol/go-sdk/mcp", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.AddTool[*github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.GreetParams any]", + "callee_pkg": "", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithPort", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "net/http.NewServeMux", + "callee_pkg": "net/http", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "net/http.ListenAndServe", + "callee_pkg": "net/http", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth", + "callee_pkg": "mcp", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.AddTool[*github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.TimeParams any]", + "callee_pkg": "", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAuthorizationServerMetadataURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetOIDCDiscoveryURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetStatusString", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "log.Println", + "callee_pkg": "log", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "caller_pkg": "examples/official/advanced", + "callee": "net/http.ListenAndServeTLS", + "callee_pkg": "net/http", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 16 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$1", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.GetUserFromContext", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 45 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$1", + "caller_pkg": "examples/official/advanced", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 45 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$1", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 45 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$1", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 45 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$2", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$2", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$2", + "caller_pkg": "examples/official/advanced", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$2", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.GetUserFromContext", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$3", + "caller_pkg": "examples/official/advanced", + "callee": "(time.Time).Format", + "callee_pkg": "time", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 91 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$3", + "caller_pkg": "examples/official/advanced", + "callee": "time.Now", + "callee_pkg": "time", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 91 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$3", + "caller_pkg": "examples/official/advanced", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 91 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$3", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 91 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$3", + "caller_pkg": "examples/official/advanced", + "callee": "github.com/Vungle/oauth-mcp-proxy.GetUserFromContext", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 91 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$3", + "caller_pkg": "examples/official/advanced", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "examples/official/advanced/main.go", + "caller_line": 91 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.getEnv", + "caller_pkg": "examples/official/simple", + "callee": "os.Getenv", + "callee_pkg": "os", + "caller_file": "examples/official/simple/main.go", + "caller_line": 90 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "os.init", + "callee_pkg": "os" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy.init", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "log.init", + "callee_pkg": "log" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.init", + "callee_pkg": "github.com/modelcontextprotocol/go-sdk/mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy/mcp.init", + "callee_pkg": "mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "caller_pkg": "examples/official/simple", + "callee": "context.init", + "callee_pkg": "context" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "log.Println", + "callee_pkg": "log", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "net/http.NewServeMux", + "callee_pkg": "net/http", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.AddTool[*github.com/Vungle/oauth-mcp-proxy/examples/official/simple.GreetParams any]", + "callee_pkg": "", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.getEnv", + "callee_pkg": "examples/official/simple", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.NewServer", + "callee_pkg": "github.com/modelcontextprotocol/go-sdk/mcp", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "net/http.ListenAndServe", + "callee_pkg": "net/http", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth", + "callee_pkg": "mcp", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "log.Fatalf", + "callee_pkg": "log", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "caller_pkg": "examples/official/simple", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/simple/main.go", + "caller_line": 15 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main$1", + "caller_pkg": "examples/official/simple", + "callee": "github.com/Vungle/oauth-mcp-proxy.GetUserFromContext", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "examples/official/simple/main.go", + "caller_line": 31 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main$1", + "caller_pkg": "examples/official/simple", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "examples/official/simple/main.go", + "caller_line": 31 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main$1", + "caller_pkg": "examples/official/simple", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "examples/official/simple/main.go", + "caller_line": 31 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Config).Validate", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "config.go", + "caller_line": 37 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).Build", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Config).Validate", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 251 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).Build", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.AutoDetectServerURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 251 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).GetAuthorizationServerMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "metadata.go", + "caller_line": 273 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorizationServerMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 81 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorizationServerMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 81 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorizationServerMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 81 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorizationServerMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 81 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorizationServerMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).GetAuthorizationServerMetadata", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 81 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorizationServerMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 81 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/url.Values).Get", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/url.URL).Query", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Redirect", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.TrimSpace", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Warn", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/url.Parse", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.truncateString", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).signState", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).isValidRedirectURI", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/url.Values).Set", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/url.Values).Encode", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/url.URL).String", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*golang.org/x/oauth2.Config).AuthCodeURL", + "callee_pkg": "golang.org/x/oauth2", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.Contains", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.isLocalhostURI", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 268 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).showSuccessPage", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Warn", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).verifyState", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.isLocalhostURI", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.Contains", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.truncateString", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/url.URL).Query", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/url.Values).Get", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Redirect", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 410 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallbackRedirect", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Redirect", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 212 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS$1", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Get", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "io.Copy", + "callee_pkg": "io", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/http.Client).Get", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 12 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 12 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 12 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 12 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 12 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "metadata.go", + "caller_line": 12 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Fprintf", + "callee_pkg": "fmt", + "caller_file": "metadata.go", + "caller_line": 12 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 222 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 222 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "metadata.go", + "caller_line": 222 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 222 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 222 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 222 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 222 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleProtectedResourceMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleProtectedResourceMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "metadata.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleProtectedResourceMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleProtectedResourceMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleProtectedResourceMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleProtectedResourceMetadata", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.Contains", + "callee_pkg": "strings", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewDecoder", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Decoder).Decode", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "time.Now", + "callee_pkg": "time", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(time.Time).Unix", + "callee_pkg": "time", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.TrimSpace", + "callee_pkg": "strings", + "caller_file": "metadata.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.Error", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.Contains", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "context.Background", + "callee_pkg": "context", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(time.Duration).Seconds", + "callee_pkg": "time", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.TrimSpace", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.truncateString", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/http.Request).FormValue", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*golang.org/x/oauth2.Token).Extra", + "callee_pkg": "golang.org/x/oauth2", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/http.Request).ParseForm", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "time.Until", + "callee_pkg": "time", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*golang.org/x/oauth2.Config).Exchange", + "callee_pkg": "golang.org/x/oauth2", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "context.WithValue", + "callee_pkg": "context", + "caller_file": "handlers.go", + "caller_line": 485 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).addSecurityHeaders", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 807 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).isValidRedirectURI", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.TrimSpace", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 772 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).isValidRedirectURI", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.Split", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 772 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).isValidRedirectURI", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Warn", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 772 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).showSuccessPage", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.truncateString", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 609 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).showSuccessPage", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 609 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).showSuccessPage", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 609 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).showSuccessPage", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Fprintf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 609 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).signState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/hex.EncodeToString", + "callee_pkg": "encoding/hex", + "caller_file": "handlers.go", + "caller_line": 692 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).signState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/base64.Encoding).EncodeToString", + "callee_pkg": "encoding/base64", + "caller_file": "handlers.go", + "caller_line": 692 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).signState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 692 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).signState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.Marshal", + "callee_pkg": "encoding/json", + "caller_file": "handlers.go", + "caller_line": 692 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).signState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/hmac.New", + "callee_pkg": "crypto/hmac", + "caller_file": "handlers.go", + "caller_line": 692 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).validateOAuthParams", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 792 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).validateOAuthParams", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/http.Request).FormValue", + "callee_pkg": "net/http", + "caller_file": "handlers.go", + "caller_line": 792 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).verifyState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/hmac.New", + "callee_pkg": "crypto/hmac", + "caller_file": "handlers.go", + "caller_line": 719 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).verifyState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/hmac.Equal", + "callee_pkg": "crypto/hmac", + "caller_file": "handlers.go", + "caller_line": 719 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).verifyState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/base64.Encoding).DecodeString", + "callee_pkg": "encoding/base64", + "caller_file": "handlers.go", + "caller_line": 719 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).verifyState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 719 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).verifyState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.Unmarshal", + "callee_pkg": "encoding/json", + "caller_file": "handlers.go", + "caller_line": 719 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).verifyState", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/hex.EncodeToString", + "callee_pkg": "encoding/hex", + "caller_file": "handlers.go", + "caller_line": 719 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAuthorizeURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 178 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetTokenURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 178 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetOIDCDiscoveryURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 178 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetCallbackURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 178 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetProtectedResourceMetadataURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 178 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAuthorizationServerMetadataURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 178 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAllEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetRegisterURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 178 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAuthorizationServerMetadataURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 137 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAuthorizeURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 157 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetCallbackURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 152 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetHTTPServerOptions", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 239 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetHTTPServerOptions", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/mark3labs/mcp-go/server.WithHTTPContextFunc", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "oauth.go", + "caller_line": 239 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetOIDCDiscoveryURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 147 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetProtectedResourceMetadataURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 142 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetRegisterURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 167 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetStatusString", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 220 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetTokenURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 162 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetCallbackURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetProtectedResourceMetadataURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetOIDCDiscoveryURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAuthorizationServerMetadataURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetAuthorizeURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetRegisterURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).LogStartup", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetTokenURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 199 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).RegisterHandlers", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/http.ServeMux).HandleFunc", + "callee_pkg": "net/http", + "caller_file": "oauth.go", + "caller_line": 92 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "oauth.go", + "caller_line": 377 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetProtectedResourceMetadataURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 377 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 377 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "oauth.go", + "caller_line": 377 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "oauth.go", + "caller_line": 377 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401InvalidToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetProtectedResourceMetadataURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 399 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401InvalidToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*encoding/json.Encoder).Encode", + "callee_pkg": "encoding/json", + "caller_file": "oauth.go", + "caller_line": 399 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401InvalidToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.NewEncoder", + "callee_pkg": "encoding/json", + "caller_file": "oauth.go", + "caller_line": 399 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401InvalidToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Set", + "callee_pkg": "net/http", + "caller_file": "oauth.go", + "caller_line": 399 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401InvalidToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 399 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).setCachedToken", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).ValidateToken", + "callee_pkg": "provider", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).ValidateToken", + "callee_pkg": "provider", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).getCachedToken", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(time.Time).Add", + "callee_pkg": "time", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "time.Now", + "callee_pkg": "time", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/sha256.Sum256", + "callee_pkg": "crypto/sha256", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 113 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.Server).WrapHandlerFunc", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).WrapHandler", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 312 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).deleteExpiredToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*sync.RWMutex).Lock", + "callee_pkg": "sync", + "caller_file": "cache.go", + "caller_line": 46 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).deleteExpiredToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(time.Time).After", + "callee_pkg": "time", + "caller_file": "cache.go", + "caller_line": 46 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).deleteExpiredToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "time.Now", + "callee_pkg": "time", + "caller_file": "cache.go", + "caller_line": 46 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).deleteExpiredToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*sync.RWMutex).Unlock", + "callee_pkg": "sync", + "caller_file": "cache.go", + "caller_line": 46 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).getCachedToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "time.Now", + "callee_pkg": "time", + "caller_file": "cache.go", + "caller_line": 26 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).getCachedToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*sync.RWMutex).RUnlock", + "callee_pkg": "sync", + "caller_file": "cache.go", + "caller_line": 26 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).getCachedToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).deleteExpiredToken", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "cache.go", + "caller_line": 26 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).getCachedToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*sync.RWMutex).RLock", + "callee_pkg": "sync", + "caller_file": "cache.go", + "caller_line": 26 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).getCachedToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(time.Time).After", + "callee_pkg": "time", + "caller_file": "cache.go", + "caller_line": 26 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).setCachedToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*sync.RWMutex).Lock", + "callee_pkg": "sync", + "caller_file": "cache.go", + "caller_line": 56 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.TokenCache).setCachedToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*sync.RWMutex).Unlock", + "callee_pkg": "sync", + "caller_file": "cache.go", + "caller_line": 56 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Debug", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "logger.go", + "caller_line": 32 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "logger.go", + "caller_line": 44 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "logger.go", + "caller_line": 36 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Warn", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "logger.go", + "caller_line": 40 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "io.ReadAll", + "callee_pkg": "io", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip$1", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.Contains", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/url.Values).Encode", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/url.ParseQuery", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/url.Values).Get", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/url.Values).Set", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "io.NopCloser", + "callee_pkg": "io", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.NewReader", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 648 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.AutoDetectServerURL", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "config.go", + "caller_line": 262 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc$1", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.TrimSpace", + "callee_pkg": "strings", + "caller_file": "middleware.go", + "caller_line": 121 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc$1", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(net/http.Header).Get", + "callee_pkg": "net/http", + "caller_file": "middleware.go", + "caller_line": 121 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc$1", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.ToLower", + "callee_pkg": "strings", + "caller_file": "middleware.go", + "caller_line": 121 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc$1", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.HasPrefix", + "callee_pkg": "strings", + "caller_file": "middleware.go", + "caller_line": 121 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc$1", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.WithOAuthToken", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "middleware.go", + "caller_line": 121 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc$1", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "middleware.go", + "caller_line": 121 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateOAuth2Handler", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2ConfigFromConfig", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 147 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateOAuth2Handler", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2Handler", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 147 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.CreateRequestAuthHook$1", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "log.Printf", + "callee_pkg": "log", + "caller_file": "middleware.go", + "caller_line": 152 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithMode", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithServerURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithClientID", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithAudience", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithIssuer", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithRedirectURIs", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithProvider", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithClientSecret", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.getEnv", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).Build", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.ConfigBuilder).WithJWTSecret", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewConfigBuilder", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.AutoDetectServerURL", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 271 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2ConfigFromConfig", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Sprintf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 164 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2ConfigFromConfig", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.getEnv", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 164 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2Handler", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2Handler", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/rand.Read", + "callee_pkg": "crypto/rand", + "caller_file": "handlers.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2Handler", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Warn", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2Handler", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewOAuth2Handler", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.discoverOIDCEndpoints", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "handlers.go", + "caller_line": 66 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.CreateOAuth2Handler", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.createValidator", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Config).Validate", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 44 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.OAuthMiddleware", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).Middleware", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "middleware.go", + "caller_line": 86 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.SetupOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.createValidator", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 102 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.SetupOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "config.go", + "caller_line": 102 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.SetupOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "config.go", + "caller_line": 102 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.WithOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "oauth.go", + "caller_line": 438 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.WithOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 438 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.WithOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/mark3labs/mcp-go/server.WithToolHandlerMiddleware", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "oauth.go", + "caller_line": 438 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.WithOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).Middleware", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 438 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.WithOAuth", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).RegisterHandlers", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "oauth.go", + "caller_line": 438 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.WithOAuthToken", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "context.WithValue", + "callee_pkg": "context", + "caller_file": "context.go", + "caller_line": 14 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.WithUser", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "context.WithValue", + "callee_pkg": "context", + "caller_file": "context.go", + "caller_line": 25 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.createValidator", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "callee_pkg": "provider", + "caller_file": "config.go", + "caller_line": 119 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.createValidator", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).Initialize", + "callee_pkg": "provider", + "caller_file": "config.go", + "caller_line": 119 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.createValidator", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "config.go", + "caller_line": 119 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.discoverOIDCEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/coreos/go-oidc/v3/oidc.NewProvider", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "handlers.go", + "caller_line": 131 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.discoverOIDCEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/coreos/go-oidc/v3/oidc.ClientContext", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "handlers.go", + "caller_line": 131 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.discoverOIDCEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "context.WithTimeout", + "callee_pkg": "context", + "caller_file": "handlers.go", + "caller_line": 131 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.discoverOIDCEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "context.Background", + "callee_pkg": "context", + "caller_file": "handlers.go", + "caller_line": 131 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.discoverOIDCEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "handlers.go", + "caller_line": 131 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.discoverOIDCEndpoints", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*github.com/coreos/go-oidc/v3/oidc.Provider).Endpoint", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "handlers.go", + "caller_line": 131 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.getEnv", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "os.LookupEnv", + "callee_pkg": "os", + "caller_file": "handlers.go", + "caller_line": 684 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "callee_pkg": "provider" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/base64.init", + "callee_pkg": "encoding/base64" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/hex.init", + "callee_pkg": "encoding/hex" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "encoding/json.init", + "callee_pkg": "encoding/json" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "io.init", + "callee_pkg": "io" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/mark3labs/mcp-go/server.init", + "callee_pkg": "github.com/mark3labs/mcp-go/server" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "time.init", + "callee_pkg": "time" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/mark3labs/mcp-go/mcp.init", + "callee_pkg": "github.com/mark3labs/mcp-go/mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "golang.org/x/oauth2.init", + "callee_pkg": "golang.org/x/oauth2" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "github.com/coreos/go-oidc/v3/oidc.init", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.init", + "callee_pkg": "strings" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "os.init", + "callee_pkg": "os" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "log.init", + "callee_pkg": "log" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/url.init", + "callee_pkg": "net/url" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/tls.init", + "callee_pkg": "crypto/tls" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "sync.init", + "callee_pkg": "sync" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/sha256.init", + "callee_pkg": "crypto/sha256" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "context.init", + "callee_pkg": "context" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/hmac.init", + "callee_pkg": "crypto/hmac" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.init", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "crypto/rand.init", + "callee_pkg": "crypto/rand" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.isLocalhostURI", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "(*net/url.URL).Hostname", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 761 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.isLocalhostURI", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "strings.ToLower", + "callee_pkg": "strings", + "caller_file": "handlers.go", + "caller_line": 761 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy.isLocalhostURI", + "caller_pkg": "github.com/Vungle/oauth-mcp-proxy", + "callee": "net/url.Parse", + "callee_pkg": "net/url", + "caller_file": "handlers.go", + "caller_line": 761 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.WithOAuth", + "caller_pkg": "mark3labs", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).RegisterHandlers", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mark3labs/oauth.go", + "caller_line": 43 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.WithOAuth", + "caller_pkg": "mark3labs", + "callee": "github.com/Vungle/oauth-mcp-proxy/mark3labs.NewMiddleware", + "callee_pkg": "mark3labs", + "caller_file": "mark3labs/oauth.go", + "caller_line": 43 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.WithOAuth", + "caller_pkg": "mark3labs", + "callee": "github.com/mark3labs/mcp-go/server.WithToolHandlerMiddleware", + "callee_pkg": "github.com/mark3labs/mcp-go/server", + "caller_file": "mark3labs/oauth.go", + "caller_line": 43 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.WithOAuth", + "caller_pkg": "mark3labs", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "mark3labs/oauth.go", + "caller_line": 43 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.WithOAuth", + "caller_pkg": "mark3labs", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mark3labs/oauth.go", + "caller_line": 43 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "caller_pkg": "mark3labs", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "caller_pkg": "mark3labs", + "callee": "github.com/mark3labs/mcp-go/mcp.init", + "callee_pkg": "github.com/mark3labs/mcp-go/mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "caller_pkg": "mark3labs", + "callee": "github.com/Vungle/oauth-mcp-proxy.init", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "caller_pkg": "mark3labs", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "caller_pkg": "mark3labs", + "callee": "github.com/mark3labs/mcp-go/server.init", + "callee_pkg": "github.com/mark3labs/mcp-go/server" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mark3labs.init", + "caller_pkg": "mark3labs", + "callee": "context.init", + "callee_pkg": "context" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth", + "caller_pkg": "mcp", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).RegisterHandlers", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mcp/oauth.go", + "caller_line": 49 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth", + "caller_pkg": "mcp", + "callee": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mcp/oauth.go", + "caller_line": 49 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth", + "caller_pkg": "mcp", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.NewStreamableHTTPHandler", + "callee_pkg": "github.com/modelcontextprotocol/go-sdk/mcp", + "caller_file": "mcp/oauth.go", + "caller_line": 49 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth", + "caller_pkg": "mcp", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "mcp/oauth.go", + "caller_line": 49 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).ValidateTokenCached", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "github.com/Vungle/oauth-mcp-proxy.WithUser", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "github.com/Vungle/oauth-mcp-proxy.WithOAuthToken", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "(*net/http.Request).WithContext", + "callee_pkg": "net/http", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "(*net/http.Request).Context", + "callee_pkg": "net/http", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "strings.TrimSpace", + "callee_pkg": "strings", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401InvalidToken", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "strings.HasPrefix", + "callee_pkg": "strings", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.Server).Return401", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "strings.ToLower", + "callee_pkg": "strings", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "(net/http.Header).Get", + "callee_pkg": "net/http", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "caller_pkg": "mcp", + "callee": "(*github.com/modelcontextprotocol/go-sdk/mcp.StreamableHTTPHandler).ServeHTTP", + "callee_pkg": "github.com/modelcontextprotocol/go-sdk/mcp", + "caller_file": "mcp/oauth.go", + "caller_line": 61 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.init", + "caller_pkg": "mcp", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.init", + "caller_pkg": "mcp", + "callee": "github.com/modelcontextprotocol/go-sdk/mcp.init", + "callee_pkg": "github.com/modelcontextprotocol/go-sdk/mcp" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.init", + "caller_pkg": "mcp", + "callee": "github.com/Vungle/oauth-mcp-proxy.init", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.init", + "caller_pkg": "mcp", + "callee": "strings.init", + "callee_pkg": "strings" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/mcp.init", + "caller_pkg": "mcp", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).Initialize", + "caller_pkg": "provider", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "provider/provider.go", + "caller_line": 62 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).Initialize", + "caller_pkg": "provider", + "callee": "(*sync.Once).Do", + "callee_pkg": "sync", + "caller_file": "provider/provider.go", + "caller_line": 62 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).validateAudience", + "callee_pkg": "provider", + "caller_file": "provider/provider.go", + "caller_line": 80 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "github.com/golang-jwt/jwt/v5.Parse", + "callee_pkg": "github.com/golang-jwt/jwt/v5", + "caller_file": "provider/provider.go", + "caller_line": 80 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "github.com/Vungle/oauth-mcp-proxy/provider.getStringClaim", + "callee_pkg": "provider", + "caller_file": "provider/provider.go", + "caller_line": 80 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "strings.TrimPrefix", + "callee_pkg": "strings", + "caller_file": "provider/provider.go", + "caller_line": 80 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "github.com/Vungle/oauth-mcp-proxy/provider.validateTokenClaims", + "callee_pkg": "provider", + "caller_file": "provider/provider.go", + "caller_line": 80 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "provider/provider.go", + "caller_line": 80 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.HMACValidator).validateAudience", + "caller_pkg": "provider", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "provider/provider.go", + "caller_line": 132 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "(*github.com/coreos/go-oidc/v3/oidc.Provider).Verifier", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "github.com/coreos/go-oidc/v3/oidc.ClientContext", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "(*github.com/Vungle/oauth-mcp-proxy/provider.noOpLogger).Info", + "callee_pkg": "provider", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "github.com/coreos/go-oidc/v3/oidc.NewProvider", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callee_pkg": "github.com/Vungle/oauth-mcp-proxy", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "context.WithTimeout", + "callee_pkg": "context", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "context.Background", + "callee_pkg": "context", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).Initialize", + "caller_pkg": "provider", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "provider/provider.go", + "caller_line": 161 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "(*github.com/coreos/go-oidc/v3/oidc.IDTokenVerifier).Verify", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "provider/provider.go", + "caller_line": 220 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "provider/provider.go", + "caller_line": 220 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).validateAudience", + "callee_pkg": "provider", + "caller_file": "provider/provider.go", + "caller_line": 220 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "(*github.com/coreos/go-oidc/v3/oidc.IDToken).Claims", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc", + "caller_file": "provider/provider.go", + "caller_line": 220 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "context.WithTimeout", + "callee_pkg": "context", + "caller_file": "provider/provider.go", + "caller_line": 220 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).ValidateToken", + "caller_pkg": "provider", + "callee": "strings.TrimPrefix", + "callee_pkg": "strings", + "caller_file": "provider/provider.go", + "caller_line": 220 + }, + { + "caller": "(*github.com/Vungle/oauth-mcp-proxy/provider.OIDCValidator).validateAudience", + "caller_pkg": "provider", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "provider/provider.go", + "caller_line": 272 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "github.com/golang-jwt/jwt/v5.init", + "callee_pkg": "github.com/golang-jwt/jwt/v5" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "net/http.init", + "callee_pkg": "net/http" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "fmt.init", + "callee_pkg": "fmt" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "strings.init", + "callee_pkg": "strings" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "context.init", + "callee_pkg": "context" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "crypto/tls.init", + "callee_pkg": "crypto/tls" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "sync.init", + "callee_pkg": "sync" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "time.init", + "callee_pkg": "time" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.init", + "caller_pkg": "provider", + "callee": "github.com/coreos/go-oidc/v3/oidc.init", + "callee_pkg": "github.com/coreos/go-oidc/v3/oidc" + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.validateTokenClaims", + "caller_pkg": "provider", + "callee": "(time.Time).Unix", + "callee_pkg": "time", + "caller_file": "provider/provider.go", + "caller_line": 301 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.validateTokenClaims", + "caller_pkg": "provider", + "callee": "fmt.Errorf", + "callee_pkg": "fmt", + "caller_file": "provider/provider.go", + "caller_line": 301 + }, + { + "caller": "github.com/Vungle/oauth-mcp-proxy/provider.validateTokenClaims", + "caller_pkg": "provider", + "callee": "time.Now", + "callee_pkg": "time", + "caller_file": "provider/provider.go", + "caller_line": 301 + } + ], + "entry_points": [ + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main", + "package": "examples/mark3labs/advanced", + "file": "examples/mark3labs/advanced/main.go", + "line": 16, + "callers": 0, + "callees": 27 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main", + "package": "examples/official/advanced", + "file": "examples/official/advanced/main.go", + "line": 16, + "callers": 0, + "callees": 25 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorize", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "handlers.go", + "line": 268, + "callers": 0, + "callees": 18 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleToken", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "handlers.go", + "line": 485, + "callers": 0, + "callees": 17 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main", + "package": "examples/mark3labs/simple", + "file": "examples/mark3labs/simple/main.go", + "line": 16, + "callers": 0, + "callees": 16 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.FromEnv", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "config.go", + "line": 271, + "callers": 0, + "callees": 13 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleCallback", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "handlers.go", + "line": 410, + "callers": 0, + "callees": 13 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/mcp.WithOAuth$2", + "package": "mcp", + "file": "mcp/oauth.go", + "line": 61, + "callers": 0, + "callees": 12 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleRegister", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "metadata.go", + "line": 152, + "callers": 0, + "callees": 12 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main", + "package": "examples/official/simple", + "file": "examples/official/simple/main.go", + "line": 15, + "callers": 0, + "callees": 11 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.pkceTransport).RoundTrip", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "handlers.go", + "line": 648, + "callers": 0, + "callees": 9 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.init", + "package": "examples/official/advanced", + "callers": 0, + "callees": 9 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.init", + "package": "examples/mark3labs/simple", + "callers": 0, + "callees": 9 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.init", + "package": "examples/mark3labs/advanced", + "callers": 0, + "callees": 9 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleJWKS", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "handlers.go", + "line": 199, + "callers": 0, + "callees": 8 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.init", + "package": "examples/official/simple", + "callers": 0, + "callees": 8 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleOIDCDiscovery", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "metadata.go", + "line": 222, + "callers": 0, + "callees": 7 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleMetadata", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "metadata.go", + "line": 12, + "callers": 0, + "callees": 7 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.CreateHTTPContextFunc$1", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "middleware.go", + "line": 121, + "callers": 0, + "callees": 6 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$3", + "package": "examples/official/advanced", + "file": "examples/official/advanced/main.go", + "line": 91, + "callers": 0, + "callees": 6 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleAuthorizationServerMetadata", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "metadata.go", + "line": 81, + "callers": 0, + "callees": 6 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.OAuth2Handler).HandleProtectedResourceMetadata", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "metadata.go", + "line": 113, + "callers": 0, + "callees": 6 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.WithOAuth", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "oauth.go", + "line": 438, + "callers": 0, + "callees": 5 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$1", + "package": "examples/official/advanced", + "file": "examples/official/advanced/main.go", + "line": 45, + "callers": 0, + "callees": 4 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$3", + "package": "examples/mark3labs/advanced", + "file": "examples/mark3labs/advanced/main.go", + "line": 85, + "callers": 0, + "callees": 4 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/advanced.main$2", + "package": "examples/mark3labs/advanced", + "file": "examples/mark3labs/advanced/main.go", + "line": 70, + "callers": 0, + "callees": 4 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/mark3labs/simple.main$1", + "package": "examples/mark3labs/simple", + "file": "examples/mark3labs/simple/main.go", + "line": 44, + "callers": 0, + "callees": 4 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/advanced.main$2", + "package": "examples/official/advanced", + "file": "examples/official/advanced/main.go", + "line": 66, + "callers": 0, + "callees": 4 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.SetupOAuth", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "config.go", + "line": 102, + "callers": 0, + "callees": 3 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy/examples/official/simple.main$1", + "package": "examples/official/simple", + "file": "examples/official/simple/main.go", + "line": 31, + "callers": 0, + "callees": 3 + } + ], + "most_called": [ + { + "name": "fmt.Sprintf", + "package": "fmt", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/fmt/print.go", + "line": 237, + "callers": 29, + "callees": 0 + }, + { + "name": "fmt.Errorf", + "package": "fmt", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/fmt/errors.go", + "line": 23, + "callers": 25, + "callees": 0 + }, + { + "name": "log.Printf", + "package": "log", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/log/log.go", + "line": 407, + "callers": 13, + "callees": 0 + }, + { + "name": "(net/http.Header).Set", + "package": "net/http", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/net/http/header.go", + "line": 39, + "callers": 13, + "callees": 0 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "logger.go", + "line": 36, + "callers": 11, + "callees": 1 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "logger.go", + "line": 44, + "callers": 11, + "callees": 1 + }, + { + "name": "net/http.Error", + "package": "net/http", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/net/http/server.go", + "line": 2301, + "callers": 9, + "callees": 0 + }, + { + "name": "encoding/json.NewEncoder", + "package": "encoding/json", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/encoding/json/stream.go", + "line": 194, + "callers": 8, + "callees": 0 + }, + { + "name": "(*encoding/json.Encoder).Encode", + "package": "encoding/json", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/encoding/json/stream.go", + "line": 204, + "callers": 8, + "callees": 0 + }, + { + "name": "fmt.init", + "package": "fmt", + "callers": 8, + "callees": 0 + }, + { + "name": "net/http.init", + "package": "net/http", + "callers": 8, + "callees": 0 + }, + { + "name": "context.init", + "package": "context", + "callers": 7, + "callees": 0 + }, + { + "name": "strings.TrimSpace", + "package": "strings", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/strings/strings.go", + "line": 1091, + "callers": 6, + "callees": 0 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.init", + "package": "github.com/Vungle/oauth-mcp-proxy", + "callers": 6, + "callees": 22 + }, + { + "name": "time.Now", + "package": "time", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/time/time.go", + "line": 1347, + "callers": 6, + "callees": 0 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.GetUserFromContext", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "context.go", + "line": 42, + "callers": 6, + "callees": 0 + }, + { + "name": "strings.Contains", + "package": "strings", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/strings/strings.go", + "line": 62, + "callers": 5, + "callees": 0 + }, + { + "name": "os.init", + "package": "os", + "callers": 5, + "callees": 0 + }, + { + "name": "log.init", + "package": "log", + "callers": 5, + "callees": 0 + }, + { + "name": "net/http.NewServeMux", + "package": "net/http", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/net/http/server.go", + "line": 2583, + "callers": 4, + "callees": 0 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.Server).GetProtectedResourceMetadataURL", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "oauth.go", + "line": 142, + "callers": 4, + "callees": 1 + }, + { + "name": "net/http.ListenAndServe", + "package": "net/http", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/net/http/server.go", + "line": 3673, + "callers": 4, + "callees": 0 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.truncateString", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "handlers.go", + "line": 634, + "callers": 4, + "callees": 0 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Warn", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "logger.go", + "line": 40, + "callers": 4, + "callees": 1 + }, + { + "name": "log.Fatalf", + "package": "log", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/log/log.go", + "line": 430, + "callers": 4, + "callees": 0 + }, + { + "name": "github.com/mark3labs/mcp-go/mcp.init", + "package": "github.com/mark3labs/mcp-go/mcp", + "callers": 4, + "callees": 0 + }, + { + "name": "github.com/mark3labs/mcp-go/server.init", + "package": "github.com/mark3labs/mcp-go/server", + "callers": 4, + "callees": 0 + }, + { + "name": "log.Println", + "package": "log", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/log/log.go", + "line": 415, + "callers": 3, + "callees": 0 + }, + { + "name": "github.com/Vungle/oauth-mcp-proxy.NewServer", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "oauth.go", + "line": 44, + "callers": 3, + "callees": 4 + }, + { + "name": "(*net/http.ServeMux).HandleFunc", + "package": "net/http", + "file": "../../../../../../opt/homebrew/Cellar/go/1.26.0/libexec/src/net/http/server.go", + "line": 2852, + "callers": 3, + "callees": 0 + } + ], + "dead_code": null, + "package_stats": { + "examples/mark3labs/advanced": { + "functions": 0, + "internal_edges": 2, + "incoming_edges": 0, + "outgoing_edges": 18 + }, + "examples/mark3labs/simple": { + "functions": 0, + "internal_edges": 1, + "incoming_edges": 0, + "outgoing_edges": 6 + }, + "examples/official/advanced": { + "functions": 0, + "internal_edges": 1, + "incoming_edges": 0, + "outgoing_edges": 18 + }, + "examples/official/simple": { + "functions": 0, + "internal_edges": 1, + "incoming_edges": 0, + "outgoing_edges": 5 + }, + "github.com/Vungle/oauth-mcp-proxy": { + "functions": 0, + "internal_edges": 87, + "incoming_edges": 51, + "outgoing_edges": 5 + }, + "mark3labs": { + "functions": 0, + "internal_edges": 1, + "incoming_edges": 4, + "outgoing_edges": 3 + }, + "mcp": { + "functions": 0, + "internal_edges": 0, + "incoming_edges": 4, + "outgoing_edges": 8 + }, + "provider": { + "functions": 0, + "internal_edges": 5, + "incoming_edges": 5, + "outgoing_edges": 1 + } + } +} diff --git a/.claude/knowledge/computed/churn-leaders.md b/.claude/knowledge/computed/churn-leaders.md new file mode 100644 index 0000000..1460be0 --- /dev/null +++ b/.claude/knowledge/computed/churn-leaders.md @@ -0,0 +1,38 @@ +# Churn Leaders +# Auto-generated — do not edit manually + +## Files With Most Lines Changed (last 90 days) + +| Rank | File | Lines Added | Lines Removed | Net | +|------|------|------------|--------------|-----| +| - | `.claude/knowledge/computed/call-graph.json` | +4277 | -0 | 4277 | +| - | `.claude/knowledge/computed/type-index.json` | +719 | -0 | 719 | +| - | `.claude/scripts/extract-call-graph.go` | +467 | -0 | 467 | +| - | `.claude/scripts/extract-concurrency.go` | +435 | -0 | 435 | +| - | `.claude/knowledge/computed/git-intelligence.json` | +400 | -376 | 24 | +| - | `.claude/knowledge/computed/summary.json` | +396 | -0 | 396 | +| - | `.claude/knowledge/computed/api-surface.json` | +380 | -0 | 380 | +| - | `.claude/scripts/extract-types.go` | +314 | -0 | 314 | +| - | `.claude/scripts/extract-interfaces.go` | +269 | -0 | 269 | +| - | `.claude/scripts/extract-api-surface.go` | +252 | -0 | 252 | +| - | `.claude/scripts/internal-deps.go` | +207 | -0 | 207 | +| - | `.claude/knowledge/computed/concurrency-map.json` | +183 | -0 | 183 | +| - | `.claude/scripts/generate-summary.py` | +162 | -0 | 162 | +| - | `.claude/scripts/generate-git-intelligence.sh` | +161 | -0 | 161 | +| - | `.claude/scripts/generate-claude-knowledge.sh` | +134 | -0 | 134 | +| - | `.claude/knowledge/computed/internal-deps.json` | +104 | -0 | 104 | +| - | `.claude/knowledge/computed/interface-map.json` | +104 | -0 | 104 | +| - | `.claude/knowledge/adrs/004-provider-abstraction.md` | +77 | -0 | 77 | +| - | `.claude/knowledge/adrs/005-context-based-user-propagation.md` | +75 | -0 | 75 | +| - | `.claude/knowledge/computed/go-mod-graph.txt` | +68 | -0 | 68 | +| - | `.claude/knowledge/adrs/003-token-caching-strategy.md` | +67 | -0 | 67 | +| - | `.claude/conventions.yml` | +66 | -0 | 66 | +| - | `.claude/knowledge/adrs/002-dual-mode-auth.md` | +60 | -0 | 60 | +| - | `.claude/knowledge/adrs/001-sdk-agnostic-core.md` | +47 | -0 | 47 | +| - | `.claude/review-rules.yml` | +41 | -0 | 41 | +| - | `.claude/knowledge/computed/meta.yml` | +16 | -0 | 16 | +| - | `.claude/scripts/go.mod` | +10 | -0 | 10 | +| - | `.claude/scripts/go.sum` | +8 | -0 | 8 | + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 2 | Authors: 0_ diff --git a/.claude/knowledge/computed/co-change-clusters.md b/.claude/knowledge/computed/co-change-clusters.md new file mode 100644 index 0000000..69aeafc --- /dev/null +++ b/.claude/knowledge/computed/co-change-clusters.md @@ -0,0 +1,8 @@ +# Co-Change Clusters +# Auto-generated — do not edit manually + +## Files That Frequently Change Together (last 90 days) + + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 2 | Authors: 0_ diff --git a/.claude/knowledge/computed/concurrency-map.json b/.claude/knowledge/computed/concurrency-map.json new file mode 100644 index 0000000..437a6b1 --- /dev/null +++ b/.claude/knowledge/computed/concurrency-map.json @@ -0,0 +1,183 @@ +{ + "generated": "2026-03-20T03:16:46Z", + "module": "github.com/Vungle/oauth-mcp-proxy", + "summary": { + "total_goroutines": 1, + "total_channels": 0, + "total_mutexes": 11, + "total_contexts": 11, + "packages_with_concurrency": 2 + }, + "goroutines": [ + { + "file": "cache.go", + "line": 37, + "function": "*TokenCache.getCachedToken", + "expr": "tc.deleteExpiredToken" + } + ], + "channels": null, + "mutexes": [ + { + "file": "provider/provider.go", + "line": 50, + "name": "secretOnce", + "type": "Once", + "action": "declaration" + }, + { + "file": "provider/provider.go", + "line": 63, + "name": "v.secretOnce", + "type": "Once", + "action": "Do" + }, + { + "file": "cache.go", + "line": 15, + "name": "mu", + "type": "RWMutex", + "action": "declaration" + }, + { + "file": "cache.go", + "line": 27, + "name": "tc.mu", + "type": "RWMutex", + "action": "RLock" + }, + { + "file": "cache.go", + "line": 31, + "name": "tc.mu", + "type": "RWMutex", + "action": "RUnlock" + }, + { + "file": "cache.go", + "line": 36, + "name": "tc.mu", + "type": "RWMutex", + "action": "RUnlock" + }, + { + "file": "cache.go", + "line": 41, + "name": "tc.mu", + "type": "RWMutex", + "action": "RUnlock" + }, + { + "file": "cache.go", + "line": 47, + "name": "tc.mu", + "type": "Mutex", + "action": "Lock" + }, + { + "file": "cache.go", + "line": 48, + "name": "tc.mu", + "type": "Mutex", + "action": "Unlock" + }, + { + "file": "cache.go", + "line": 57, + "name": "tc.mu", + "type": "Mutex", + "action": "Lock" + }, + { + "file": "cache.go", + "line": 58, + "name": "tc.mu", + "type": "Mutex", + "action": "Unlock" + } + ], + "contexts": [ + { + "file": "provider/provider.go", + "line": 176, + "function": "*OIDCValidator.Initialize", + "pattern": "WithTimeout" + }, + { + "file": "provider/provider.go", + "line": 176, + "function": "*OIDCValidator.Initialize", + "pattern": "Background" + }, + { + "file": "provider/provider.go", + "line": 225, + "function": "*OIDCValidator.ValidateToken", + "pattern": "WithTimeout" + }, + { + "file": "context.go", + "line": 15, + "function": "WithOAuthToken", + "pattern": "WithValue" + }, + { + "file": "context.go", + "line": 26, + "function": "WithUser", + "pattern": "WithValue" + }, + { + "file": "handlers.go", + "line": 132, + "function": "discoverOIDCEndpoints", + "pattern": "WithTimeout" + }, + { + "file": "handlers.go", + "line": 132, + "function": "discoverOIDCEndpoints", + "pattern": "Background" + }, + { + "file": "handlers.go", + "line": 551, + "function": "*OAuth2Handler.HandleToken", + "pattern": "Background" + }, + { + "file": "handlers.go", + "line": 562, + "function": "*OAuth2Handler.HandleToken", + "pattern": "WithValue" + }, + { + "file": "middleware.go", + "line": 47, + "function": "*Server.Middleware", + "pattern": "WithValue" + }, + { + "file": "middleware.go", + "line": 68, + "function": "*Server.Middleware", + "pattern": "WithValue" + } + ], + "package_summary": [ + { + "package": ".", + "goroutines": 1, + "channels": 0, + "mutexes": 9, + "contexts": 8 + }, + { + "package": "provider", + "goroutines": 0, + "channels": 0, + "mutexes": 2, + "contexts": 3 + } + ] +} diff --git a/.claude/knowledge/computed/contributor-summary.md b/.claude/knowledge/computed/contributor-summary.md new file mode 100644 index 0000000..5d045ec --- /dev/null +++ b/.claude/knowledge/computed/contributor-summary.md @@ -0,0 +1,10 @@ +# Contributor Summary +# Auto-generated — do not edit manually + +## Top Contributors (last 90 days) + +| Author | Commits | Files Touched | +|--------|---------|--------------| + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 2 | Authors: 0_ diff --git a/.claude/knowledge/computed/git-intelligence.json b/.claude/knowledge/computed/git-intelligence.json new file mode 100644 index 0000000..c49cd6d --- /dev/null +++ b/.claude/knowledge/computed/git-intelligence.json @@ -0,0 +1,24 @@ +{ + "generated": "2026-03-23T00:00:00Z", + "period_days": 180, + "since": "2025-09-24", + "recent_activity": { + "total_commits": 39, + "unique_authors": 3, + "fix_bug_commits": 11, + "first_commit_date": "2025-10-17", + "last_commit_date": "2026-03-20", + "commits_per_month": { + "2025-10": 27, "2025-11": 5, "2025-12": 6, "2026-03": 1 + } + }, + "top_authors": [ + {"author": "Tommy Nguyen", "commits": 32}, {"author": "Zhong Liang Ong", "commits": 6}, {"author": "Mian Leow", "commits": 1} + ], + "file_churn": [ + {"file": "README.md", "commits": 16}, {"file": "oauth.go", "commits": 10}, {"file": "middleware.go", "commits": 8}, {"file": "config.go", "commits": 8}, {"file": "examples/README.md", "commits": 7}, {"file": "docs/implementation.md", "commits": 7}, {"file": "examples/simple/main.go", "commits": 6}, {"file": "go.mod", "commits": 5}, {"file": "docs/plan.md", "commits": 5}, {"file": "CLAUDE.md", "commits": 5}, {"file": "metadata.go", "commits": 4}, {"file": "handlers.go", "commits": 4}, {"file": "docs/providers/OKTA.md", "commits": 4}, {"file": "docs/providers/HMAC.md", "commits": 4}, {"file": "docs/providers/GOOGLE.md", "commits": 4}, {"file": "docs/providers/AZURE.md", "commits": 4}, {"file": "docs/CLIENT-SETUP.md", "commits": 4}, {"file": ".github/workflows/test.yml", "commits": 4}, {"file": ".github/workflows/cursor.yml", "commits": 4}, {"file": "security_test.go", "commits": 3}, {"file": "provider/provider.go", "commits": 3}, {"file": "mcp/oauth.go", "commits": 3}, {"file": "mark3labs/oauth.go", "commits": 3}, {"file": "examples/mark3labs/simple/main.go", "commits": 3}, {"file": "examples/mark3labs/advanced/main.go", "commits": 3}, {"file": "docs/TROUBLESHOOTING.md", "commits": 3}, {"file": "docs/MIGRATION.md", "commits": 3}, {"file": "docs/mcpoauth-mvp-scope.md", "commits": 3}, {"file": "context_propagation_test.go", "commits": 3}, {"file": "api_test.go", "commits": 3}, {"file": "security_scenarios_test.go", "commits": 2}, {"file": "providers.go", "commits": 2}, {"file": "middleware_compatibility_test.go", "commits": 2}, {"file": "metadata_test.go", "commits": 2}, {"file": "mark3labs/middleware.go", "commits": 2} + ], + "bug_hotspots": [ + {"file": "oauth.go", "fix_commits": 5}, {"file": "README.md", "fix_commits": 4}, {"file": "middleware.go", "fix_commits": 4}, {"file": "examples/simple/main.go", "fix_commits": 3}, {"file": "examples/README.md", "fix_commits": 3}, {"file": "docs/TROUBLESHOOTING.md", "fix_commits": 3}, {"file": "docs/providers/OKTA.md", "fix_commits": 3}, {"file": "docs/providers/HMAC.md", "fix_commits": 3}, {"file": "docs/providers/GOOGLE.md", "fix_commits": 3}, {"file": "docs/providers/AZURE.md", "fix_commits": 3}, {"file": "docs/MIGRATION.md", "fix_commits": 3}, {"file": "docs/CLIENT-SETUP.md", "fix_commits": 3}, {"file": "config.go", "fix_commits": 3}, {"file": "mcp/oauth.go", "fix_commits": 2}, {"file": "mark3labs/oauth.go", "fix_commits": 2}, {"file": "examples/mark3labs/simple/main.go", "fix_commits": 2}, {"file": "examples/mark3labs/advanced/main.go", "fix_commits": 2}, {"file": "docs/SECURITY.md", "fix_commits": 2}, {"file": "docs/oauth.md", "fix_commits": 2}, {"file": "docs/mcpoauth-mvp-scope.md", "fix_commits": 2} + ] +} diff --git a/.claude/knowledge/computed/go-mod-graph.txt b/.claude/knowledge/computed/go-mod-graph.txt new file mode 100644 index 0000000..e83c3da --- /dev/null +++ b/.claude/knowledge/computed/go-mod-graph.txt @@ -0,0 +1,68 @@ +github.com/Vungle/oauth-mcp-proxy github.com/bahlo/generic-list-go@v0.2.0 +github.com/Vungle/oauth-mcp-proxy github.com/buger/jsonparser@v1.1.1 +github.com/Vungle/oauth-mcp-proxy github.com/coreos/go-oidc/v3@v3.16.0 +github.com/Vungle/oauth-mcp-proxy github.com/go-jose/go-jose/v4@v4.1.3 +github.com/Vungle/oauth-mcp-proxy github.com/golang-jwt/jwt/v5@v5.3.0 +github.com/Vungle/oauth-mcp-proxy github.com/google/jsonschema-go@v0.3.0 +github.com/Vungle/oauth-mcp-proxy github.com/google/uuid@v1.6.0 +github.com/Vungle/oauth-mcp-proxy github.com/invopop/jsonschema@v0.13.0 +github.com/Vungle/oauth-mcp-proxy github.com/mailru/easyjson@v0.7.7 +github.com/Vungle/oauth-mcp-proxy github.com/mark3labs/mcp-go@v0.41.1 +github.com/Vungle/oauth-mcp-proxy github.com/modelcontextprotocol/go-sdk@v1.0.0 +github.com/Vungle/oauth-mcp-proxy github.com/spf13/cast@v1.8.0 +github.com/Vungle/oauth-mcp-proxy github.com/wk8/go-ordered-map/v2@v2.1.8 +github.com/Vungle/oauth-mcp-proxy github.com/yosida95/uritemplate/v3@v3.0.2 +github.com/Vungle/oauth-mcp-proxy go@1.24.9 +github.com/Vungle/oauth-mcp-proxy golang.org/x/oauth2@v0.32.0 +github.com/Vungle/oauth-mcp-proxy gopkg.in/yaml.v3@v3.0.1 +github.com/coreos/go-oidc/v3@v3.16.0 github.com/go-jose/go-jose/v4@v4.1.3 +github.com/coreos/go-oidc/v3@v3.16.0 golang.org/x/oauth2@v0.28.0 +github.com/coreos/go-oidc/v3@v3.16.0 go@1.24.0 +github.com/go-jose/go-jose/v4@v4.1.3 go@1.24.0 +github.com/golang-jwt/jwt/v5@v5.3.0 go@1.21 +github.com/google/jsonschema-go@v0.3.0 github.com/google/go-cmp@v0.7.0 +github.com/google/jsonschema-go@v0.3.0 go@1.23.0 +github.com/invopop/jsonschema@v0.13.0 github.com/stretchr/testify@v1.8.1 +github.com/invopop/jsonschema@v0.13.0 github.com/wk8/go-ordered-map/v2@v2.1.8 +github.com/invopop/jsonschema@v0.13.0 github.com/bahlo/generic-list-go@v0.2.0 +github.com/invopop/jsonschema@v0.13.0 github.com/buger/jsonparser@v1.1.1 +github.com/invopop/jsonschema@v0.13.0 github.com/davecgh/go-spew@v1.1.1 +github.com/invopop/jsonschema@v0.13.0 github.com/mailru/easyjson@v0.7.7 +github.com/invopop/jsonschema@v0.13.0 github.com/pmezard/go-difflib@v1.0.0 +github.com/invopop/jsonschema@v0.13.0 gopkg.in/yaml.v3@v3.0.1 +github.com/mailru/easyjson@v0.7.7 github.com/josharian/intern@v1.0.0 +github.com/mark3labs/mcp-go@v0.41.1 github.com/google/uuid@v1.6.0 +github.com/mark3labs/mcp-go@v0.41.1 github.com/invopop/jsonschema@v0.13.0 +github.com/mark3labs/mcp-go@v0.41.1 github.com/spf13/cast@v1.7.1 +github.com/mark3labs/mcp-go@v0.41.1 github.com/stretchr/testify@v1.9.0 +github.com/mark3labs/mcp-go@v0.41.1 github.com/yosida95/uritemplate/v3@v3.0.2 +github.com/mark3labs/mcp-go@v0.41.1 github.com/bahlo/generic-list-go@v0.2.0 +github.com/mark3labs/mcp-go@v0.41.1 github.com/buger/jsonparser@v1.1.1 +github.com/mark3labs/mcp-go@v0.41.1 github.com/davecgh/go-spew@v1.1.1 +github.com/mark3labs/mcp-go@v0.41.1 github.com/mailru/easyjson@v0.7.7 +github.com/mark3labs/mcp-go@v0.41.1 github.com/pmezard/go-difflib@v1.0.0 +github.com/mark3labs/mcp-go@v0.41.1 github.com/wk8/go-ordered-map/v2@v2.1.8 +github.com/mark3labs/mcp-go@v0.41.1 gopkg.in/yaml.v3@v3.0.1 +github.com/mark3labs/mcp-go@v0.41.1 go@1.23 +github.com/modelcontextprotocol/go-sdk@v1.0.0 github.com/golang-jwt/jwt/v5@v5.2.2 +github.com/modelcontextprotocol/go-sdk@v1.0.0 github.com/google/go-cmp@v0.7.0 +github.com/modelcontextprotocol/go-sdk@v1.0.0 github.com/google/jsonschema-go@v0.3.0 +github.com/modelcontextprotocol/go-sdk@v1.0.0 github.com/yosida95/uritemplate/v3@v3.0.2 +github.com/modelcontextprotocol/go-sdk@v1.0.0 golang.org/x/tools@v0.34.0 +github.com/modelcontextprotocol/go-sdk@v1.0.0 go@1.23.0 +github.com/spf13/cast@v1.8.0 github.com/frankban/quicktest@v1.14.6 +github.com/spf13/cast@v1.8.0 github.com/google/go-cmp@v0.5.9 +github.com/spf13/cast@v1.8.0 github.com/kr/pretty@v0.3.1 +github.com/spf13/cast@v1.8.0 github.com/kr/text@v0.2.0 +github.com/spf13/cast@v1.8.0 github.com/rogpeppe/go-internal@v1.9.0 +github.com/wk8/go-ordered-map/v2@v2.1.8 github.com/bahlo/generic-list-go@v0.2.0 +github.com/wk8/go-ordered-map/v2@v2.1.8 github.com/buger/jsonparser@v1.1.1 +github.com/wk8/go-ordered-map/v2@v2.1.8 github.com/mailru/easyjson@v0.7.7 +github.com/wk8/go-ordered-map/v2@v2.1.8 github.com/stretchr/testify@v1.8.1 +github.com/wk8/go-ordered-map/v2@v2.1.8 gopkg.in/yaml.v3@v3.0.1 +github.com/wk8/go-ordered-map/v2@v2.1.8 github.com/davecgh/go-spew@v1.1.1 +github.com/wk8/go-ordered-map/v2@v2.1.8 github.com/pmezard/go-difflib@v1.0.0 +go@1.24.9 toolchain@go1.24.9 +golang.org/x/oauth2@v0.32.0 cloud.google.com/go/compute/metadata@v0.3.0 +golang.org/x/oauth2@v0.32.0 go@1.24.0 +gopkg.in/yaml.v3@v3.0.1 gopkg.in/check.v1@v0.0.0-20161208181325-20d25e280405 diff --git a/.claude/knowledge/computed/hotspot-map.md b/.claude/knowledge/computed/hotspot-map.md new file mode 100644 index 0000000..d79e3c7 --- /dev/null +++ b/.claude/knowledge/computed/hotspot-map.md @@ -0,0 +1,39 @@ +# Git Hotspot Map +# Auto-generated — do not edit manually +# Re-run: .claude/scripts/git-hotspots.sh + +## Most Frequently Changed Files (last 90 days) + +| Rank | File | Commits | Last Changed | +|------|------|---------|-------------| +| - | `.claude/knowledge/computed/git-intelligence.json` | 2 | 2026-03-23 | +| - | `.claude/scripts/internal-deps.go` | 1 | 2026-03-20 | +| - | `.claude/scripts/go.sum` | 1 | 2026-03-20 | +| - | `.claude/scripts/go.mod` | 1 | 2026-03-20 | +| - | `.claude/scripts/generate-summary.py` | 1 | 2026-03-20 | +| - | `.claude/scripts/generate-git-intelligence.sh` | 1 | 2026-03-20 | +| - | `.claude/scripts/generate-claude-knowledge.sh` | 1 | 2026-03-20 | +| - | `.claude/scripts/extract-types.go` | 1 | 2026-03-20 | +| - | `.claude/scripts/extract-interfaces.go` | 1 | 2026-03-20 | +| - | `.claude/scripts/extract-concurrency.go` | 1 | 2026-03-20 | +| - | `.claude/scripts/extract-call-graph.go` | 1 | 2026-03-20 | +| - | `.claude/scripts/extract-api-surface.go` | 1 | 2026-03-20 | +| - | `.claude/review-rules.yml` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/type-index.json` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/summary.json` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/meta.yml` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/internal-deps.json` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/interface-map.json` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/go-mod-graph.txt` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/concurrency-map.json` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/call-graph.json` | 1 | 2026-03-20 | +| - | `.claude/knowledge/computed/api-surface.json` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/005-context-based-user-propagation.md` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/004-provider-abstraction.md` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/003-token-caching-strategy.md` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/002-dual-mode-auth.md` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/001-sdk-agnostic-core.md` | 1 | 2026-03-20 | +| - | `.claude/conventions.yml` | 1 | 2026-03-20 | + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 2 | Authors: 0_ diff --git a/.claude/knowledge/computed/interface-map.json b/.claude/knowledge/computed/interface-map.json new file mode 100644 index 0000000..ca31898 --- /dev/null +++ b/.claude/knowledge/computed/interface-map.json @@ -0,0 +1,104 @@ +{ + "generated": "2026-03-20T03:16:41Z", + "module": "github.com/Vungle/oauth-mcp-proxy", + "interfaces": [ + { + "name": "Logger", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "logger.go", + "line": 22, + "methods": [ + { + "name": "Debug", + "signature": "func(msg string, args ...interface{})" + }, + { + "name": "Error", + "signature": "func(msg string, args ...interface{})" + }, + { + "name": "Info", + "signature": "func(msg string, args ...interface{})" + }, + { + "name": "Warn", + "signature": "func(msg string, args ...interface{})" + } + ], + "implementors": [ + { + "name": "Logger", + "package": "github.com/Vungle/oauth-mcp-proxy/provider", + "file": "provider/provider.go", + "line": 24, + "pointer": false + } + ] + }, + { + "name": "Logger", + "package": "github.com/Vungle/oauth-mcp-proxy/provider", + "file": "provider/provider.go", + "line": 24, + "methods": [ + { + "name": "Debug", + "signature": "func(msg string, args ...interface{})" + }, + { + "name": "Error", + "signature": "func(msg string, args ...interface{})" + }, + { + "name": "Info", + "signature": "func(msg string, args ...interface{})" + }, + { + "name": "Warn", + "signature": "func(msg string, args ...interface{})" + } + ], + "implementors": [ + { + "name": "Logger", + "package": "github.com/Vungle/oauth-mcp-proxy", + "file": "logger.go", + "line": 22, + "pointer": false + } + ] + }, + { + "name": "TokenValidator", + "package": "github.com/Vungle/oauth-mcp-proxy/provider", + "file": "provider/provider.go", + "line": 41, + "methods": [ + { + "name": "Initialize", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy/provider.Config) error" + }, + { + "name": "ValidateToken", + "signature": "func(ctx context.Context, token string) (*github.com/Vungle/oauth-mcp-proxy/provider.User, error)" + } + ], + "implementors": [ + { + "name": "HMACValidator", + "package": "github.com/Vungle/oauth-mcp-proxy/provider", + "file": "provider/provider.go", + "line": 47, + "pointer": true + }, + { + "name": "OIDCValidator", + "package": "github.com/Vungle/oauth-mcp-proxy/provider", + "file": "provider/provider.go", + "line": 54, + "pointer": true + } + ] + } + ] +} diff --git a/.claude/knowledge/computed/internal-deps.json b/.claude/knowledge/computed/internal-deps.json new file mode 100644 index 0000000..e85b4e6 --- /dev/null +++ b/.claude/knowledge/computed/internal-deps.json @@ -0,0 +1,104 @@ +{ + "generated": "2026-03-20T03:16:43Z", + "module": "github.com/Vungle/oauth-mcp-proxy", + "total_packages": 8, + "packages": { + ".": { + "path": ".", + "rel_dir": ".", + "imports": [ + "provider" + ], + "imported_by": [ + "examples/mark3labs/advanced", + "examples/mark3labs/simple", + "examples/official/advanced", + "examples/official/simple", + "mark3labs", + "mcp" + ], + "file_count": 8 + }, + "examples/mark3labs/advanced": { + "path": "examples/mark3labs/advanced", + "rel_dir": "examples/mark3labs/advanced", + "imports": [ + ".", + "mark3labs" + ], + "imported_by": null, + "file_count": 1 + }, + "examples/mark3labs/simple": { + "path": "examples/mark3labs/simple", + "rel_dir": "examples/mark3labs/simple", + "imports": [ + ".", + "mark3labs" + ], + "imported_by": null, + "file_count": 1 + }, + "examples/official/advanced": { + "path": "examples/official/advanced", + "rel_dir": "examples/official/advanced", + "imports": [ + ".", + "mcp" + ], + "imported_by": null, + "file_count": 1 + }, + "examples/official/simple": { + "path": "examples/official/simple", + "rel_dir": "examples/official/simple", + "imports": [ + ".", + "mcp" + ], + "imported_by": null, + "file_count": 1 + }, + "mark3labs": { + "path": "mark3labs", + "rel_dir": "mark3labs", + "imports": [ + "." + ], + "imported_by": [ + "examples/mark3labs/advanced", + "examples/mark3labs/simple" + ], + "file_count": 2 + }, + "mcp": { + "path": "mcp", + "rel_dir": "mcp", + "imports": [ + "." + ], + "imported_by": [ + "examples/official/advanced", + "examples/official/simple" + ], + "file_count": 1 + }, + "provider": { + "path": "provider", + "rel_dir": "provider", + "imports": null, + "imported_by": [ + "." + ], + "file_count": 1 + } + }, + "external_deps": [ + "github.com/coreos/go-oidc/v3/oidc", + "github.com/golang-jwt/jwt/v5", + "github.com/mark3labs/mcp-go/mcp", + "github.com/mark3labs/mcp-go/server", + "github.com/modelcontextprotocol/go-sdk/mcp", + "golang.org/x/oauth2" + ] +} diff --git a/.claude/knowledge/computed/meta.yml b/.claude/knowledge/computed/meta.yml new file mode 100644 index 0000000..fca2452 --- /dev/null +++ b/.claude/knowledge/computed/meta.yml @@ -0,0 +1,16 @@ +# Claude knowledge layer metadata +generated: 2026-03-23T00:00:00Z +source_commit: 1606ff5701a1f16fdee905fa04cb8d3e0e8f15f7 +stale_if_older_than: 7d +artifacts: + api-surface.json: { bytes: 13148, tokens: ~3287 } + call-graph.json: { bytes: 158378, tokens: ~39594 } + concurrency-map.json: { bytes: 3694, tokens: ~923 } + git-intelligence.json: { bytes: 3423, tokens: ~855 } + interface-map.json: { bytes: 2771, tokens: ~692 } + internal-deps.json: { bytes: 2350, tokens: ~587 } + summary.json: { bytes: 8353, tokens: ~2088 } + type-index.json: { bytes: 22141, tokens: ~5535 } + go-mod-graph.txt: { bytes: 4607, tokens: ~1151 } +total_bytes: 222685 +total_tokens: ~55671 diff --git a/.claude/knowledge/computed/summary.json b/.claude/knowledge/computed/summary.json new file mode 100644 index 0000000..0c24aad --- /dev/null +++ b/.claude/knowledge/computed/summary.json @@ -0,0 +1,396 @@ +{ + "generated": "2026-03-20T03:16:48Z", + "description": "Compact index of oauth-mcp-proxy computed knowledge artifacts. Load full artifacts for detail.", + "type_index": { + "total_types": 15, + "total_functions": 19, + "total_packages": 4, + "top_packages": [ + { + "pkg": "github.com/Vungle/oauth-mcp-proxy", + "structs": 8, + "interfaces": 1, + "functions": 16 + }, + { + "pkg": "provider", + "structs": 4, + "interfaces": 2, + "functions": 0 + }, + { + "pkg": "mark3labs", + "structs": 0, + "interfaces": 0, + "functions": 2 + }, + { + "pkg": "mcp", + "structs": 0, + "interfaces": 0, + "functions": 1 + } + ], + "full_artifact": "type-index.json" + }, + "interface_map": { + "total_interfaces": 3, + "top_by_implementors": [ + { + "name": "TokenValidator", + "package": "provider", + "methods": 2, + "implementors": 2 + }, + { + "name": "Logger", + "package": "github.com/Vungle/oauth-mcp-proxy", + "methods": 4, + "implementors": 1 + }, + { + "name": "Logger", + "package": "provider", + "methods": 4, + "implementors": 1 + } + ], + "full_artifact": "interface-map.json" + }, + "internal_deps": { + "total_packages": 8, + "most_depended_on": [ + { + "pkg": ".", + "imported_by_count": 6 + }, + { + "pkg": "mark3labs", + "imported_by_count": 2 + }, + { + "pkg": "mcp", + "imported_by_count": 2 + }, + { + "pkg": "provider", + "imported_by_count": 1 + }, + { + "pkg": "examples/mark3labs/advanced", + "imported_by_count": 0 + }, + { + "pkg": "examples/mark3labs/simple", + "imported_by_count": 0 + }, + { + "pkg": "examples/official/advanced", + "imported_by_count": 0 + }, + { + "pkg": "examples/official/simple", + "imported_by_count": 0 + } + ], + "most_dependencies": [ + { + "pkg": "examples/mark3labs/advanced", + "imports_count": 2 + }, + { + "pkg": "examples/mark3labs/simple", + "imports_count": 2 + }, + { + "pkg": "examples/official/advanced", + "imports_count": 2 + }, + { + "pkg": "examples/official/simple", + "imports_count": 2 + }, + { + "pkg": ".", + "imports_count": 1 + }, + { + "pkg": "mark3labs", + "imports_count": 1 + }, + { + "pkg": "mcp", + "imports_count": 1 + }, + { + "pkg": "provider", + "imports_count": 0 + } + ], + "full_artifact": "internal-deps.json" + }, + "git_intelligence": { + "period_days": 180, + "top_churn_files": [ + { + "file": "state_test.go", + "commits": 1, + "lines_added": 158, + "lines_deleted": 0 + }, + { + "file": "security_test.go", + "commits": 1, + "lines_added": 427, + "lines_deleted": 0 + }, + { + "file": "provider/provider.go", + "commits": 1, + "lines_added": 346, + "lines_deleted": 0 + }, + { + "file": "provider/provider_test.go", + "commits": 1, + "lines_added": 373, + "lines_deleted": 0 + }, + { + "file": "oauth.go", + "commits": 1, + "lines_added": 447, + "lines_deleted": 0 + }, + { + "file": "middleware.go", + "commits": 1, + "lines_added": 158, + "lines_deleted": 0 + }, + { + "file": "middleware_compatibility_test.go", + "commits": 1, + "lines_added": 226, + "lines_deleted": 0 + }, + { + "file": "metadata.go", + "commits": 1, + "lines_added": 322, + "lines_deleted": 0 + }, + { + "file": "metadata_test.go", + "commits": 1, + "lines_added": 231, + "lines_deleted": 0 + }, + { + "file": "mcp/oauth.go", + "commits": 1, + "lines_added": 124, + "lines_deleted": 0 + }, + { + "file": "mark3labs/oauth.go", + "commits": 1, + "lines_added": 52, + "lines_deleted": 0 + }, + { + "file": "mark3labs/middleware.go", + "commits": 1, + "lines_added": 40, + "lines_deleted": 0 + }, + { + "file": "logger.go", + "commits": 1, + "lines_added": 46, + "lines_deleted": 0 + }, + { + "file": "integration_test.go", + "commits": 1, + "lines_added": 351, + "lines_deleted": 0 + }, + { + "file": "handlers.go", + "commits": 1, + "lines_added": 812, + "lines_deleted": 0 + } + ], + "top_bug_hotspots": [ + { + "file": "state_test.go", + "fix_commits": 1 + }, + { + "file": "security_test.go", + "fix_commits": 1 + }, + { + "file": "provider/provider.go", + "fix_commits": 1 + }, + { + "file": "provider/provider_test.go", + "fix_commits": 1 + }, + { + "file": "oauth.go", + "fix_commits": 1 + }, + { + "file": "middleware.go", + "fix_commits": 1 + }, + { + "file": "middleware_compatibility_test.go", + "fix_commits": 1 + }, + { + "file": "metadata.go", + "fix_commits": 1 + }, + { + "file": "metadata_test.go", + "fix_commits": 1 + }, + { + "file": "mcp/oauth.go", + "fix_commits": 1 + } + ], + "full_artifact": "git-intelligence.json" + }, + "api_surface": { + "note": "API surface artifact is small enough to load directly.", + "full_artifact": "api-surface.json" + }, + "concurrency_map": { + "total_goroutines": 1, + "total_channels": 0, + "total_mutexes": 11, + "total_contexts": 11, + "packages_with_concurrency": 2, + "top_packages": [ + { + "package": ".", + "goroutines": 1, + "channels": 0, + "mutexes": 9, + "contexts": 8 + }, + { + "package": "provider", + "goroutines": 0, + "channels": 0, + "mutexes": 2, + "contexts": 3 + } + ], + "full_artifact": "concurrency-map.json" + }, + "call_graph": { + "total_edges": 489, + "total_functions": 234, + "entry_points": 30, + "most_called_top10": [ + { + "name": "fmt.Sprintf", + "callers": 29 + }, + { + "name": "fmt.Errorf", + "callers": 25 + }, + { + "name": "log.Printf", + "callers": 13 + }, + { + "name": "(net/http.Header).Set", + "callers": 13 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Info", + "callers": 11 + }, + { + "name": "(*github.com/Vungle/oauth-mcp-proxy.defaultLogger).Error", + "callers": 11 + }, + { + "name": "net/http.Error", + "callers": 9 + }, + { + "name": "encoding/json.NewEncoder", + "callers": 8 + }, + { + "name": "(*encoding/json.Encoder).Encode", + "callers": 8 + }, + { + "name": "fmt.init", + "callers": 8 + } + ], + "top_packages_by_internal_edges": [ + { + "pkg": "github.com/Vungle/oauth-mcp-proxy", + "internal": 87, + "incoming": 51, + "outgoing": 5 + }, + { + "pkg": "provider", + "internal": 5, + "incoming": 5, + "outgoing": 1 + }, + { + "pkg": "examples/mark3labs/advanced", + "internal": 2, + "incoming": 0, + "outgoing": 18 + }, + { + "pkg": "examples/mark3labs/simple", + "internal": 1, + "incoming": 0, + "outgoing": 6 + }, + { + "pkg": "examples/official/advanced", + "internal": 1, + "incoming": 0, + "outgoing": 18 + }, + { + "pkg": "examples/official/simple", + "internal": 1, + "incoming": 0, + "outgoing": 5 + }, + { + "pkg": "mark3labs", + "internal": 1, + "incoming": 4, + "outgoing": 3 + }, + { + "pkg": "mcp", + "internal": 0, + "incoming": 4, + "outgoing": 8 + } + ], + "full_artifact": "call-graph.json" + } +} diff --git a/.claude/knowledge/computed/type-index.json b/.claude/knowledge/computed/type-index.json new file mode 100644 index 0000000..1fa5b5e --- /dev/null +++ b/.claude/knowledge/computed/type-index.json @@ -0,0 +1,719 @@ +{ + "generated": "2026-03-20T03:16:42Z", + "module": "github.com/Vungle/oauth-mcp-proxy", + "total_types": 15, + "total_functions": 19, + "packages": [ + { + "path": "github.com/Vungle/oauth-mcp-proxy", + "rel_dir": ".", + "structs": [ + { + "name": "CachedToken", + "file": "cache.go", + "line": 20, + "fields": [ + { + "name": "User", + "type": "*github.com/Vungle/oauth-mcp-proxy.User" + }, + { + "name": "ExpiresAt", + "type": "time.Time" + } + ], + "methods": null + }, + { + "name": "Config", + "file": "config.go", + "line": 10, + "fields": [ + { + "name": "Mode", + "type": "string" + }, + { + "name": "Provider", + "type": "string" + }, + { + "name": "RedirectURIs", + "type": "string" + }, + { + "name": "Issuer", + "type": "string" + }, + { + "name": "Audience", + "type": "string" + }, + { + "name": "ClientID", + "type": "string" + }, + { + "name": "ClientSecret", + "type": "string" + }, + { + "name": "ServerURL", + "type": "string" + }, + { + "name": "JWTSecret", + "type": "[]byte" + }, + { + "name": "Logger", + "type": "github.com/Vungle/oauth-mcp-proxy.Logger" + } + ], + "methods": [ + { + "name": "Validate", + "signature": "func() error", + "receiver": "*T" + } + ] + }, + { + "name": "ConfigBuilder", + "file": "config.go", + "line": 156, + "fields": null, + "methods": [ + { + "name": "WithMode", + "signature": "func(mode string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithProvider", + "signature": "func(provider string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithRedirectURIs", + "signature": "func(uris string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithIssuer", + "signature": "func(issuer string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithAudience", + "signature": "func(audience string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithClientID", + "signature": "func(clientID string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithClientSecret", + "signature": "func(secret string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithJWTSecret", + "signature": "func(secret []byte) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithLogger", + "signature": "func(logger github.com/Vungle/oauth-mcp-proxy.Logger) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithServerURL", + "signature": "func(url string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithHost", + "signature": "func(host string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithPort", + "signature": "func(port string) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "WithTLS", + "signature": "func(useTLS bool) *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "receiver": "*T" + }, + { + "name": "Build", + "signature": "func() (*github.com/Vungle/oauth-mcp-proxy.Config, error)", + "receiver": "*T" + } + ] + }, + { + "name": "Endpoint", + "file": "oauth.go", + "line": 172, + "fields": [ + { + "name": "Path", + "type": "string" + }, + { + "name": "Description", + "type": "string" + } + ], + "methods": null + }, + { + "name": "OAuth2Config", + "file": "handlers.go", + "line": 38, + "fields": [ + { + "name": "Enabled", + "type": "bool" + }, + { + "name": "Mode", + "type": "string" + }, + { + "name": "Provider", + "type": "string" + }, + { + "name": "RedirectURIs", + "type": "string" + }, + { + "name": "Issuer", + "type": "string" + }, + { + "name": "Audience", + "type": "string" + }, + { + "name": "ClientID", + "type": "string" + }, + { + "name": "ClientSecret", + "type": "string" + }, + { + "name": "MCPHost", + "type": "string" + }, + { + "name": "MCPPort", + "type": "string" + }, + { + "name": "Scheme", + "type": "string" + }, + { + "name": "MCPURL", + "type": "string" + }, + { + "name": "Version", + "type": "string" + } + ], + "methods": null + }, + { + "name": "OAuth2Handler", + "file": "handlers.go", + "line": 26, + "fields": null, + "methods": [ + { + "name": "GetConfig", + "signature": "func() *github.com/Vungle/oauth-mcp-proxy.OAuth2Config", + "receiver": "*T" + }, + { + "name": "HandleJWKS", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleAuthorize", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleCallback", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleToken", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleMetadata", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleAuthorizationServerMetadata", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleProtectedResourceMetadata", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleRegister", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleCallbackRedirect", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "HandleOIDCDiscovery", + "signature": "func(w net/http.ResponseWriter, r *net/http.Request)", + "receiver": "*T" + }, + { + "name": "GetAuthorizationServerMetadata", + "signature": "func() map[string]interface{}", + "receiver": "*T" + } + ] + }, + { + "name": "Server", + "file": "oauth.go", + "line": 22, + "fields": null, + "methods": [ + { + "name": "Middleware", + "signature": "func() func(github.com/mark3labs/mcp-go/server.ToolHandlerFunc) github.com/mark3labs/mcp-go/server.ToolHandlerFunc", + "receiver": "*T" + }, + { + "name": "RegisterHandlers", + "signature": "func(mux *net/http.ServeMux)", + "receiver": "*T" + }, + { + "name": "ValidateTokenCached", + "signature": "func(ctx context.Context, token string) (*github.com/Vungle/oauth-mcp-proxy.User, error)", + "receiver": "*T" + }, + { + "name": "GetAuthorizationServerMetadataURL", + "signature": "func() string", + "receiver": "*T" + }, + { + "name": "GetProtectedResourceMetadataURL", + "signature": "func() string", + "receiver": "*T" + }, + { + "name": "GetOIDCDiscoveryURL", + "signature": "func() string", + "receiver": "*T" + }, + { + "name": "GetCallbackURL", + "signature": "func() string", + "receiver": "*T" + }, + { + "name": "GetAuthorizeURL", + "signature": "func() string", + "receiver": "*T" + }, + { + "name": "GetTokenURL", + "signature": "func() string", + "receiver": "*T" + }, + { + "name": "GetRegisterURL", + "signature": "func() string", + "receiver": "*T" + }, + { + "name": "GetAllEndpoints", + "signature": "func() []github.com/Vungle/oauth-mcp-proxy.Endpoint", + "receiver": "*T" + }, + { + "name": "LogStartup", + "signature": "func(useTLS bool)", + "receiver": "*T" + }, + { + "name": "GetStatusString", + "signature": "func(useTLS bool) string", + "receiver": "*T" + }, + { + "name": "GetHTTPServerOptions", + "signature": "func() []github.com/mark3labs/mcp-go/server.StreamableHTTPOption", + "receiver": "*T" + }, + { + "name": "WrapHandler", + "signature": "func(next net/http.Handler) net/http.Handler", + "receiver": "*T" + }, + { + "name": "WrapHandlerFunc", + "signature": "func(next net/http.HandlerFunc) net/http.HandlerFunc", + "receiver": "*T" + }, + { + "name": "WrapMCPEndpoint", + "signature": "func(handler net/http.Handler) net/http.HandlerFunc", + "receiver": "*T" + }, + { + "name": "Return401", + "signature": "func(w net/http.ResponseWriter)", + "receiver": "*T" + }, + { + "name": "Return401InvalidToken", + "signature": "func(w net/http.ResponseWriter)", + "receiver": "*T" + } + ] + }, + { + "name": "TokenCache", + "file": "cache.go", + "line": 14, + "fields": null, + "methods": null + } + ], + "interfaces": [ + { + "name": "Logger", + "file": "logger.go", + "line": 22, + "methods": [ + { + "name": "Debug", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + }, + { + "name": "Error", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + }, + { + "name": "Info", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + }, + { + "name": "Warn", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + } + ] + } + ], + "functions": [ + { + "name": "AutoDetectServerURL", + "signature": "func(host string, port string, useTLS bool) string", + "file": "config.go", + "line": 262 + }, + { + "name": "CreateHTTPContextFunc", + "signature": "func() func(context.Context, *net/http.Request) context.Context", + "file": "middleware.go", + "line": 120 + }, + { + "name": "CreateOAuth2Handler", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config, version string, logger github.com/Vungle/oauth-mcp-proxy.Logger) *github.com/Vungle/oauth-mcp-proxy.OAuth2Handler", + "file": "config.go", + "line": 147 + }, + { + "name": "CreateRequestAuthHook", + "signature": "func(validator provider.TokenValidator) func(context.Context, interface{}, interface{}) error", + "file": "middleware.go", + "line": 151 + }, + { + "name": "FromEnv", + "signature": "func() (*github.com/Vungle/oauth-mcp-proxy.Config, error)", + "file": "config.go", + "line": 271 + }, + { + "name": "GetOAuthToken", + "signature": "func(ctx context.Context) (string, bool)", + "file": "context.go", + "line": 19 + }, + { + "name": "GetUserFromContext", + "signature": "func(ctx context.Context) (*github.com/Vungle/oauth-mcp-proxy.User, bool)", + "file": "context.go", + "line": 42 + }, + { + "name": "NewConfigBuilder", + "signature": "func() *github.com/Vungle/oauth-mcp-proxy.ConfigBuilder", + "file": "config.go", + "line": 164 + }, + { + "name": "NewOAuth2ConfigFromConfig", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config, version string) *github.com/Vungle/oauth-mcp-proxy.OAuth2Config", + "file": "handlers.go", + "line": 164 + }, + { + "name": "NewOAuth2Handler", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.OAuth2Config, logger github.com/Vungle/oauth-mcp-proxy.Logger) *github.com/Vungle/oauth-mcp-proxy.OAuth2Handler", + "file": "handlers.go", + "line": 66 + }, + { + "name": "NewServer", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config) (*github.com/Vungle/oauth-mcp-proxy.Server, error)", + "file": "oauth.go", + "line": 44 + }, + { + "name": "OAuthMiddleware", + "signature": "func(validator provider.TokenValidator, enabled bool) func(github.com/mark3labs/mcp-go/server.ToolHandlerFunc) github.com/mark3labs/mcp-go/server.ToolHandlerFunc", + "file": "middleware.go", + "line": 86 + }, + { + "name": "SetupOAuth", + "signature": "func(cfg *github.com/Vungle/oauth-mcp-proxy.Config) (provider.TokenValidator, error)", + "file": "config.go", + "line": 102 + }, + { + "name": "WithOAuth", + "signature": "func(mux *net/http.ServeMux, cfg *github.com/Vungle/oauth-mcp-proxy.Config) (*github.com/Vungle/oauth-mcp-proxy.Server, github.com/mark3labs/mcp-go/server.ServerOption, error)", + "file": "oauth.go", + "line": 438 + }, + { + "name": "WithOAuthToken", + "signature": "func(ctx context.Context, token string) context.Context", + "file": "context.go", + "line": 14 + }, + { + "name": "WithUser", + "signature": "func(ctx context.Context, user *github.com/Vungle/oauth-mcp-proxy.User) context.Context", + "file": "context.go", + "line": 25 + } + ] + }, + { + "path": "github.com/Vungle/oauth-mcp-proxy/mark3labs", + "rel_dir": "mark3labs", + "functions": [ + { + "name": "NewMiddleware", + "signature": "func(s *github.com/Vungle/oauth-mcp-proxy.Server) func(github.com/mark3labs/mcp-go/server.ToolHandlerFunc) github.com/mark3labs/mcp-go/server.ToolHandlerFunc", + "file": "mark3labs/middleware.go", + "line": 22 + }, + { + "name": "WithOAuth", + "signature": "func(mux *net/http.ServeMux, cfg *github.com/Vungle/oauth-mcp-proxy.Config) (*github.com/Vungle/oauth-mcp-proxy.Server, github.com/mark3labs/mcp-go/server.ServerOption, error)", + "file": "mark3labs/oauth.go", + "line": 43 + } + ] + }, + { + "path": "github.com/Vungle/oauth-mcp-proxy/mcp", + "rel_dir": "mcp", + "functions": [ + { + "name": "WithOAuth", + "signature": "func(mux *net/http.ServeMux, cfg *github.com/Vungle/oauth-mcp-proxy.Config, mcpServer *github.com/modelcontextprotocol/go-sdk/mcp.Server) (*github.com/Vungle/oauth-mcp-proxy.Server, net/http.Handler, error)", + "file": "mcp/oauth.go", + "line": 49 + } + ] + }, + { + "path": "github.com/Vungle/oauth-mcp-proxy/provider", + "rel_dir": "provider", + "structs": [ + { + "name": "Config", + "file": "provider/provider.go", + "line": 32, + "fields": [ + { + "name": "Provider", + "type": "string" + }, + { + "name": "Issuer", + "type": "string" + }, + { + "name": "Audience", + "type": "string" + }, + { + "name": "JWTSecret", + "type": "[]byte" + }, + { + "name": "Logger", + "type": "provider.Logger" + } + ], + "methods": null + }, + { + "name": "HMACValidator", + "file": "provider/provider.go", + "line": 47, + "fields": null, + "methods": [ + { + "name": "Initialize", + "signature": "func(cfg *provider.Config) error", + "receiver": "*T" + }, + { + "name": "ValidateToken", + "signature": "func(ctx context.Context, tokenString string) (*provider.User, error)", + "receiver": "*T" + } + ] + }, + { + "name": "OIDCValidator", + "file": "provider/provider.go", + "line": 54, + "fields": null, + "methods": [ + { + "name": "Initialize", + "signature": "func(cfg *provider.Config) error", + "receiver": "*T" + }, + { + "name": "ValidateToken", + "signature": "func(ctx context.Context, tokenString string) (*provider.User, error)", + "receiver": "*T" + } + ] + }, + { + "name": "User", + "file": "provider/provider.go", + "line": 17, + "fields": [ + { + "name": "Username", + "type": "string" + }, + { + "name": "Email", + "type": "string" + }, + { + "name": "Subject", + "type": "string" + } + ], + "methods": null + } + ], + "interfaces": [ + { + "name": "Logger", + "file": "provider/provider.go", + "line": 24, + "methods": [ + { + "name": "Debug", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + }, + { + "name": "Error", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + }, + { + "name": "Info", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + }, + { + "name": "Warn", + "signature": "func(msg string, args ...interface{})", + "file": "", + "line": 0 + } + ] + }, + { + "name": "TokenValidator", + "file": "provider/provider.go", + "line": 41, + "methods": [ + { + "name": "Initialize", + "signature": "func(cfg *provider.Config) error", + "file": "", + "line": 0 + }, + { + "name": "ValidateToken", + "signature": "func(ctx context.Context, token string) (*provider.User, error)", + "file": "", + "line": 0 + } + ] + } + ] + } + ] +} diff --git a/.claude/review-rules.yml b/.claude/review-rules.yml new file mode 100644 index 0000000..75bf683 --- /dev/null +++ b/.claude/review-rules.yml @@ -0,0 +1,38 @@ +# oauth-mcp-proxy review rules + +global: + - "Security considerations documented for any auth-related changes" + - "No raw tokens in log statements — use SHA-256 hash prefix only" + +core_package: + - "Token validation changes affect ALL adapters — test with both mark3labs and mcp adapter patterns" + - "Cache TTL changes (currently 5min) affect security posture — document trade-offs" + - "Core package must NOT import any MCP SDK (mark3labs or official)" + - "New context keys must use typed contextKey constants, not raw strings" + - "Config.Validate() changes may break FromEnv() — test both paths" + +provider/: + - "New providers must implement provider.TokenValidator interface (ValidateToken + Initialize)" + - "OIDC changes need testing against real provider or well-structured mocks" + - "Audience validation is security-critical — both HMAC and OIDC must check aud claim" + - "HMACValidator is sync.Once-initialized — test concurrent access" + - "OIDCValidator uses 30s init timeout, 10s validation timeout — document if changed" + +mark3labs/: + - "Adapter changes must match mark3labs/mcp-go SDK API (currently v0.41.1)" + - "Middleware must propagate context: GetOAuthToken -> ValidateTokenCached -> WithUser" + - "WithOAuth returns (*Server, ServerOption, error) — do not change return signature without major version bump" + - "NewMiddleware wraps server.ToolHandlerFunc — verify compatibility with SDK updates" + +mcp/: + - "Adapter changes must match modelcontextprotocol/go-sdk API (currently v1.0.0)" + - "WithOAuth returns (*Server, http.Handler, error) — do not change return signature without major version bump" + - "Handler must validate token AND add to context before passing to StreamableHTTPHandler" + - "OPTIONS passthrough is required for CORS — do not add auth checks to OPTIONS" + +handlers.go: + - "Endpoint changes affect OAuth discovery (/.well-known/* URLs)" + - "Redirect URI validation is security-critical — changes require security review" + - "State parameter HMAC signing prevents CSRF — do not remove or weaken" + - "RFC 8414 metadata endpoint must stay backward-compatible" + - "New endpoints must be registered in RegisterHandlers()" diff --git a/.claude/scripts/extract-api-surface.go b/.claude/scripts/extract-api-surface.go new file mode 100644 index 0000000..25b393f --- /dev/null +++ b/.claude/scripts/extract-api-surface.go @@ -0,0 +1,252 @@ +// extract-api-surface.go — Extracts the exported public API per package. +// Focused on what consumers (other repos/packages) can use. +// Run from repo root: go run .claude/scripts/extract-api-surface.go [./...] +package main + +import ( + "encoding/json" + "fmt" + "go/types" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "golang.org/x/tools/go/packages" +) + +type APIFunc struct { + Name string `json:"name"` + Signature string `json:"signature"` +} + +type APIType struct { + Name string `json:"name"` + Kind string `json:"kind"` // struct, interface, alias, other + Methods []APIFunc `json:"methods,omitempty"` +} + +type APIConst struct { + Name string `json:"name"` + Type string `json:"type"` + Value string `json:"value,omitempty"` +} + +type APIVar struct { + Name string `json:"name"` + Type string `json:"type"` +} + +type PackageAPI struct { + Path string `json:"path"` + RelDir string `json:"rel_dir"` + Types []APIType `json:"types,omitempty"` + Functions []APIFunc `json:"functions,omitempty"` + Constants []APIConst `json:"constants,omitempty"` + Variables []APIVar `json:"variables,omitempty"` +} + +type Output struct { + Generated string `json:"generated"` + Module string `json:"module"` + Packages []PackageAPI `json:"packages"` +} + +func main() { + repoRoot, err := findRepoRoot() + if err != nil { + fmt.Fprintf(os.Stderr, "error finding repo root: %v\n", err) + os.Exit(1) + } + + patterns := []string{"./..."} + if len(os.Args) > 1 { + patterns = os.Args[1:] + } + + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedTypes | packages.NeedFiles, + Dir: repoRoot, + Tests: false, + } + + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading packages: %v\n", err) + os.Exit(1) + } + + modulePath := inferModulePath(pkgs) + var result []PackageAPI + + for _, pkg := range pkgs { + if pkg.Types == nil { + continue + } + if strings.HasSuffix(pkg.PkgPath, "_test") { + continue + } + + api := PackageAPI{ + Path: pkg.PkgPath, + RelDir: relPath(repoRoot, pkgDir(pkg)), + } + + scope := pkg.Types.Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + if !obj.Exported() { + continue + } + + switch o := obj.(type) { + case *types.TypeName: + named, ok := o.Type().(*types.Named) + if !ok { + continue + } + + at := APIType{Name: name} + switch named.Underlying().(type) { + case *types.Struct: + at.Kind = "struct" + case *types.Interface: + at.Kind = "interface" + default: + at.Kind = "alias" + } + + for i := 0; i < named.NumMethods(); i++ { + m := named.Method(i) + if m.Exported() { + at.Methods = append(at.Methods, APIFunc{ + Name: m.Name(), + Signature: types.TypeString(m.Type(), qualifier(modulePath)), + }) + } + } + api.Types = append(api.Types, at) + + case *types.Func: + api.Functions = append(api.Functions, APIFunc{ + Name: name, + Signature: types.TypeString(o.Type(), qualifier(modulePath)), + }) + + case *types.Const: + ci := APIConst{ + Name: name, + Type: types.TypeString(o.Type(), qualifier(modulePath)), + } + if o.Val() != nil { + ci.Value = o.Val().ExactString() + } + api.Constants = append(api.Constants, ci) + + case *types.Var: + api.Variables = append(api.Variables, APIVar{ + Name: name, + Type: types.TypeString(o.Type(), qualifier(modulePath)), + }) + } + } + + if len(api.Types)+len(api.Functions)+len(api.Constants)+len(api.Variables) > 0 { + result = append(result, api) + } + } + + sort.Slice(result, func(i, j int) bool { + return result[i].Path < result[j].Path + }) + + out := Output{ + Generated: time.Now().UTC().Format(time.RFC3339), + Module: modulePath, + Packages: result, + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(out); err != nil { + fmt.Fprintf(os.Stderr, "error encoding JSON: %v\n", err) + os.Exit(1) + } +} + +func qualifier(modulePath string) types.Qualifier { + return func(pkg *types.Package) string { + p := pkg.Path() + if strings.HasPrefix(p, modulePath+"/") { + return strings.TrimPrefix(p, modulePath+"/") + } + return p + } +} + +func inferModulePath(pkgs []*packages.Package) string { + for _, pkg := range pkgs { + for _, prefix := range []string{"/internal/", "/pkg/", "/cmd/"} { + parts := strings.SplitN(pkg.PkgPath, prefix, 2) + if len(parts) == 2 { + return parts[0] + } + } + } + if len(pkgs) > 0 { + shortest := pkgs[0].PkgPath + for _, pkg := range pkgs[1:] { + if len(pkg.PkgPath) < len(shortest) { + shortest = pkg.PkgPath + } + } + return shortest + } + return "" +} + +func pkgDir(pkg *packages.Package) string { + if len(pkg.GoFiles) > 0 { + return filepath.Dir(pkg.GoFiles[0]) + } + return "" +} + +func findRepoRoot() (string, error) { + dir, err := os.Getwd() + if err != nil { + return "", err + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + if !isInsideDotClaude(dir) { + return dir, nil + } + } + parent := filepath.Dir(dir) + if parent == dir { + break + } + dir = parent + } + wd, _ := os.Getwd() + return wd, nil +} + +func isInsideDotClaude(dir string) bool { + for d := dir; d != filepath.Dir(d); d = filepath.Dir(d) { + if filepath.Base(d) == ".claude" { + return true + } + } + return false +} + +func relPath(base, path string) string { + rel, err := filepath.Rel(base, path) + if err != nil { + return path + } + return rel +} diff --git a/.claude/scripts/extract-call-graph.go b/.claude/scripts/extract-call-graph.go new file mode 100644 index 0000000..090ed20 --- /dev/null +++ b/.claude/scripts/extract-call-graph.go @@ -0,0 +1,467 @@ +// extract-call-graph.go — Builds a function call graph using VTA (Variable Type Analysis). +// Produces caller->callee edges for all packages in the module. +// Run from repo root: go run .claude/scripts/extract-call-graph.go [./...] +package main + +import ( + "encoding/json" + "fmt" + "go/token" + "go/types" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "golang.org/x/tools/go/callgraph" + "golang.org/x/tools/go/callgraph/vta" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/ssa" + "golang.org/x/tools/go/ssa/ssautil" +) + +type Edge struct { + Caller string `json:"caller"` + CallerPkg string `json:"caller_pkg"` + Callee string `json:"callee"` + CalleePkg string `json:"callee_pkg"` + CallerFile string `json:"caller_file,omitempty"` + CallerLine int `json:"caller_line,omitempty"` +} + +type FunctionNode struct { + Name string `json:"name"` + Package string `json:"package"` + File string `json:"file,omitempty"` + Line int `json:"line,omitempty"` + Callers int `json:"callers"` + Callees int `json:"callees"` + CalleeList []string `json:"callees_list,omitempty"` +} + +type Output struct { + Generated string `json:"generated"` + Module string `json:"module"` + TotalEdges int `json:"total_edges"` + TotalFuncs int `json:"total_functions"` + Edges []Edge `json:"edges"` + EntryPoints []FunctionNode `json:"entry_points"` + MostCalled []FunctionNode `json:"most_called"` + DeadCode []FunctionNode `json:"dead_code"` + PackageStats map[string]PackageStat `json:"package_stats"` +} + +type PackageStat struct { + Functions int `json:"functions"` + InternalEdges int `json:"internal_edges"` + IncomingEdges int `json:"incoming_edges"` + OutgoingEdges int `json:"outgoing_edges"` +} + +func main() { + repoRoot, err := findRepoRoot() + if err != nil { + fmt.Fprintf(os.Stderr, "error finding repo root: %v\n", err) + os.Exit(1) + } + + patterns := []string{"./..."} + if len(os.Args) > 1 { + patterns = os.Args[1:] + } + + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | + packages.NeedImports | packages.NeedTypes | packages.NeedSyntax | + packages.NeedTypesInfo | packages.NeedDeps, + Dir: repoRoot, + Tests: false, + } + + initial, err := packages.Load(cfg, patterns...) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading packages: %v\n", err) + os.Exit(1) + } + + // Check for package load errors + var loadErrors []string + packages.Visit(initial, nil, func(pkg *packages.Package) { + for _, err := range pkg.Errors { + loadErrors = append(loadErrors, fmt.Sprintf("%s: %s", pkg.PkgPath, err.Msg)) + } + }) + if len(loadErrors) > 10 { + fmt.Fprintf(os.Stderr, "warning: %d package load errors (showing first 10)\n", len(loadErrors)) + for _, e := range loadErrors[:10] { + fmt.Fprintf(os.Stderr, " %s\n", e) + } + } + + modulePath := inferModulePath(initial) + + // Build SSA + prog, ssaPkgs := ssautil.AllPackages(initial, ssa.InstantiateGenerics) + prog.Build() + + // Filter to only our module's packages for VTA entry + moduleFuncs := make(map[*ssa.Function]bool) + for _, pkg := range ssaPkgs { + if pkg == nil { + continue + } + if !strings.HasPrefix(pkg.Pkg.Path(), modulePath) { + continue + } + for _, member := range pkg.Members { + if fn, ok := member.(*ssa.Function); ok { + moduleFuncs[fn] = true + } + } + // Also grab all functions including anonymous and methods + for _, fn := range allFunctions(pkg) { + moduleFuncs[fn] = true + } + } + + // Build call graph using VTA (pass nil for initial graph) + cg := vta.CallGraph(moduleFuncs, nil) + + // Collect edges (only internal-to-module edges) + var edges []Edge + callerCount := make(map[string]int) // callee -> number of callers + calleeCount := make(map[string]int) // caller -> number of callees + funcFiles := make(map[string]string) + funcLines := make(map[string]int) + pkgStats := make(map[string]*PackageStat) + + ensurePkgStat := func(pkg string) *PackageStat { + if _, ok := pkgStats[pkg]; !ok { + pkgStats[pkg] = &PackageStat{} + } + return pkgStats[pkg] + } + + seen := make(map[string]bool) + + cg.DeleteSyntheticNodes() + + err = callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error { + callerFn := edge.Caller.Func + calleeFn := edge.Callee.Func + + if callerFn == nil || calleeFn == nil { + return nil + } + + callerPkg := "" + calleePkg := "" + if callerFn.Package() != nil { + callerPkg = callerFn.Package().Pkg.Path() + } + if calleeFn.Package() != nil { + calleePkg = calleeFn.Package().Pkg.Path() + } + + // Only keep edges where at least one side is in our module + if !strings.HasPrefix(callerPkg, modulePath) && !strings.HasPrefix(calleePkg, modulePath) { + return nil + } + + callerName := callerFn.String() + calleeName := calleeFn.String() + + // Deduplicate + edgeKey := callerName + " -> " + calleeName + if seen[edgeKey] { + return nil + } + seen[edgeKey] = true + + // Shorten to relative package paths + shortCallerPkg := strings.TrimPrefix(callerPkg, modulePath+"/") + shortCalleePkg := strings.TrimPrefix(calleePkg, modulePath+"/") + shortCaller := shortenFuncName(callerName, modulePath) + shortCallee := shortenFuncName(calleeName, modulePath) + + // Get position info + callerFile, callerLine := funcPos(callerFn, repoRoot) + + edge_ := Edge{ + Caller: shortCaller, + CallerPkg: shortCallerPkg, + Callee: shortCallee, + CalleePkg: shortCalleePkg, + CallerFile: callerFile, + CallerLine: callerLine, + } + edges = append(edges, edge_) + + callerCount[shortCallee]++ + calleeCount[shortCaller]++ + + // Track file/line + if callerFile != "" { + funcFiles[shortCaller] = callerFile + funcLines[shortCaller] = callerLine + } + if f, l := funcPos(calleeFn, repoRoot); f != "" { + funcFiles[shortCallee] = f + funcLines[shortCallee] = l + } + + // Package stats + if strings.HasPrefix(callerPkg, modulePath) && strings.HasPrefix(calleePkg, modulePath) { + if callerPkg == calleePkg { + ensurePkgStat(shortCallerPkg).InternalEdges++ + } else { + ensurePkgStat(shortCallerPkg).OutgoingEdges++ + ensurePkgStat(shortCalleePkg).IncomingEdges++ + } + } + + return nil + }) + if err != nil { + fmt.Fprintf(os.Stderr, "error visiting call graph: %v\n", err) + } + + // Sort edges + sort.Slice(edges, func(i, j int) bool { + if edges[i].CallerPkg != edges[j].CallerPkg { + return edges[i].CallerPkg < edges[j].CallerPkg + } + return edges[i].Caller < edges[j].Caller + }) + + // Find entry points (functions with no callers that are exported) + allFuncsSet := make(map[string]bool) + for _, e := range edges { + allFuncsSet[e.Caller] = true + allFuncsSet[e.Callee] = true + } + var entryPoints []FunctionNode + for fn := range allFuncsSet { + if callerCount[fn] == 0 && calleeCount[fn] > 0 { + entryPoints = append(entryPoints, FunctionNode{ + Name: fn, + Package: funcPkgFromEdges(fn, edges), + File: funcFiles[fn], + Line: funcLines[fn], + Callers: 0, + Callees: calleeCount[fn], + }) + } + } + sort.Slice(entryPoints, func(i, j int) bool { + return entryPoints[i].Callees > entryPoints[j].Callees + }) + if len(entryPoints) > 30 { + entryPoints = entryPoints[:30] + } + + // Most called functions + type kv struct { + fn string + count int + } + var mostCalledList []kv + for fn, count := range callerCount { + mostCalledList = append(mostCalledList, kv{fn, count}) + } + sort.Slice(mostCalledList, func(i, j int) bool { + return mostCalledList[i].count > mostCalledList[j].count + }) + var mostCalled []FunctionNode + for i, mc := range mostCalledList { + if i >= 30 { + break + } + mostCalled = append(mostCalled, FunctionNode{ + Name: mc.fn, + Package: funcPkgFromEdges(mc.fn, edges), + File: funcFiles[mc.fn], + Line: funcLines[mc.fn], + Callers: mc.count, + Callees: calleeCount[mc.fn], + }) + } + + // Dead code: functions defined but never called (no callers, no callees as caller) + var deadCode []FunctionNode + for fn := range allFuncsSet { + if callerCount[fn] == 0 && calleeCount[fn] == 0 { + deadCode = append(deadCode, FunctionNode{ + Name: fn, + Package: funcPkgFromEdges(fn, edges), + File: funcFiles[fn], + Line: funcLines[fn], + }) + } + } + sort.Slice(deadCode, func(i, j int) bool { + return deadCode[i].Name < deadCode[j].Name + }) + if len(deadCode) > 50 { + deadCode = deadCode[:50] + } + + // Convert pkgStats + pkgStatsOut := make(map[string]PackageStat) + for k, v := range pkgStats { + pkgStatsOut[k] = *v + } + + out := Output{ + Generated: time.Now().UTC().Format(time.RFC3339), + Module: modulePath, + TotalEdges: len(edges), + TotalFuncs: len(allFuncsSet), + Edges: edges, + EntryPoints: entryPoints, + MostCalled: mostCalled, + DeadCode: deadCode, + PackageStats: pkgStatsOut, + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(out); err != nil { + fmt.Fprintf(os.Stderr, "error encoding JSON: %v\n", err) + os.Exit(1) + } +} + +func allFunctions(pkg *ssa.Package) []*ssa.Function { + var fns []*ssa.Function + for _, member := range pkg.Members { + switch m := member.(type) { + case *ssa.Function: + fns = append(fns, m) + // Add anonymous functions within + for _, anon := range m.AnonFuncs { + fns = append(fns, anon) + } + case *ssa.Type: + // Methods on named types (handle both Named and Alias types in Go 1.23+) + named, ok := m.Type().(*types.Named) + if !ok { + mset := types.NewMethodSet(m.Type()) + for i := 0; i < mset.Len(); i++ { + if fn, ok := mset.At(i).Obj().(*types.Func); ok { + if method := prog_lookup(pkg.Prog, fn); method != nil { + fns = append(fns, method) + } + } + } + break + } + for i := 0; i < named.NumMethods(); i++ { + method := prog_lookup(pkg.Prog, named.Method(i)) + if method != nil { + fns = append(fns, method) + } + } + // Methods on pointer to named types + ptr := types.NewPointer(named) + mset := types.NewMethodSet(ptr) + for i := 0; i < mset.Len(); i++ { + sel := mset.At(i) + if fn, ok := sel.Obj().(*types.Func); ok { + if method := prog_lookup(pkg.Prog, fn); method != nil { + fns = append(fns, method) + } + } + } + } + } + return fns +} + +func prog_lookup(prog *ssa.Program, fn *types.Func) *ssa.Function { + return prog.FuncValue(fn) +} + +func shortenFuncName(name, modulePath string) string { + return strings.ReplaceAll(name, "("+modulePath+"/", "(") +} + +func funcPos(fn *ssa.Function, repoRoot string) (string, int) { + if fn.Pos() == token.NoPos { + return "", 0 + } + pos := fn.Prog.Fset.Position(fn.Pos()) + return relPath(repoRoot, pos.Filename), pos.Line +} + +func funcPkgFromEdges(fn string, edges []Edge) string { + for _, e := range edges { + if e.Caller == fn { + return e.CallerPkg + } + if e.Callee == fn { + return e.CalleePkg + } + } + return "" +} + +func inferModulePath(pkgs []*packages.Package) string { + for _, pkg := range pkgs { + for _, prefix := range []string{"/internal/", "/pkg/", "/cmd/"} { + parts := strings.SplitN(pkg.PkgPath, prefix, 2) + if len(parts) == 2 { + return parts[0] + } + } + } + if len(pkgs) > 0 { + shortest := pkgs[0].PkgPath + for _, pkg := range pkgs[1:] { + if len(pkg.PkgPath) < len(shortest) { + shortest = pkg.PkgPath + } + } + return shortest + } + return "" +} + +func findRepoRoot() (string, error) { + dir, err := os.Getwd() + if err != nil { + return "", err + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + if !isInsideDotClaude(dir) { + return dir, nil + } + } + parent := filepath.Dir(dir) + if parent == dir { + break + } + dir = parent + } + wd, _ := os.Getwd() + return wd, nil +} + +func isInsideDotClaude(dir string) bool { + for d := dir; d != filepath.Dir(d); d = filepath.Dir(d) { + if filepath.Base(d) == ".claude" { + return true + } + } + return false +} + +func relPath(base, path string) string { + rel, err := filepath.Rel(base, path) + if err != nil { + return path + } + return rel +} diff --git a/.claude/scripts/extract-concurrency.go b/.claude/scripts/extract-concurrency.go new file mode 100644 index 0000000..dcac1c4 --- /dev/null +++ b/.claude/scripts/extract-concurrency.go @@ -0,0 +1,435 @@ +// extract-concurrency.go — Scans for concurrency patterns: goroutine spawns, +// channels, mutexes, context propagation, and sync primitives. +// Run from repo root: go run .claude/scripts/extract-concurrency.go [./...] +package main + +import ( + "encoding/json" + "fmt" + "go/ast" + "go/token" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "golang.org/x/tools/go/packages" +) + +type GoroutineSpawn struct { + File string `json:"file"` + Line int `json:"line"` + Function string `json:"function"` + Expr string `json:"expr"` +} + +type ChannelUsage struct { + File string `json:"file"` + Line int `json:"line"` + Name string `json:"name"` + Type string `json:"type"` + Kind string `json:"kind"` // "make", "send", "receive", "declaration" +} + +type MutexUsage struct { + File string `json:"file"` + Line int `json:"line"` + Name string `json:"name"` + Type string `json:"type"` // "Mutex", "RWMutex", "Once", "WaitGroup" + Action string `json:"action"` // "declaration", "Lock", "Unlock", "RLock", "RUnlock", "Do", "Add", "Wait", "Done" +} + +type ContextUsage struct { + File string `json:"file"` + Line int `json:"line"` + Function string `json:"function"` + Pattern string `json:"pattern"` // "WithCancel", "WithTimeout", "WithDeadline", "WithValue", "Background", "TODO", "param" +} + +type PackageSummary struct { + Package string `json:"package"` + Goroutines int `json:"goroutines"` + Channels int `json:"channels"` + Mutexes int `json:"mutexes"` + Contexts int `json:"contexts"` +} + +type Output struct { + Generated string `json:"generated"` + Module string `json:"module"` + Summary Summary `json:"summary"` + Goroutines []GoroutineSpawn `json:"goroutines"` + Channels []ChannelUsage `json:"channels"` + Mutexes []MutexUsage `json:"mutexes"` + Contexts []ContextUsage `json:"contexts"` + PackageSummary []PackageSummary `json:"package_summary"` +} + +type Summary struct { + TotalGoroutines int `json:"total_goroutines"` + TotalChannels int `json:"total_channels"` + TotalMutexes int `json:"total_mutexes"` + TotalContexts int `json:"total_contexts"` + TotalPackages int `json:"packages_with_concurrency"` +} + +func main() { + repoRoot, err := findRepoRoot() + if err != nil { + fmt.Fprintf(os.Stderr, "error finding repo root: %v\n", err) + os.Exit(1) + } + + patterns := []string{"./..."} + if len(os.Args) > 1 { + patterns = os.Args[1:] + } + + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedFiles | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo, + Dir: repoRoot, + Tests: false, + } + + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading packages: %v\n", err) + os.Exit(1) + } + + modulePath := inferModulePath(pkgs) + + var goroutines []GoroutineSpawn + var channels []ChannelUsage + var mutexes []MutexUsage + var contexts []ContextUsage + pkgCounts := make(map[string]*PackageSummary) + + for _, pkg := range pkgs { + if !strings.HasPrefix(pkg.PkgPath, modulePath) { + continue + } + if strings.HasSuffix(pkg.PkgPath, "_test") { + continue + } + + shortPkg := strings.TrimPrefix(pkg.PkgPath, modulePath+"/") + if pkg.PkgPath == modulePath { + shortPkg = "." + } + summary := &PackageSummary{Package: shortPkg} + + for _, file := range pkg.Syntax { + filename := pkg.Fset.Position(file.Pos()).Filename + relFile := relPath(repoRoot, filename) + + // Skip test files + if strings.HasSuffix(relFile, "_test.go") { + continue + } + + enclosingFunc := "" + + ast.Inspect(file, func(n ast.Node) bool { + if n == nil { + return false + } + + // Track enclosing function + switch fn := n.(type) { + case *ast.FuncDecl: + enclosingFunc = fn.Name.Name + if fn.Recv != nil && len(fn.Recv.List) > 0 { + enclosingFunc = exprString(fn.Recv.List[0].Type) + "." + fn.Name.Name + } + } + + pos := pkg.Fset.Position(n.Pos()) + + switch node := n.(type) { + // Goroutine spawns: go expr + case *ast.GoStmt: + goroutines = append(goroutines, GoroutineSpawn{ + File: relFile, + Line: pos.Line, + Function: enclosingFunc, + Expr: truncate(exprString(node.Call.Fun), 80), + }) + summary.Goroutines++ + + // Channel makes: make(chan T) + case *ast.CallExpr: + if ident, ok := node.Fun.(*ast.Ident); ok && ident.Name == "make" { + if len(node.Args) > 0 { + if ch, ok := node.Args[0].(*ast.ChanType); ok { + channels = append(channels, ChannelUsage{ + File: relFile, + Line: pos.Line, + Type: exprString(ch.Value), + Kind: "make", + }) + summary.Channels++ + } + } + } + + // Context creation patterns + if sel, ok := node.Fun.(*ast.SelectorExpr); ok { + selName := sel.Sel.Name + switch selName { + case "WithCancel", "WithTimeout", "WithDeadline", "WithValue", "Background", "TODO": + if ident, ok := sel.X.(*ast.Ident); ok && ident.Name == "context" { + contexts = append(contexts, ContextUsage{ + File: relFile, + Line: pos.Line, + Function: enclosingFunc, + Pattern: selName, + }) + summary.Contexts++ + } + } + + // Mutex operations + switch selName { + case "Lock", "Unlock", "RLock", "RUnlock": + mutexes = append(mutexes, MutexUsage{ + File: relFile, + Line: pos.Line, + Name: exprString(sel.X), + Type: inferMutexType(selName), + Action: selName, + }) + summary.Mutexes++ + case "Do": + // sync.Once.Do + mutexes = append(mutexes, MutexUsage{ + File: relFile, + Line: pos.Line, + Name: exprString(sel.X), + Type: "Once", + Action: "Do", + }) + summary.Mutexes++ + case "Add", "Done", "Wait": + nameStr := exprString(sel.X) + if strings.Contains(strings.ToLower(nameStr), "wg") || + strings.Contains(strings.ToLower(nameStr), "wait") || + strings.Contains(strings.ToLower(nameStr), "group") { + mutexes = append(mutexes, MutexUsage{ + File: relFile, + Line: pos.Line, + Name: nameStr, + Type: "WaitGroup", + Action: selName, + }) + summary.Mutexes++ + } + } + + } + + // Channel send + case *ast.SendStmt: + channels = append(channels, ChannelUsage{ + File: relFile, + Line: pos.Line, + Name: exprString(node.Chan), + Kind: "send", + }) + summary.Channels++ + + // Channel receive: <-ch + case *ast.UnaryExpr: + if node.Op == token.ARROW { + channels = append(channels, ChannelUsage{ + File: relFile, + Line: pos.Line, + Name: exprString(node.X), + Kind: "receive", + }) + summary.Channels++ + } + + // Field declarations with sync types + case *ast.Field: + typeStr := exprString(node.Type) + if strings.Contains(typeStr, "sync.Mutex") || + strings.Contains(typeStr, "sync.RWMutex") || + strings.Contains(typeStr, "sync.Once") || + strings.Contains(typeStr, "sync.WaitGroup") { + name := "" + if len(node.Names) > 0 { + name = node.Names[0].Name + } + mutexes = append(mutexes, MutexUsage{ + File: relFile, + Line: pos.Line, + Name: name, + Type: strings.TrimPrefix(typeStr, "sync."), + Action: "declaration", + }) + summary.Mutexes++ + } + + // Channel field declarations + if _, ok := node.Type.(*ast.ChanType); ok { + name := "" + if len(node.Names) > 0 { + name = node.Names[0].Name + } + channels = append(channels, ChannelUsage{ + File: relFile, + Line: pos.Line, + Name: name, + Kind: "declaration", + }) + summary.Channels++ + } + } + + return true + }) + } + + if summary.Goroutines+summary.Channels+summary.Mutexes+summary.Contexts > 0 { + pkgCounts[shortPkg] = summary + } + } + + // Build package summaries sorted by total concurrency usage + var pkgSummaries []PackageSummary + for _, s := range pkgCounts { + pkgSummaries = append(pkgSummaries, *s) + } + sort.Slice(pkgSummaries, func(i, j int) bool { + ti := pkgSummaries[i].Goroutines + pkgSummaries[i].Channels + pkgSummaries[i].Mutexes + tj := pkgSummaries[j].Goroutines + pkgSummaries[j].Channels + pkgSummaries[j].Mutexes + return ti > tj + }) + + out := Output{ + Generated: time.Now().UTC().Format(time.RFC3339), + Module: modulePath, + Summary: Summary{ + TotalGoroutines: len(goroutines), + TotalChannels: len(channels), + TotalMutexes: len(mutexes), + TotalContexts: len(contexts), + TotalPackages: len(pkgSummaries), + }, + Goroutines: goroutines, + Channels: channels, + Mutexes: mutexes, + Contexts: contexts, + PackageSummary: pkgSummaries, + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(out); err != nil { + fmt.Fprintf(os.Stderr, "error encoding JSON: %v\n", err) + os.Exit(1) + } +} + +func exprString(expr ast.Expr) string { + if expr == nil { + return "" + } + switch e := expr.(type) { + case *ast.Ident: + return e.Name + case *ast.SelectorExpr: + return exprString(e.X) + "." + e.Sel.Name + case *ast.StarExpr: + return "*" + exprString(e.X) + case *ast.IndexExpr: + return exprString(e.X) + "[" + exprString(e.Index) + "]" + case *ast.FuncLit: + return "func literal" + case *ast.CallExpr: + return exprString(e.Fun) + "(...)" + case *ast.ParenExpr: + return "(" + exprString(e.X) + ")" + case *ast.UnaryExpr: + return e.Op.String() + exprString(e.X) + default: + return fmt.Sprintf("<%T>", expr) + } +} + +func inferMutexType(action string) string { + switch action { + case "RLock", "RUnlock": + return "RWMutex" + default: + return "Mutex" + } +} + +func truncate(s string, max int) string { + if len(s) <= max { + return s + } + return s[:max-3] + "..." +} + +func inferModulePath(pkgs []*packages.Package) string { + for _, pkg := range pkgs { + for _, prefix := range []string{"/internal/", "/pkg/", "/cmd/"} { + parts := strings.SplitN(pkg.PkgPath, prefix, 2) + if len(parts) == 2 { + return parts[0] + } + } + } + if len(pkgs) > 0 { + shortest := pkgs[0].PkgPath + for _, pkg := range pkgs[1:] { + if len(pkg.PkgPath) < len(shortest) { + shortest = pkg.PkgPath + } + } + return shortest + } + return "" +} + +func findRepoRoot() (string, error) { + dir, err := os.Getwd() + if err != nil { + return "", err + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + if !isInsideDotClaude(dir) { + return dir, nil + } + } + parent := filepath.Dir(dir) + if parent == dir { + break + } + dir = parent + } + wd, _ := os.Getwd() + return wd, nil +} + +func isInsideDotClaude(dir string) bool { + for d := dir; d != filepath.Dir(d); d = filepath.Dir(d) { + if filepath.Base(d) == ".claude" { + return true + } + } + return false +} + +func relPath(base, path string) string { + rel, err := filepath.Rel(base, path) + if err != nil { + return path + } + return rel +} diff --git a/.claude/scripts/extract-interfaces.go b/.claude/scripts/extract-interfaces.go new file mode 100644 index 0000000..9811dfc --- /dev/null +++ b/.claude/scripts/extract-interfaces.go @@ -0,0 +1,269 @@ +// extract-interfaces.go — Extracts all interface definitions and their implementors. +// Run from repo root: go run .claude/scripts/extract-interfaces.go [./...] +package main + +import ( + "encoding/json" + "fmt" + "go/types" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "golang.org/x/tools/go/packages" +) + +type Method struct { + Name string `json:"name"` + Signature string `json:"signature"` +} + +type InterfaceEntry struct { + Name string `json:"name"` + Package string `json:"package"` + File string `json:"file"` + Line int `json:"line"` + Methods []Method `json:"methods"` + Implementors []Implementor `json:"implementors"` +} + +type Implementor struct { + Name string `json:"name"` + Package string `json:"package"` + File string `json:"file"` + Line int `json:"line"` + Pointer bool `json:"pointer"` // true if *T implements, not T +} + +type Output struct { + Generated string `json:"generated"` + Module string `json:"module"` + Interfaces []InterfaceEntry `json:"interfaces"` +} + +func main() { + repoRoot, err := findRepoRoot() + if err != nil { + fmt.Fprintf(os.Stderr, "error finding repo root: %v\n", err) + os.Exit(1) + } + + patterns := []string{"./..."} + if len(os.Args) > 1 { + patterns = os.Args[1:] + } + + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedTypes | packages.NeedSyntax | + packages.NeedTypesInfo | packages.NeedFiles, + Dir: repoRoot, + Tests: false, + } + + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading packages: %v\n", err) + os.Exit(1) + } + + // Collect all interfaces and all named types + type ifaceInfo struct { + name string + pkg string + file string + line int + iface *types.Interface + methods []Method + } + + type namedInfo struct { + name string + pkg string + file string + line int + typ *types.Named + } + + var ifaces []ifaceInfo + var namedTypes []namedInfo + modulePath := inferModulePath(pkgs) + + for _, pkg := range pkgs { + if pkg.Types == nil { + continue + } + + scope := pkg.Types.Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + tn, ok := obj.(*types.TypeName) + if !ok || !tn.Exported() { + continue + } + + named, ok := tn.Type().(*types.Named) + if !ok { + continue + } + + pos := pkg.Fset.Position(obj.Pos()) + relFile := relPath(repoRoot, pos.Filename) + + if iface, ok := named.Underlying().(*types.Interface); ok { + var methods []Method + for i := 0; i < iface.NumMethods(); i++ { + m := iface.Method(i) + methods = append(methods, Method{ + Name: m.Name(), + Signature: types.TypeString(m.Type(), nil), + }) + } + ifaces = append(ifaces, ifaceInfo{ + name: name, + pkg: pkg.PkgPath, + file: relFile, + line: pos.Line, + iface: iface, + methods: methods, + }) + } + + namedTypes = append(namedTypes, namedInfo{ + name: name, + pkg: pkg.PkgPath, + file: relFile, + line: pos.Line, + typ: named, + }) + } + } + + // For each interface, find implementors + var entries []InterfaceEntry + for _, ifc := range ifaces { + if ifc.iface.NumMethods() == 0 { + continue // skip empty interfaces + } + + entry := InterfaceEntry{ + Name: ifc.name, + Package: ifc.pkg, + File: ifc.file, + Line: ifc.line, + Methods: ifc.methods, + } + + for _, nt := range namedTypes { + // Skip the interface itself + if nt.pkg == ifc.pkg && nt.name == ifc.name { + continue + } + + // Check if T implements the interface + if types.Implements(nt.typ, ifc.iface) { + entry.Implementors = append(entry.Implementors, Implementor{ + Name: nt.name, + Package: nt.pkg, + File: nt.file, + Line: nt.line, + Pointer: false, + }) + } else if ptr := types.NewPointer(nt.typ); types.Implements(ptr, ifc.iface) { + entry.Implementors = append(entry.Implementors, Implementor{ + Name: nt.name, + Package: nt.pkg, + File: nt.file, + Line: nt.line, + Pointer: true, + }) + } + } + + sort.Slice(entry.Implementors, func(i, j int) bool { + return entry.Implementors[i].Package+"."+entry.Implementors[i].Name < + entry.Implementors[j].Package+"."+entry.Implementors[j].Name + }) + + entries = append(entries, entry) + } + + sort.Slice(entries, func(i, j int) bool { + return entries[i].Package+"."+entries[i].Name < + entries[j].Package+"."+entries[j].Name + }) + + out := Output{ + Generated: time.Now().UTC().Format(time.RFC3339), + Module: modulePath, + Interfaces: entries, + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(out); err != nil { + fmt.Fprintf(os.Stderr, "error encoding JSON: %v\n", err) + os.Exit(1) + } +} + +func inferModulePath(pkgs []*packages.Package) string { + for _, pkg := range pkgs { + for _, prefix := range []string{"/internal/", "/pkg/", "/cmd/"} { + parts := strings.SplitN(pkg.PkgPath, prefix, 2) + if len(parts) == 2 { + return parts[0] + } + } + } + // For libraries without internal/pkg/cmd, use the shortest package path + if len(pkgs) > 0 { + shortest := pkgs[0].PkgPath + for _, pkg := range pkgs[1:] { + if len(pkg.PkgPath) < len(shortest) { + shortest = pkg.PkgPath + } + } + return shortest + } + return "" +} + +func findRepoRoot() (string, error) { + dir, err := os.Getwd() + if err != nil { + return "", err + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + if !isInsideDotClaude(dir) { + return dir, nil + } + } + parent := filepath.Dir(dir) + if parent == dir { + break + } + dir = parent + } + wd, _ := os.Getwd() + return wd, nil +} + +func isInsideDotClaude(dir string) bool { + for d := dir; d != filepath.Dir(d); d = filepath.Dir(d) { + if filepath.Base(d) == ".claude" { + return true + } + } + return false +} + +func relPath(base, path string) string { + rel, err := filepath.Rel(base, path) + if err != nil { + return path + } + return rel +} diff --git a/.claude/scripts/extract-types.go b/.claude/scripts/extract-types.go new file mode 100644 index 0000000..81a4e95 --- /dev/null +++ b/.claude/scripts/extract-types.go @@ -0,0 +1,314 @@ +// extract-types.go — Produces a type index of all exported symbols per package. +// Run from repo root: go run .claude/scripts/extract-types.go [./...] +package main + +import ( + "encoding/json" + "fmt" + "go/types" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "golang.org/x/tools/go/packages" +) + +type FuncInfo struct { + Name string `json:"name"` + Signature string `json:"signature"` + File string `json:"file"` + Line int `json:"line"` +} + +type FieldInfo struct { + Name string `json:"name"` + Type string `json:"type"` + Tag string `json:"tag,omitempty"` +} + +type MethodInfo struct { + Name string `json:"name"` + Signature string `json:"signature"` + Receiver string `json:"receiver"` +} + +type StructInfo struct { + Name string `json:"name"` + File string `json:"file"` + Line int `json:"line"` + Fields []FieldInfo `json:"fields"` + Methods []MethodInfo `json:"methods"` +} + +type InterfaceInfo struct { + Name string `json:"name"` + File string `json:"file"` + Line int `json:"line"` + Methods []FuncInfo `json:"methods"` +} + +type ConstInfo struct { + Name string `json:"name"` + Type string `json:"type,omitempty"` + Value string `json:"value,omitempty"` +} + +type PackageIndex struct { + Path string `json:"path"` + RelDir string `json:"rel_dir"` + Structs []StructInfo `json:"structs,omitempty"` + Interfaces []InterfaceInfo `json:"interfaces,omitempty"` + Functions []FuncInfo `json:"functions,omitempty"` + Constants []ConstInfo `json:"constants,omitempty"` +} + +type Output struct { + Generated string `json:"generated"` + Module string `json:"module"` + TotalTypes int `json:"total_types"` + TotalFuncs int `json:"total_functions"` + Packages []PackageIndex `json:"packages"` +} + +func main() { + repoRoot, err := findRepoRoot() + if err != nil { + fmt.Fprintf(os.Stderr, "error finding repo root: %v\n", err) + os.Exit(1) + } + + patterns := []string{"./..."} + if len(os.Args) > 1 { + patterns = os.Args[1:] + } + + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedTypes | packages.NeedSyntax | + packages.NeedTypesInfo | packages.NeedFiles, + Dir: repoRoot, + Tests: false, + } + + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading packages: %v\n", err) + os.Exit(1) + } + + modulePath := inferModulePath(pkgs) + var result []PackageIndex + totalTypes := 0 + totalFuncs := 0 + + for _, pkg := range pkgs { + if pkg.Types == nil { + continue + } + // Skip test packages + if strings.HasSuffix(pkg.PkgPath, "_test") { + continue + } + + idx := PackageIndex{ + Path: pkg.PkgPath, + RelDir: relPath(repoRoot, pkgDir(pkg)), + } + + scope := pkg.Types.Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + if !obj.Exported() { + continue + } + + pos := pkg.Fset.Position(obj.Pos()) + relFile := relPath(repoRoot, pos.Filename) + + switch o := obj.(type) { + case *types.TypeName: + named, ok := o.Type().(*types.Named) + if !ok { + continue + } + totalTypes++ + + switch u := named.Underlying().(type) { + case *types.Struct: + si := StructInfo{ + Name: name, + File: relFile, + Line: pos.Line, + } + for i := 0; i < u.NumFields(); i++ { + f := u.Field(i) + if f.Exported() { + si.Fields = append(si.Fields, FieldInfo{ + Name: f.Name(), + Type: types.TypeString(f.Type(), qualifier(modulePath)), + Tag: u.Tag(i), + }) + } + } + // Methods on the named type + for i := 0; i < named.NumMethods(); i++ { + m := named.Method(i) + if m.Exported() { + sig := m.Type().(*types.Signature) + recv := "T" + if _, ok := sig.Recv().Type().(*types.Pointer); ok { + recv = "*T" + } + si.Methods = append(si.Methods, MethodInfo{ + Name: m.Name(), + Signature: types.TypeString(m.Type(), qualifier(modulePath)), + Receiver: recv, + }) + } + } + idx.Structs = append(idx.Structs, si) + + case *types.Interface: + ii := InterfaceInfo{ + Name: name, + File: relFile, + Line: pos.Line, + } + for i := 0; i < u.NumMethods(); i++ { + m := u.Method(i) + ii.Methods = append(ii.Methods, FuncInfo{ + Name: m.Name(), + Signature: types.TypeString(m.Type(), qualifier(modulePath)), + }) + } + idx.Interfaces = append(idx.Interfaces, ii) + } + + case *types.Func: + totalFuncs++ + idx.Functions = append(idx.Functions, FuncInfo{ + Name: name, + Signature: types.TypeString(o.Type(), qualifier(modulePath)), + File: relFile, + Line: pos.Line, + }) + + case *types.Const: + ci := ConstInfo{ + Name: name, + Type: types.TypeString(o.Type(), qualifier(modulePath)), + } + if o.Val() != nil { + ci.Value = o.Val().ExactString() + } + idx.Constants = append(idx.Constants, ci) + } + } + + // Only include packages that have exported symbols + if len(idx.Structs)+len(idx.Interfaces)+len(idx.Functions)+len(idx.Constants) > 0 { + result = append(result, idx) + } + } + + sort.Slice(result, func(i, j int) bool { + return result[i].Path < result[j].Path + }) + + out := Output{ + Generated: time.Now().UTC().Format(time.RFC3339), + Module: modulePath, + TotalTypes: totalTypes, + TotalFuncs: totalFuncs, + Packages: result, + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(out); err != nil { + fmt.Fprintf(os.Stderr, "error encoding JSON: %v\n", err) + os.Exit(1) + } +} + +// qualifier returns a types.Qualifier that shortens the module prefix for readability +func qualifier(modulePath string) types.Qualifier { + return func(pkg *types.Package) string { + p := pkg.Path() + if strings.HasPrefix(p, modulePath+"/") { + return strings.TrimPrefix(p, modulePath+"/") + } + return p + } +} + +func inferModulePath(pkgs []*packages.Package) string { + for _, pkg := range pkgs { + for _, prefix := range []string{"/internal/", "/pkg/", "/cmd/"} { + parts := strings.SplitN(pkg.PkgPath, prefix, 2) + if len(parts) == 2 { + return parts[0] + } + } + } + // For libraries without internal/pkg/cmd, use the shortest package path + if len(pkgs) > 0 { + shortest := pkgs[0].PkgPath + for _, pkg := range pkgs[1:] { + if len(pkg.PkgPath) < len(shortest) { + shortest = pkg.PkgPath + } + } + return shortest + } + return "" +} + +func pkgDir(pkg *packages.Package) string { + if len(pkg.GoFiles) > 0 { + return filepath.Dir(pkg.GoFiles[0]) + } + if len(pkg.CompiledGoFiles) > 0 { + return filepath.Dir(pkg.CompiledGoFiles[0]) + } + return "" +} + +func findRepoRoot() (string, error) { + dir, err := os.Getwd() + if err != nil { + return "", err + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + if !isInsideDotClaude(dir) { + return dir, nil + } + } + parent := filepath.Dir(dir) + if parent == dir { + break + } + dir = parent + } + wd, _ := os.Getwd() + return wd, nil +} + +func isInsideDotClaude(dir string) bool { + for d := dir; d != filepath.Dir(d); d = filepath.Dir(d) { + if filepath.Base(d) == ".claude" { + return true + } + } + return false +} + +func relPath(base, path string) string { + rel, err := filepath.Rel(base, path) + if err != nil { + return path + } + return rel +} diff --git a/.claude/scripts/generate-claude-knowledge.sh b/.claude/scripts/generate-claude-knowledge.sh new file mode 100755 index 0000000..8f04a95 --- /dev/null +++ b/.claude/scripts/generate-claude-knowledge.sh @@ -0,0 +1,134 @@ +#!/bin/bash +# generate-claude-knowledge.sh — Master script to generate all computed knowledge artifacts. +# Run from repo root: .claude/scripts/generate-claude-knowledge.sh +# Called by CI on every merge to main. +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SCRIPTS_DIR="$REPO_ROOT/.claude/scripts" +OUTDIR="$REPO_ROOT/.claude/knowledge/computed" + +cd "$REPO_ROOT" +mkdir -p "$OUTDIR" + +echo "=== Generating Claude knowledge artifacts for $(basename "$REPO_ROOT") ===" +echo " Repo root: $REPO_ROOT" +echo " Output: $OUTDIR" +echo "" + +FAILED=0 + +run_step() { + local name="$1" + local outfile="$2" + shift 2 + echo -n " [$name] ... " + local start_time + start_time=$(date +%s) + if "$@" > "$OUTDIR/$outfile" 2>"$OUTDIR/${outfile%.json}.err"; then + local end_time + end_time=$(date +%s) + local bytes + bytes=$(wc -c < "$OUTDIR/$outfile" | tr -d ' ') + local tokens=$(( bytes / 4 )) + echo "OK (${bytes} bytes, ~${tokens} tokens, $((end_time - start_time))s)" + rm -f "$OUTDIR/${outfile%.json}.err" + else + local end_time + end_time=$(date +%s) + echo "FAILED ($((end_time - start_time))s) — see ${outfile%.json}.err" + FAILED=$((FAILED + 1)) + fi +} + +############################################################################### +# P0: Must Have +############################################################################### +echo "--- P0: Must Have ---" + +# Interface->Implementor Map +run_step "interface-map" "interface-map.json" \ + go run -C "$SCRIPTS_DIR" ./extract-interfaces.go + +# AST Type Index +run_step "type-index" "type-index.json" \ + go run -C "$SCRIPTS_DIR" ./extract-types.go + +# Module Dependency Graph (simple — no custom tool needed) +run_step "go-mod-graph" "go-mod-graph.txt" \ + go mod graph + +# Internal Package Dependency DAG +run_step "internal-deps" "internal-deps.json" \ + go run -C "$SCRIPTS_DIR" ./internal-deps.go + +# Exported API Surface +run_step "api-surface" "api-surface.json" \ + go run -C "$SCRIPTS_DIR" ./extract-api-surface.go + +############################################################################### +# P1: Should Have +############################################################################### +echo "" +echo "--- P1: Should Have ---" + +# Git Intelligence +run_step "git-intelligence" "git-intelligence.json" \ + bash "$SCRIPTS_DIR/generate-git-intelligence.sh" 180 + +# Concurrency Map +run_step "concurrency-map" "concurrency-map.json" \ + go run -C "$SCRIPTS_DIR" ./extract-concurrency.go + +# Call Graph (VTA — builds SSA, may take 1-2 minutes) +run_step "call-graph" "call-graph.json" \ + go run -C "$SCRIPTS_DIR" ./extract-call-graph.go + +############################################################################### +# Summary: Compact index for quick Claude consumption +############################################################################### +echo "" +echo "--- Summary Index ---" + +run_step "summary" "summary.json" \ + python3 "$SCRIPTS_DIR/generate-summary.py" "$OUTDIR" + +############################################################################### +# Meta: Token budget and freshness +############################################################################### +echo "" +echo "--- Generating meta.yml ---" + +{ + echo "# Claude knowledge layer metadata" + echo "generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" + echo "source_commit: ${GITHUB_SHA:-$(git rev-parse HEAD)}" + echo "stale_if_older_than: 7d" + echo "artifacts:" + for f in "$OUTDIR"/*.json "$OUTDIR"/*.txt; do + [ -f "$f" ] || continue + fname=$(basename "$f") + bytes=$(wc -c < "$f" | tr -d ' ') + tokens=$(( bytes / 4 )) + echo " $fname: { bytes: $bytes, tokens: ~$tokens }" + done + total_bytes=0 + for f in "$OUTDIR"/*.json "$OUTDIR"/*.txt; do + [ -f "$f" ] || continue + b=$(wc -c < "$f" | tr -d ' ') + total_bytes=$((total_bytes + b)) + done + echo "total_bytes: $total_bytes" + echo "total_tokens: ~$((total_bytes / 4))" +} > "$OUTDIR/meta.yml" + +# Clean up error files if empty +find "$OUTDIR" -name "*.err" -empty -delete 2>/dev/null || true + +echo "" +echo "=== Done: $(ls "$OUTDIR" | grep -c -E '\.(json|txt|yml)$') artifacts generated ===" + +if [ "$FAILED" -gt 0 ]; then + echo "WARNING: $FAILED artifact(s) failed to generate. Check .err files." + exit 1 +fi diff --git a/.claude/scripts/generate-git-intelligence.sh b/.claude/scripts/generate-git-intelligence.sh new file mode 100755 index 0000000..a1dbd86 --- /dev/null +++ b/.claude/scripts/generate-git-intelligence.sh @@ -0,0 +1,161 @@ +#!/bin/bash +# generate-git-intelligence.sh — Analyzes git history for churn, hotspots, and co-change coupling. +# Outputs JSON to stdout. +# Usage: .claude/scripts/generate-git-intelligence.sh [days=180] +set -euo pipefail + +DAYS="${1:-180}" +SINCE="$(date -u -v-${DAYS}d +%Y-%m-%d 2>/dev/null || date -u -d "${DAYS} days ago" +%Y-%m-%d)" + +# Ensure we're in a git repo +if ! git rev-parse --is-inside-work-tree &>/dev/null; then + echo '{"error": "not a git repository"}' >&2 + exit 1 +fi + +TMPDIR="$(mktemp -d)" +trap 'rm -rf "$TMPDIR"' EXIT + +############################################################################### +# 1. File churn: commits + lines added/deleted per file +############################################################################### +git log --since="$SINCE" --numstat --format='%H' -- '*.go' \ + | awk ' + /^[0-9a-f]{40}$/ { commits[$0]=1; current=$0; next } + NF==3 && $1 != "-" { + file=$3 + added[file]+=$1 + deleted[file]+=$2 + if (!(file SUBSEP current in seen)) { + seen[file, current]=1 + ncommits[file]++ + } + } + END { + for (f in ncommits) + printf "%s\t%d\t%d\t%d\n", f, ncommits[f], added[f], deleted[f] + } + ' | sort -t$'\t' -k2 -rn > "$TMPDIR/churn.tsv" + +############################################################################### +# 2. Bug hotspots: files most frequently changed in commits mentioning fix/bug +############################################################################### +git log --since="$SINCE" --numstat --format='%H %s' -- '*.go' \ + | awk ' + /^[0-9a-f]{40} / { + current=$1 + $1="" + msg=tolower($0) + is_fix = (msg ~ /fix|bug|hotfix|patch|revert|issue/) + next + } + NF==3 && is_fix && $1 != "-" { + file=$3 + if (!(file SUBSEP current in seen)) { + seen[file, current]=1 + fixes[file]++ + } + } + END { + for (f in fixes) + printf "%s\t%d\n", f, fixes[f] + } + ' | sort -t$'\t' -k2 -rn > "$TMPDIR/hotspots.tsv" + +############################################################################### +# 3. Co-change coupling: files frequently changed together +############################################################################### +git log --since="$SINCE" --name-only --pretty=format:'COMMIT_SEP' -- '*.go' \ + | awk ' + /^COMMIT_SEP$/ { if (n>0) { for(i=0;i0 { files[n++]=$0 } + END { + for (p in count) if (count[p]>=3) + printf "%s\t%d\n", p, count[p] + } + ' | sort -t$'\t' -k3 -rn | head -50 > "$TMPDIR/cochange.tsv" + +############################################################################### +# 4. Directory ownership: top committers per top-level directory +############################################################################### +git log --since="$SINCE" --format='%aN' --name-only -- '*.go' \ + | awk ' + /^$/ { next } + !/:/ && NF>0 && prev=="" { author=$0; prev="author"; next } + NF>0 && prev=="author" { + split($0, parts, "/") + dir=parts[1] + if (parts[1]=="internal" || parts[1]=="pkg" || parts[1]=="cmd") + dir=parts[1] "/" parts[2] + key=dir "\t" author + count[key]++ + prev="" + next + } + { prev="" } + END { + for (k in count) printf "%s\t%d\n", k, count[k] + } + ' | sort -t$'\t' -k1,1 -k3 -rn > "$TMPDIR/ownership.tsv" + +############################################################################### +# 5. Assemble JSON output +############################################################################### +python3 -c " +import json, sys, os +from datetime import datetime, timezone + +tmpdir = sys.argv[1] +days = int(sys.argv[2]) + +def read_tsv(path, cols): + rows = [] + if not os.path.exists(path): + return rows + with open(path) as f: + for line in f: + parts = line.rstrip('\n').split('\t') + if len(parts) >= len(cols): + row = {} + for i, col in enumerate(cols): + row[col] = int(parts[i]) if cols[col] == 'int' else parts[i] + rows.append(row) + return rows + +# File churn — top 50 +churn = read_tsv(f'{tmpdir}/churn.tsv', {'file': 'str', 'commits': 'int', 'lines_added': 'int', 'lines_deleted': 'int'})[:50] + +# Bug hotspots — top 30 +hotspots = read_tsv(f'{tmpdir}/hotspots.tsv', {'file': 'str', 'fix_commits': 'int'})[:30] + +# Co-change coupling — already limited to top 50 +cochange_raw = read_tsv(f'{tmpdir}/cochange.tsv', {'file_a': 'str', 'file_b': 'str', 'times': 'int'}) +cochange = [{'file_a': r['file_a'], 'file_b': r['file_b'], 'times': r['times']} for r in cochange_raw] + +# Directory ownership — group by dir, top 3 authors each +ownership_raw = read_tsv(f'{tmpdir}/ownership.tsv', {'directory': 'str', 'author': 'str', 'commits': 'int'}) +dir_owners = {} +for r in ownership_raw: + d = r['directory'] + if d not in dir_owners: + dir_owners[d] = [] + dir_owners[d].append({'author': r['author'], 'commits': r['commits']}) +# Top 3 per dir +ownership = [] +for d in sorted(dir_owners): + top = sorted(dir_owners[d], key=lambda x: x['commits'], reverse=True)[:3] + ownership.append({'directory': d, 'top_authors': top}) + +out = { + 'generated': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'period_days': days, + 'file_churn': churn, + 'bug_hotspots': hotspots, + 'co_change_coupling': cochange, + 'directory_ownership': ownership, +} + +json.dump(out, sys.stdout, indent=2) +print() +" "$TMPDIR" "$DAYS" diff --git a/.claude/scripts/generate-summary.py b/.claude/scripts/generate-summary.py new file mode 100644 index 0000000..7b36ed0 --- /dev/null +++ b/.claude/scripts/generate-summary.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +"""generate-summary.py — Creates a compact summary index from full computed artifacts. + +The summary is small enough (~2-4K tokens) for Claude to load on demand, +with pointers to full artifacts when detail is needed. +""" +import json +import os +import sys +from datetime import datetime, timezone + +def main(): + computed_dir = sys.argv[1] if len(sys.argv) > 1 else ".claude/knowledge/computed" + + summary = { + "generated": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "description": "Compact index of oauth-mcp-proxy computed knowledge artifacts. Load full artifacts for detail.", + } + + # Summarize type-index: just package names + counts + type_index_path = os.path.join(computed_dir, "type-index.json") + if os.path.exists(type_index_path): + with open(type_index_path) as f: + data = json.load(f) + pkg_summary = [] + for pkg in data.get("packages") or []: + path = pkg["path"] + short = path.split("/oauth-mcp-proxy/")[-1] if "/oauth-mcp-proxy/" in path else path + pkg_summary.append({ + "pkg": short, + "structs": len(pkg.get("structs") or []), + "interfaces": len(pkg.get("interfaces") or []), + "functions": len(pkg.get("functions") or []), + }) + # Sort by total symbols descending + pkg_summary.sort(key=lambda x: x["structs"] + x["interfaces"] + x["functions"], reverse=True) + summary["type_index"] = { + "total_types": data.get("total_types", 0), + "total_functions": data.get("total_functions", 0), + "total_packages": len(pkg_summary), + "top_packages": pkg_summary[:30], + "full_artifact": "type-index.json", + } + + # Summarize interface-map: interfaces with most implementors + iface_path = os.path.join(computed_dir, "interface-map.json") + if os.path.exists(iface_path): + with open(iface_path) as f: + data = json.load(f) + ifaces = data.get("interfaces") or [] + iface_summary = [] + for iface in ifaces: + impls = iface.get("implementors") or [] + short_pkg = iface["package"].split("/oauth-mcp-proxy/")[-1] if "/oauth-mcp-proxy/" in iface["package"] else iface["package"] + iface_summary.append({ + "name": iface["name"], + "package": short_pkg, + "methods": len(iface.get("methods", [])), + "implementors": len(impls), + }) + iface_summary.sort(key=lambda x: x["implementors"], reverse=True) + summary["interface_map"] = { + "total_interfaces": len(ifaces), + "top_by_implementors": iface_summary[:20], + "full_artifact": "interface-map.json", + } + + # Summarize internal-deps: most-imported and most-importing packages + deps_path = os.path.join(computed_dir, "internal-deps.json") + if os.path.exists(deps_path): + with open(deps_path) as f: + data = json.load(f) + pkgs = data.get("packages", {}) + + most_imported = sorted( + pkgs.items(), + key=lambda x: len(x[1].get("imported_by") or []), + reverse=True + )[:15] + + most_importing = sorted( + pkgs.items(), + key=lambda x: len(x[1].get("imports") or []), + reverse=True + )[:15] + + summary["internal_deps"] = { + "total_packages": data.get("total_packages", len(pkgs)), + "most_depended_on": [ + {"pkg": k, "imported_by_count": len(v.get("imported_by") or [])} + for k, v in most_imported + ], + "most_dependencies": [ + {"pkg": k, "imports_count": len(v.get("imports") or [])} + for k, v in most_importing + ], + "full_artifact": "internal-deps.json", + } + + # Summarize git intelligence: top churn files and hotspots + git_path = os.path.join(computed_dir, "git-intelligence.json") + if os.path.exists(git_path): + with open(git_path) as f: + data = json.load(f) + summary["git_intelligence"] = { + "period_days": data.get("period_days"), + "top_churn_files": data.get("file_churn", [])[:15], + "top_bug_hotspots": data.get("bug_hotspots", [])[:10], + "full_artifact": "git-intelligence.json", + } + + # API surface is already small, just reference it + api_path = os.path.join(computed_dir, "api-surface.json") + if os.path.exists(api_path): + summary["api_surface"] = { + "note": "API surface artifact is small enough to load directly.", + "full_artifact": "api-surface.json", + } + + # Summarize concurrency map: top packages by concurrency usage + conc_path = os.path.join(computed_dir, "concurrency-map.json") + if os.path.exists(conc_path): + with open(conc_path) as f: + data = json.load(f) + s = data.get("summary", {}) + pkg_summ = data.get("package_summary") or [] + summary["concurrency_map"] = { + "total_goroutines": s.get("total_goroutines", 0), + "total_channels": s.get("total_channels", 0), + "total_mutexes": s.get("total_mutexes", 0), + "total_contexts": s.get("total_contexts", 0), + "packages_with_concurrency": s.get("packages_with_concurrency", 0), + "top_packages": pkg_summ[:15], + "full_artifact": "concurrency-map.json", + } + + # Summarize call graph: entry points, most called, package stats + cg_path = os.path.join(computed_dir, "call-graph.json") + if os.path.exists(cg_path): + with open(cg_path) as f: + data = json.load(f) + most_called = data.get("most_called") or [] + pkg_stats = data.get("package_stats") or {} + top_pkgs = sorted(pkg_stats.items(), key=lambda x: x[1].get("internal_edges", 0), reverse=True)[:15] + summary["call_graph"] = { + "total_edges": data.get("total_edges", 0), + "total_functions": data.get("total_functions", 0), + "entry_points": len(data.get("entry_points") or []), + "most_called_top10": [{"name": f["name"][:80], "callers": f["callers"]} for f in most_called[:10]], + "top_packages_by_internal_edges": [ + {"pkg": k, "internal": v.get("internal_edges", 0), "incoming": v.get("incoming_edges", 0), "outgoing": v.get("outgoing_edges", 0)} + for k, v in top_pkgs + ], + "full_artifact": "call-graph.json", + } + + json.dump(summary, sys.stdout, indent=2) + print() + + +if __name__ == "__main__": + main() diff --git a/.claude/scripts/go.mod b/.claude/scripts/go.mod new file mode 100644 index 0000000..4efb3e4 --- /dev/null +++ b/.claude/scripts/go.mod @@ -0,0 +1,10 @@ +module github.com/Vungle/oauth-mcp-proxy/.claude/scripts + +go 1.24.9 + +require golang.org/x/tools v0.32.0 + +require ( + golang.org/x/mod v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect +) diff --git a/.claude/scripts/go.sum b/.claude/scripts/go.sum new file mode 100644 index 0000000..1e03c26 --- /dev/null +++ b/.claude/scripts/go.sum @@ -0,0 +1,8 @@ +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= diff --git a/.claude/scripts/internal-deps.go b/.claude/scripts/internal-deps.go new file mode 100644 index 0000000..7610f16 --- /dev/null +++ b/.claude/scripts/internal-deps.go @@ -0,0 +1,207 @@ +// internal-deps.go — Builds the internal package dependency DAG. +// Run from repo root: go run .claude/scripts/internal-deps.go [./...] +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "golang.org/x/tools/go/packages" +) + +type PackageNode struct { + Path string `json:"path"` + RelDir string `json:"rel_dir"` + Imports []string `json:"imports"` + ImportedBy []string `json:"imported_by"` + FileCount int `json:"file_count"` +} + +type Output struct { + Generated string `json:"generated"` + Module string `json:"module"` + TotalPkgs int `json:"total_packages"` + Packages map[string]PackageNode `json:"packages"` + ExternalDeps []string `json:"external_deps"` +} + +func main() { + repoRoot, err := findRepoRoot() + if err != nil { + fmt.Fprintf(os.Stderr, "error finding repo root: %v\n", err) + os.Exit(1) + } + + patterns := []string{"./..."} + if len(os.Args) > 1 { + patterns = os.Args[1:] + } + + cfg := &packages.Config{ + Mode: packages.NeedName | packages.NeedImports | packages.NeedFiles, + Dir: repoRoot, + Tests: false, + } + + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + fmt.Fprintf(os.Stderr, "error loading packages: %v\n", err) + os.Exit(1) + } + + modulePath := inferModulePath(pkgs) + nodes := make(map[string]PackageNode) + externalSet := make(map[string]struct{}) + + // First pass: collect all internal packages and their imports + for _, pkg := range pkgs { + if strings.HasSuffix(pkg.PkgPath, "_test") { + continue + } + if !strings.HasPrefix(pkg.PkgPath, modulePath+"/") && pkg.PkgPath != modulePath { + continue + } + + relPkg := pkg.PkgPath + if pkg.PkgPath != modulePath { + relPkg = strings.TrimPrefix(pkg.PkgPath, modulePath+"/") + } else { + relPkg = "." + } + + var internalImports []string + for imp := range pkg.Imports { + if strings.HasPrefix(imp, modulePath+"/") { + internalImports = append(internalImports, strings.TrimPrefix(imp, modulePath+"/")) + } else if imp == modulePath { + internalImports = append(internalImports, ".") + } else if !isStdLib(imp) { + externalSet[imp] = struct{}{} + } + } + sort.Strings(internalImports) + + pDir := "" + if len(pkg.GoFiles) > 0 { + pDir = relPath(repoRoot, filepath.Dir(pkg.GoFiles[0])) + } + + nodes[relPkg] = PackageNode{ + Path: relPkg, + RelDir: pDir, + Imports: internalImports, + FileCount: len(pkg.GoFiles), + } + } + + // Second pass: compute imported_by (reverse edges) + for pkgName, node := range nodes { + for _, imp := range node.Imports { + if target, ok := nodes[imp]; ok { + target.ImportedBy = append(target.ImportedBy, pkgName) + nodes[imp] = target + } + } + } + + // Sort imported_by + for k, node := range nodes { + sort.Strings(node.ImportedBy) + nodes[k] = node + } + + // External deps sorted + var externalDeps []string + for dep := range externalSet { + externalDeps = append(externalDeps, dep) + } + sort.Strings(externalDeps) + + out := Output{ + Generated: time.Now().UTC().Format(time.RFC3339), + Module: modulePath, + TotalPkgs: len(nodes), + Packages: nodes, + ExternalDeps: externalDeps, + } + + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(out); err != nil { + fmt.Fprintf(os.Stderr, "error encoding JSON: %v\n", err) + os.Exit(1) + } +} + +func inferModulePath(pkgs []*packages.Package) string { + for _, pkg := range pkgs { + for _, prefix := range []string{"/internal/", "/pkg/", "/cmd/"} { + parts := strings.SplitN(pkg.PkgPath, prefix, 2) + if len(parts) == 2 { + return parts[0] + } + } + } + if len(pkgs) > 0 { + shortest := pkgs[0].PkgPath + for _, pkg := range pkgs[1:] { + if len(pkg.PkgPath) < len(shortest) { + shortest = pkg.PkgPath + } + } + return shortest + } + return "" +} + +// isStdLib is a rough heuristic: std lib packages have no dots in the first path element. +func isStdLib(path string) bool { + first := path + if i := strings.IndexByte(path, '/'); i >= 0 { + first = path[:i] + } + return !strings.Contains(first, ".") +} + +func findRepoRoot() (string, error) { + dir, err := os.Getwd() + if err != nil { + return "", err + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + if !isInsideDotClaude(dir) { + return dir, nil + } + } + parent := filepath.Dir(dir) + if parent == dir { + break + } + dir = parent + } + wd, _ := os.Getwd() + return wd, nil +} + +func isInsideDotClaude(dir string) bool { + for d := dir; d != filepath.Dir(d); d = filepath.Dir(d) { + if filepath.Base(d) == ".claude" { + return true + } + } + return false +} + +func relPath(base, path string) string { + rel, err := filepath.Rel(base, path) + if err != nil { + return path + } + return rel +}