ADR-003: Stateless Singleton Plugins
Date: 2026-01-26
Source: mcpaql-adapter/docs/adr/ADR-003-stateless-plugins.md
On this page
Jump to a section
Use the outline to move through longer pages without losing your place.
Status: Accepted Date: 2026-01-26
Context
Plugins handle transport, protocol, serialization, and authentication. The runtime needs a strategy for plugin lifecycle management:
Stateful instances — Create a new plugin instance per request. Plugins can maintain request-specific state.
Stateless singletons — One plugin instance per type, shared across all requests. Plugins must not maintain request-specific state.
Decision
Plugins are stateless singletons. The runtime resolves each plugin once at adapter load time and reuses the same instance for all requests. Plugins MUST NOT maintain request-specific state between calls.
Rationale
- Simplicity — No instance management, no lifecycle hooks, no cleanup
- Thread safety — No shared mutable state eliminates concurrency issues
- Testability — Stateless functions are trivially unit testable
- Memory — One instance per plugin type, not per request
- Predictability — No hidden state means no order-dependent bugs
Request-specific context is passed explicitly via function parameters (TransportRequest, AuthConfig, AuthContext), not stored in plugin instances.
Consequences
Positive:
- Simple mental model — plugins are pure functions with a namespace
- No memory leaks from accumulated request state
- Safe to share across concurrent requests
- Easy to mock in tests
Negative:
- Plugins that need persistent state (connection pools, caches) must use external mechanisms
- OAuth token refresh requires state — the
AuthContextinterface providesgetEnv()as an escape hatch; richer state management deferred to future specification