Class: Parse::Agent::MCPRackApp::CancellationRegistry Private

Inherits:
Object
  • Object
show all
Defined in:
lib/parse/agent/mcp_rack_app.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Per-app store of in-flight cancellable requests. Lookups for cancellation are keyed by ‘[correlation_id, request_id]`, but every #register returns an opaque entry-id token that uniquely identifies the registration. #deregister requires that entry-id and removes the matching token only when it still owns the slot — so a second registration under the same `(correlation_id, request_id)` key cannot cause the first registration’s ‘on_close` to evict the wrong token.

SSEBody registers an entry before spawning its dispatcher_thread and deregisters via the MCPRackApp-supplied on_close hook. A ‘notifications/cancelled` POST calls #cancel to trip the matching CancellationToken.

Identity binding: cancellation requires the cancelling request’s ‘X-MCP-Session-Id` (sanitized into `agent.correlation_id`) to match the original request’s. This prevents an attacker who guesses sequential JSON-RPC request ids from cancelling other clients’ in-flight requests. A registration with a nil correlation_id is dropped silently (cancellation is disabled for the request).

Scope: per MCPRackApp instance. Cancellation does NOT span multiple mount points within a process, nor multiple processes in a clustered deployment.

Instance Method Summary collapse

Constructor Details

#initializeCancellationRegistry

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of CancellationRegistry.



961
962
963
964
# File 'lib/parse/agent/mcp_rack_app.rb', line 961

def initialize
  @entries = {}
  @mutex   = Mutex.new
end

Instance Method Details

#cancel(correlation_id, request_id, reason: :notifications_cancelled) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Trip the matching token. Silent no-op when the entry is missing — by design, to avoid a probe oracle.

Returns:

  • (Boolean)

    true if a matching token was tripped.



1016
1017
1018
1019
1020
1021
1022
# File 'lib/parse/agent/mcp_rack_app.rb', line 1016

def cancel(correlation_id, request_id, reason: :notifications_cancelled)
  return false if correlation_id.nil? || correlation_id.to_s.empty?
  entry = @mutex.synchronize { @entries[[correlation_id, request_id]] }
  return false unless entry
  entry[1].cancel!(reason: reason)
  true
end

#deregister(correlation_id, request_id, entry_id) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Release a previously-registered entry. Removes the slot only when the current owner matches the passed entry-id, so a stale on_close from a request whose slot was overwritten by a sibling registration cannot evict the sibling’s token. Idempotent.

Returns:

  • (Boolean)

    true if this call removed the entry.



998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
# File 'lib/parse/agent/mcp_rack_app.rb', line 998

def deregister(correlation_id, request_id, entry_id)
  return false if correlation_id.nil? || correlation_id.to_s.empty?
  return false if entry_id.nil?
  @mutex.synchronize do
    current = @entries[[correlation_id, request_id]]
    if current && current[0] == entry_id
      @entries.delete([correlation_id, request_id])
      true
    else
      false
    end
  end
end

#register(correlation_id, request_id, token) ⇒ String?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Register a cancellation token for the given session and request id pair. Returns an opaque entry-id that the caller must pass to #deregister to release the slot. If multiple registrations land on the same key (legitimate id-reuse by the same session, or a request retry), only the latest registration is reachable for #cancel; older entries can still be safely released via their entry-id even though they no longer “own” the slot.

Parameters:

Returns:

  • (String, nil)

    opaque entry-id, or nil when registration was refused (no correlation_id).



982
983
984
985
986
987
988
989
# File 'lib/parse/agent/mcp_rack_app.rb', line 982

def register(correlation_id, request_id, token)
  return nil if correlation_id.nil? || correlation_id.to_s.empty?
  entry_id = SecureRandom.uuid
  @mutex.synchronize do
    @entries[[correlation_id, request_id]] = [entry_id, token]
  end
  entry_id
end

#sizeInteger

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns number of currently-registered tokens. Used by tests and operator dashboards.

Returns:

  • (Integer)

    number of currently-registered tokens. Used by tests and operator dashboards.



1026
1027
1028
# File 'lib/parse/agent/mcp_rack_app.rb', line 1026

def size
  @mutex.synchronize { @entries.size }
end