Class: MCP::ServerSession
- Inherits:
-
Object
- Object
- MCP::ServerSession
- Defined in:
- lib/mcp/server_session.rb
Overview
Holds per-connection state for a single client session.
Created by the transport layer; delegates request handling to the shared Server.
Instance Attribute Summary collapse
-
#client ⇒ Object
readonly
Returns the value of attribute client.
-
#logging_message_notification ⇒ Object
readonly
Returns the value of attribute logging_message_notification.
-
#session_id ⇒ Object
readonly
Returns the value of attribute session_id.
Instance Method Summary collapse
-
#cancel_incoming(request_id:, reason: nil) ⇒ Object
Flips the
Cancellationfor a matching in-flight request received from the peer. -
#cancel_request(request_id:, reason: nil) ⇒ Object
Sends
notifications/cancelledto the peer for a previously-issued request. -
#client_capabilities ⇒ Object
Returns per-session client capabilities, falling back to global.
-
#configure_logging(logging_message_notification) ⇒ Object
Called by
Server#configure_logging_level. -
#create_form_elicitation(message:, requested_schema:, related_request_id: nil) ⇒ Object
Sends an
elicitation/createrequest (form mode) scoped to this session. -
#create_sampling_message(related_request_id: nil, **kwargs) ⇒ Object
deprecated
Deprecated.
MCP Sampling (
sampling/createMessage) is deprecated as of MCP protocol version 2026-07-28 (SEP-2577). Use direct LLM provider APIs instead. -
#create_url_elicitation(message:, url:, elicitation_id:, related_request_id: nil) ⇒ Object
Sends an
elicitation/createrequest (URL mode) scoped to this session. - #handle(request) ⇒ Object
- #handle_json(request_json) ⇒ Object
-
#initialize(server:, transport:, session_id: nil) ⇒ ServerSession
constructor
A new instance of ServerSession.
-
#initialized? ⇒ Boolean
Whether
initializehas already completed for this session. -
#list_roots(related_request_id: nil) ⇒ Object
deprecated
Deprecated.
MCP Roots (
roots/listandnotifications/roots/list_changed) is deprecated as of MCP protocol version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs, server configuration, or environment variables instead. - #lookup_in_flight(request_id) ⇒ Object
-
#mark_initialized! ⇒ Object
Called by
Server#initafter a successfulinitializeresponse, so subsequentinitializerequests on the same session can be rejected per MCP spec (the initialization phase MUST be the first interaction). -
#notify_elicitation_complete(elicitation_id:) ⇒ Object
Sends an elicitation complete notification scoped to this session.
-
#notify_log_message(data:, level:, logger: nil, related_request_id: nil) ⇒ Object
deprecated
Deprecated.
MCP Logging (
logging/setLevelandnotifications/message) is deprecated as of MCP protocol version 2026-07-28 (SEP-2577). Use stderr or OpenTelemetry instead. -
#notify_progress(progress_token:, progress:, total: nil, message: nil, related_request_id: nil) ⇒ Object
Sends a progress notification to this session only.
-
#notify_resources_updated(uri:) ⇒ Object
Sends a resource updated notification to this session only.
-
#ping(related_request_id: nil) ⇒ Object
Sends a
pingrequest scoped to this session. -
#register_in_flight(request_id) ⇒ Object
Registers a
Cancellationtoken for an in-flight request. -
#send_peer_cancellation(nested_request_id:, related_request_id: nil, reason: nil) ⇒ Object
Sends
notifications/cancelledto the peer for a nested server-to-client request that was started inside a now-cancelled parent request. -
#store_client_info(client:, capabilities: nil) ⇒ Object
Called by
Server#initduring the initialization handshake. - #unregister_in_flight(request_id) ⇒ Object
Constructor Details
#initialize(server:, transport:, session_id: nil) ⇒ ServerSession
Returns a new instance of ServerSession.
12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/mcp/server_session.rb', line 12 def initialize(server:, transport:, session_id: nil) @server = server @transport = transport @session_id = session_id @client = nil @client_capabilities = nil @logging_message_notification = nil @in_flight = {} @in_flight_mutex = Mutex.new @initialized = false end |
Instance Attribute Details
#client ⇒ Object (readonly)
Returns the value of attribute client.
10 11 12 |
# File 'lib/mcp/server_session.rb', line 10 def client @client end |
#logging_message_notification ⇒ Object (readonly)
Returns the value of attribute logging_message_notification.
10 11 12 |
# File 'lib/mcp/server_session.rb', line 10 def @logging_message_notification end |
#session_id ⇒ Object (readonly)
Returns the value of attribute session_id.
10 11 12 |
# File 'lib/mcp/server_session.rb', line 10 def session_id @session_id end |
Instance Method Details
#cancel_incoming(request_id:, reason: nil) ⇒ Object
Flips the Cancellation for a matching in-flight request received from the peer.
Silently ignores unknown IDs per MCP spec (cancellation utilities, item 5).
57 58 59 60 |
# File 'lib/mcp/server_session.rb', line 57 def cancel_incoming(request_id:, reason: nil) cancellation = lookup_in_flight(request_id) cancellation&.cancel(reason: reason) end |
#cancel_request(request_id:, reason: nil) ⇒ Object
Sends notifications/cancelled to the peer for a previously-issued request.
Also unblocks any transport-level send_request waiting on a response for request_id.
64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/mcp/server_session.rb', line 64 def cancel_request(request_id:, reason: nil) params = { requestId: request_id } params[:reason] = reason if reason send_to_transport(Methods::NOTIFICATIONS_CANCELLED, params) if @transport.respond_to?(:cancel_pending_request) @transport.cancel_pending_request(request_id, reason: reason) end rescue => e MCP.configuration.exception_reporter.call(e, { notification: "cancelled", request_id: request_id }) end |
#client_capabilities ⇒ Object
Returns per-session client capabilities, falling back to global.
96 97 98 |
# File 'lib/mcp/server_session.rb', line 96 def client_capabilities @client_capabilities || @server.client_capabilities end |
#configure_logging(logging_message_notification) ⇒ Object
Called by Server#configure_logging_level.
91 92 93 |
# File 'lib/mcp/server_session.rb', line 91 def configure_logging() @logging_message_notification = end |
#create_form_elicitation(message:, requested_schema:, related_request_id: nil) ⇒ Object
Sends an elicitation/create request (form mode) scoped to this session.
131 132 133 134 135 136 137 138 139 |
# File 'lib/mcp/server_session.rb', line 131 def create_form_elicitation(message:, requested_schema:, related_request_id: nil) unless client_capabilities&.dig(:elicitation) raise "Client does not support elicitation. " \ "The client must declare the `elicitation` capability during initialization." end params = { mode: "form", message: , requestedSchema: requested_schema } send_to_transport_request(Methods::ELICITATION_CREATE, params, related_request_id: ) end |
#create_sampling_message(related_request_id: nil, **kwargs) ⇒ Object
MCP Sampling (sampling/createMessage) is deprecated as of
MCP protocol version 2026-07-28 (SEP-2577). Use direct LLM provider
APIs instead.
Sends a sampling/createMessage request scoped to this session.
125 126 127 128 |
# File 'lib/mcp/server_session.rb', line 125 def (related_request_id: nil, **kwargs) params = @server.build_sampling_params(client_capabilities, **kwargs) send_to_transport_request(Methods::SAMPLING_CREATE_MESSAGE, params, related_request_id: ) end |
#create_url_elicitation(message:, url:, elicitation_id:, related_request_id: nil) ⇒ Object
Sends an elicitation/create request (URL mode) scoped to this session.
142 143 144 145 146 147 148 149 150 |
# File 'lib/mcp/server_session.rb', line 142 def create_url_elicitation(message:, url:, elicitation_id:, related_request_id: nil) unless client_capabilities&.dig(:elicitation, :url) raise "Client does not support URL mode elicitation. " \ "The client must declare the `elicitation.url` capability during initialization." end params = { mode: "url", message: , url: url, elicitationId: elicitation_id } send_to_transport_request(Methods::ELICITATION_CREATE, params, related_request_id: ) end |
#handle(request) ⇒ Object
76 77 78 |
# File 'lib/mcp/server_session.rb', line 76 def handle(request) @server.handle(request, session: self) end |
#handle_json(request_json) ⇒ Object
80 81 82 |
# File 'lib/mcp/server_session.rb', line 80 def handle_json(request_json) @server.handle_json(request_json, session: self) end |
#initialized? ⇒ Boolean
Whether initialize has already completed for this session.
25 26 27 |
# File 'lib/mcp/server_session.rb', line 25 def initialized? @initialized end |
#list_roots(related_request_id: nil) ⇒ Object
MCP Roots (roots/list and
notifications/roots/list_changed) is deprecated as of MCP protocol
version 2026-07-28 (SEP-2577). Use tool parameters, resource URIs,
server configuration, or environment variables instead.
Sends a roots/list request scoped to this session.
105 106 107 108 109 110 111 |
# File 'lib/mcp/server_session.rb', line 105 def list_roots(related_request_id: nil) unless client_capabilities&.dig(:roots) raise "Client does not support roots." end send_to_transport_request(Methods::ROOTS_LIST, nil, related_request_id: ) end |
#lookup_in_flight(request_id) ⇒ Object
51 52 53 |
# File 'lib/mcp/server_session.rb', line 51 def lookup_in_flight(request_id) @in_flight_mutex.synchronize { @in_flight[request_id] } end |
#mark_initialized! ⇒ Object
Called by Server#init after a successful initialize response, so subsequent
initialize requests on the same session can be rejected per MCP spec
(the initialization phase MUST be the first interaction).
32 33 34 |
# File 'lib/mcp/server_session.rb', line 32 def mark_initialized! @initialized = true end |
#notify_elicitation_complete(elicitation_id:) ⇒ Object
Sends an elicitation complete notification scoped to this session.
170 171 172 173 174 |
# File 'lib/mcp/server_session.rb', line 170 def notify_elicitation_complete(elicitation_id:) send_to_transport(Methods::NOTIFICATIONS_ELICITATION_COMPLETE, { elicitationId: elicitation_id }) rescue => e @server.report_exception(e, notification: "elicitation_complete") end |
#notify_log_message(data:, level:, logger: nil, related_request_id: nil) ⇒ Object
MCP Logging (logging/setLevel and notifications/message)
is deprecated as of MCP protocol version 2026-07-28 (SEP-2577).
Use stderr or OpenTelemetry instead.
Sends a log message notification to this session only.
201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/mcp/server_session.rb', line 201 def (data:, level:, logger: nil, related_request_id: nil) effective_logging = @logging_message_notification || @server. return unless effective_logging&.should_notify?(level) params = { "data" => data, "level" => level } params["logger"] = logger if logger send_to_transport(Methods::NOTIFICATIONS_MESSAGE, params, related_request_id: ) rescue => e @server.report_exception(e, { notification: "log_message" }) end |
#notify_progress(progress_token:, progress:, total: nil, message: nil, related_request_id: nil) ⇒ Object
Sends a progress notification to this session only.
184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/mcp/server_session.rb', line 184 def notify_progress(progress_token:, progress:, total: nil, message: nil, related_request_id: nil) params = { "progressToken" => progress_token, "progress" => progress, "total" => total, "message" => , }.compact send_to_transport(Methods::NOTIFICATIONS_PROGRESS, params, related_request_id: ) rescue => e @server.report_exception(e, notification: "progress") end |
#notify_resources_updated(uri:) ⇒ Object
Sends a resource updated notification to this session only.
177 178 179 180 181 |
# File 'lib/mcp/server_session.rb', line 177 def notify_resources_updated(uri:) send_to_transport(Methods::NOTIFICATIONS_RESOURCES_UPDATED, { "uri" => uri }) rescue => e @server.report_exception(e, notification: "resources_updated") end |
#ping(related_request_id: nil) ⇒ Object
Sends a ping request scoped to this session.
114 115 116 117 118 119 |
# File 'lib/mcp/server_session.rb', line 114 def ping(related_request_id: nil) result = send_to_transport_request(Methods::PING, nil, related_request_id: ) raise Server::ValidationError, "Response validation failed: invalid `result`" unless result.is_a?(Hash) result end |
#register_in_flight(request_id) ⇒ Object
Registers a Cancellation token for an in-flight request.
37 38 39 40 41 42 43 |
# File 'lib/mcp/server_session.rb', line 37 def register_in_flight(request_id) return if request_id.nil? cancellation = Cancellation.new(request_id: request_id) @in_flight_mutex.synchronize { @in_flight[request_id] = cancellation } cancellation end |
#send_peer_cancellation(nested_request_id:, related_request_id: nil, reason: nil) ⇒ Object
Sends notifications/cancelled to the peer for a nested server-to-client request
that was started inside a now-cancelled parent request. related_request_id
is the parent request id so the notification is routed to the same stream
(e.g. the parent's POST response stream on StreamableHTTPTransport) rather than
the GET SSE stream.
157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/mcp/server_session.rb', line 157 def send_peer_cancellation(nested_request_id:, related_request_id: nil, reason: nil) params = { requestId: nested_request_id } params[:reason] = reason if reason send_to_transport(Methods::NOTIFICATIONS_CANCELLED, params, related_request_id: ) if @transport.respond_to?(:cancel_pending_request) @transport.cancel_pending_request(nested_request_id, reason: reason) end rescue => e MCP.configuration.exception_reporter.call(e, { notification: "cancelled", request_id: nested_request_id }) end |
#store_client_info(client:, capabilities: nil) ⇒ Object
Called by Server#init during the initialization handshake.
85 86 87 88 |
# File 'lib/mcp/server_session.rb', line 85 def store_client_info(client:, capabilities: nil) @client = client @client_capabilities = capabilities end |
#unregister_in_flight(request_id) ⇒ Object
45 46 47 48 49 |
# File 'lib/mcp/server_session.rb', line 45 def unregister_in_flight(request_id) return if request_id.nil? @in_flight_mutex.synchronize { @in_flight.delete(request_id) } end |