Class: MCP::Client::OAuth::Provider

Inherits:
Object
  • Object
show all
Includes:
StorageBackedProvider
Defined in:
lib/mcp/client/oauth/provider.rb

Overview

Pluggable OAuth client configuration for the OAuth 2.1 Authorization Code + PKCE flow, handed to ‘MCP::Client::HTTP` via the `oauth:` keyword. Inspired by the OAuthClientProvider in the TypeScript SDK and the httpx.Auth-based provider in the Python SDK. For the non-interactive machine-to-machine `client_credentials` grant, use `ClientCredentialsProvider` instead.

Required keyword arguments:

  • ‘client_metadata` - Hash sent to the authorization server’s Dynamic Client Registration endpoint. Must include at minimum ‘redirect_uris`, `grant_types`, `response_types`, and `token_endpoint_auth_method`.

  • ‘redirect_uri` - String: the redirect URI used for the authorization request. Must be one of `redirect_uris` in `client_metadata`.

  • ‘redirect_handler` - Callable invoked with the fully-built authorization URL (a `URI`). Implementations typically open the user’s browser.

  • ‘callback_handler` - Callable invoked after `redirect_handler`. Returns `[code, state]` where `code` is the authorization code and `state` is the `state` parameter received on the redirect URI.

Optional keyword arguments:

  • ‘scope` - String of space-separated scopes to request when the server’s ‘WWW-Authenticate` does not specify one.

  • ‘storage` - Object responding to `tokens`, `save_tokens(tokens)`, `client_information`, and `save_client_information(info)`. Defaults to an `InMemoryStorage`.

  • ‘client_id_metadata_document_url` - URL where the client publishes its Client ID Metadata Document (`draft-ietf-oauth-client-id-metadata-document-00` and the MCP authorization specification). When the authorization server advertises `client_id_metadata_document_supported: true`, the SDK uses this URL as the OAuth `client_id` and skips Dynamic Client Registration. Spec-required: `https://` scheme, a non-root path, and no fragment, userinfo, or `.`/`..` segments. The SDK additionally refuses to send query strings (the draft marks them only SHOULD NOT include, but different encodings of the same query would yield different `client_id` strings for the same document). The document served at the URL is a separate JSON artifact from the `client_metadata` keyword: DCR `client_metadata` MUST NOT include `client_id`, while the CIMD document MUST include `client_id` set to the URL, `client_name`, and `redirect_uris` covering `redirect_uri`.

Defined Under Namespace

Classes: InsecureRedirectURIError, InvalidClientIDMetadataDocumentURLError, UnregisteredRedirectURIError

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from StorageBackedProvider

#access_token, #clear_tokens!, #client_information, #save_client_information, #save_tokens, #tokens

Constructor Details

#initialize(client_metadata:, redirect_uri:, redirect_handler:, callback_handler:, scope: nil, storage: nil, client_id_metadata_document_url: nil) ⇒ Provider

Returns a new instance of Provider.



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
# File 'lib/mcp/client/oauth/provider.rb', line 71

def initialize(
  client_metadata:,
  redirect_uri:,
  redirect_handler:,
  callback_handler:,
  scope: nil,
  storage: nil,
  client_id_metadata_document_url: nil
)
  unless Discovery.secure_url?(redirect_uri)
    raise InsecureRedirectURIError,
      "redirect_uri #{redirect_uri.inspect} must use https or be a loopback http URL " \
        "(localhost, 127.0.0.0/8, or ::1) per the MCP authorization Communication Security requirement."
  end

  registered = Array([:redirect_uris] || ["redirect_uris"])
  unless registered.include?(redirect_uri)
    raise UnregisteredRedirectURIError,
      "redirect_uri #{redirect_uri.inspect} must be listed in client_metadata[:redirect_uris] " \
        "(got #{registered.inspect}); otherwise the authorization server will reject the authorization request."
  end

  if  && !Discovery.()
    raise InvalidClientIDMetadataDocumentURLError,
      "client_id_metadata_document_url #{.inspect} must be an https URL " \
        "with a non-root path and no fragment, query, userinfo, or `.`/`..` segments, " \
        "per the MCP authorization specification and `draft-ietf-oauth-client-id-metadata-document`."
  end

  @client_metadata = 
  @redirect_uri = redirect_uri
  @redirect_handler = redirect_handler
  @callback_handler = callback_handler
  @scope = scope
  @storage = storage || InMemoryStorage.new
  @client_id_metadata_document_url = 
end

Instance Attribute Details

#callback_handlerObject (readonly)

Returns the value of attribute callback_handler.



63
64
65
# File 'lib/mcp/client/oauth/provider.rb', line 63

def callback_handler
  @callback_handler
end

#client_id_metadata_document_urlObject (readonly)

Returns the value of attribute client_id_metadata_document_url.



63
64
65
# File 'lib/mcp/client/oauth/provider.rb', line 63

def 
  @client_id_metadata_document_url
end

#client_metadataObject (readonly)

Returns the value of attribute client_metadata.



63
64
65
# File 'lib/mcp/client/oauth/provider.rb', line 63

def 
  @client_metadata
end

#redirect_handlerObject (readonly)

Returns the value of attribute redirect_handler.



63
64
65
# File 'lib/mcp/client/oauth/provider.rb', line 63

def redirect_handler
  @redirect_handler
end

#redirect_uriObject (readonly)

Returns the value of attribute redirect_uri.



63
64
65
# File 'lib/mcp/client/oauth/provider.rb', line 63

def redirect_uri
  @redirect_uri
end

#scopeObject (readonly)

Returns the value of attribute scope.



63
64
65
# File 'lib/mcp/client/oauth/provider.rb', line 63

def scope
  @scope
end

#storageObject (readonly)

Returns the value of attribute storage.



63
64
65
# File 'lib/mcp/client/oauth/provider.rb', line 63

def storage
  @storage
end

Instance Method Details

#authorization_flowObject

Identifies the OAuth flow this provider drives. ‘Flow` dispatches on this rather than inspecting `client_metadata`, which is protocol metadata for the authorization server, not an SDK control signal.



112
113
114
# File 'lib/mcp/client/oauth/provider.rb', line 112

def authorization_flow
  :authorization_code
end