Class: VectorMCP::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/vector_mcp/session.rb

Overview

Represents the state of a single client-server connection session in MCP. It tracks initialization status, and negotiated capabilities between the client and server.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(server, transport = nil, id: SecureRandom.uuid) ⇒ Session

Initializes a new session.

Parameters:

  • server (VectorMCP::Server)

    The server instance managing this session.

  • transport (VectorMCP::Transport::Base, nil) (defaults to: nil)

    The transport handling this session. Required for sampling.

  • id (String) (defaults to: SecureRandom.uuid)

    A unique identifier for this session (e.g., from transport layer).



25
26
27
28
29
30
31
32
33
34
# File 'lib/vector_mcp/session.rb', line 25

def initialize(server, transport = nil, id: SecureRandom.uuid)
  @server = server
  @transport = transport # Store the transport for sending requests
  @id = id
  @initialized_state = :pending # :pending, :succeeded, :failed
  @client_info = nil
  @client_capabilities = nil
  @data = {} # Initialize user data hash
  @logger = server.logger
end

Instance Attribute Details

#client_capabilitiesHash? (readonly)

Capabilities supported by the client, received during initialization.

Returns:

  • (Hash, nil)

    the current value of client_capabilities



16
17
18
# File 'lib/vector_mcp/session.rb', line 16

def client_capabilities
  @client_capabilities
end

#client_infoHash? (readonly)

Information about the client, received during initialization.

Returns:

  • (Hash, nil)

    the current value of client_info



16
17
18
# File 'lib/vector_mcp/session.rb', line 16

def client_info
  @client_info
end

#dataObject

For user-defined session-specific storage



18
19
20
# File 'lib/vector_mcp/session.rb', line 18

def data
  @data
end

#idObject (readonly)

Returns the value of attribute id.



17
18
19
# File 'lib/vector_mcp/session.rb', line 17

def id
  @id
end

#protocol_versionString (readonly)

The MCP protocol version used by the server.

Returns:

  • (String)

    the current value of protocol_version



16
17
18
# File 'lib/vector_mcp/session.rb', line 16

def protocol_version
  @protocol_version
end

#serverObject (readonly)

Returns the value of attribute server.



17
18
19
# File 'lib/vector_mcp/session.rb', line 17

def server
  @server
end

#server_capabilitiesHash (readonly)

Capabilities supported by the server.

Returns:

  • (Hash)

    the current value of server_capabilities



16
17
18
# File 'lib/vector_mcp/session.rb', line 16

def server_capabilities
  @server_capabilities
end

#server_infoHash (readonly)

Information about the server.

Returns:

  • (Hash)

    the current value of server_info



16
17
18
# File 'lib/vector_mcp/session.rb', line 16

def server_info
  @server_info
end

#transportObject (readonly)

Returns the value of attribute transport.



17
18
19
# File 'lib/vector_mcp/session.rb', line 17

def transport
  @transport
end

Instance Method Details

#initialize!(params) ⇒ Hash

Marks the session as initialized using parameters from the client's initialize request.

Parameters:

  • params (Hash)

    The parameters from the client's initialize request. Expected keys include "protocolVersion", "clientInfo", and "capabilities".

Returns:

  • (Hash)

    A hash suitable for the server's initialize response result.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/vector_mcp/session.rb', line 41

def initialize!(params)
  raise InitializationError, "Session already initialized or initialization attempt in progress." unless @initialized_state == :pending

  # TODO: More robust validation of params against MCP spec for initialize request
  params["protocolVersion"]
  client_capabilities_raw = params["capabilities"]
  client_info_raw = params["clientInfo"]

  # For now, we mostly care about clientInfo and capabilities for the session object.
  # Protocol version matching is more of a server/transport concern at a lower level if strict checks are needed.
  @client_info = client_info_raw.transform_keys(&:to_sym) if client_info_raw.is_a?(Hash)
  @client_capabilities = client_capabilities_raw.transform_keys(&:to_sym) if client_capabilities_raw.is_a?(Hash)

  @initialized_state = :succeeded
  @logger.info("[Session #{@id}] Initialized successfully. Client: #{@client_info&.dig(:name)}")

  {
    protocolVersion: @server.protocol_version,
    serverInfo: @server.server_info,
    capabilities: @server.server_capabilities
  }
rescue StandardError => e
  @initialized_state = :failed
  @logger.error("[Session #{@id}] Initialization failed: #{e.message}")
  # Re-raise as an InitializationError if it's not already one of our ProtocolErrors
  raise e if e.is_a?(ProtocolError)

  raise InitializationError, "Initialization processing error: #{e.message}", details: { original_error: e.to_s }
end

#initialized?Boolean

Checks if the session has been successfully initialized.

Returns:

  • (Boolean)

    True if the session is initialized, false otherwise.



74
75
76
# File 'lib/vector_mcp/session.rb', line 74

def initialized?
  @initialized_state == :succeeded
end

#sample(request_params, timeout: nil) ⇒ VectorMCP::Sampling::Result

Initiates an MCP sampling request to the client associated with this session. This is a blocking call that waits for the client's response.

Parameters:

  • request_params (Hash)

    Parameters for the sampling/createMessage request. See VectorMCP::Sampling::Request for expected structure (e.g., :messages, :max_tokens).

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

    Optional timeout in seconds for this specific request. Defaults to the transport's default request timeout.

Returns:

Raises:

  • (VectorMCP::SamplingError)

    if the sampling request fails, is rejected, or times out.

  • (StandardError)

    if the session's transport does not support send_request.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/vector_mcp/session.rb', line 95

def sample(request_params, timeout: nil)
  validate_sampling_preconditions

  # Create middleware context for sampling
  context = VectorMCP::Middleware::Context.new(
    operation_type: :sampling,
    operation_name: "createMessage",
    params: request_params,
    session: self,
    server: @server,
    metadata: { start_time: Time.now, timeout: timeout }
  )

  # Execute before_sampling_request hooks
  context = @server.middleware_manager.execute_hooks(:before_sampling_request, context)
  raise context.error if context.error?

  begin
    sampling_req_obj = VectorMCP::Sampling::Request.new(request_params)
    @logger.info("[Session #{@id}] Sending sampling/createMessage request to client.")

    result = send_sampling_request(sampling_req_obj, timeout)

    # Set result in context
    context.result = result

    # Execute after_sampling_response hooks
    context = @server.middleware_manager.execute_hooks(:after_sampling_response, context)

    context.result
  rescue StandardError => e
    # Set error in context and execute error hooks
    context.error = e
    context = @server.middleware_manager.execute_hooks(:on_sampling_error, context)

    # Re-raise unless middleware handled the error
    raise e unless context.result

    context.result
  end
end