vector_mcp 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +122 -0
  3. data/lib/vector_mcp/definitions.rb +25 -9
  4. data/lib/vector_mcp/errors.rb +2 -3
  5. data/lib/vector_mcp/handlers/core.rb +206 -50
  6. data/lib/vector_mcp/logger.rb +148 -0
  7. data/lib/vector_mcp/middleware/base.rb +171 -0
  8. data/lib/vector_mcp/middleware/context.rb +76 -0
  9. data/lib/vector_mcp/middleware/hook.rb +169 -0
  10. data/lib/vector_mcp/middleware/manager.rb +179 -0
  11. data/lib/vector_mcp/middleware.rb +43 -0
  12. data/lib/vector_mcp/request_context.rb +182 -0
  13. data/lib/vector_mcp/sampling/result.rb +11 -1
  14. data/lib/vector_mcp/security/middleware.rb +2 -28
  15. data/lib/vector_mcp/security/strategies/api_key.rb +2 -24
  16. data/lib/vector_mcp/security/strategies/jwt_token.rb +6 -3
  17. data/lib/vector_mcp/server/capabilities.rb +5 -7
  18. data/lib/vector_mcp/server/message_handling.rb +11 -5
  19. data/lib/vector_mcp/server.rb +74 -20
  20. data/lib/vector_mcp/session.rb +131 -8
  21. data/lib/vector_mcp/transport/base_session_manager.rb +320 -0
  22. data/lib/vector_mcp/transport/http_stream/event_store.rb +151 -0
  23. data/lib/vector_mcp/transport/http_stream/session_manager.rb +189 -0
  24. data/lib/vector_mcp/transport/http_stream/stream_handler.rb +269 -0
  25. data/lib/vector_mcp/transport/http_stream.rb +779 -0
  26. data/lib/vector_mcp/transport/sse.rb +74 -19
  27. data/lib/vector_mcp/transport/sse_session_manager.rb +188 -0
  28. data/lib/vector_mcp/transport/stdio.rb +70 -13
  29. data/lib/vector_mcp/transport/stdio_session_manager.rb +181 -0
  30. data/lib/vector_mcp/util.rb +39 -1
  31. data/lib/vector_mcp/version.rb +1 -1
  32. data/lib/vector_mcp.rb +10 -35
  33. metadata +25 -24
  34. data/lib/vector_mcp/logging/component.rb +0 -131
  35. data/lib/vector_mcp/logging/configuration.rb +0 -156
  36. data/lib/vector_mcp/logging/constants.rb +0 -21
  37. data/lib/vector_mcp/logging/core.rb +0 -175
  38. data/lib/vector_mcp/logging/filters/component.rb +0 -69
  39. data/lib/vector_mcp/logging/filters/level.rb +0 -23
  40. data/lib/vector_mcp/logging/formatters/base.rb +0 -52
  41. data/lib/vector_mcp/logging/formatters/json.rb +0 -83
  42. data/lib/vector_mcp/logging/formatters/text.rb +0 -72
  43. data/lib/vector_mcp/logging/outputs/base.rb +0 -64
  44. data/lib/vector_mcp/logging/outputs/console.rb +0 -35
  45. data/lib/vector_mcp/logging/outputs/file.rb +0 -157
  46. data/lib/vector_mcp/logging.rb +0 -71
@@ -9,6 +9,7 @@ require "concurrent-ruby"
9
9
  require_relative "../errors"
10
10
  require_relative "../util"
11
11
  require_relative "../session"
12
+ require_relative "sse_session_manager"
12
13
  require_relative "sse/client_connection"
13
14
  require_relative "sse/stream_manager"
14
15
  require_relative "sse/message_handler"
@@ -41,7 +42,7 @@ module VectorMCP
41
42
  # @attr_reader port [Integer] The port number the server will listen on.
42
43
  # @attr_reader path_prefix [String] The base URL path for MCP endpoints (e.g., "/mcp").
43
44
  class SSE
44
- attr_reader :logger, :server, :host, :port, :path_prefix
45
+ attr_reader :logger, :server, :host, :port, :path_prefix, :session_manager
45
46
 
46
47
  # Initializes a new SSE transport.
47
48
  #
@@ -50,6 +51,8 @@ module VectorMCP
50
51
  # @option options [String] :host ("localhost") The hostname or IP to bind to.
51
52
  # @option options [Integer] :port (8000) The port to listen on.
52
53
  # @option options [String] :path_prefix ("/mcp") The base path for HTTP endpoints.
54
+ # @option options [Boolean] :disable_session_manager (false) **DEPRECATED**: Whether to disable secure session isolation.
55
+ # When false (default), each client gets isolated sessions. When true, all clients share a global session (security risk).
53
56
  def initialize(server, options = {})
54
57
  @server = server
55
58
  @logger = server.logger
@@ -61,9 +64,19 @@ module VectorMCP
61
64
  @sse_path = "#{@path_prefix}/sse"
62
65
  @message_path = "#{@path_prefix}/message"
63
66
 
64
- # Thread-safe client storage using concurrent-ruby
67
+ # Thread-safe client storage using concurrent-ruby (legacy approach)
65
68
  @clients = Concurrent::Hash.new
66
69
  @session = nil # Global session for this transport instance, initialized in run
70
+
71
+ # Initialize session manager for secure multi-client session isolation (default behavior)
72
+ # Legacy shared session behavior can be enabled with disable_session_manager: true (deprecated)
73
+ if options[:disable_session_manager]
74
+ logger.warn("[DEPRECATED] SSE shared session mode is deprecated and poses security risks in multi-client scenarios. " \
75
+ "Consider removing disable_session_manager: true to use secure per-client sessions.")
76
+ @session_manager = nil
77
+ else
78
+ @session_manager = SseSessionManager.new(self)
79
+ end
67
80
  @puma_server = nil
68
81
  @running = false
69
82
 
@@ -77,7 +90,8 @@ module VectorMCP
77
90
  # @raise [StandardError] if there's a fatal error during server startup.
78
91
  def run
79
92
  logger.info("Starting server with Puma SSE transport on #{@host}:#{@port}")
80
- create_session
93
+ # Only create shared session if explicitly using legacy mode (deprecated)
94
+ create_session unless @session_manager
81
95
  start_puma_server
82
96
  rescue StandardError => e
83
97
  handle_fatal_error(e)
@@ -106,13 +120,32 @@ module VectorMCP
106
120
 
107
121
  # --- Public methods for Server to send notifications ---
108
122
 
123
+ # Sends a JSON-RPC notification to the first available client session.
124
+ # If no clients are connected, returns false.
125
+ #
126
+ # @param method [String] The method name of the notification.
127
+ # @param params [Hash, Array, nil] The parameters for the notification (optional).
128
+ # @return [Boolean] True if the message was sent successfully, false otherwise.
129
+ def send_notification(method, params = nil)
130
+ return false if @clients.empty?
131
+
132
+ # Send to first available client
133
+ first_client = @clients.values.first
134
+ return false unless first_client
135
+
136
+ message = { jsonrpc: "2.0", method: method }
137
+ message[:params] = params if params
138
+
139
+ StreamManager.enqueue_message(first_client, message)
140
+ end
141
+
109
142
  # Sends a JSON-RPC notification to a specific client session via its SSE stream.
110
143
  #
111
144
  # @param session_id [String] The ID of the client session to send the notification to.
112
145
  # @param method [String] The method name of the notification.
113
146
  # @param params [Hash, Array, nil] The parameters for the notification (optional).
114
147
  # @return [Boolean] True if the message was successfully enqueued, false otherwise (e.g., client not found).
115
- def send_notification(session_id, method, params = nil)
148
+ def send_notification_to_session(session_id, method, params = nil)
116
149
  message = { jsonrpc: "2.0", method: method }
117
150
  message[:params] = params if params
118
151
 
@@ -128,7 +161,7 @@ module VectorMCP
128
161
  # @param params [Hash, Array, nil] The parameters for the notification (optional).
129
162
  # @return [void]
130
163
  def broadcast_notification(method, params = nil)
131
- logger.debug { "Broadcasting notification '#{method}' to #{@clients.size} client(s)" }
164
+ # Broadcasting notification to clients
132
165
  message = { jsonrpc: "2.0", method: method }
133
166
  message[:params] = params if params
134
167
 
@@ -150,11 +183,26 @@ module VectorMCP
150
183
  # Stops the transport and cleans up resources
151
184
  def stop
152
185
  @running = false
153
- cleanup_clients
186
+ if @session_manager
187
+ @session_manager.cleanup_all_sessions
188
+ else
189
+ cleanup_clients
190
+ end
154
191
  @puma_server&.stop
155
192
  logger.info("SSE transport stopped")
156
193
  end
157
194
 
195
+ # Cleans up all client connections (legacy mode)
196
+ def cleanup_clients
197
+ logger.info("Cleaning up #{@clients.size} client connection(s)")
198
+ @clients.each_value do |client_conn|
199
+ client_conn.close if client_conn.respond_to?(:close)
200
+ rescue StandardError => e
201
+ logger.warn("Error closing client connection: #{e.message}")
202
+ end
203
+ @clients.clear
204
+ end
205
+
158
206
  # --- Private methods ---
159
207
  private
160
208
 
@@ -177,8 +225,8 @@ module VectorMCP
177
225
  @puma_server.run.join # This blocks until server stops
178
226
  logger.info("Puma server stopped.")
179
227
  ensure
180
- cleanup_clients
181
- @session = nil
228
+ # Only cleanup if session manager is enabled
229
+ @session_manager&.cleanup_all_sessions
182
230
  logger.info("SSE transport and resources shut down.")
183
231
  end
184
232
 
@@ -194,13 +242,6 @@ module VectorMCP
194
242
  end
195
243
  end
196
244
 
197
- # Cleans up resources for all connected clients on server shutdown.
198
- def cleanup_clients
199
- logger.info("Cleaning up #{@clients.size} client connection(s)...")
200
- @clients.each_value(&:close)
201
- @clients.clear
202
- end
203
-
204
245
  # Handles fatal errors during server startup or main run loop.
205
246
  def handle_fatal_error(error)
206
247
  logger.fatal("Fatal error in SSE transport: #{error.message}\n#{error.backtrace.join("\n")}")
@@ -245,11 +286,17 @@ module VectorMCP
245
286
 
246
287
  # Create client connection
247
288
  client_conn = ClientConnection.new(session_id, logger)
248
- @clients[session_id] = client_conn
289
+
290
+ # Store client connection
291
+ if @session_manager
292
+ @session_manager.register_client(session_id, client_conn)
293
+ else
294
+ @clients[session_id] = client_conn
295
+ end
249
296
 
250
297
  # Build message POST URL for this client
251
298
  message_post_url = build_post_url(session_id)
252
- logger.debug("Client #{session_id} should POST messages to: #{message_post_url}")
299
+ # Client message POST URL configured
253
300
 
254
301
  # Set up SSE stream
255
302
  headers = sse_headers
@@ -268,10 +315,18 @@ module VectorMCP
268
315
  "Missing session_id parameter")
269
316
  end
270
317
 
271
- client_conn = @clients[session_id]
318
+ # Get client connection and session
319
+ if @session_manager
320
+ client_conn = @session_manager.clients[session_id]
321
+ shared_session = @session_manager.shared_session
322
+ else
323
+ client_conn = @clients[session_id]
324
+ shared_session = @session
325
+ end
326
+
272
327
  return error_response(nil, VectorMCP::NotFoundError.new("Invalid session_id").code, "Invalid session_id") unless client_conn
273
328
 
274
- MessageHandler.new(@server, @session, logger).handle_post_message(env, client_conn)
329
+ MessageHandler.new(@server, shared_session, logger).handle_post_message(env, client_conn)
275
330
  end
276
331
 
277
332
  # Helper methods
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_session_manager"
4
+
5
+ module VectorMCP
6
+ module Transport
7
+ # Session manager for SSE transport with single shared session and client connection management.
8
+ # Extends BaseSessionManager with SSE-specific functionality.
9
+ #
10
+ # The SSE transport uses a single shared session for all client connections,
11
+ # but manages multiple client connections separately.
12
+ class SseSessionManager < BaseSessionManager
13
+ attr_reader :clients
14
+
15
+ # Initializes a new SSE session manager.
16
+ #
17
+ # @param transport [SSE] The parent transport instance
18
+ # @param session_timeout [Integer] Session timeout in seconds
19
+ def initialize(transport, session_timeout = 300)
20
+ @clients = Concurrent::Hash.new
21
+ super
22
+
23
+ # Create the single shared session for SSE transport
24
+ @shared_session = create_shared_session
25
+ end
26
+
27
+ # Gets the shared session for SSE transport.
28
+ # SSE uses a single session shared across all client connections.
29
+ #
30
+ # @return [Session] The shared session
31
+ def shared_session
32
+ @shared_session.touch!
33
+ @shared_session
34
+ end
35
+
36
+ # Registers a client connection with the session manager.
37
+ #
38
+ # @param client_id [String] The client connection ID
39
+ # @param client_connection [Object] The client connection object
40
+ # @return [void]
41
+ def register_client(client_id, client_connection)
42
+ @clients[client_id] = client_connection
43
+ session_metadata_updated?(@shared_session.id, clients_count: @clients.size)
44
+ logger.debug { "Client registered: #{client_id}" }
45
+ end
46
+
47
+ # Unregisters a client connection from the session manager.
48
+ #
49
+ # @param client_id [String] The client connection ID
50
+ # @return [Boolean] True if client was found and removed
51
+ def client_unregistered?(client_id)
52
+ client = @clients.delete(client_id)
53
+ return false unless client
54
+
55
+ session_metadata_updated?(@shared_session.id, clients_count: @clients.size)
56
+ logger.debug { "Client unregistered: #{client_id}" }
57
+ true
58
+ end
59
+
60
+ # Gets all client connections.
61
+ #
62
+ # @return [Hash] Hash of client_id => client_connection
63
+ def all_clients
64
+ @clients.dup
65
+ end
66
+
67
+ # Gets the number of connected clients.
68
+ #
69
+ # @return [Integer] Number of connected clients
70
+ def client_count
71
+ @clients.size
72
+ end
73
+
74
+ # Cleans up all clients and the shared session.
75
+ #
76
+ # @return [void]
77
+ def cleanup_all_sessions
78
+ logger.info { "Cleaning up #{@clients.size} client connection(s)" }
79
+
80
+ @clients.each_value do |client_conn|
81
+ close_client_connection(client_conn)
82
+ end
83
+ @clients.clear
84
+
85
+ super
86
+ end
87
+
88
+ protected
89
+
90
+ # Override: SSE doesn't need automatic cleanup since it has a single shared session.
91
+ def auto_cleanup_enabled?
92
+ false
93
+ end
94
+
95
+ # Override: Called when the shared session is terminated.
96
+ def on_session_terminated(_session)
97
+ # Clean up all client connections when session is terminated
98
+ @clients.each_value do |client_conn|
99
+ close_client_connection(client_conn)
100
+ end
101
+ @clients.clear
102
+ end
103
+
104
+ # Override: Returns metadata for SSE sessions.
105
+ def create_session_metadata
106
+ { clients_count: 0, session_type: :sse_shared }
107
+ end
108
+
109
+ # Override: Checks if any clients are connected to receive messages.
110
+ def can_send_message_to_session?(_session)
111
+ !@clients.empty?
112
+ end
113
+
114
+ # Override: Sends a message to the first available client.
115
+ def send_message_to_session(_session, message)
116
+ return false if @clients.empty?
117
+
118
+ first_client = @clients.values.first
119
+ return false unless first_client
120
+
121
+ @transport.class::StreamManager.enqueue_message(first_client, message)
122
+ end
123
+
124
+ # Override: Broadcasts messages to all connected clients.
125
+ def broadcast_message(message)
126
+ count = 0
127
+ @clients.each_value do |client_conn|
128
+ count += 1 if @transport.class::StreamManager.enqueue_message(client_conn, message)
129
+ end
130
+
131
+ logger.debug { "Message broadcasted to #{count} client(s)" }
132
+ count
133
+ end
134
+
135
+ private
136
+
137
+ # Creates the single shared session for SSE transport.
138
+ #
139
+ # @return [BaseSessionManager::Session] The shared session
140
+ def create_shared_session(rack_env = nil)
141
+ session_id = "sse_shared_session_#{SecureRandom.uuid}"
142
+ now = Time.now
143
+
144
+ # Create VectorMCP session context with request context
145
+ session_context = create_session_with_context(session_id, rack_env)
146
+
147
+ # Create internal session record using base session manager struct
148
+ session = BaseSessionManager::Session.new(
149
+ session_id,
150
+ session_context,
151
+ now,
152
+ now,
153
+ create_session_metadata
154
+ )
155
+
156
+ @sessions[session_id] = session
157
+ logger.info { "Shared SSE session created: #{session_id}" }
158
+ session
159
+ end
160
+
161
+ # Creates a VectorMCP::Session with proper request context from Rack environment
162
+ def create_session_with_context(session_id, rack_env)
163
+ request_context = if rack_env
164
+ # Create request context from Rack environment
165
+ VectorMCP::RequestContext.from_rack_env(rack_env, "sse")
166
+ else
167
+ # Fallback to minimal context for cases where rack_env is not available
168
+ VectorMCP::RequestContext.minimal("sse")
169
+ end
170
+ VectorMCP::Session.new(@transport.server, @transport, id: session_id, request_context: request_context)
171
+ end
172
+
173
+ # Closes a client connection safely.
174
+ #
175
+ # @param client_conn [Object] The client connection to close
176
+ # @return [void]
177
+ def close_client_connection(client_conn)
178
+ return unless client_conn
179
+
180
+ begin
181
+ client_conn.close if client_conn.respond_to?(:close)
182
+ rescue StandardError => e
183
+ logger.warn { "Error closing client connection: #{e.message}" }
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -4,6 +4,7 @@
4
4
  require "json"
5
5
  require_relative "../errors"
6
6
  require_relative "../util"
7
+ require_relative "stdio_session_manager"
7
8
  require "securerandom" # For generating unique request IDs
8
9
  require "timeout" # For request timeouts
9
10
 
@@ -20,6 +21,8 @@ module VectorMCP
20
21
  attr_reader :server
21
22
  # @return [Logger] The logger instance, shared with the server.
22
23
  attr_reader :logger
24
+ # @return [StdioSessionManager] The session manager for this transport.
25
+ attr_reader :session_manager
23
26
 
24
27
  # Timeout for waiting for a response to a server-initiated request (in seconds)
25
28
  DEFAULT_REQUEST_TIMEOUT = 30 # Configurable if needed
@@ -27,9 +30,12 @@ module VectorMCP
27
30
  # Initializes a new Stdio transport.
28
31
  #
29
32
  # @param server [VectorMCP::Server] The server instance that will handle messages.
30
- def initialize(server)
33
+ # @param options [Hash] Optional configuration options.
34
+ # @option options [Boolean] :enable_session_manager (false) Whether to enable the unified session manager.
35
+ def initialize(server, options = {})
31
36
  @server = server
32
37
  @logger = server.logger
38
+ @session_manager = options[:enable_session_manager] ? StdioSessionManager.new(self) : nil
33
39
  @input_mutex = Mutex.new
34
40
  @output_mutex = Mutex.new
35
41
  @running = false
@@ -109,6 +115,43 @@ module VectorMCP
109
115
  write_message(notification)
110
116
  end
111
117
 
118
+ # Sends a JSON-RPC notification message to a specific session.
119
+ # For stdio transport, this behaves the same as send_notification since there's only one session.
120
+ #
121
+ # @param _session_id [String] The session ID (ignored for stdio transport).
122
+ # @param method [String] The method name of the notification.
123
+ # @param params [Hash, Array, nil] The parameters for the notification (optional).
124
+ # @return [Boolean] True if the notification was sent successfully.
125
+ # rubocop:disable Naming/PredicateMethod
126
+ def send_notification_to_session(_session_id, method, params = nil)
127
+ send_notification(method, params)
128
+ true
129
+ end
130
+ # rubocop:enable Naming/PredicateMethod
131
+
132
+ # Sends a JSON-RPC notification message to a specific session.
133
+ # For stdio transport, this behaves the same as send_notification since there's only one session.
134
+ #
135
+ # @param _session_id [String] The session ID (ignored for stdio transport).
136
+ # @param method [String] The method name of the notification.
137
+ # @param params [Hash, Array, nil] The parameters for the notification (optional).
138
+ # @return [Boolean] True if the notification was sent successfully.
139
+ def notification_sent_to_session?(_session_id, method, params = nil)
140
+ send_notification(method, params)
141
+ true
142
+ end
143
+
144
+ # Broadcasts a JSON-RPC notification message to all sessions.
145
+ # For stdio transport, this behaves the same as send_notification since there's only one session.
146
+ #
147
+ # @param method [String] The method name of the notification.
148
+ # @param params [Hash, Array, nil] The parameters for the notification (optional).
149
+ # @return [Integer] Number of sessions the notification was sent to (always 1 for stdio).
150
+ def broadcast_notification(method, params = nil)
151
+ send_notification(method, params)
152
+ 1
153
+ end
154
+
112
155
  # Sends a server-initiated JSON-RPC request to the client and waits for a response.
113
156
  # This is a blocking call.
114
157
  #
@@ -126,7 +169,7 @@ module VectorMCP
126
169
  request_payload[:params] = params if params
127
170
 
128
171
  setup_request_tracking(request_id)
129
- logger.debug "[Stdio Transport] Sending request ID #{request_id}: #{method}"
172
+ # Sending request to client
130
173
  write_message(request_payload)
131
174
 
132
175
  response = wait_for_response(request_id, method, timeout)
@@ -184,7 +227,6 @@ module VectorMCP
184
227
 
185
228
  return handle_outgoing_response(message) if outgoing_response?(message)
186
229
 
187
- ensure_session_exists
188
230
  handle_server_message(message)
189
231
  end
190
232
 
@@ -196,7 +238,21 @@ module VectorMCP
196
238
  message["id"] && !message["method"] && (message.key?("result") || message.key?("error"))
197
239
  end
198
240
 
199
- # Ensures a global session exists for this stdio transport.
241
+ # Gets the global session for this stdio transport.
242
+ # @api private
243
+ # @return [VectorMCP::Session] The current session.
244
+ def session
245
+ # Try session manager first, fallback to old method for backward compatibility
246
+ if @session_manager
247
+ session_wrapper = @session_manager.global_session
248
+ return session_wrapper.context if session_wrapper
249
+ end
250
+
251
+ # Fallback to old session creation for backward compatibility
252
+ ensure_session_exists
253
+ end
254
+
255
+ # Ensures a global session exists for this stdio transport (legacy method).
200
256
  # @api private
201
257
  # @return [VectorMCP::Session] The current session.
202
258
  def ensure_session_exists
@@ -208,11 +264,11 @@ module VectorMCP
208
264
  # @param message [Hash] The parsed message.
209
265
  # @return [void]
210
266
  def handle_server_message(message)
211
- session = ensure_session_exists
212
- session_id = session.id
267
+ current_session = session
268
+ session_id = current_session.id
213
269
 
214
270
  begin
215
- result = @server.handle_message(message, session, session_id)
271
+ result = @server.handle_message(message, current_session, session_id)
216
272
  send_response(message["id"], result) if message["id"] && result
217
273
  rescue VectorMCP::ProtocolError => e
218
274
  handle_protocol_error(e, message)
@@ -223,11 +279,11 @@ module VectorMCP
223
279
 
224
280
  # --- Run helpers (private) ---
225
281
 
226
- # Creates a new session for the stdio connection.
282
+ # Gets the session for the stdio connection.
227
283
  # @api private
228
- # @return [VectorMCP::Session] The newly created session.
284
+ # @return [VectorMCP::Session] The session.
229
285
  def create_session
230
- VectorMCP::Session.new(@server, self)
286
+ session
231
287
  end
232
288
 
233
289
  # Launches the input reading loop in a new thread.
@@ -250,6 +306,7 @@ module VectorMCP
250
306
  def shutdown_transport
251
307
  @running = false
252
308
  @input_thread&.kill if @input_thread&.alive?
309
+ @session_manager&.cleanup_all_sessions
253
310
  logger.info("Stdio transport shut down")
254
311
  end
255
312
 
@@ -261,7 +318,7 @@ module VectorMCP
261
318
  # @return [void]
262
319
  def handle_outgoing_response(message)
263
320
  request_id = message["id"]
264
- logger.debug "[Stdio Transport] Received response for outgoing request ID #{request_id}"
321
+ # Received response for outgoing request
265
322
 
266
323
  @mutex.synchronize do
267
324
  # Store the response (convert keys to symbols for consistency)
@@ -272,7 +329,7 @@ module VectorMCP
272
329
  condition = @outgoing_request_conditions[request_id]
273
330
  if condition
274
331
  condition.signal
275
- logger.debug "[Stdio Transport] Signaled condition for request ID #{request_id}"
332
+ # Signaled condition for request
276
333
  else
277
334
  logger.warn "[Stdio Transport] Received response for request ID #{request_id} but no thread is waiting"
278
335
  end
@@ -342,7 +399,7 @@ module VectorMCP
342
399
  # @return [void]
343
400
  def write_message(message)
344
401
  json_msg = message.to_json
345
- logger.debug { "Sending stdio message: #{json_msg}" }
402
+ # Sending stdio message
346
403
 
347
404
  begin
348
405
  @output_mutex.synchronize do