actionmcp 0.72.0 → 0.80.1

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/controllers/action_mcp/application_controller.rb +20 -12
  4. data/app/models/action_mcp/session/message.rb +31 -20
  5. data/app/models/action_mcp/session/resource.rb +35 -20
  6. data/app/models/action_mcp/session/sse_event.rb +23 -17
  7. data/app/models/action_mcp/session/subscription.rb +22 -15
  8. data/app/models/action_mcp/session.rb +42 -119
  9. data/app/models/concerns/{mcp_console_helpers.rb → action_mcp/mcp_console_helpers.rb} +4 -3
  10. data/app/models/concerns/{mcp_message_inspect.rb → action_mcp/mcp_message_inspect.rb} +4 -3
  11. data/config/routes.rb +0 -13
  12. data/db/migrate/20250727000001_remove_oauth_support.rb +59 -0
  13. data/lib/action_mcp/client/streamable_http_transport.rb +1 -46
  14. data/lib/action_mcp/client.rb +2 -25
  15. data/lib/action_mcp/configuration.rb +51 -24
  16. data/lib/action_mcp/engine.rb +0 -7
  17. data/lib/action_mcp/filtered_logger.rb +2 -6
  18. data/lib/action_mcp/gateway_identifier.rb +187 -3
  19. data/lib/action_mcp/gateway_identifiers/api_key_identifier.rb +56 -0
  20. data/lib/action_mcp/gateway_identifiers/devise_identifier.rb +34 -0
  21. data/lib/action_mcp/gateway_identifiers/request_env_identifier.rb +58 -0
  22. data/lib/action_mcp/gateway_identifiers/warden_identifier.rb +38 -0
  23. data/lib/action_mcp/gateway_identifiers.rb +26 -0
  24. data/lib/action_mcp/resource_template.rb +1 -0
  25. data/lib/action_mcp/server/base_session.rb +2 -0
  26. data/lib/action_mcp/server/resources.rb +8 -7
  27. data/lib/action_mcp/version.rb +1 -1
  28. data/lib/action_mcp.rb +1 -6
  29. data/lib/generators/action_mcp/identifier/identifier_generator.rb +189 -0
  30. data/lib/generators/action_mcp/identifier/templates/identifier.rb.erb +35 -0
  31. data/lib/generators/action_mcp/install/install_generator.rb +1 -1
  32. data/lib/generators/action_mcp/install/templates/application_gateway.rb +80 -31
  33. data/lib/generators/action_mcp/install/templates/mcp.yml +4 -21
  34. metadata +15 -99
  35. data/app/controllers/action_mcp/oauth/endpoints_controller.rb +0 -265
  36. data/app/controllers/action_mcp/oauth/metadata_controller.rb +0 -125
  37. data/app/controllers/action_mcp/oauth/registration_controller.rb +0 -201
  38. data/app/models/action_mcp/oauth_client.rb +0 -159
  39. data/app/models/action_mcp/oauth_token.rb +0 -142
  40. data/db/migrate/20250608112101_add_oauth_to_sessions.rb +0 -28
  41. data/db/migrate/20250708105124_create_action_mcp_oauth_clients.rb +0 -44
  42. data/db/migrate/20250708105226_create_action_mcp_oauth_tokens.rb +0 -39
  43. data/lib/action_mcp/client/jwt_client_provider.rb +0 -135
  44. data/lib/action_mcp/client/oauth_client_provider/memory_storage.rb +0 -47
  45. data/lib/action_mcp/client/oauth_client_provider.rb +0 -234
  46. data/lib/action_mcp/jwt_decoder.rb +0 -28
  47. data/lib/action_mcp/jwt_identifier.rb +0 -28
  48. data/lib/action_mcp/none_identifier.rb +0 -19
  49. data/lib/action_mcp/o_auth_identifier.rb +0 -34
  50. data/lib/action_mcp/oauth/active_record_storage.rb +0 -183
  51. data/lib/action_mcp/oauth/error.rb +0 -79
  52. data/lib/action_mcp/oauth/memory_storage.rb +0 -132
  53. data/lib/action_mcp/oauth/middleware.rb +0 -128
  54. data/lib/action_mcp/oauth/provider.rb +0 -406
  55. data/lib/action_mcp/oauth.rb +0 -12
  56. data/lib/action_mcp/omniauth/mcp_strategy.rb +0 -162
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionmcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.72.0
4
+ version: 0.80.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -51,20 +51,6 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: 0.5.3
54
- - !ruby/object:Gem::Dependency
55
- name: jwt
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '2.10'
61
- type: :runtime
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '2.10'
68
54
  - !ruby/object:Gem::Dependency
69
55
  name: multi_json
70
56
  requirement: !ruby/object:Gem::Requirement
@@ -107,34 +93,6 @@ dependencies:
107
93
  - - "~>"
108
94
  - !ruby/object:Gem::Version
109
95
  version: '2.6'
110
- - !ruby/object:Gem::Dependency
111
- name: omniauth
112
- requirement: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - "~>"
115
- - !ruby/object:Gem::Version
116
- version: '2.1'
117
- type: :runtime
118
- prerelease: false
119
- version_requirements: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - "~>"
122
- - !ruby/object:Gem::Version
123
- version: '2.1'
124
- - !ruby/object:Gem::Dependency
125
- name: omniauth-oauth2
126
- requirement: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - "~>"
129
- - !ruby/object:Gem::Version
130
- version: '1.7'
131
- type: :runtime
132
- prerelease: false
133
- version_requirements: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - "~>"
136
- - !ruby/object:Gem::Version
137
- version: '1.7'
138
96
  - !ruby/object:Gem::Dependency
139
97
  name: ostruct
140
98
  requirement: !ruby/object:Gem::Requirement
@@ -149,34 +107,6 @@ dependencies:
149
107
  - - ">="
150
108
  - !ruby/object:Gem::Version
151
109
  version: '0'
152
- - !ruby/object:Gem::Dependency
153
- name: faraday
154
- requirement: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - "~>"
157
- - !ruby/object:Gem::Version
158
- version: '2.7'
159
- type: :runtime
160
- prerelease: false
161
- version_requirements: !ruby/object:Gem::Requirement
162
- requirements:
163
- - - "~>"
164
- - !ruby/object:Gem::Version
165
- version: '2.7'
166
- - !ruby/object:Gem::Dependency
167
- name: pkce_challenge
168
- requirement: !ruby/object:Gem::Requirement
169
- requirements:
170
- - - "~>"
171
- - !ruby/object:Gem::Version
172
- version: '1.0'
173
- type: :runtime
174
- prerelease: false
175
- version_requirements: !ruby/object:Gem::Requirement
176
- requirements:
177
- - - "~>"
178
- - !ruby/object:Gem::Version
179
- version: '1.0'
180
110
  - !ruby/object:Gem::Dependency
181
111
  name: json_schemer
182
112
  requirement: !ruby/object:Gem::Requirement
@@ -191,8 +121,9 @@ dependencies:
191
121
  - - "~>"
192
122
  - !ruby/object:Gem::Version
193
123
  version: '2.0'
194
- description: It offers base classes and helpers for creating MCP applications, making
195
- it easier to integrate your Ruby/Rails application with the MCP standard
124
+ description: A streamlined, production-focused toolkit for building MCP servers in
125
+ Rails applications. Provides essential base classes, authentication gateways, and
126
+ HTTP transport with minimal dependencies.
196
127
  email:
197
128
  - terminale@gmail.com
198
129
  executables:
@@ -204,26 +135,19 @@ files:
204
135
  - README.md
205
136
  - Rakefile
206
137
  - app/controllers/action_mcp/application_controller.rb
207
- - app/controllers/action_mcp/oauth/endpoints_controller.rb
208
- - app/controllers/action_mcp/oauth/metadata_controller.rb
209
- - app/controllers/action_mcp/oauth/registration_controller.rb
210
138
  - app/models/action_mcp.rb
211
139
  - app/models/action_mcp/application_record.rb
212
- - app/models/action_mcp/oauth_client.rb
213
- - app/models/action_mcp/oauth_token.rb
214
140
  - app/models/action_mcp/session.rb
215
141
  - app/models/action_mcp/session/message.rb
216
142
  - app/models/action_mcp/session/resource.rb
217
143
  - app/models/action_mcp/session/sse_event.rb
218
144
  - app/models/action_mcp/session/subscription.rb
219
- - app/models/concerns/mcp_console_helpers.rb
220
- - app/models/concerns/mcp_message_inspect.rb
145
+ - app/models/concerns/action_mcp/mcp_console_helpers.rb
146
+ - app/models/concerns/action_mcp/mcp_message_inspect.rb
221
147
  - config/routes.rb
222
148
  - db/migrate/20250512154359_consolidated_migration.rb
223
- - db/migrate/20250608112101_add_oauth_to_sessions.rb
224
- - db/migrate/20250708105124_create_action_mcp_oauth_clients.rb
225
- - db/migrate/20250708105226_create_action_mcp_oauth_tokens.rb
226
149
  - db/migrate/20250715070713_add_consents_to_action_mcp_sess.rb
150
+ - db/migrate/20250727000001_remove_oauth_support.rb
227
151
  - exe/actionmcp_cli
228
152
  - lib/action_mcp.rb
229
153
  - lib/action_mcp/base_response.rb
@@ -237,11 +161,8 @@ files:
237
161
  - lib/action_mcp/client/collection.rb
238
162
  - lib/action_mcp/client/elicitation.rb
239
163
  - lib/action_mcp/client/json_rpc_handler.rb
240
- - lib/action_mcp/client/jwt_client_provider.rb
241
164
  - lib/action_mcp/client/logging.rb
242
165
  - lib/action_mcp/client/messaging.rb
243
- - lib/action_mcp/client/oauth_client_provider.rb
244
- - lib/action_mcp/client/oauth_client_provider/memory_storage.rb
245
166
  - lib/action_mcp/client/prompt_book.rb
246
167
  - lib/action_mcp/client/prompts.rb
247
168
  - lib/action_mcp/client/request_timeouts.rb
@@ -272,25 +193,19 @@ files:
272
193
  - lib/action_mcp/filtered_logger.rb
273
194
  - lib/action_mcp/gateway.rb
274
195
  - lib/action_mcp/gateway_identifier.rb
196
+ - lib/action_mcp/gateway_identifiers.rb
197
+ - lib/action_mcp/gateway_identifiers/api_key_identifier.rb
198
+ - lib/action_mcp/gateway_identifiers/devise_identifier.rb
199
+ - lib/action_mcp/gateway_identifiers/request_env_identifier.rb
200
+ - lib/action_mcp/gateway_identifiers/warden_identifier.rb
275
201
  - lib/action_mcp/gem_version.rb
276
202
  - lib/action_mcp/instrumentation/controller_runtime.rb
277
203
  - lib/action_mcp/instrumentation/instrumentation.rb
278
204
  - lib/action_mcp/instrumentation/resource_instrumentation.rb
279
205
  - lib/action_mcp/integer_array.rb
280
206
  - lib/action_mcp/json_rpc_handler_base.rb
281
- - lib/action_mcp/jwt_decoder.rb
282
- - lib/action_mcp/jwt_identifier.rb
283
207
  - lib/action_mcp/log_subscriber.rb
284
208
  - lib/action_mcp/logging.rb
285
- - lib/action_mcp/none_identifier.rb
286
- - lib/action_mcp/o_auth_identifier.rb
287
- - lib/action_mcp/oauth.rb
288
- - lib/action_mcp/oauth/active_record_storage.rb
289
- - lib/action_mcp/oauth/error.rb
290
- - lib/action_mcp/oauth/memory_storage.rb
291
- - lib/action_mcp/oauth/middleware.rb
292
- - lib/action_mcp/oauth/provider.rb
293
- - lib/action_mcp/omniauth/mcp_strategy.rb
294
209
  - lib/action_mcp/prompt.rb
295
210
  - lib/action_mcp/prompt_response.rb
296
211
  - lib/action_mcp/prompts_registry.rb
@@ -346,6 +261,8 @@ files:
346
261
  - lib/action_mcp/uri_ambiguity_checker.rb
347
262
  - lib/action_mcp/version.rb
348
263
  - lib/actionmcp.rb
264
+ - lib/generators/action_mcp/identifier/identifier_generator.rb
265
+ - lib/generators/action_mcp/identifier/templates/identifier.rb.erb
349
266
  - lib/generators/action_mcp/install/install_generator.rb
350
267
  - lib/generators/action_mcp/install/templates/application_gateway.rb
351
268
  - lib/generators/action_mcp/install/templates/application_mcp_prompt.rb
@@ -383,6 +300,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
383
300
  requirements: []
384
301
  rubygems_version: 3.6.9
385
302
  specification_version: 4
386
- summary: Provides essential tooling for building Model Context Protocol (MCP) capable
387
- servers
303
+ summary: Lightweight Model Context Protocol (MCP) server toolkit for Ruby/Rails
388
304
  test_files: []
@@ -1,265 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "ostruct"
4
-
5
- module ActionMCP
6
- module OAuth
7
- # OAuth 2.1 endpoints controller
8
- # Handles authorization, token, introspection, and revocation endpoints
9
- class EndpointsController < ActionController::Base
10
- protect_from_forgery with: :null_session
11
- before_action :check_oauth_enabled
12
-
13
- # GET /oauth/authorize
14
- # Authorization endpoint for OAuth 2.1 authorization code flow
15
- def authorize
16
- # Extract parameters
17
- client_id = params[:client_id]
18
- redirect_uri = params[:redirect_uri]
19
- response_type = params[:response_type]
20
- scope = params[:scope]
21
- state = params[:state]
22
- code_challenge = params[:code_challenge]
23
- code_challenge_method = params[:code_challenge_method]
24
-
25
- # Validate required parameters
26
- if client_id.blank? || redirect_uri.blank? || response_type.blank?
27
- return render_error("invalid_request", "Missing required parameters")
28
- end
29
-
30
- # Validate response type
31
- unless response_type == "code"
32
- return render_error("unsupported_response_type", "Only authorization code flow supported")
33
- end
34
-
35
- # Validate PKCE if required
36
- if oauth_config["pkce_required"] && code_challenge.blank?
37
- return render_error("invalid_request", "PKCE required")
38
- end
39
-
40
- # In a real implementation, this would show a consent page
41
- # For now, we'll auto-approve for configured clients
42
- if auto_approve_client?(client_id)
43
- # Generate authorization code
44
- user_id = current_user&.id || "anonymous"
45
-
46
- begin
47
- code = ActionMCP::OAuth::Provider.generate_authorization_code(
48
- client_id: client_id,
49
- redirect_uri: redirect_uri,
50
- scope: scope || default_scope,
51
- code_challenge: code_challenge,
52
- code_challenge_method: code_challenge_method,
53
- user_id: user_id
54
- )
55
-
56
- # Redirect back to client with authorization code
57
- redirect_params = { code: code }
58
- redirect_params[:state] = state if state
59
- redirect_to "#{redirect_uri}?#{redirect_params.to_query}", allow_other_host: true
60
- rescue ActionMCP::OAuth::Error => e
61
- render_error(e.oauth_error_code, e.message)
62
- end
63
- else
64
- # In production, show consent page
65
- render_consent_page(client_id, redirect_uri, scope, state, code_challenge, code_challenge_method)
66
- end
67
- end
68
-
69
- # POST /oauth/token
70
- # Token endpoint for exchanging authorization codes and refreshing tokens
71
- def token
72
- grant_type = params[:grant_type]
73
-
74
- case grant_type
75
- when "authorization_code"
76
- handle_authorization_code_grant
77
- when "refresh_token"
78
- handle_refresh_token_grant
79
- when "client_credentials"
80
- handle_client_credentials_grant
81
- else
82
- render_token_error("unsupported_grant_type", "Unsupported grant type")
83
- end
84
- rescue ActionMCP::OAuth::Error => e
85
- render_token_error(e.oauth_error_code, e.message)
86
- end
87
-
88
- # POST /oauth/introspect
89
- # Token introspection endpoint (RFC 7662)
90
- def introspect
91
- token = params[:token]
92
- return render_introspection_error unless token
93
-
94
- # Authenticate client for introspection
95
- client_id, = extract_client_credentials
96
- return render_introspection_error unless client_id
97
-
98
- begin
99
- token_info = ActionMCP::OAuth::Provider.introspect_token(token)
100
- render json: token_info
101
- rescue ActionMCP::OAuth::Error
102
- render json: { active: false }
103
- end
104
- end
105
-
106
- # POST /oauth/revoke
107
- # Token revocation endpoint (RFC 7009)
108
- def revoke
109
- token = params[:token]
110
- token_type_hint = params[:token_type_hint]
111
-
112
- return head :bad_request unless token
113
-
114
- # Authenticate client
115
- client_id, = extract_client_credentials
116
- return head :unauthorized unless client_id
117
-
118
- begin
119
- ActionMCP::OAuth::Provider.revoke_token(token, token_type_hint: token_type_hint)
120
- head :ok
121
- rescue ActionMCP::OAuth::Error
122
- head :bad_request
123
- end
124
- end
125
-
126
- private
127
-
128
- def check_oauth_enabled
129
- auth_methods = ActionMCP.configuration.authentication_methods
130
- return if auth_methods&.include?("oauth")
131
-
132
- head :not_found
133
- end
134
-
135
- def oauth_config
136
- @oauth_config ||= ActionMCP.configuration.oauth_config || {}
137
- end
138
-
139
- def handle_authorization_code_grant
140
- code = params[:code]
141
- client_id = params[:client_id]
142
- client_secret = params[:client_secret]
143
- redirect_uri = params[:redirect_uri]
144
- code_verifier = params[:code_verifier]
145
-
146
- # Extract client credentials from Authorization header if not in params
147
- client_id, client_secret = extract_client_credentials if client_id.blank?
148
-
149
- return render_token_error("invalid_request", "Missing required parameters") if code.blank? || client_id.blank?
150
-
151
- token_response = ActionMCP::OAuth::Provider.exchange_code_for_token(
152
- code: code,
153
- client_id: client_id,
154
- client_secret: client_secret,
155
- redirect_uri: redirect_uri,
156
- code_verifier: code_verifier
157
- )
158
-
159
- render json: token_response
160
- end
161
-
162
- def handle_refresh_token_grant
163
- refresh_token = params[:refresh_token]
164
- scope = params[:scope]
165
-
166
- # Extract client credentials
167
- client_id, client_secret = extract_client_credentials
168
- client_id ||= params[:client_id]
169
- client_secret ||= params[:client_secret]
170
-
171
- if refresh_token.blank? || client_id.blank?
172
- return render_token_error("invalid_request",
173
- "Missing required parameters")
174
- end
175
-
176
- token_response = ActionMCP::OAuth::Provider.refresh_access_token(
177
- refresh_token: refresh_token,
178
- client_id: client_id,
179
- client_secret: client_secret,
180
- scope: scope
181
- )
182
-
183
- render json: token_response
184
- end
185
-
186
- def handle_client_credentials_grant
187
- scope = params[:scope]
188
-
189
- # Extract client credentials
190
- client_id, client_secret = extract_client_credentials
191
- client_id ||= params[:client_id]
192
- client_secret ||= params[:client_secret]
193
-
194
- return render_token_error("invalid_request", "Missing client credentials") if client_id.blank?
195
-
196
- token_response = ActionMCP::OAuth::Provider.client_credentials_grant(
197
- client_id: client_id,
198
- client_secret: client_secret,
199
- scope: scope
200
- )
201
-
202
- render json: token_response
203
- end
204
-
205
- def extract_client_credentials
206
- auth_header = request.headers["Authorization"]
207
- if auth_header&.start_with?("Basic ")
208
- encoded = auth_header.split(" ", 2).last
209
- decoded = Base64.decode64(encoded)
210
- decoded.split(":", 2)
211
- else
212
- [ nil, nil ]
213
- end
214
- end
215
-
216
- def auto_approve_client?(client_id)
217
- # In development/testing, auto-approve known clients
218
- # In production, this should check a proper client registry
219
- Rails.env.development? || Rails.env.test? || oauth_config["auto_approve_clients"]&.include?(client_id)
220
- end
221
-
222
- def current_user
223
- # This should be implemented by the application
224
- # For now, return a default user for development
225
- if Rails.env.development? || Rails.env.test?
226
- OpenStruct.new(id: "dev_user", email: "dev@example.com")
227
- else
228
- # In production, this should integrate with your authentication system
229
- nil
230
- end
231
- end
232
-
233
- def default_scope
234
- oauth_config["default_scope"] || "mcp:tools mcp:resources mcp:prompts"
235
- end
236
-
237
- def render_error(error_code, description)
238
- render json: {
239
- error: error_code,
240
- error_description: description
241
- }, status: :bad_request
242
- end
243
-
244
- def render_token_error(error_code, description)
245
- render json: {
246
- error: error_code,
247
- error_description: description
248
- }, status: :bad_request
249
- end
250
-
251
- def render_introspection_error
252
- render json: { active: false }, status: :bad_request
253
- end
254
-
255
- def render_consent_page(_client_id, _redirect_uri, _scope, _state, _code_challenge, _code_challenge_method)
256
- # In production, this would render a proper consent page
257
- # For now, just auto-deny unknown clients
258
- render json: {
259
- error: "access_denied",
260
- error_description: "User denied authorization"
261
- }, status: :forbidden
262
- end
263
- end
264
- end
265
- end
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ActionMCP
4
- module OAuth
5
- # Controller for OAuth 2.1 metadata endpoints
6
- # Provides server discovery information as per RFC 8414
7
- class MetadataController < ActionController::Base
8
- before_action :check_oauth_enabled
9
-
10
- # GET /.well-known/oauth-authorization-server
11
- # Returns OAuth Authorization Server Metadata as per RFC 8414
12
- def authorization_server
13
- metadata = {
14
- issuer: issuer_url,
15
- authorization_endpoint: authorization_endpoint,
16
- token_endpoint: token_endpoint,
17
- introspection_endpoint: introspection_endpoint,
18
- revocation_endpoint: revocation_endpoint,
19
- response_types_supported: response_types_supported,
20
- grant_types_supported: grant_types_supported,
21
- token_endpoint_auth_methods_supported: token_endpoint_auth_methods_supported,
22
- scopes_supported: scopes_supported,
23
- code_challenge_methods_supported: code_challenge_methods_supported,
24
- service_documentation: service_documentation
25
- }
26
-
27
- # Add optional fields based on configuration
28
- metadata[:registration_endpoint] = registration_endpoint if oauth_config[:enable_dynamic_registration]
29
-
30
- metadata[:jwks_uri] = oauth_config[:jwks_uri] if oauth_config[:jwks_uri]
31
-
32
- render json: metadata
33
- end
34
-
35
- # GET /.well-known/oauth-protected-resource
36
- # Returns Protected Resource Metadata as per RFC 8705
37
- def protected_resource
38
- metadata = {
39
- resource: issuer_url,
40
- authorization_servers: [ issuer_url ],
41
- scopes_supported: scopes_supported,
42
- bearer_methods_supported: [ "header" ],
43
- resource_documentation: resource_documentation
44
- }
45
-
46
- render json: metadata
47
- end
48
-
49
- private
50
-
51
- def check_oauth_enabled
52
- auth_methods = ActionMCP.configuration.authentication_methods
53
- return if auth_methods&.include?("oauth")
54
-
55
- head :not_found
56
- end
57
-
58
- def oauth_config
59
- @oauth_config ||= HashWithIndifferentAccess.new(ActionMCP.configuration.oauth_config || {})
60
- end
61
-
62
- def issuer_url
63
- @issuer_url ||= oauth_config.fetch(:issuer_url, request.base_url)
64
- end
65
-
66
- def authorization_endpoint
67
- "#{issuer_url}/oauth/authorize"
68
- end
69
-
70
- def token_endpoint
71
- "#{issuer_url}/oauth/token"
72
- end
73
-
74
- def introspection_endpoint
75
- "#{issuer_url}/oauth/introspect"
76
- end
77
-
78
- def revocation_endpoint
79
- "#{issuer_url}/oauth/revoke"
80
- end
81
-
82
- def registration_endpoint
83
- "#{issuer_url}/oauth/register"
84
- end
85
-
86
- def response_types_supported
87
- [ "code" ]
88
- end
89
-
90
- def grant_types_supported
91
- grants = [ "authorization_code" ]
92
- grants << "refresh_token" if oauth_config[:enable_refresh_tokens]
93
- grants << "client_credentials" if oauth_config[:enable_client_credentials]
94
- grants
95
- end
96
-
97
- def token_endpoint_auth_methods_supported
98
- methods = %w[client_secret_basic client_secret_post]
99
- methods << "none" if oauth_config[:allow_public_clients]
100
- methods
101
- end
102
-
103
- def scopes_supported
104
- oauth_config.fetch(:scopes_supported, [ "mcp:tools", "mcp:resources", "mcp:prompts" ])
105
- end
106
-
107
- def code_challenge_methods_supported
108
- methods = []
109
- if oauth_config[:pkce_required] || oauth_config[:pkce_supported]
110
- methods << "S256"
111
- methods << "plain" if oauth_config[:allow_plain_pkce]
112
- end
113
- methods
114
- end
115
-
116
- def service_documentation
117
- oauth_config.fetch(:service_documentation, "#{request.base_url}/docs")
118
- end
119
-
120
- def resource_documentation
121
- oauth_config.fetch(:resource_documentation, "#{request.base_url}/docs/api")
122
- end
123
- end
124
- end
125
- end