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

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

Overview

Pluggable OAuth client configuration handed to ‘MCP::Client::HTTP` via the `oauth:` keyword. Inspired by the OAuthClientProvider in the TypeScript SDK and httpx.Auth-based provider in the Python SDK.

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

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.



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

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.



59
60
61
# File 'lib/mcp/client/oauth/provider.rb', line 59

def callback_handler
  @callback_handler
end

#client_id_metadata_document_urlObject (readonly)

Returns the value of attribute client_id_metadata_document_url.



59
60
61
# File 'lib/mcp/client/oauth/provider.rb', line 59

def 
  @client_id_metadata_document_url
end

#client_metadataObject (readonly)

Returns the value of attribute client_metadata.



59
60
61
# File 'lib/mcp/client/oauth/provider.rb', line 59

def 
  @client_metadata
end

#redirect_handlerObject (readonly)

Returns the value of attribute redirect_handler.



59
60
61
# File 'lib/mcp/client/oauth/provider.rb', line 59

def redirect_handler
  @redirect_handler
end

#redirect_uriObject (readonly)

Returns the value of attribute redirect_uri.



59
60
61
# File 'lib/mcp/client/oauth/provider.rb', line 59

def redirect_uri
  @redirect_uri
end

#scopeObject (readonly)

Returns the value of attribute scope.



59
60
61
# File 'lib/mcp/client/oauth/provider.rb', line 59

def scope
  @scope
end

#storageObject (readonly)

Returns the value of attribute storage.



59
60
61
# File 'lib/mcp/client/oauth/provider.rb', line 59

def storage
  @storage
end

Instance Method Details

#access_tokenObject



105
106
107
# File 'lib/mcp/client/oauth/provider.rb', line 105

def access_token
  tokens&.dig("access_token") || tokens&.dig(:access_token)
end

#clear_tokens!Object



125
126
127
# File 'lib/mcp/client/oauth/provider.rb', line 125

def clear_tokens!
  @storage.save_tokens(nil)
end

#client_informationObject



117
118
119
# File 'lib/mcp/client/oauth/provider.rb', line 117

def client_information
  @storage.client_information
end

#save_client_information(info) ⇒ Object



121
122
123
# File 'lib/mcp/client/oauth/provider.rb', line 121

def save_client_information(info)
  @storage.save_client_information(info)
end

#save_tokens(tokens) ⇒ Object



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

def save_tokens(tokens)
  @storage.save_tokens(tokens)
end

#tokensObject



109
110
111
# File 'lib/mcp/client/oauth/provider.rb', line 109

def tokens
  @storage.tokens
end