Documentation
¶
Overview ¶
Package approval implements the manifest approval state machine: the hard part of mcp-shield isn't hashing JSON, it's deciding "is this capability change acceptable?" and never letting that decision be bypassed or silently reversed.
Index ¶
- Variables
- type CheckResult
- type FailMode
- type Option
- type Workflow
- func (w *Workflow) Approve(ctx context.Context, manifestID int64, username, reason string) error
- func (w *Workflow) CheckAndRecord(ctx context.Context, serverID int64, m *manifest.Manifest) (*CheckResult, error)
- func (w *Workflow) Reject(ctx context.Context, manifestID int64, username, reason string) error
Constants ¶
This section is empty.
Variables ¶
var ( ErrNotPending = errors.New("approval: manifest is not in PENDING state") ErrInvalidTransition = errors.New("approval: invalid state transition") )
Functions ¶
This section is empty.
Types ¶
type CheckResult ¶
type CheckResult struct {
ManifestID int64
State string
Warn bool // failMode is warn: everything is allowed, this is advisory only
SafeTools map[string]bool
SafePrompts map[string]bool
SafeResources map[string]bool
}
CheckResult reports the gate's decision for one manifest snapshot. A manifest that is not fully APPROVED is no longer all-or-nothing: tools (and prompts/resources) that are byte-identical to the current approved baseline stay in Safe* and keep working, while only the new or changed ones are withheld pending a decision. A server with no approved baseline at all (first-ever connect) has empty Safe* sets, which is the fail closed starting state.
type FailMode ¶
type FailMode string
FailMode controls what happens when traffic hits an unapproved manifest.
type Option ¶ added in v0.1.1
type Option func(*Workflow)
Option adjusts optional workflow behaviour. Zero options is the plain gate, unchanged.
func WithNotifications ¶ added in v0.1.1
func WithNotifications() Option
WithNotifications makes the workflow record a notification event in the same transaction as each state change it writes, so an operator can be told that a capability was withheld.
The workflow's involvement ends there. It writes a row; it never learns where events go, never waits for one, and cannot fail because of one. All this option can cost a gate decision is one INSERT into a transaction that was already open.
type Workflow ¶
type Workflow struct {
// contains filtered or unexported fields
}
func (*Workflow) Approve ¶
Approve marks a PENDING manifest APPROVED, supersedes the server's previous APPROVED manifest (if any), and records an immutable audit entry — all inside one transaction.
func (*Workflow) CheckAndRecord ¶
func (w *Workflow) CheckAndRecord(ctx context.Context, serverID int64, m *manifest.Manifest) (*CheckResult, error)
CheckAndRecord is the gate: given a live manifest fetched from an upstream server, it looks up (or creates) the matching manifest row and computes which tools/prompts/resources are safe to expose right now.
- Hash matches the current APPROVED baseline exactly -> everything safe.
- Otherwise (hash is PENDING/REJECTED/SUPERSEDED, or new) -> only the subset unchanged from the current approved baseline is safe; new or changed items are withheld. A brand new manifest row is inserted (diffed against the baseline) the first time a given hash is seen; a hash seen before just reuses its existing row/state.
- failMode warn overrides withholding: everything is marked safe, Warn is set, but the manifest's real state is still recorded as-is.