ruby-mcp-client 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -225,7 +225,8 @@ module MCPClient
225
225
  # OAuth authorization server metadata
226
226
  class ServerMetadata
227
227
  attr_reader :issuer, :authorization_endpoint, :token_endpoint, :registration_endpoint,
228
- :scopes_supported, :response_types_supported, :grant_types_supported
228
+ :scopes_supported, :response_types_supported, :grant_types_supported,
229
+ :code_challenge_methods_supported, :client_id_metadata_document_supported
229
230
 
230
231
  # @param issuer [String] Issuer identifier URL
231
232
  # @param authorization_endpoint [String] Authorization endpoint URL
@@ -234,8 +235,14 @@ module MCPClient
234
235
  # @param scopes_supported [Array<String>, nil] Supported OAuth scopes
235
236
  # @param response_types_supported [Array<String>, nil] Supported response types
236
237
  # @param grant_types_supported [Array<String>, nil] Supported grant types
238
+ # @param code_challenge_methods_supported [Array<String>, nil] Supported PKCE code challenge methods (RFC 8414)
239
+ # @param client_id_metadata_document_supported [Boolean, nil] Whether the server accepts
240
+ # Client ID Metadata Document client IDs (MCP 2025-11-25 / SEP-991)
241
+ # rubocop:disable Metrics/ParameterLists
237
242
  def initialize(issuer:, authorization_endpoint:, token_endpoint:, registration_endpoint: nil,
238
- scopes_supported: nil, response_types_supported: nil, grant_types_supported: nil)
243
+ scopes_supported: nil, response_types_supported: nil, grant_types_supported: nil,
244
+ code_challenge_methods_supported: nil, client_id_metadata_document_supported: nil)
245
+ # rubocop:enable Metrics/ParameterLists
239
246
  @issuer = issuer
240
247
  @authorization_endpoint = authorization_endpoint
241
248
  @token_endpoint = token_endpoint
@@ -243,6 +250,8 @@ module MCPClient
243
250
  @scopes_supported = scopes_supported
244
251
  @response_types_supported = response_types_supported
245
252
  @grant_types_supported = grant_types_supported
253
+ @code_challenge_methods_supported = code_challenge_methods_supported
254
+ @client_id_metadata_document_supported = client_id_metadata_document_supported
246
255
  end
247
256
 
248
257
  # Check if dynamic client registration is supported
@@ -251,6 +260,13 @@ module MCPClient
251
260
  !@registration_endpoint.nil?
252
261
  end
253
262
 
263
+ # Check if the server accepts clients using Client ID Metadata Documents
264
+ # (MCP 2025-11-25 / SEP-991), i.e. HTTPS URLs as client identifiers
265
+ # @return [Boolean] true if client_id_metadata_document_supported is true
266
+ def supports_client_id_metadata_documents?
267
+ @client_id_metadata_document_supported == true
268
+ end
269
+
254
270
  # Convert to hash
255
271
  # @return [Hash] Hash representation
256
272
  def to_h
@@ -261,7 +277,9 @@ module MCPClient
261
277
  registration_endpoint: @registration_endpoint,
262
278
  scopes_supported: @scopes_supported,
263
279
  response_types_supported: @response_types_supported,
264
- grant_types_supported: @grant_types_supported
280
+ grant_types_supported: @grant_types_supported,
281
+ code_challenge_methods_supported: @code_challenge_methods_supported,
282
+ client_id_metadata_document_supported: @client_id_metadata_document_supported
265
283
  }.compact
266
284
  end
267
285
 
@@ -276,20 +294,38 @@ module MCPClient
276
294
  registration_endpoint: data[:registration_endpoint] || data['registration_endpoint'],
277
295
  scopes_supported: data[:scopes_supported] || data['scopes_supported'],
278
296
  response_types_supported: data[:response_types_supported] || data['response_types_supported'],
279
- grant_types_supported: data[:grant_types_supported] || data['grant_types_supported']
297
+ grant_types_supported: data[:grant_types_supported] || data['grant_types_supported'],
298
+ code_challenge_methods_supported: data[:code_challenge_methods_supported] ||
299
+ data['code_challenge_methods_supported'],
300
+ client_id_metadata_document_supported: fetch_boolean(data, :client_id_metadata_document_supported)
280
301
  )
281
302
  end
303
+
304
+ # Fetch a possibly-false value from a hash by symbol or string key.
305
+ # Unlike the `||` chains above, this preserves an explicit false.
306
+ # @param data [Hash] Source hash
307
+ # @param key [Symbol] Key to fetch
308
+ # @return [Object, nil] The value, or nil when absent under both keys
309
+ def self.fetch_boolean(data, key)
310
+ return data[key] if data.key?(key)
311
+
312
+ data[key.to_s]
313
+ end
314
+ private_class_method :fetch_boolean
282
315
  end
283
316
 
284
317
  # Protected resource metadata for authorization server discovery
285
318
  class ResourceMetadata
286
- attr_reader :resource, :authorization_servers
319
+ attr_reader :resource, :authorization_servers, :scopes_supported
287
320
 
288
321
  # @param resource [String] Resource server identifier
289
322
  # @param authorization_servers [Array<String>] List of authorization server URLs
290
- def initialize(resource:, authorization_servers:)
323
+ # @param scopes_supported [Array<String>, nil] Scopes the resource supports (RFC 9728);
324
+ # per MCP, the default scope set when a challenge provides none
325
+ def initialize(resource:, authorization_servers:, scopes_supported: nil)
291
326
  @resource = resource
292
327
  @authorization_servers = authorization_servers
328
+ @scopes_supported = scopes_supported
293
329
  end
294
330
 
295
331
  # Convert to hash
@@ -297,8 +333,9 @@ module MCPClient
297
333
  def to_h
298
334
  {
299
335
  resource: @resource,
300
- authorization_servers: @authorization_servers
301
- }
336
+ authorization_servers: @authorization_servers,
337
+ scopes_supported: @scopes_supported
338
+ }.compact
302
339
  end
303
340
 
304
341
  # Create resource metadata from hash
@@ -307,7 +344,8 @@ module MCPClient
307
344
  def self.from_h(data)
308
345
  new(
309
346
  resource: data[:resource] || data['resource'],
310
- authorization_servers: data[:authorization_servers] || data['authorization_servers']
347
+ authorization_servers: data[:authorization_servers] || data['authorization_servers'],
348
+ scopes_supported: data[:scopes_supported] || data['scopes_supported']
311
349
  )
312
350
  end
313
351
  end