process

package
v0.1.32 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package process provides process lifecycle management for MCP servers.

Package process provides process lifecycle management for MCP servers.

Index

Constants

View Source
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
)
View Source
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

View Source
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

func LockFileBlocking(path string, timeout time.Duration) (release func(), err error)

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

func ProcessStartIdentity(pid int) (int64, error)

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

func (h *Handle) Client() *mcp.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

func (h *Handle) Generation() uint64

Generation identifies this exact process/transport generation.

func (*Handle) ID

func (h *Handle) ID() string

ID returns the server ID.

func (*Handle) InitError added in v0.1.13

func (h *Handle) InitError() error

InitError returns the MCP initialization error, if any.

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) IsRunning

func (h *Handle) IsRunning() bool

IsRunning returns true if the process is still running.

func (*Handle) Kind

func (h *Handle) Kind() HandleKind

Kind returns the handle type (stdio or HTTP).

func (*Handle) Logs

func (h *Handle) Logs() []string

Logs returns the captured stderr logs.

func (*Handle) NeedsLogin added in v0.1.29

func (h *Handle) NeedsLogin() bool

NeedsLogin reports whether initialization stopped at the OAuth login gate.

func (*Handle) NextDiscoverySequence added in v0.1.31

func (h *Handle) NextDiscoverySequence() uint64

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) PID

func (h *Handle) PID() int

PID returns the process ID (0 for HTTP handles).

func (*Handle) ServerURL

func (h *Handle) ServerURL() string

ServerURL returns the server URL (for HTTP handles).

func (*Handle) SetTools

func (h *Handle) SetTools(tools []mcp.Tool)

SetTools sets the discovered tools (thread-safe).

func (*Handle) StartedAt

func (h *Handle) StartedAt() time.Time

StartedAt returns when the process started.

func (*Handle) Stop

func (h *Handle) Stop() error

Stop gracefully stops the server (process or HTTP connection).

func (*Handle) Tools

func (h *Handle) Tools() []mcp.Tool

Tools returns the discovered tools.

func (*Handle) ToolsReady added in v0.1.12

func (h *Handle) ToolsReady() bool

ToolsReady returns true if tool discovery has completed (non-blocking).

func (*Handle) Uptime

func (h *Handle) Uptime() time.Duration

Uptime returns how long the process has been running.

func (*Handle) WaitForTools

func (h *Handle) WaitForTools(ctx context.Context) error

WaitForTools waits for init + tool discovery to complete or context to be cancelled. Returns initErr if MCP initialization failed.

type HandleKind

type HandleKind int

HandleKind represents the type of server handle.

const (
	HandleKindStdio HandleKind = iota
	HandleKindHTTP
)

type InstanceID added in v0.1.29

type InstanceID struct {
	Server  string `json:"server"`
	Session string `json:"session,omitempty"`
}

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

type ManagerLockInfo struct {
	PID  int    `json:"pid"`
	Mode string `json:"mode"` // "tui" or "web"
}

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) Add

func (pt *PIDTracker) Add(serverID string, pid int, command string, args []string) error

Add tracks a new PID for a server.

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

Clone isolates queued notification payloads from caller-owned buffers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL