ruby-mcp-client 1.1.0 → 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.
@@ -226,7 +226,7 @@ module MCPClient
226
226
  class ServerMetadata
227
227
  attr_reader :issuer, :authorization_endpoint, :token_endpoint, :registration_endpoint,
228
228
  :scopes_supported, :response_types_supported, :grant_types_supported,
229
- :code_challenge_methods_supported
229
+ :code_challenge_methods_supported, :client_id_metadata_document_supported
230
230
 
231
231
  # @param issuer [String] Issuer identifier URL
232
232
  # @param authorization_endpoint [String] Authorization endpoint URL
@@ -236,9 +236,13 @@ module MCPClient
236
236
  # @param response_types_supported [Array<String>, nil] Supported response types
237
237
  # @param grant_types_supported [Array<String>, nil] Supported grant types
238
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
239
242
  def initialize(issuer:, authorization_endpoint:, token_endpoint:, registration_endpoint: nil,
240
243
  scopes_supported: nil, response_types_supported: nil, grant_types_supported: nil,
241
- code_challenge_methods_supported: nil)
244
+ code_challenge_methods_supported: nil, client_id_metadata_document_supported: nil)
245
+ # rubocop:enable Metrics/ParameterLists
242
246
  @issuer = issuer
243
247
  @authorization_endpoint = authorization_endpoint
244
248
  @token_endpoint = token_endpoint
@@ -247,6 +251,7 @@ module MCPClient
247
251
  @response_types_supported = response_types_supported
248
252
  @grant_types_supported = grant_types_supported
249
253
  @code_challenge_methods_supported = code_challenge_methods_supported
254
+ @client_id_metadata_document_supported = client_id_metadata_document_supported
250
255
  end
251
256
 
252
257
  # Check if dynamic client registration is supported
@@ -255,6 +260,13 @@ module MCPClient
255
260
  !@registration_endpoint.nil?
256
261
  end
257
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
+
258
270
  # Convert to hash
259
271
  # @return [Hash] Hash representation
260
272
  def to_h
@@ -266,7 +278,8 @@ module MCPClient
266
278
  scopes_supported: @scopes_supported,
267
279
  response_types_supported: @response_types_supported,
268
280
  grant_types_supported: @grant_types_supported,
269
- code_challenge_methods_supported: @code_challenge_methods_supported
281
+ code_challenge_methods_supported: @code_challenge_methods_supported,
282
+ client_id_metadata_document_supported: @client_id_metadata_document_supported
270
283
  }.compact
271
284
  end
272
285
 
@@ -283,20 +296,36 @@ module MCPClient
283
296
  response_types_supported: data[:response_types_supported] || data['response_types_supported'],
284
297
  grant_types_supported: data[:grant_types_supported] || data['grant_types_supported'],
285
298
  code_challenge_methods_supported: data[:code_challenge_methods_supported] ||
286
- 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)
287
301
  )
288
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
289
315
  end
290
316
 
291
317
  # Protected resource metadata for authorization server discovery
292
318
  class ResourceMetadata
293
- attr_reader :resource, :authorization_servers
319
+ attr_reader :resource, :authorization_servers, :scopes_supported
294
320
 
295
321
  # @param resource [String] Resource server identifier
296
322
  # @param authorization_servers [Array<String>] List of authorization server URLs
297
- 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)
298
326
  @resource = resource
299
327
  @authorization_servers = authorization_servers
328
+ @scopes_supported = scopes_supported
300
329
  end
301
330
 
302
331
  # Convert to hash
@@ -304,8 +333,9 @@ module MCPClient
304
333
  def to_h
305
334
  {
306
335
  resource: @resource,
307
- authorization_servers: @authorization_servers
308
- }
336
+ authorization_servers: @authorization_servers,
337
+ scopes_supported: @scopes_supported
338
+ }.compact
309
339
  end
310
340
 
311
341
  # Create resource metadata from hash
@@ -314,7 +344,8 @@ module MCPClient
314
344
  def self.from_h(data)
315
345
  new(
316
346
  resource: data[:resource] || data['resource'],
317
- 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']
318
349
  )
319
350
  end
320
351
  end