Documentation
¶
Overview ¶
Package process provides process lifecycle management for MCP servers.
Package process provides process lifecycle management for MCP servers.
Index ¶
- Constants
- Variables
- func LockFileBlocking(path string, timeout time.Duration) (release func(), err error)
- func ProcessStartIdentity(pid int) (int64, error)
- type DiscoveryResult
- type Handle
- func (h *Handle) AuthStatus() mcp.AuthStatus
- func (h *Handle) Capabilities() mcp.ServerCapabilities
- func (h *Handle) Client() *mcp.Client
- func (h *Handle) DiscoveryResult() (DiscoveryResult, bool)
- func (h *Handle) Generation() uint64
- func (h *Handle) ID() string
- func (h *Handle) InitError() error
- func (h *Handle) InstanceID() InstanceID
- func (h *Handle) IsRunning() bool
- func (h *Handle) Kind() HandleKind
- func (h *Handle) Logs() []string
- func (h *Handle) NeedsLogin() bool
- func (h *Handle) NextDiscoverySequence() uint64
- func (h *Handle) OAuthMeta() *oauth.AuthorizationServerMetadata
- func (h *Handle) PID() int
- func (h *Handle) ServerURL() string
- func (h *Handle) SetTools(tools []mcp.Tool)
- func (h *Handle) StartedAt() time.Time
- func (h *Handle) Stop() error
- func (h *Handle) Tools() []mcp.Tool
- func (h *Handle) ToolsReady() bool
- func (h *Handle) Uptime() time.Duration
- func (h *Handle) WaitForTools(ctx context.Context) error
- type HandleKind
- type InstanceID
- type ManagerLock
- type ManagerLockInfo
- type Observer
- type PIDTracker
- func (pt *PIDTracker) Add(serverID string, pid int, command string, args []string) error
- func (pt *PIDTracker) AddInstance(instance InstanceID, pid, pgid int, command string, args []string) error
- func (pt *PIDTracker) AddLegacy(serverID string, pid int, command string) error
- func (pt *PIDTracker) CleanupOrphans() int
- func (pt *PIDTracker) Remove(serverID string) error
- func (pt *PIDTracker) RemoveInstance(instance InstanceID) error
- func (pt *PIDTracker) RemoveInstancePID(instance InstanceID, expectedPID int) error
- type Supervisor
- func (s *Supervisor) CredentialStore() oauth.CredentialStore
- func (s *Supervisor) Get(id string) *Handle
- func (s *Supervisor) GetInstance(id InstanceID) *Handle
- func (s *Supervisor) LoginOAuth(ctx context.Context, name string) error
- func (s *Supervisor) Restart(ctx context.Context, name string, srv config.ServerConfig) (*Handle, error)
- func (s *Supervisor) RestartInstance(ctx context.Context, id InstanceID, srv config.ServerConfig) (*Handle, error)
- func (s *Supervisor) RestartInstanceValidated(ctx context.Context, id InstanceID, srv config.ServerConfig, ...) (*Handle, error)
- func (s *Supervisor) RunningCount() int
- func (s *Supervisor) RunningServers() []string
- func (s *Supervisor) SetObserver(observer Observer)
- func (s *Supervisor) SetToolCache(tc *config.ToolCache)
- func (s *Supervisor) Start(ctx context.Context, name string, srv config.ServerConfig) (*Handle, error)
- func (s *Supervisor) StartInstance(ctx context.Context, id InstanceID, srv config.ServerConfig, ...) (*Handle, error)
- func (s *Supervisor) Stop(id string) error
- func (s *Supervisor) StopAll()
- func (s *Supervisor) StopInstance(id InstanceID) error
- func (s *Supervisor) StopSessionInstances(session string)
- type SupervisorOptions
- type UpstreamNotification
Constants ¶
const ( // GracefulShutdownTimeout is how long to wait for SIGTERM before SIGKILL. GracefulShutdownTimeout = 5 * time.Second // MaxInitRetries is the maximum number of MCP initialization attempts. MaxInitRetries = 3 // InitAttemptTimeout is the maximum duration of one MCP initialization attempt. InitAttemptTimeout = 30 * time.Second // InitRetryBaseDelay is the base delay between retry attempts. InitRetryBaseDelay = 500 * time.Millisecond )
const ( // MaxRetryCount is the maximum number of cleanup attempts before giving up. // This prevents the PID file from growing unbounded with unverifiable entries. MaxRetryCount = 5 )
Variables ¶
var ErrNeedsLogin = errors.New("oauth login required")
ErrNeedsLogin indicates that an HTTP MCP server supports OAuth but no usable credentials are available. The handle remains available for OAuth metadata, but it is not a running MCP connection and may be replaced by the next use.
Functions ¶
func LockFileBlocking ¶ added in v0.1.31
LockFileBlocking opens (or creates) the file at path and acquires an exclusive lock on it, retrying the non-blocking platform primitive until timeout. It returns a release function that drops the lock by closing the file descriptor. The lock file is intentionally never deleted — see ManagerLock.Release for the flock inode race that deletion would open.
The implementation lives in internal/flock alongside the atomic-write helpers; this wrapper keeps the historical entry point for metrics and tests.
func ProcessStartIdentity ¶ added in v0.1.29
ProcessStartIdentity returns the OS-provided start identity for pid. The value is only meaningful when compared with another observation from the same platform; it is used to reject reused PIDs before signalling them.
Types ¶
type DiscoveryResult ¶ added in v0.1.29
type DiscoveryResult struct {
Instance InstanceID
Generation uint64
// Sequence orders results *within* one Generation, which Generation alone
// cannot: the initial discovery and a list_changed refresh both describe
// generation N, so without it an older snapshot applied late overwrites
// newer tools. Producers stamp it with Handle.NextDiscoverySequence at the
// moment the data is obtained — not at publish or apply time, which can be
// arbitrarily delayed by scheduling. Zero means unsequenced: ordering falls
// back to Generation alone.
Sequence uint64
Initialized bool
Capabilities mcp.ServerCapabilities
Tools []mcp.Tool
Err error
}
DiscoveryResult is the immutable result of one upstream catalog probe: the Supervisor-owned initialize plus initial tools/list, or a later refresh of the same generation. Generation prevents a late result from an old process from replacing catalog data for a newer process; Sequence does the same job between the producers that share one generation.
func (DiscoveryResult) Clone ¶ added in v0.1.29
func (r DiscoveryResult) Clone() DiscoveryResult
Clone returns a deep-enough copy for transfer between Supervisor and Core.
func (DiscoveryResult) ToolDiscoverySucceeded ¶ added in v0.1.29
func (r DiscoveryResult) ToolDiscoverySucceeded() bool
ToolDiscoverySucceeded reports whether the result verifies a tool catalog. Initialization anchors verification: a server that does not advertise tools is verified empty even though no tools/list request was made.
type Handle ¶
type Handle struct {
// contains filtered or unexported fields
}
Handle represents a running server (process or HTTP connection).
func (*Handle) AuthStatus ¶
func (h *Handle) AuthStatus() mcp.AuthStatus
AuthStatus returns the authentication status (for HTTP handles).
func (*Handle) Capabilities ¶ added in v0.1.27
func (h *Handle) Capabilities() mcp.ServerCapabilities
Capabilities returns the capabilities advertised by the upstream server at initialize time. Returns the zero value if the handle has no client yet (e.g., before initialization completes or for needs-auth HTTP handles).
func (*Handle) Client ¶
Client returns the MCP client, or nil if the handle has no usable connection (needs-auth HTTP handles clear it).
func (*Handle) DiscoveryResult ¶ added in v0.1.29
func (h *Handle) DiscoveryResult() (DiscoveryResult, bool)
DiscoveryResult returns the Supervisor-owned initial discovery result.
func (*Handle) Generation ¶ added in v0.1.29
Generation identifies this exact process/transport generation.
func (*Handle) InstanceID ¶ added in v0.1.29
func (h *Handle) InstanceID() InstanceID
InstanceID returns the stable identity used by the Supervisor and PID registry.
func (*Handle) Kind ¶
func (h *Handle) Kind() HandleKind
Kind returns the handle type (stdio or HTTP).
func (*Handle) NeedsLogin ¶ added in v0.1.29
NeedsLogin reports whether initialization stopped at the OAuth login gate.
func (*Handle) NextDiscoverySequence ¶ added in v0.1.31
NextDiscoverySequence stamps a DiscoveryResult produced for this handle. Call it where the catalog data is obtained (right after the upstream responds), so that a snapshot taken earlier always carries a lower sequence than a later one even if the goroutine carrying it is descheduled before it reaches the catalog. See DiscoveryResult.Sequence.
func (*Handle) OAuthMeta ¶ added in v0.1.4
func (h *Handle) OAuthMeta() *oauth.AuthorizationServerMetadata
OAuthMeta returns the cached OAuth metadata for servers needing login.
func (*Handle) ToolsReady ¶ added in v0.1.12
ToolsReady returns true if tool discovery has completed (non-blocking).
type HandleKind ¶
type HandleKind int
HandleKind represents the type of server handle.
const ( HandleKindStdio HandleKind = iota HandleKindHTTP )
type InstanceID ¶ added in v0.1.29
InstanceID is the stable identity of one upstream MCP server instance. Shared instances have only Server set. Session is reserved for the private per-session instances introduced by the daemon isolation phase.
func PrivateInstanceID ¶ added in v0.1.29
func PrivateInstanceID(server, session string) InstanceID
PrivateInstanceID returns the identity for a server owned by one downstream session. session must be stable and unique within the owning Supervisor.
func SharedInstanceID ¶ added in v0.1.29
func SharedInstanceID(server string) InstanceID
SharedInstanceID returns the daemon-wide identity for a shared server.
func (InstanceID) IsShared ¶ added in v0.1.29
func (id InstanceID) IsShared() bool
IsShared reports whether the instance is shared across sessions.
func (InstanceID) String ¶ added in v0.1.29
func (id InstanceID) String() string
String returns a stable, human-readable identity.
type ManagerLock ¶ added in v0.1.22
type ManagerLock struct {
// contains filtered or unexported fields
}
ManagerLock manages the manager.lock file co-located with the config. Both TUI and web acquire this lock on startup; serve ignores it.
Uses flock(LOCK_EX|LOCK_NB) for race-free mutual exclusion. The file descriptor is held open for the process lifetime so the OS enforces the lock even if two processes start simultaneously.
func NewManagerLock ¶ added in v0.1.22
func NewManagerLock(configPath string) (*ManagerLock, error)
NewManagerLock creates a lock manager for the given config path. The lock file is placed in the same directory as the config file.
func (*ManagerLock) Acquire ¶ added in v0.1.22
func (l *ManagerLock) Acquire(mode string) error
Acquire attempts to claim the manager lock for the given mode. Uses flock for atomic mutual exclusion — two concurrent callers cannot both succeed. If the lock is already held, returns an error with the holder's mode and PID.
func (*ManagerLock) Release ¶ added in v0.1.22
func (l *ManagerLock) Release()
Release drops the flock by closing the file descriptor. Safe to call multiple times. The lock file is intentionally NOT deleted — with flock, removing the path after close creates a window where a second process flocks the old inode while a third creates a new file at the same path, letting two managers coexist on different inodes. Leaving the file in place (like the PID registry files and toolcache.json) avoids this race entirely.
type ManagerLockInfo ¶ added in v0.1.22
ManagerLockInfo describes who holds the manager lock.
type Observer ¶ added in v0.1.29
type Observer interface {
OnDiscoveryResult(DiscoveryResult)
OnInstanceStopped(InstanceID, uint64)
OnUpstreamNotification(UpstreamNotification)
}
Observer receives lifecycle output owned by Supervisor. Implementations must return promptly from OnUpstreamNotification because it is invoked by the MCP client's response-reader goroutine.
type PIDTracker ¶
type PIDTracker struct {
// contains filtered or unexported fields
}
PIDTracker tracks running server PIDs to detect and clean up orphans.
func NewPIDTracker ¶
func NewPIDTracker() (*PIDTracker, error)
NewPIDTracker creates a new PID tracker using the default directory.
func NewPIDTrackerInDir ¶ added in v0.1.22
func NewPIDTrackerInDir(dir, prefix string) (*PIDTracker, error)
NewPIDTrackerInDir creates a per-owner PID tracker in the given directory. If dir is empty, uses the default ~/.config/mcpmu/ directory. The optional prefix is retained for operator readability; owner identity, rather than manager mode, provides concurrency-safe isolation.
func NewPIDTrackerWithDir ¶ added in v0.1.7
func NewPIDTrackerWithDir(dir string) (*PIDTracker, error)
NewPIDTrackerWithDir creates a new PID tracker storing its state in the given directory.
func (*PIDTracker) AddInstance ¶ added in v0.1.29
func (pt *PIDTracker) AddInstance(instance InstanceID, pid, pgid int, command string, args []string) error
AddInstance tracks a process leader and its process group for one instance.
func (*PIDTracker) AddLegacy ¶
func (pt *PIDTracker) AddLegacy(serverID string, pid int, command string) error
Legacy compatibility: Add with old signature (for existing callers) Deprecated: Use AddWithArgs instead.
func (*PIDTracker) CleanupOrphans ¶
func (pt *PIDTracker) CleanupOrphans() int
CleanupOrphans scans other owners' registry files. Live, identity-matching owners are skipped; dead owners are cleaned conservatively.
func (*PIDTracker) Remove ¶
func (pt *PIDTracker) Remove(serverID string) error
Remove stops tracking a PID.
func (*PIDTracker) RemoveInstance ¶ added in v0.1.29
func (pt *PIDTracker) RemoveInstance(instance InstanceID) error
RemoveInstance stops tracking an instance in this owner's registry.
func (*PIDTracker) RemoveInstancePID ¶ added in v0.1.29
func (pt *PIDTracker) RemoveInstancePID(instance InstanceID, expectedPID int) error
RemoveInstancePID removes an entry only if it still refers to the expected leader PID. A zero PID requests unconditional removal for compatibility.
type Supervisor ¶
type Supervisor struct {
// contains filtered or unexported fields
}
Supervisor manages MCP server process lifecycles.
func NewSupervisor ¶
func NewSupervisor(bus *events.Bus) *Supervisor
NewSupervisor creates a new process supervisor. It also cleans up any orphan processes from previous runs.
func NewSupervisorWithOptions ¶
func NewSupervisorWithOptions(bus *events.Bus, opts SupervisorOptions) *Supervisor
NewSupervisorWithOptions creates a new process supervisor with options.
func (*Supervisor) CredentialStore ¶
func (s *Supervisor) CredentialStore() oauth.CredentialStore
CredentialStore returns the OAuth credential store.
func (*Supervisor) Get ¶
func (s *Supervisor) Get(id string) *Handle
Get returns the handle for a server, or nil if not running.
func (*Supervisor) GetInstance ¶ added in v0.1.29
func (s *Supervisor) GetInstance(id InstanceID) *Handle
GetInstance returns the handle for one stable instance identity.
func (*Supervisor) LoginOAuth ¶ added in v0.1.4
func (s *Supervisor) LoginOAuth(ctx context.Context, name string) error
LoginOAuth triggers the OAuth login flow for a server that needs authentication. It opens a browser for the user to authenticate, then reconnects.
func (*Supervisor) Restart ¶ added in v0.1.29
func (s *Supervisor) Restart(ctx context.Context, name string, srv config.ServerConfig) (*Handle, error)
Restart stops and starts a shared instance as one serialized lifecycle operation.
func (*Supervisor) RestartInstance ¶ added in v0.1.29
func (s *Supervisor) RestartInstance(ctx context.Context, id InstanceID, srv config.ServerConfig) (*Handle, error)
RestartInstance stops and starts one instance as one serialized lifecycle operation.
func (*Supervisor) RestartInstanceValidated ¶ added in v0.1.29
func (s *Supervisor) RestartInstanceValidated( ctx context.Context, id InstanceID, srv config.ServerConfig, validate func() error, ) (*Handle, error)
RestartInstanceValidated stops and starts one instance only after validate succeeds while holding the same lifecycle lock used by start and stop.
func (*Supervisor) RunningCount ¶
func (s *Supervisor) RunningCount() int
RunningCount returns the number of running servers.
func (*Supervisor) RunningServers ¶
func (s *Supervisor) RunningServers() []string
RunningServers returns the IDs of running servers.
func (*Supervisor) SetObserver ¶ added in v0.1.29
func (s *Supervisor) SetObserver(observer Observer)
SetObserver installs the Core observer. It must be called before Start.
func (*Supervisor) SetToolCache ¶ added in v0.1.9
func (s *Supervisor) SetToolCache(tc *config.ToolCache)
SetToolCache sets the tool cache for token counting.
func (*Supervisor) Start ¶
func (s *Supervisor) Start(ctx context.Context, name string, srv config.ServerConfig) (*Handle, error)
Start starts or joins the start of a shared MCP server instance.
func (*Supervisor) StartInstance ¶ added in v0.1.29
func (s *Supervisor) StartInstance( ctx context.Context, id InstanceID, srv config.ServerConfig, validate func() error, ) (*Handle, error)
StartInstance starts or joins one instance under its lifecycle lock. validate runs while that lock is held, immediately before inspecting or creating the handle; Core uses it as the config-generation barrier.
func (*Supervisor) Stop ¶
func (s *Supervisor) Stop(id string) error
Stop stops a running MCP server process.
func (*Supervisor) StopAll ¶
func (s *Supervisor) StopAll()
StopAll stops all running servers gracefully. Logs any errors that occur during shutdown but does not return them, as this is typically called during application shutdown where we want to attempt stopping all servers regardless of individual failures.
func (*Supervisor) StopInstance ¶ added in v0.1.29
func (s *Supervisor) StopInstance(id InstanceID) error
StopInstance stops one instance under the same lifecycle lock used by start.
func (*Supervisor) StopSessionInstances ¶ added in v0.1.29
func (s *Supervisor) StopSessionInstances(session string)
StopSessionInstances stops and forgets every private instance owned by a downstream session. Shared instances are deliberately untouched.
type SupervisorOptions ¶
type SupervisorOptions struct {
// CredentialStoreMode specifies the OAuth credential store mode.
// If empty, defaults to "auto".
CredentialStoreMode string
// PIDTrackerDir overrides the directory used for the PID tracking file.
// If empty, the default ~/.config/mcpmu/ directory is used.
PIDTrackerDir string
// PIDFilePrefix labels this owner process's registry file by manager mode.
// Unique owner identity, not the prefix, prevents concurrent clobbering.
PIDFilePrefix string
// GlobalOAuthCallbackPort is the global fallback OAuth callback port.
// Per-server oauth.callback_port takes precedence over this.
GlobalOAuthCallbackPort *int
// InitAttemptTimeout overrides the timeout for each MCP initialization
// attempt. If non-positive, the package default is used.
InitAttemptTimeout time.Duration
// InitRetryBaseDelay overrides the base exponential backoff between MCP
// initialization attempts. If non-positive, the package default is used.
InitRetryBaseDelay time.Duration
}
SupervisorOptions configures a Supervisor.
type UpstreamNotification ¶ added in v0.1.29
type UpstreamNotification struct {
Instance InstanceID
Generation uint64
Method string
Params json.RawMessage
Upstream bool
}
UpstreamNotification identifies the exact process generation that emitted an MCP notification.
func (UpstreamNotification) Clone ¶ added in v0.1.29
func (n UpstreamNotification) Clone() UpstreamNotification
Clone isolates queued notification payloads from caller-owned buffers.