Class: RailsAiBridge::Registry::ContextProviderClient

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_ai_bridge/registry/context_provider_client.rb

Overview

Performs one provider exchange through an MCP HTTP transport.

The client owns policy verification, remote tool metadata checks, result normalization, and safe error translation. It never stores credentials and closes the transport in an ensure block.

Defined Under Namespace

Classes: Result

Constant Summary collapse

DEFAULT_CLEANUP_DEADLINE_SECONDS =

Upper bound on time to wait for transport.close in the ensure block. Callers may pass a smaller value, but never a larger one.

5.0

Instance Method Summary collapse

Constructor Details

#initialize(provider:, policy:, transport_factory:, auth_resolver:, timeout_seconds: nil, cleanup_deadline_seconds: DEFAULT_CLEANUP_DEADLINE_SECONDS) ⇒ ContextProviderClient

Parameters:

  • provider (ContextProviderDefinition)

    provider manifest

  • policy (EndpointPolicy)

    policy used to validate the endpoint

  • transport_factory (#call)

    callable returning an MCP client

  • timeout_seconds (Numeric, nil) (defaults to: nil)

    per-call timeout in seconds; nil disables the timeout

  • cleanup_deadline_seconds (Numeric, nil) (defaults to: DEFAULT_CLEANUP_DEADLINE_SECONDS)

    maximum time to wait for transport.close; nil disables the deadline



34
35
36
37
38
39
40
41
# File 'lib/rails_ai_bridge/registry/context_provider_client.rb', line 34

def initialize(provider:, policy:, transport_factory:, auth_resolver:, timeout_seconds: nil, cleanup_deadline_seconds: DEFAULT_CLEANUP_DEADLINE_SECONDS)
  @provider = provider
  @policy = policy
  @transport_factory = transport_factory
  @auth_resolver = auth_resolver
  @timeout_seconds = timeout_seconds
  @cleanup_deadline_seconds = clamp_cleanup_deadline(cleanup_deadline_seconds, timeout_seconds)
end

Instance Method Details

#call_tool(tool_name, arguments: {}) ⇒ Result

Calls a single remote tool and returns a typed result.

Parameters:

  • tool_name (String)

    name of the tool to call

  • arguments (Hash) (defaults to: {})

    tool arguments

Returns:



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/rails_ai_bridge/registry/context_provider_client.rb', line 48

def call_tool(tool_name, arguments: {})
  endpoint = @provider.endpoint
  transport = nil
  tool_label = tool_name.inspect
  begin
    with_timeout(@timeout_seconds, 'provider call timed out') do
      policy_result = @policy.call(endpoint)
      return Result.new(status: :error, error: policy_result.error) unless policy_result.success?

      return Result.new(status: :error, error: RemoteToolError.new("tool #{tool_label} is not declared in the provider manifest")) unless tool_declared?(tool_name)

      canonical_uri = policy_result.uri
      headers = resolve_auth(endpoint, canonical_uri)
      transport = @transport_factory.call(canonical_uri, policy_result.addresses, headers)
      transport.connect
      remote_tool = find_tool(transport, tool_name)
      return Result.new(status: :error, error: RemoteToolError.new("tool #{tool_label} is not allowed")) unless remote_tool

      raw = transport.call_tool(name: tool_name, arguments: arguments)
      content = raw.is_a?(Hash) && raw.key?('result') ? raw.dig('result', 'content') : raw
      Result.new(status: :success, content: sanitize_content(content), provenance: provenance_for(canonical_uri), error: nil)
    end
  rescue RailsAiBridge::Registry::ContextProviderError => error
    Result.new(status: :error, error: error)
  rescue Timeout::Error
    Result.new(status: :error, error: RailsAiBridge::Registry::TimeoutError.new('provider call timed out'))
  rescue StandardError => error
    Result.new(status: :error, error: RailsAiBridge::Registry::ConnectionError.new("provider call failed (#{error.class}): #{sanitize_message(error.message)}"))
  ensure
    close_transport(transport)
  end
end

#probe(timeout: nil) ⇒ Result

Lightweight reachability probe: validates the endpoint, opens a transport, lists tools, and closes. Does not call any tool.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    per-probe timeout in seconds; nil falls back to the configured per-tool timeout

Returns:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/rails_ai_bridge/registry/context_provider_client.rb', line 86

def probe(timeout: nil)
  endpoint = @provider.endpoint
  transport = nil
  effective_timeout = timeout || @timeout_seconds
  begin
    with_timeout(effective_timeout, 'provider probe timed out') do
      policy_result = @policy.call(endpoint)
      return Result.new(status: :error, error: policy_result.error) unless policy_result.success?

      canonical_uri = policy_result.uri
      headers = resolve_auth(endpoint, canonical_uri)
      transport = @transport_factory.call(canonical_uri, policy_result.addresses, headers)
      transport.connect
      transport.tools
      Result.new(status: :success, content: nil, provenance: provenance_for(canonical_uri), error: nil)
    end
  rescue RailsAiBridge::Registry::ContextProviderError => error
    Result.new(status: :error, error: error)
  rescue Timeout::Error
    Result.new(status: :error, error: RailsAiBridge::Registry::TimeoutError.new('provider probe timed out'))
  rescue StandardError => error
    Result.new(status: :error, error: RailsAiBridge::Registry::ConnectionError.new("provider probe failed (#{error.class}): #{sanitize_message(error.message)}"))
  ensure
    close_transport(transport)
  end
end