Class: Woods::MCP::BootstrapState

Inherits:
Object
  • Object
show all
Defined in:
lib/woods/mcp/bootstrap_state.rb

Overview

Tracks the lifecycle state of an MCP server bootstrap sequence.

Status transitions flow forward: initializinghydratinghydrated (success path), or initializing/hydratingdegraded (provider unreachable) or failed (config-invalid). States are mutated via #mark so the woods_status MCP tool always reads consistent values.

Examples:

Bootstrapper usage

state = Woods::MCP::BootstrapState.new
state.mark(:hydrating)
vector_store = Snapshotter::Vector.load_or_empty(artifact)
state.mark(:hydrated)
# or, on provider failure:
state.mark(:degraded, reason: ProviderUnreachable.new("..."))

Constant Summary collapse

VALID_STATUSES =
%i[initializing hydrating hydrated degraded failed].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBootstrapState

Returns a new instance of BootstrapState.



44
45
46
47
48
49
50
# File 'lib/woods/mcp/bootstrap_state.rb', line 44

def initialize
  @status = :initializing
  @reason = nil
  @hydrated_at = nil
  @degraded_since = nil
  @resolved_config = nil
end

Instance Attribute Details

#degraded_sinceTime? (readonly)

Returns set when status transitions to :degraded.

Returns:

  • (Time, nil)

    set when status transitions to :degraded



36
37
38
# File 'lib/woods/mcp/bootstrap_state.rb', line 36

def degraded_since
  @degraded_since
end

#hydrated_atTime? (readonly)

Returns set when status transitions to :hydrated.

Returns:

  • (Time, nil)

    set when status transitions to :hydrated



33
34
35
# File 'lib/woods/mcp/bootstrap_state.rb', line 33

def hydrated_at
  @hydrated_at
end

#reasonException? (readonly)

Returns the exception that caused degradation or failure.

Returns:

  • (Exception, nil)

    the exception that caused degradation or failure



30
31
32
# File 'lib/woods/mcp/bootstrap_state.rb', line 30

def reason
  @reason
end

#resolved_configWoods::ResolvedConfig?

Returns captured at embed time and read back from woods.json during boot. Used by woods_status to report the provider/model actually in play instead of the stale defaults on Woods.configuration.

Returns:

  • (Woods::ResolvedConfig, nil)

    captured at embed time and read back from woods.json during boot. Used by woods_status to report the provider/model actually in play instead of the stale defaults on Woods.configuration.



42
43
44
# File 'lib/woods/mcp/bootstrap_state.rb', line 42

def resolved_config
  @resolved_config
end

#statusSymbol (readonly)

Returns one of :initializing, :hydrating, :hydrated, :degraded, :failed.

Returns:

  • (Symbol)

    one of :initializing, :hydrating, :hydrated, :degraded, :failed



27
28
29
# File 'lib/woods/mcp/bootstrap_state.rb', line 27

def status
  @status
end

Instance Method Details

#mark(new_status, reason: nil, now: Time.now.utc) ⇒ self

Transition to a new status.

hydrated_at is recorded on :hydrated; degraded_since is recorded on :degraded. reason: is accepted for :degraded and :failed.

Parameters:

  • new_status (Symbol)

    target status (must be in VALID_STATUSES)

  • reason (Exception, nil) (defaults to: nil)

    causal exception for degraded/failed states

  • now (Time) (defaults to: Time.now.utc)

    timestamp for the transition (default: UTC now)

Returns:

  • (self)

Raises:

  • (ArgumentError)

    when new_status is not a recognised status



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/woods/mcp/bootstrap_state.rb', line 62

def mark(new_status, reason: nil, now: Time.now.utc)
  unless VALID_STATUSES.include?(new_status)
    raise ArgumentError,
          "Unknown status #{new_status.inspect}. " \
          "Must be one of: #{VALID_STATUSES.map(&:inspect).join(', ')}"
  end

  @status = new_status
  @reason = reason

  case new_status
  when :hydrated
    @hydrated_at = now
  when :degraded
    @degraded_since = now
  end

  self
end

#to_hHash

Returns a hash suitable for embedding in a woods_status MCP response.

Returns:

  • (Hash)


85
86
87
88
89
90
91
# File 'lib/woods/mcp/bootstrap_state.rb', line 85

def to_h
  h = { status: @status }
  h[:reason] = "#{@reason.class}: #{@reason.message}" if @reason
  h[:hydrated_at] = @hydrated_at.iso8601 if @hydrated_at
  h[:degraded_since] = @degraded_since.iso8601 if @degraded_since
  h
end