Documentation
¶
Overview ¶
Package serverpool manages a pool of MCP servers keyed by GitLab token and URL.
Each unique GitLab Personal Access Token and GitLab URL pair gets its own *mcp.Server, GitLab client, and server-scoped configuration snapshot with independently registered tools, resources, prompts, detected token scopes, and detected CE/EE edition. This provides zero cross-contamination between HTTP clients by construction: each client operates on its own server entry.
The pool has a configurable maximum size (WithMaxSize) and uses LRU eviction when the limit is reached. Token plus URL hashes (SHA-256) are used as pool keys so that raw tokens are never stored in memory.
Usage ¶
Create a pool with New, retrieve or create servers with ServerPool.GetOrCreate, and extract tokens from HTTP requests with ExtractToken:
pool := serverpool.New(cfg, factory, serverpool.WithMaxSize(100))
defer pool.Close()
handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
token := serverpool.ExtractToken(r)
gitlabURL, err := serverpool.ExtractGitLabURL(r, cfg.GitLabURL)
if err != nil {
return nil
}
srv, err := pool.GetOrCreate(token, gitlabURL)
if err != nil {
return nil
}
return srv
}, opts)
pool.go implements a bounded LRU pool of per-token MCP servers for HTTP mode.
ratelimit.go implements per-IP rate limiting for the HTTP MCP server, protecting against abuse of the server pool endpoint.
token.go provides token and GitLab URL extraction for HTTP mode authentication.
Index ¶
Constants ¶
const DefaultRevalidateInterval = 15 * time.Minute
DefaultRevalidateInterval is the default period between token re-validation checks via a lightweight GitLab API call.
const RequestOptionGitLabURL = "GITLAB-URL"
RequestOptionGitLabURL identifies the per-request GitLab URL header option.
Variables ¶
This section is empty.
Functions ¶
func ExtractGitLabURL ¶ added in v1.1.0
ExtractGitLabURL resolves the GitLab instance URL for an HTTP request. It is a compatibility wrapper around ResolveRequestOptions.
func ExtractToken ¶
ExtractToken retrieves the GitLab Personal Access Token from the HTTP request. It checks the following sources in order:
- PRIVATE-TOKEN header (GitLab standard)
- Authorization header with Bearer scheme
Returns the token string, or empty string if no token is found.
Types ¶
type AuthRateLimiter ¶
type AuthRateLimiter struct {
// contains filtered or unexported fields
}
AuthRateLimiter tracks authentication failures per client IP and blocks clients that exceed the maximum failure count within the configured window.
func NewAuthRateLimiter ¶
func NewAuthRateLimiter(maxFails int, window time.Duration) *AuthRateLimiter
NewAuthRateLimiter creates a rate limiter that blocks a client IP after maxFails authentication failures within the given time window.
func (*AuthRateLimiter) Cleanup ¶
func (l *AuthRateLimiter) Cleanup()
Cleanup removes expired entries. Call periodically to prevent memory growth.
func (*AuthRateLimiter) IsBlocked ¶
func (l *AuthRateLimiter) IsBlocked(ip string) bool
IsBlocked returns true if the IP has exceeded the failure limit within the window.
func (*AuthRateLimiter) RecordFailure ¶
func (l *AuthRateLimiter) RecordFailure(ip string)
RecordFailure records an authentication failure for the given IP.
type InvalidGitLabURLError ¶ added in v1.1.0
type InvalidGitLabURLError struct {
// URL is the offending URL value. It is retained for programmatic
// inspection by callers but is deliberately omitted from [Error] output.
URL string
Reason string
}
InvalidGitLabURLError is returned when the GITLAB-URL header contains an invalid URL. The raw URL value is intentionally not included in the error message to avoid leaking embedded credentials or sensitive query parameters into server logs.
func (*InvalidGitLabURLError) Error ¶ added in v1.1.0
func (e *InvalidGitLabURLError) Error() string
Error implements the [error] interface. The returned message contains only the validation [InvalidGitLabURLError.Reason], never the raw URL, to avoid leaking credentials in logs.
type Metrics ¶
type Metrics struct {
Hits atomic.Int64
Misses atomic.Int64
Evictions atomic.Int64
RevalidationsFailed atomic.Int64
RevalidationsSucceeded atomic.Int64
}
Metrics holds operational counters for the ServerPool. All counters are monotonically increasing and use lock-free atomic increments.
type Option ¶
type Option func(*ServerPool)
Option configures pool behavior.
func WithMaxSize ¶
WithMaxSize sets the maximum number of unique token entries in the pool. Values ≤ 0 are ignored; the default is 100.
func WithRevalidateInterval ¶
WithRevalidateInterval sets the interval between periodic token re-validation checks. Values ≤ 0 disable revalidation.
type RequestOptions ¶ added in v1.4.1
RequestOptions contains the effective per-request options after applying server-wide MCP configuration precedence.
func ResolveRequestOptions ¶ added in v1.4.1
func ResolveRequestOptions(r *http.Request, defaultURL string) (RequestOptions, error)
ResolveRequestOptions applies server-wide MCP configuration precedence to the request-provided options. When defaultURL is set, it is authoritative and any GITLAB-URL header is ignored. When defaultURL is empty, callers must provide GITLAB-URL per request. Effective URLs are normalized so equivalent values hash to the same server-pool session key.
func (RequestOptions) HasIgnoredOptions ¶ added in v1.4.1
func (o RequestOptions) HasIgnoredOptions() bool
HasIgnoredOptions reports whether any request-provided options were ignored because server-wide MCP configuration is authoritative.
func (RequestOptions) IgnoredOptionsCopy ¶ added in v1.4.1
func (o RequestOptions) IgnoredOptionsCopy() []string
IgnoredOptionsCopy returns a defensive copy of the ignored option names.
type ServerFactory ¶
type ServerFactory func(client *gitlabclient.Client, cfg *config.ServerConfig) *mcp.Server
ServerFactory creates a fully configured *mcp.Server with all tools, resources, and prompts registered for the given GitLab client and per-entry configuration. This is provided by the caller to decouple pool management from registration logic.
type ServerPool ¶
type ServerPool struct {
// contains filtered or unexported fields
}
ServerPool maintains a bounded set of *mcp.Server instances keyed by token plus GitLab URL hash (SHA-256). When the pool reaches maxSize, the least recently used entry is evicted. Entries are periodically re-validated against the GitLab API; entries with revoked tokens are evicted automatically.
func New ¶
func New(cfg *config.Config, factory ServerFactory, opts ...Option) *ServerPool
New creates a ServerPool. The cfg provides shared server-wide settings (GitLabURL, SkipTLSVerify, etc.). The factory function creates a fully registered *mcp.Server for each new GitLab client.
func (*ServerPool) Close ¶
func (p *ServerPool) Close()
Close removes all entries from the pool. Active MCP sessions for evicted servers are not forcefully terminated — they will expire naturally via [StreamableHTTPOptions.SessionTimeout].
func (*ServerPool) GetOrCreate ¶
func (p *ServerPool) GetOrCreate(token, gitlabURL string) (*mcp.Server, error)
GetOrCreate returns the *mcp.Server for the given token and GitLab URL, creating one if it doesn't exist. The pool key is derived from both the token and gitlabURL, so the same token against different GitLab instances gets separate server entries. It is safe for concurrent use. Returns an error if the GitLab client cannot be created.
func (*ServerPool) Size ¶
func (p *ServerPool) Size() int
Size returns the current number of entries in the pool.
func (*ServerPool) StartRevalidation ¶
func (p *ServerPool) StartRevalidation(ctx context.Context)
StartRevalidation launches a background goroutine that periodically checks all pool entries for token validity using a lightweight GitLab API call. Entries that fail validation are evicted. Cancel the context to stop.
func (*ServerPool) Stats ¶
func (p *ServerPool) Stats() Snapshot
Stats returns a point-in-time Snapshot of pool metrics and state.
type Snapshot ¶
type Snapshot struct {
Hits int64 `json:"hits"`
Misses int64 `json:"misses"`
Evictions int64 `json:"evictions"`
RevalidationsFailed int64 `json:"revalidations_failed"`
RevalidationsSucceeded int64 `json:"revalidations_succeeded"`
CurrentSize int `json:"current_size"`
MaxSize int `json:"max_size"`
CreatedAt time.Time `json:"created_at"`
}
Snapshot is a point-in-time copy of pool Metrics plus current state. Safe for JSON serialization and cross-goroutine use.