Class: VectorMCP::Server

Inherits:
Object
  • Object
show all
Includes:
Definitions, Capabilities, MessageHandling, Registry
Defined in:
lib/vector_mcp/server.rb,
lib/vector_mcp/server/registry.rb,
lib/vector_mcp/server/capabilities.rb,
lib/vector_mcp/server/message_handling.rb

Overview

The Server class is the central component for an MCP server implementation. It manages tools, resources, prompts, and handles the MCP message lifecycle.

A server instance is typically initialized, configured with capabilities (tools, resources, prompts), and then run with a chosen transport mechanism (e.g., Stdio, SSE).

Examples:

Creating and running a simple server

server = VectorMCP::Server.new(name: "MySimpleServer", version: "1.0")

server.register_tool(
  name: "echo",
  description: "Echoes back the input string.",
  input_schema: { type: "object", properties: { message: { type: "string" } } }
) do |args|
  args["message"]
end

server.run(transport: :stdio) # Runs with Stdio transport by default

Defined Under Namespace

Modules: Capabilities, MessageHandling, Registry

Constant Summary collapse

PROTOCOL_VERSION =

The specific version of the Model Context Protocol this server implements.

"2024-11-05"

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MessageHandling

#handle_message, #on_notification, #on_request

Methods included from Capabilities

#clear_prompts_list_changed, #clear_roots_list_changed, #notify_prompts_list_changed, #notify_roots_list_changed, #sampling_config, #server_capabilities, #server_info, #subscribe_prompts

Methods included from Registry

#register_image_prompt, #register_image_resource, #register_image_resource_from_data, #register_image_tool, #register_prompt, #register_resource, #register_root, #register_root_from_path, #register_tool

Constructor Details

#initialize(name_pos = nil, name: nil, version: "0.1.0", **options) ⇒ Server

Initializes a new VectorMCP server.

Parameters:

  • name_pos (String) (defaults to: nil)

    Positional name argument (deprecated, use name: instead).

  • name (String) (defaults to: nil)

    The name of the server.

  • version (String) (defaults to: "0.1.0")

    The version of the server.

  • options (Hash)

    Additional server options:

    • :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
    • :protocol_version [String] The MCP protocol version to use.
    • :sampling_config [Hash] Configuration for sampling capabilities. Available options:
      • :enabled [Boolean] Whether sampling is enabled (default: true)
      • :methods [Array] Supported sampling methods (default: ["createMessage"])
      • :supports_streaming [Boolean] Whether streaming is supported (default: false)
      • :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
      • :supports_images [Boolean] Whether image content is supported (default: false)
      • :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
      • :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
      • :context_inclusion_methods [Array] Supported context inclusion methods (default: ["none", "thisServer"])
      • :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)

Raises:

  • (ArgumentError)


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
# File 'lib/vector_mcp/server.rb', line 96

def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
  raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

  @name = name_pos || name || "UnnamedServer"
  @version = version
  @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
  @logger = VectorMCP.logger_for("server")
  # NOTE: log level should be configured via VectorMCP.configure_logging instead

  @transport = nil
  @tools = {}
  @resources = {}
  @prompts = {}
  @roots = {}
  @request_handlers = {}
  @notification_handlers = {}
  @in_flight_requests = {}
  @prompts_list_changed = false
  @prompt_subscribers = []
  @roots_list_changed = false

  # Configure sampling capabilities
  @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

  # Initialize security components
  @auth_manager = Security::AuthManager.new
  @authorization = Security::Authorization.new
  @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

  # Initialize middleware manager
  @middleware_manager = Middleware::Manager.new

  setup_default_handlers

  @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
end

Instance Attribute Details

#auth_managerObject (readonly)

Returns the value of attribute auth_manager.



73
74
75
# File 'lib/vector_mcp/server.rb', line 73

def auth_manager
  @auth_manager
end

#authorizationObject (readonly)

Returns the value of attribute authorization.



73
74
75
# File 'lib/vector_mcp/server.rb', line 73

def authorization
  @authorization
end

#in_flight_requestsHash (readonly)

Returns A hash tracking currently processing requests, for cancellation purposes.

Returns:

  • (Hash)

    A hash tracking currently processing requests, for cancellation purposes.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#loggerLogger (readonly)

Returns The logger instance for this server.

Returns:

  • (Logger)

    The logger instance for this server.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#middleware_managerObject (readonly)

Returns the value of attribute middleware_manager.



73
74
75
# File 'lib/vector_mcp/server.rb', line 73

def middleware_manager
  @middleware_manager
end

#nameString (readonly)

Returns The name of the server.

Returns:

  • (String)

    The name of the server.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#promptsHash<String, VectorMCP::Definitions::Prompt> (readonly)

Returns Registered prompts, keyed by name.

Returns:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#protocol_versionString (readonly)

Returns The MCP protocol version this server implements.

Returns:

  • (String)

    The MCP protocol version this server implements.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#resourcesHash<String, VectorMCP::Definitions::Resource> (readonly)

Returns Registered resources, keyed by URI string.

Returns:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#rootsHash<String, VectorMCP::Definitions::Root> (readonly)

Returns Registered roots, keyed by URI string.

Returns:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#security_middlewareObject (readonly)

Returns the value of attribute security_middleware.



73
74
75
# File 'lib/vector_mcp/server.rb', line 73

def security_middleware
  @security_middleware
end

#toolsHash<String, VectorMCP::Definitions::Tool> (readonly)

Returns Registered tools, keyed by name.

Returns:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#transportVectorMCP::Transport::Base?

Returns The active transport instance, if any.

Returns:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

#versionString (readonly)

Returns The version of the server software.

Returns:

  • (String)

    The version of the server software.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/vector_mcp/server.rb', line 64

class Server
  include Definitions # Make Tool, Resource, Prompt, Root structs easily available
  include Registry
  include Capabilities
  include MessageHandling

  # The specific version of the Model Context Protocol this server implements.
  PROTOCOL_VERSION = "2024-11-05"

  attr_reader :logger, :name, :version, :protocol_version, :tools, :resources, :prompts, :roots, :in_flight_requests,
              :auth_manager, :authorization, :security_middleware, :middleware_manager
  attr_accessor :transport

  # Initializes a new VectorMCP server.
  #
  # @param name_pos [String] Positional name argument (deprecated, use name: instead).
  # @param name [String] The name of the server.
  # @param version [String] The version of the server.
  # @param options [Hash] Additional server options:
  #   - :log_level [Integer] The logging level (Logger::DEBUG, Logger::INFO, etc.).
  #   - :protocol_version [String] The MCP protocol version to use.
  #   - :sampling_config [Hash] Configuration for sampling capabilities. Available options:
  #     - :enabled [Boolean] Whether sampling is enabled (default: true)
  #     - :methods [Array<String>] Supported sampling methods (default: ["createMessage"])
  #     - :supports_streaming [Boolean] Whether streaming is supported (default: false)
  #     - :supports_tool_calls [Boolean] Whether tool calls are supported (default: false)
  #     - :supports_images [Boolean] Whether image content is supported (default: false)
  #     - :max_tokens_limit [Integer, nil] Maximum tokens limit (default: nil, no limit)
  #     - :timeout_seconds [Integer] Default timeout for sampling requests (default: 30)
  #     - :context_inclusion_methods [Array<String>] Supported context inclusion methods
  #       (default: ["none", "thisServer"])
  #     - :model_preferences_supported [Boolean] Whether model preferences are supported (default: true)
  def initialize(name_pos = nil, *, name: nil, version: "0.1.0", **options)
    raise ArgumentError, "Name provided both positionally (#{name_pos}) and as keyword argument (#{name})" if name_pos && name && name_pos != name

    @name = name_pos || name || "UnnamedServer"
    @version = version
    @protocol_version = options[:protocol_version] || PROTOCOL_VERSION
    @logger = VectorMCP.logger_for("server")
    # NOTE: log level should be configured via VectorMCP.configure_logging instead

    @transport = nil
    @tools = {}
    @resources = {}
    @prompts = {}
    @roots = {}
    @request_handlers = {}
    @notification_handlers = {}
    @in_flight_requests = {}
    @prompts_list_changed = false
    @prompt_subscribers = []
    @roots_list_changed = false

    # Configure sampling capabilities
    @sampling_config = configure_sampling_capabilities(options[:sampling_config] || {})

    # Initialize security components
    @auth_manager = Security::AuthManager.new
    @authorization = Security::Authorization.new
    @security_middleware = Security::Middleware.new(@auth_manager, @authorization)

    # Initialize middleware manager
    @middleware_manager = Middleware::Manager.new

    setup_default_handlers

    @logger.info("Server instance '#{@name}' v#{@version} (MCP Protocol: #{@protocol_version}, Gem: v#{VectorMCP::VERSION}) initialized.")
  end

  # --- Server Execution ---

  # Runs the server using the specified transport mechanism.
  #
  # @param transport [:stdio, :sse, VectorMCP::Transport::Base] The transport to use.
  #   Can be a symbol (`:stdio`, `:sse`) or an initialized transport instance.
  #   If a symbol is provided, the method will instantiate the corresponding transport class.
  #   If `:sse` is chosen, it uses Puma as the HTTP server.
  # @param options [Hash] Transport-specific options (e.g., `:host`, `:port` for SSE).
  #   These are passed to the transport's constructor if a symbol is provided for `transport`.
  # @return [void]
  # @raise [ArgumentError] if an unsupported transport symbol is given.
  # @raise [NotImplementedError] if `:sse` transport is specified (currently a placeholder).
  def run(transport: :stdio, **options)
    active_transport = case transport
                       when :stdio
                         VectorMCP::Transport::Stdio.new(self, **options)
                       when :sse
                         begin
                           require_relative "transport/sse"
                           VectorMCP::Transport::SSE.new(self, **options)
                         rescue LoadError => e
                           logger.fatal("SSE transport requires additional dependencies.")
                           raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                         end
                       when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                         transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                         transport
                       else
                         logger.fatal("Unsupported transport type: #{transport.inspect}")
                         raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                       end
    self.transport = active_transport
    active_transport.run
  end

  # --- Security Configuration ---

  # Enable authentication with specified strategy and configuration
  # @param strategy [Symbol] the authentication strategy (:api_key, :jwt, :custom)
  # @param options [Hash] strategy-specific configuration options
  # @return [void]
  def enable_authentication!(strategy: :api_key, **options, &block)
    # Clear existing strategies when switching to a new configuration
    clear_auth_strategies unless @auth_manager.strategies.empty?

    @auth_manager.enable!(default_strategy: strategy)

    case strategy
    when :api_key
      add_api_key_auth(options[:keys] || [])
    when :jwt
      add_jwt_auth(options)
    when :custom
      handler = block || options[:handler]
      raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

      add_custom_auth(&handler)

    else
      raise ArgumentError, "Unknown authentication strategy: #{strategy}"
    end

    @logger.info("Authentication enabled with strategy: #{strategy}")
  end

  # Disable authentication (return to pass-through mode)
  # @return [void]
  def disable_authentication!
    @auth_manager.disable!
    @logger.info("Authentication disabled")
  end

  # Enable authorization with optional policy configuration block
  # @param block [Proc] optional block for configuring authorization policies
  # @return [void]
  def enable_authorization!(&block)
    @authorization.enable!
    instance_eval(&block) if block_given?
    @logger.info("Authorization enabled")
  end

  # Disable authorization (return to pass-through mode)
  # @return [void]
  def disable_authorization!
    @authorization.disable!
    @logger.info("Authorization disabled")
  end

  # Add authorization policy for tools
  # @param block [Proc] policy block that receives (user, action, tool)
  # @return [void]
  def authorize_tools(&block)
    @authorization.add_policy(:tool, &block)
  end

  # Add authorization policy for resources
  # @param block [Proc] policy block that receives (user, action, resource)
  # @return [void]
  def authorize_resources(&block)
    @authorization.add_policy(:resource, &block)
  end

  # Add authorization policy for prompts
  # @param block [Proc] policy block that receives (user, action, prompt)
  # @return [void]
  def authorize_prompts(&block)
    @authorization.add_policy(:prompt, &block)
  end

  # Add authorization policy for roots
  # @param block [Proc] policy block that receives (user, action, root)
  # @return [void]
  def authorize_roots(&block)
    @authorization.add_policy(:root, &block)
  end

  # Check if security features are enabled
  # @return [Boolean] true if authentication or authorization is enabled
  def security_enabled?
    @security_middleware.security_enabled?
  end

  # Get current security status for debugging/monitoring
  # @return [Hash] security configuration status
  def security_status
    @security_middleware.security_status
  end

  # --- Middleware Management ---

  # Register middleware for specific hook types
  # @param middleware_class [Class] Middleware class inheriting from VectorMCP::Middleware::Base
  # @param hooks [Symbol, Array<Symbol>] Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])
  # @param priority [Integer] Execution priority (lower numbers execute first, default: 100)
  # @param conditions [Hash] Conditions for when middleware should run
  # @option conditions [Array<String>] :only_operations Only run for these operations
  # @option conditions [Array<String>] :except_operations Don't run for these operations
  # @option conditions [Array<String>] :only_users Only run for these user IDs
  # @option conditions [Array<String>] :except_users Don't run for these user IDs
  # @option conditions [Boolean] :critical If true, errors in this middleware stop execution
  # @example
  #   server.use_middleware(MyMiddleware, :before_tool_call)
  #   server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
  #   server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })
  def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
    @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
    @logger.info("Registered middleware: #{middleware_class.name}")
  end

  # Remove all middleware hooks for a specific class
  # @param middleware_class [Class] Middleware class to remove
  def remove_middleware(middleware_class)
    @middleware_manager.unregister(middleware_class)
    @logger.info("Removed middleware: #{middleware_class.name}")
  end

  # Get middleware statistics
  # @return [Hash] Statistics about registered middleware
  def middleware_stats
    @middleware_manager.stats
  end

  # Clear all middleware (useful for testing)
  def clear_middleware!
    @middleware_manager.clear!
    @logger.info("Cleared all middleware")
  end

  private

  # Add API key authentication strategy
  # @param keys [Array<String>] array of valid API keys
  # @return [void]
  def add_api_key_auth(keys)
    strategy = Security::Strategies::ApiKey.new(keys: keys)
    @auth_manager.add_strategy(:api_key, strategy)
  end

  # Add JWT authentication strategy
  # @param options [Hash] JWT configuration options
  # @return [void]
  def add_jwt_auth(options)
    strategy = Security::Strategies::JwtToken.new(**options)
    @auth_manager.add_strategy(:jwt, strategy)
  end

  # Add custom authentication strategy
  # @param handler [Proc] custom authentication handler block
  # @return [void]
  def add_custom_auth(&block)
    strategy = Security::Strategies::Custom.new(&block)
    @auth_manager.add_strategy(:custom, strategy)
  end

  # Clear all authentication strategies
  # @return [void]
  def clear_auth_strategies
    @auth_manager.strategies.each_key do |strategy_name|
      @auth_manager.remove_strategy(strategy_name)
    end
  end
end

Instance Method Details

#authorize_prompts(&block) ⇒ void

This method returns an undefined value.

Add authorization policy for prompts

Parameters:

  • block (Proc)

    policy block that receives (user, action, prompt)



239
240
241
# File 'lib/vector_mcp/server.rb', line 239

def authorize_prompts(&block)
  @authorization.add_policy(:prompt, &block)
end

#authorize_resources(&block) ⇒ void

This method returns an undefined value.

Add authorization policy for resources

Parameters:

  • block (Proc)

    policy block that receives (user, action, resource)



232
233
234
# File 'lib/vector_mcp/server.rb', line 232

def authorize_resources(&block)
  @authorization.add_policy(:resource, &block)
end

#authorize_roots(&block) ⇒ void

This method returns an undefined value.

Add authorization policy for roots

Parameters:

  • block (Proc)

    policy block that receives (user, action, root)



246
247
248
# File 'lib/vector_mcp/server.rb', line 246

def authorize_roots(&block)
  @authorization.add_policy(:root, &block)
end

#authorize_tools(&block) ⇒ void

This method returns an undefined value.

Add authorization policy for tools

Parameters:

  • block (Proc)

    policy block that receives (user, action, tool)



225
226
227
# File 'lib/vector_mcp/server.rb', line 225

def authorize_tools(&block)
  @authorization.add_policy(:tool, &block)
end

#clear_middleware!Object

Clear all middleware (useful for testing)



297
298
299
300
# File 'lib/vector_mcp/server.rb', line 297

def clear_middleware!
  @middleware_manager.clear!
  @logger.info("Cleared all middleware")
end

#disable_authentication!void

This method returns an undefined value.

Disable authentication (return to pass-through mode)



201
202
203
204
# File 'lib/vector_mcp/server.rb', line 201

def disable_authentication!
  @auth_manager.disable!
  @logger.info("Authentication disabled")
end

#disable_authorization!void

This method returns an undefined value.

Disable authorization (return to pass-through mode)



217
218
219
220
# File 'lib/vector_mcp/server.rb', line 217

def disable_authorization!
  @authorization.disable!
  @logger.info("Authorization disabled")
end

#enable_authentication!(strategy: :api_key, **options, &block) ⇒ void

This method returns an undefined value.

Enable authentication with specified strategy and configuration

Parameters:

  • strategy (Symbol) (defaults to: :api_key)

    the authentication strategy (:api_key, :jwt, :custom)

  • options (Hash)

    strategy-specific configuration options



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/vector_mcp/server.rb', line 175

def enable_authentication!(strategy: :api_key, **options, &block)
  # Clear existing strategies when switching to a new configuration
  clear_auth_strategies unless @auth_manager.strategies.empty?

  @auth_manager.enable!(default_strategy: strategy)

  case strategy
  when :api_key
    add_api_key_auth(options[:keys] || [])
  when :jwt
    add_jwt_auth(options)
  when :custom
    handler = block || options[:handler]
    raise ArgumentError, "Custom authentication strategy requires a handler block" unless handler

    add_custom_auth(&handler)

  else
    raise ArgumentError, "Unknown authentication strategy: #{strategy}"
  end

  @logger.info("Authentication enabled with strategy: #{strategy}")
end

#enable_authorization!(&block) ⇒ void

This method returns an undefined value.

Enable authorization with optional policy configuration block

Parameters:

  • block (Proc)

    optional block for configuring authorization policies



209
210
211
212
213
# File 'lib/vector_mcp/server.rb', line 209

def enable_authorization!(&block)
  @authorization.enable!
  instance_eval(&block) if block_given?
  @logger.info("Authorization enabled")
end

#middleware_statsHash

Get middleware statistics

Returns:

  • (Hash)

    Statistics about registered middleware



292
293
294
# File 'lib/vector_mcp/server.rb', line 292

def middleware_stats
  @middleware_manager.stats
end

#remove_middleware(middleware_class) ⇒ Object

Remove all middleware hooks for a specific class

Parameters:

  • middleware_class (Class)

    Middleware class to remove



285
286
287
288
# File 'lib/vector_mcp/server.rb', line 285

def remove_middleware(middleware_class)
  @middleware_manager.unregister(middleware_class)
  @logger.info("Removed middleware: #{middleware_class.name}")
end

#run(transport: :stdio, **options) ⇒ void

This method returns an undefined value.

Runs the server using the specified transport mechanism.

Parameters:

  • transport (:stdio, :sse, VectorMCP::Transport::Base) (defaults to: :stdio)

    The transport to use. Can be a symbol (:stdio, :sse) or an initialized transport instance. If a symbol is provided, the method will instantiate the corresponding transport class. If :sse is chosen, it uses Puma as the HTTP server.

  • options (Hash)

    Transport-specific options (e.g., :host, :port for SSE). These are passed to the transport's constructor if a symbol is provided for transport.

Raises:

  • (ArgumentError)

    if an unsupported transport symbol is given.

  • (NotImplementedError)

    if :sse transport is specified (currently a placeholder).



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/vector_mcp/server.rb', line 146

def run(transport: :stdio, **options)
  active_transport = case transport
                     when :stdio
                       VectorMCP::Transport::Stdio.new(self, **options)
                     when :sse
                       begin
                         require_relative "transport/sse"
                         VectorMCP::Transport::SSE.new(self, **options)
                       rescue LoadError => e
                         logger.fatal("SSE transport requires additional dependencies.")
                         raise NotImplementedError, "SSE transport dependencies not available: #{e.message}"
                       end
                     when VectorMCP::Transport::Base # Allow passing an initialized transport instance
                       transport.server = self if transport.respond_to?(:server=) && transport.server.nil? # Ensure server is set
                       transport
                     else
                       logger.fatal("Unsupported transport type: #{transport.inspect}")
                       raise ArgumentError, "Unsupported transport: #{transport.inspect}"
                     end
  self.transport = active_transport
  active_transport.run
end

#security_enabled?Boolean

Check if security features are enabled

Returns:

  • (Boolean)

    true if authentication or authorization is enabled



252
253
254
# File 'lib/vector_mcp/server.rb', line 252

def security_enabled?
  @security_middleware.security_enabled?
end

#security_statusHash

Get current security status for debugging/monitoring

Returns:

  • (Hash)

    security configuration status



258
259
260
# File 'lib/vector_mcp/server.rb', line 258

def security_status
  @security_middleware.security_status
end

#use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {}) ⇒ Object

Register middleware for specific hook types

Examples:

server.use_middleware(MyMiddleware, :before_tool_call)
server.use_middleware(AuthMiddleware, [:before_request, :after_response], priority: 10)
server.use_middleware(LoggingMiddleware, :after_tool_call, conditions: { only_operations: ['important_tool'] })

Parameters:

  • middleware_class (Class)

    Middleware class inheriting from VectorMCP::Middleware::Base

  • hooks (Symbol, Array<Symbol>)

    Hook types to register for (e.g., :before_tool_call, [:before_tool_call, :after_tool_call])

  • priority (Integer) (defaults to: Middleware::Hook::DEFAULT_PRIORITY)

    Execution priority (lower numbers execute first, default: 100)

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

    Conditions for when middleware should run

Options Hash (conditions:):

  • :only_operations (Array<String>)

    Only run for these operations

  • :except_operations (Array<String>)

    Don't run for these operations

  • :only_users (Array<String>)

    Only run for these user IDs

  • :except_users (Array<String>)

    Don't run for these user IDs

  • :critical (Boolean)

    If true, errors in this middleware stop execution



278
279
280
281
# File 'lib/vector_mcp/server.rb', line 278

def use_middleware(middleware_class, hooks, priority: Middleware::Hook::DEFAULT_PRIORITY, conditions: {})
  @middleware_manager.register(middleware_class, hooks, priority: priority, conditions: conditions)
  @logger.info("Registered middleware: #{middleware_class.name}")
end