Core Concepts
The @ithena-one/mcp-governance
SDK introduces several key concepts built around the base @modelcontextprotocol/sdk
.
1. GovernedServer
The GovernedServer
is the central orchestrator for applying governance to your MCP server.
Key Responsibilities:
- Wraps Base Server: Acts as a layer around a standard
@modelcontextprotocol/sdk
Server
. - Applies Governance: Manages the execution of the Governance Pipeline (identity, RBAC, auditing, etc.).
- Handles Handlers: You register your MCP request/notification handlers directly with
GovernedServer
. - Manages Lifecycle: Coordinates the
initialize()
andshutdown()
methods of configured governance components via its internalLifecycleManager
.
Basic Usage:
- Create a base MCP
Server
. - Instantiate
GovernedServer
with the base server andGovernedServerOptions
(containing your logger, stores, resolvers). - Register handlers (e.g.,
setRequestHandler
) on theGovernedServer
instance. - Use
governedServer.connect(transport)
andgovernedServer.close()
for server lifecycle.
2. Governance Pipeline
This sequence of steps is executed by GovernedServer
for every incoming message.
Request Pipeline
Setup Context
A unique eventId
, request-scoped Logger
, TraceContext
, initial OperationContext
, and AuditRecord
are prepared.
Resolve Identity
Uses the configured IdentityResolver
to determine the caller’s UserIdentity
.
Failure (throwing AuthenticationError
) typically stops the pipeline.
Check RBAC (Authorization)
If enableRbac
is true:
- Requires a resolved
UserIdentity
.🚫Missing identity results in
AuthorizationError
. - Derives the required permission (e.g.,
tool:call:my_tool
) usingderivePermission
. - Fetches roles via
RoleStore
. - Checks permission against roles via
PermissionStore
.🚫If no role grants permission,
AuthorizationError
is thrown. - Records the authorization decision.
Run Post-Authorization Hook
If configured, this async hook (postAuthorizationHook
) runs after successful authorization.
Resolve Credentials
Uses the configured CredentialResolver
to fetch necessary secrets/credentials for the handler.
Failure can stop the pipeline (CredentialResolutionError
) depending on the failOnCredentialResolutionError
option.
Execute Governed Handler
Your registered MCP handler (tool, resource, etc.) is called with enriched GovernedRequestHandlerExtra
.
Perform Auditing
Assembles the final AuditRecord
, sanitizes it (sanitizeForAudit
), and logs it via AuditLogStore
if auditing is enabled for the outcome.
Send Response/Error
Returns the handler’s result or a mapped JSON-RPC error.
Notification Pipeline
A simpler flow for notifications:
Setup Context
Similar to requests (eventId, Logger, TraceContext, etc.).
Resolve Identity (Optional)
Mainly for logging/auditing; failure usually doesn’t stop the pipeline.
Execute Governed Handler
Runs the registered notification handler, if any.
Perform Auditing
Logs the audit record if auditNotifications
is true.
Context Immutability
Design Principle: To prevent unintended side effects between pipeline steps, the OperationContext
passed through the various stages is treated as immutable.
Implementation:
- The core context object created at the beginning of each step is shallowly frozen using
Object.freeze()
. - The
transportContext.headers
object is wrapped in aProxy
that silently ignores any attempts to modify header values.
- Goal: Ensures governance components don’t accidentally modify context affecting subsequent steps or the handler.
- Shallow Freezing:
Object.freeze()
is shallow. Nested objects within the context might still be mutable. - Silent Proxy: The header proxy silently ignores modifications. This enforces immutability but could hide bugs if a component wrongly tries to change headers (no error is thrown).
- Performance: Generally negligible overhead from
Object.freeze
and the proxy.
It’s recommended that tests for custom governance components consider scenarios involving attempted context modifications to ensure they behave as expected within this immutability model.
(Refer to the Mermaid diagram in README.md
for a visual representation)
3. OperationContext
This object carries context throughout the governance pipeline stages for a single request/notification.
Key Properties:
eventId
: Unique ID for this specific operation.timestamp
: When processing started.transportContext
: Info about the connection (type, headers, IP).traceContext
: Distributed tracing IDs (traceId
,spanId
).logger
: Request-scoped logger instance.mcpMessage
: The raw incoming MCP message.serviceIdentifier
: Optional server instance ID.identity
: (Added later) ResolvedUserIdentity
.derivedPermission
: (Added later) Permission string for RBAC checks.roles
: (Added later) Roles associated with the identity.
4. GovernedRequestHandlerExtra
/ GovernedNotificationHandlerExtra
These objects provide the enriched context directly to your MCP handlers.
Key Properties (Request Handler):
eventId
: Unique operation ID.logger
: Request-scoped logger.identity
: ResolvedUserIdentity
(or null).roles
: Resolved roles (if RBAC enabled, otherwise empty array).resolvedCredentials
: Credentials fromCredentialResolver
.traceContext
: Distributed tracing info.transportContext
: Transport info.signal
:AbortSignal
from the base SDK.sessionId
: Session ID from the transport.
(GovernedNotificationHandlerExtra
contains a subset relevant to notifications.)
5. Lifecycle Management (LifecycleManager
)
This internal helper manages the initialize()
and shutdown()
phases of your governance components.
- Initialization: During
GovernedServer.connect()
, it calls the optionalinitialize()
method on each component sequentially. If one fails, connection aborts, and already-initialized components are shut down. - Shutdown: During
GovernedServer.close()
, it calls the optionalshutdown()
method on all successfully initialized components in parallel. Errors are logged but don’t stop the overall shutdown.