Class: MCP::Server::PendingResponse

Inherits:
Object
  • Object
show all
Defined in:
lib/mcp/server/pending_response.rb

Overview

A one-shot, timeout-aware handoff between the thread awaiting a server-to-client response and whichever thread resolves it (the client's response, a cancellation, or session teardown).

Queue#pop only accepts a timeout: on Ruby 3.2 and later, and this gem supports 2.7, so the wait is expressed with a ConditionVariable. The push/pop names mirror the Queue this replaces, keeping the resolving call sites unchanged.

First writer wins: a second push is ignored, so a cancellation that races a real response cannot overwrite it. pop returns the pushed value, or the on_timeout result when the deadline passes with nothing pushed.

Instance Method Summary collapse

Constructor Details

#initializePendingResponse

Returns a new instance of PendingResponse.



16
17
18
19
20
21
# File 'lib/mcp/server/pending_response.rb', line 16

def initialize
  @mutex = Mutex.new
  @condition = ConditionVariable.new
  @delivered = false
  @value = nil
end

Instance Method Details

#pop(timeout:) ⇒ Object

Blocks until a value is pushed or timeout seconds elapse, and yields to the caller on expiry so it can decide what a timeout means. ConditionVariable#wait can return spuriously, so the deadline is re-checked against the monotonic clock.

The expiry block runs after the lock is released: it typically cancels the request, which resolves this same object, and Ruby's Mutex is not reentrant.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/mcp/server/pending_response.rb', line 40

def pop(timeout:)
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
  expired = false

  value = @mutex.synchronize do
    until @delivered
      remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
      if remaining <= 0
        expired = true
        break
      end

      @condition.wait(@mutex, remaining)
    end

    @value
  end

  expired ? yield : value
end

#push(value) ⇒ Object

Resolves the wait. Ignored when a value was already delivered.



24
25
26
27
28
29
30
31
32
# File 'lib/mcp/server/pending_response.rb', line 24

def push(value)
  @mutex.synchronize do
    next if @delivered

    @delivered = true
    @value = value
    @condition.broadcast
  end
end