rolemodel-rails 1.1.0 → 2.1.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +117 -7
  3. data/lib/generators/rolemodel/all_generator.rb +0 -1
  4. data/lib/generators/rolemodel/optics/icons/README.md +6 -1
  5. data/lib/generators/rolemodel/optics/icons/icons_generator.rb +14 -9
  6. data/lib/generators/rolemodel/optics/icons/templates/app/helpers/icon_helper.rb.tt +4 -2
  7. data/lib/generators/rolemodel/testing/README.md +4 -1
  8. data/lib/generators/rolemodel/testing/all_generator.rb +3 -3
  9. data/lib/generators/rolemodel/testing/jasmine_playwright/jasmine_playwright_generator.rb +1 -1
  10. data/lib/generators/rolemodel/testing/rspec/README.md +1 -1
  11. data/lib/generators/rolemodel/testing/rspec/USAGE +1 -1
  12. data/lib/generators/rolemodel/testing/rspec/rspec_generator.rb +15 -14
  13. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/capybara_config.rb.tt +9 -0
  14. data/lib/{generators/rolemodel/optics/icons/templates/app/icon_builders → rolemodel/optics}/custom_icon_builder.rb +1 -1
  15. data/lib/{generators/rolemodel/optics/icons/templates/app/icon_builders → rolemodel/optics}/feather_icon_builder.rb +1 -1
  16. data/lib/{generators/rolemodel/optics/icons/templates/app/icon_builders → rolemodel/optics}/icon_builder.rb +10 -19
  17. data/lib/{generators/rolemodel/optics/icons/templates/app/icon_builders → rolemodel/optics}/lucide_icon_builder.rb +1 -1
  18. data/lib/{generators/rolemodel/optics/icons/templates/app/icon_builders → rolemodel/optics}/material_icon_builder.rb +1 -1
  19. data/lib/{generators/rolemodel/optics/icons/templates/app/icon_builders → rolemodel/optics}/phosphor_icon_builder.rb +1 -1
  20. data/lib/{generators/rolemodel/optics/icons/templates/app/icon_builders → rolemodel/optics}/tabler_icon_builder.rb +1 -1
  21. data/lib/rolemodel/optics.rb +9 -0
  22. data/lib/rolemodel/utility/task_tools.rb +69 -0
  23. data/lib/rolemodel/utility.rb +3 -0
  24. data/lib/rolemodel/version.rb +1 -1
  25. data/lib/rolemodel-rails.rb +2 -0
  26. metadata +35 -42
  27. data/lib/generators/rolemodel/mcp/README.md +0 -13
  28. data/lib/generators/rolemodel/mcp/USAGE +0 -8
  29. data/lib/generators/rolemodel/mcp/mcp_generator.rb +0 -110
  30. data/lib/generators/rolemodel/mcp/templates/app/assets/stylesheets/components/doorkeeper.css +0 -140
  31. data/lib/generators/rolemodel/mcp/templates/app/controllers/doorkeeper/base_controller.rb +0 -7
  32. data/lib/generators/rolemodel/mcp/templates/app/controllers/mcp_controller.rb.tt +0 -91
  33. data/lib/generators/rolemodel/mcp/templates/app/controllers/oauth_registrations_controller.rb +0 -46
  34. data/lib/generators/rolemodel/mcp/templates/app/controllers/well_known_controller.rb +0 -39
  35. data/lib/generators/rolemodel/mcp/templates/app/mcp/prompts/sample.rb +0 -36
  36. data/lib/generators/rolemodel/mcp/templates/app/mcp/resources/controller.rb +0 -57
  37. data/lib/generators/rolemodel/mcp/templates/app/mcp/resources/docs/SAMPLE_DOC.md +0 -4
  38. data/lib/generators/rolemodel/mcp/templates/app/mcp/resources/docs_controller.rb +0 -46
  39. data/lib/generators/rolemodel/mcp/templates/app/mcp/tools/sample.rb +0 -42
  40. data/lib/generators/rolemodel/mcp/templates/app/views/doorkeeper/authorizations/error.html.slim.tt +0 -13
  41. data/lib/generators/rolemodel/mcp/templates/app/views/doorkeeper/authorizations/new.html.slim.tt +0 -41
  42. data/lib/generators/rolemodel/mcp/templates/app/views/layouts/doorkeeper.html.slim +0 -7
  43. data/lib/generators/rolemodel/mcp/templates/config/initializers/doorkeeper.rb +0 -537
  44. data/lib/generators/rolemodel/mcp/templates/spec/mcp/prompts/sample_spec.rb +0 -15
  45. data/lib/generators/rolemodel/mcp/templates/spec/mcp/resources/controller_spec.rb +0 -16
  46. data/lib/generators/rolemodel/mcp/templates/spec/mcp/resources/docs_controller_spec.rb +0 -55
  47. data/lib/generators/rolemodel/mcp/templates/spec/mcp/tools/sample_spec.rb +0 -15
  48. data/lib/generators/rolemodel/mcp/templates/spec/requests/mcp_controller_spec.rb +0 -84
  49. data/lib/generators/rolemodel/mcp/templates/spec/requests/oauth_registrations_controller_spec.rb +0 -62
  50. data/lib/generators/rolemodel/mcp/templates/spec/requests/well_known_controller_spec.rb +0 -30
  51. data/lib/generators/rolemodel/testing/rspec/templates/support/capybara_testid.rb +0 -5
  52. /data/lib/generators/rolemodel/testing/rspec/templates/{rails_helper.rb → spec/rails_helper.rb.tt} +0 -0
  53. /data/lib/generators/rolemodel/testing/rspec/templates/{spec_helper.rb → spec/spec_helper.rb.tt} +0 -0
  54. /data/lib/generators/rolemodel/testing/rspec/templates/{support/capybara_drivers.rb → spec/support/capybara_drivers.rb.tt} +0 -0
  55. /data/lib/generators/rolemodel/testing/rspec/templates/{support → spec/support}/helpers/action_cable_helper.rb +0 -0
  56. /data/lib/generators/rolemodel/testing/rspec/templates/{support → spec/support}/helpers/capybara_helper.rb +0 -0
  57. /data/lib/generators/rolemodel/testing/rspec/templates/{support → spec/support}/helpers/playwright_helper.rb +0 -0
  58. /data/lib/generators/rolemodel/testing/rspec/templates/{support → spec/support}/helpers/select_helper.rb +0 -0
  59. /data/lib/generators/rolemodel/testing/rspec/templates/{support → spec/support}/helpers/test_element_helper.rb +0 -0
  60. /data/lib/generators/rolemodel/testing/rspec/templates/{support/helpers.rb → spec/support/helpers.rb.tt} +0 -0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rolemodel-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - RoleModel Software Inc
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '7.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: benchmark
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: bundler
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -208,30 +222,6 @@ files:
208
222
  - lib/generators/rolemodel/mailers/templates/config/initializers/premailer_rails.rb
209
223
  - lib/generators/rolemodel/mailers/templates/public/logo.png
210
224
  - lib/generators/rolemodel/mailers/templates/spec/mailers/previews/example_mailer_preview.rb
211
- - lib/generators/rolemodel/mcp/README.md
212
- - lib/generators/rolemodel/mcp/USAGE
213
- - lib/generators/rolemodel/mcp/mcp_generator.rb
214
- - lib/generators/rolemodel/mcp/templates/app/assets/stylesheets/components/doorkeeper.css
215
- - lib/generators/rolemodel/mcp/templates/app/controllers/doorkeeper/base_controller.rb
216
- - lib/generators/rolemodel/mcp/templates/app/controllers/mcp_controller.rb.tt
217
- - lib/generators/rolemodel/mcp/templates/app/controllers/oauth_registrations_controller.rb
218
- - lib/generators/rolemodel/mcp/templates/app/controllers/well_known_controller.rb
219
- - lib/generators/rolemodel/mcp/templates/app/mcp/prompts/sample.rb
220
- - lib/generators/rolemodel/mcp/templates/app/mcp/resources/controller.rb
221
- - lib/generators/rolemodel/mcp/templates/app/mcp/resources/docs/SAMPLE_DOC.md
222
- - lib/generators/rolemodel/mcp/templates/app/mcp/resources/docs_controller.rb
223
- - lib/generators/rolemodel/mcp/templates/app/mcp/tools/sample.rb
224
- - lib/generators/rolemodel/mcp/templates/app/views/doorkeeper/authorizations/error.html.slim.tt
225
- - lib/generators/rolemodel/mcp/templates/app/views/doorkeeper/authorizations/new.html.slim.tt
226
- - lib/generators/rolemodel/mcp/templates/app/views/layouts/doorkeeper.html.slim
227
- - lib/generators/rolemodel/mcp/templates/config/initializers/doorkeeper.rb
228
- - lib/generators/rolemodel/mcp/templates/spec/mcp/prompts/sample_spec.rb
229
- - lib/generators/rolemodel/mcp/templates/spec/mcp/resources/controller_spec.rb
230
- - lib/generators/rolemodel/mcp/templates/spec/mcp/resources/docs_controller_spec.rb
231
- - lib/generators/rolemodel/mcp/templates/spec/mcp/tools/sample_spec.rb
232
- - lib/generators/rolemodel/mcp/templates/spec/requests/mcp_controller_spec.rb
233
- - lib/generators/rolemodel/mcp/templates/spec/requests/oauth_registrations_controller_spec.rb
234
- - lib/generators/rolemodel/mcp/templates/spec/requests/well_known_controller_spec.rb
235
225
  - lib/generators/rolemodel/optics/README.md
236
226
  - lib/generators/rolemodel/optics/USAGE
237
227
  - lib/generators/rolemodel/optics/all_generator.rb
@@ -242,13 +232,6 @@ files:
242
232
  - lib/generators/rolemodel/optics/icons/USAGE
243
233
  - lib/generators/rolemodel/optics/icons/icons_generator.rb
244
234
  - lib/generators/rolemodel/optics/icons/templates/app/helpers/icon_helper.rb.tt
245
- - lib/generators/rolemodel/optics/icons/templates/app/icon_builders/custom_icon_builder.rb
246
- - lib/generators/rolemodel/optics/icons/templates/app/icon_builders/feather_icon_builder.rb
247
- - lib/generators/rolemodel/optics/icons/templates/app/icon_builders/icon_builder.rb
248
- - lib/generators/rolemodel/optics/icons/templates/app/icon_builders/lucide_icon_builder.rb
249
- - lib/generators/rolemodel/optics/icons/templates/app/icon_builders/material_icon_builder.rb
250
- - lib/generators/rolemodel/optics/icons/templates/app/icon_builders/phosphor_icon_builder.rb
251
- - lib/generators/rolemodel/optics/icons/templates/app/icon_builders/tabler_icon_builder.rb
252
235
  - lib/generators/rolemodel/react/README.md
253
236
  - lib/generators/rolemodel/react/USAGE
254
237
  - lib/generators/rolemodel/react/react_generator.rb
@@ -360,16 +343,16 @@ files:
360
343
  - lib/generators/rolemodel/testing/rspec/README.md
361
344
  - lib/generators/rolemodel/testing/rspec/USAGE
362
345
  - lib/generators/rolemodel/testing/rspec/rspec_generator.rb
363
- - lib/generators/rolemodel/testing/rspec/templates/rails_helper.rb
364
- - lib/generators/rolemodel/testing/rspec/templates/spec_helper.rb
365
- - lib/generators/rolemodel/testing/rspec/templates/support/capybara_drivers.rb
366
- - lib/generators/rolemodel/testing/rspec/templates/support/capybara_testid.rb
367
- - lib/generators/rolemodel/testing/rspec/templates/support/helpers.rb
368
- - lib/generators/rolemodel/testing/rspec/templates/support/helpers/action_cable_helper.rb
369
- - lib/generators/rolemodel/testing/rspec/templates/support/helpers/capybara_helper.rb
370
- - lib/generators/rolemodel/testing/rspec/templates/support/helpers/playwright_helper.rb
371
- - lib/generators/rolemodel/testing/rspec/templates/support/helpers/select_helper.rb
372
- - lib/generators/rolemodel/testing/rspec/templates/support/helpers/test_element_helper.rb
346
+ - lib/generators/rolemodel/testing/rspec/templates/spec/rails_helper.rb.tt
347
+ - lib/generators/rolemodel/testing/rspec/templates/spec/spec_helper.rb.tt
348
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/capybara_config.rb.tt
349
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/capybara_drivers.rb.tt
350
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers.rb.tt
351
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/action_cable_helper.rb
352
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/capybara_helper.rb
353
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/playwright_helper.rb
354
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/select_helper.rb
355
+ - lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/test_element_helper.rb
373
356
  - lib/generators/rolemodel/testing/test_prof/README.md
374
357
  - lib/generators/rolemodel/testing/test_prof/USAGE
375
358
  - lib/generators/rolemodel/testing/test_prof/test_prof_generator.rb
@@ -415,7 +398,17 @@ files:
415
398
  - lib/rolemodel-rails.rb
416
399
  - lib/rolemodel/engine.rb
417
400
  - lib/rolemodel/generator_base.rb
401
+ - lib/rolemodel/optics.rb
402
+ - lib/rolemodel/optics/custom_icon_builder.rb
403
+ - lib/rolemodel/optics/feather_icon_builder.rb
404
+ - lib/rolemodel/optics/icon_builder.rb
405
+ - lib/rolemodel/optics/lucide_icon_builder.rb
406
+ - lib/rolemodel/optics/material_icon_builder.rb
407
+ - lib/rolemodel/optics/phosphor_icon_builder.rb
408
+ - lib/rolemodel/optics/tabler_icon_builder.rb
418
409
  - lib/rolemodel/replace_content_helper.rb
410
+ - lib/rolemodel/utility.rb
411
+ - lib/rolemodel/utility/task_tools.rb
419
412
  - lib/rolemodel/version.rb
420
413
  homepage: https://github.com/RoleModel/rolemodel_rails
421
414
  licenses:
@@ -1,13 +0,0 @@
1
- # MCP Generator
2
-
3
- Install boilerplate for your very own MCP server.
4
-
5
- ## What you get
6
-
7
- ### Doorkeeper
8
-
9
- OAuth 2.1-enabled flow with dynamic application registration.
10
-
11
- ### MCP
12
-
13
- A basic MCP controller that you can build on to serve tools, resources, and prompts.
@@ -1,8 +0,0 @@
1
- Description:
2
- Sets up RoleModel MCP support and any required application wiring for the MCP endpoint.
3
-
4
- Example:
5
- rails generate rolemodel:mcp
6
-
7
- This generator adds the files and route updates needed to enable RoleModel MCP
8
- in your Rails application.
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rolemodel
4
- class McpGenerator < GeneratorBase
5
- source_root File.expand_path('templates', __dir__)
6
-
7
- def update_inflections
8
- inflections_path = File.join(destination_root, 'config/initializers/inflections.rb')
9
- block_start = "\nActiveSupport::Inflector.inflections(:en) do |inflect|\n"
10
-
11
- return if File.read(inflections_path).include?("inflect.acronym 'MCP'")
12
-
13
- if File.read(inflections_path).include?(block_start)
14
- inject_into_file inflections_path, " inflect.acronym 'MCP'\n", after: block_start
15
- else
16
- append_to_file inflections_path, <<~RUBY
17
-
18
- ActiveSupport::Inflector.inflections(:en) do |inflect|
19
- inflect.acronym 'MCP'
20
- end
21
- RUBY
22
- end
23
- end
24
-
25
- def install_mcp
26
- bundle_command 'add mcp'
27
- template 'app/controllers/mcp_controller.rb'
28
- copy_file 'spec/requests/mcp_controller_spec.rb'
29
-
30
- route <<~RUBY
31
- match '/mcp', to: 'mcp#handle', via: %i[get post delete]
32
- RUBY
33
- end
34
-
35
- def add_sample_mcp_resource
36
- copy_file 'app/mcp/resources/controller.rb'
37
- copy_file 'spec/mcp/resources/controller_spec.rb'
38
-
39
- copy_file 'app/mcp/resources/docs/SAMPLE_DOC.md'
40
- copy_file 'app/mcp/resources/docs_controller.rb'
41
- copy_file 'spec/mcp/resources/docs_controller_spec.rb'
42
- end
43
-
44
- def add_sample_mcp_prompt
45
- copy_file 'app/mcp/prompts/sample.rb'
46
- copy_file 'spec/mcp/prompts/sample_spec.rb'
47
- end
48
-
49
- def add_sample_mcp_tool
50
- copy_file 'app/mcp/tools/sample.rb'
51
- copy_file 'spec/mcp/tools/sample_spec.rb'
52
- end
53
-
54
- def install_doorkeeper
55
- bundle_command 'add doorkeeper'
56
- run_bundle
57
- generate 'doorkeeper:install'
58
- end
59
-
60
- def configure_doorkeeper
61
- copy_file 'config/initializers/doorkeeper.rb', force: true
62
- copy_file 'app/controllers/doorkeeper/base_controller.rb'
63
-
64
- copy_file 'app/views/layouts/doorkeeper.html.slim'
65
- template 'app/views/doorkeeper/authorizations/new.html.slim'
66
- template 'app/views/doorkeeper/authorizations/error.html.slim'
67
-
68
- copy_file 'app/assets/stylesheets/components/doorkeeper.css'
69
-
70
- route 'use_doorkeeper'
71
- end
72
-
73
- def apply_doorkeeper_css
74
- css_manifest = if File.exist?(File.join(destination_root, 'app/assets/stylesheets/application.scss'))
75
- 'app/assets/stylesheets/application.scss'
76
- else
77
- 'app/assets/stylesheets/application.css'
78
- end
79
-
80
- return if File.read(File.join(destination_root, css_manifest)).include?("@import 'components/doorkeeper.css';")
81
-
82
- append_to_file css_manifest, <<~CSS
83
- @import 'components/doorkeeper.css';
84
- CSS
85
- end
86
-
87
- def add_oauth_dynamic_registrations
88
- copy_file 'app/controllers/oauth_registrations_controller.rb'
89
- copy_file 'spec/requests/oauth_registrations_controller_spec.rb'
90
- route <<~RUBY
91
- post '/oauth/register', to: 'oauth_registrations#create'
92
- RUBY
93
- end
94
-
95
- def add_well_known_route
96
- copy_file 'app/controllers/well_known_controller.rb'
97
- copy_file 'spec/requests/well_known_controller_spec.rb'
98
- route <<~RUBY
99
- get '/.well-known/oauth-protected-resource', to: 'well_known#oauth_protected_resource'
100
- get '/.well-known/oauth-authorization-server', to: 'well_known#oauth_authorization_server'
101
- RUBY
102
- end
103
-
104
- private
105
-
106
- def application_name
107
- Rails.application.name
108
- end
109
- end
110
- end
@@ -1,140 +0,0 @@
1
- .doorkeeper {
2
- position: fixed;
3
- inset: 0;
4
- box-sizing: border-box;
5
- display: flex;
6
- align-items: center;
7
- justify-content: center;
8
- padding: var(--op-space-x-large) var(--op-space-large);
9
- overflow: auto;
10
- background-color: var(--op-color-neutral-plus-eight);
11
- color: var(--op-color-neutral-minus-max);
12
- font-family: var(--op-font-family);
13
- }
14
-
15
- .doorkeeper__card {
16
- width: min(100%, 48rem);
17
- background-color: var(--op-color-white);
18
- border: var(--op-border-width) solid var(--op-color-neutral-plus-six);
19
- border-radius: var(--op-radius-x-large);
20
- box-shadow: var(--op-shadow-large);
21
- display: flex;
22
- flex-direction: column;
23
- gap: var(--op-space-large);
24
- padding: var(--op-space-2x-large);
25
- }
26
-
27
- .doorkeeper__brand {
28
- display: flex;
29
- flex-direction: column;
30
- align-items: center;
31
- gap: var(--op-space-large);
32
- margin-bottom: var(--op-space-small);
33
- }
34
-
35
- .doorkeeper__logo {
36
- width: 180px;
37
- height: auto;
38
- }
39
-
40
- .doorkeeper__title {
41
- margin: 0;
42
- font-size: var(--op-font-2x-large);
43
- font-weight: var(--op-font-weight-bold);
44
- color: var(--op-color-primary-minus-two);
45
- text-align: center;
46
- letter-spacing: -0.04em;
47
- }
48
-
49
- .doorkeeper__prompt {
50
- margin: 0;
51
- font-size: var(--op-font-large);
52
- line-height: var(--op-line-height-loose);
53
- color: var(--op-color-neutral-minus-three);
54
- text-align: center;
55
- }
56
-
57
- .doorkeeper__client-name {
58
- color: var(--op-color-primary-base);
59
- font-weight: var(--op-font-weight-bold);
60
- }
61
-
62
- .doorkeeper__permissions {
63
- background-color: var(--op-color-primary-plus-eight);
64
- border: var(--op-border-width) solid var(--op-color-primary-plus-six);
65
- border-radius: var(--op-radius-medium);
66
- padding: var(--op-space-large);
67
- display: flex;
68
- flex-direction: column;
69
- gap: var(--op-space-medium);
70
- }
71
-
72
- .doorkeeper__permissions-label {
73
- margin: 0;
74
- font-size: var(--op-font-small);
75
- text-transform: uppercase;
76
- letter-spacing: 0.05em;
77
- font-weight: var(--op-font-weight-bold);
78
- color: var(--op-color-primary-minus-three);
79
- }
80
-
81
- .doorkeeper__scope-list {
82
- margin: 0;
83
- padding-left: var(--op-space-large);
84
- display: grid;
85
- gap: var(--op-space-small);
86
- color: var(--op-color-primary-minus-max);
87
- }
88
-
89
- .doorkeeper__scope-item {
90
- line-height: var(--op-line-height-base);
91
- font-weight: var(--op-font-weight-medium);
92
- }
93
-
94
- .doorkeeper__actions {
95
- display: flex;
96
- flex-direction: column;
97
- gap: var(--op-space-medium);
98
- margin-top: var(--op-space-medium);
99
- }
100
-
101
- .doorkeeper__error {
102
- align-self: stretch;
103
- }
104
-
105
- .doorkeeper__error-description {
106
- margin: 0;
107
- font-size: var(--op-font-medium);
108
- line-height: var(--op-line-height-base);
109
- white-space: pre-wrap;
110
- overflow-wrap: anywhere;
111
- }
112
-
113
- .doorkeeper__form {
114
- margin: 0;
115
- }
116
-
117
- .doorkeeper__button {
118
- width: 100%;
119
- justify-content: center;
120
- font-weight: var(--op-font-weight-semi-bold);
121
- }
122
-
123
- @media (max-width: 640px) {
124
- .doorkeeper {
125
- padding: var(--op-space-large) var(--op-space-medium);
126
- }
127
-
128
- .doorkeeper__card {
129
- padding: var(--op-space-large);
130
- border-radius: var(--op-radius-large);
131
- }
132
-
133
- .doorkeeper__logo {
134
- width: 140px;
135
- }
136
-
137
- .doorkeeper__title {
138
- font-size: var(--op-font-x-large);
139
- }
140
- }
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Doorkeeper
4
- class BaseController < ::ApplicationController
5
- skip_forgery_protection
6
- end
7
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class MCPController < ApplicationController
4
- # skip_before_action :authenticate_user!
5
- skip_forgery_protection
6
-
7
- before_action :authorize_mcp
8
- before_action :set_current_user
9
-
10
- def handle
11
- server = build_server
12
- transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, stateless: true)
13
- server.transport = transport
14
-
15
- status, response_headers, body = transport.handle_request(request)
16
- respond_with(status, response_headers, body)
17
- end
18
-
19
- private
20
-
21
- def respond_with(status, headers, body)
22
- headers.each { |key, value| response.set_header(key, value) }
23
- response.status = status
24
- self.response_body = body
25
- end
26
-
27
- def authorize_mcp
28
- doorkeeper_authorize! :mcp
29
- set_mcp_resource_metadata_header if response.status == 401
30
- end
31
-
32
- def set_current_user
33
- @current_user = User.find_by(id: doorkeeper_token&.resource_owner_id)
34
- unauthorized_request if @current_user.blank?
35
- end
36
-
37
- def unauthorized_request
38
- set_mcp_resource_metadata_header
39
- render json: { error: 'Unauthorized' }, status: :unauthorized
40
- end
41
-
42
- def set_mcp_resource_metadata_header
43
- metadata = %(resource_metadata="#{request.base_url}/.well-known/oauth-protected-resource")
44
- response.set_header('WWW-Authenticate', %(Bearer realm="<%= application_name.titleize %>", #{metadata}))
45
- end
46
-
47
- def build_server
48
- server = MCP::Server.new(**mcp_server_config)
49
- handle_resources(server)
50
-
51
- server
52
- end
53
-
54
- def handle_resources(server) # rubocop:disable Metrics/MethodLength
55
- controllers = [
56
- Resources::DocsController
57
- ]
58
-
59
- server.resources_read_handler do |params|
60
- uri = params[:uri].to_s
61
- controller = controllers.find { |h| h.serves?(uri) }
62
-
63
- unless controller
64
- raise MCP::Server::RequestHandlerError.new(
65
- "Unable to serve resource for URI: #{uri}. Supported schemas: #{controllers.map(&:schema).join(', ')}",
66
- params,
67
- error_type: :invalid_params
68
- )
69
- end
70
-
71
- controller.call(params, server_context)
72
- end
73
- end
74
-
75
- def mcp_server_config # rubocop:disable Metrics/MethodLength
76
- {
77
- name: '<%= application_name.underscore %>_mcp',
78
- version: '1.0.0',
79
- tools: [Tools::Sample],
80
- prompts: [Prompts::Sample],
81
- server_context:,
82
- resources: [
83
- *Resources::DocsController.resource_list,
84
- ],
85
- }
86
- end
87
-
88
- def server_context
89
- @server_context ||= { current_user: @current_user }
90
- end
91
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class OauthRegistrationsController < ApplicationController
4
- # skip_before_action :authenticate_user!
5
- skip_forgery_protection
6
-
7
- def create
8
- app = Doorkeeper::Application.new(doorkeeper_params)
9
- return client_metadata_error('redirect_uris is required') if app.redirect_uri.blank?
10
-
11
- if app.save
12
- render json: base_response(app), status: :created
13
- else
14
- client_metadata_error(app.errors.full_messages.join(', '))
15
- end
16
- end
17
-
18
- private
19
-
20
- def base_response(app)
21
- {
22
- client_id: app.uid,
23
- client_name: app.name,
24
- redirect_uris: app.redirect_uri.split("\n"),
25
- grant_types: %w[authorization_code refresh_token],
26
- response_types: ['code'],
27
- token_endpoint_auth_method: app.confidential? ? 'client_secret_basic' : 'none',
28
- client_id_issued_at: app.created_at.to_i,
29
- scope: 'mcp',
30
- client_secret: app.confidential? ? app.secret : nil,
31
- }.compact
32
- end
33
-
34
- def client_metadata_error(description)
35
- render json: { error: 'invalid_client_metadata', error_description: description }, status: :bad_request
36
- end
37
-
38
- def doorkeeper_params
39
- {
40
- name: params[:client_name].presence || 'MCP Client',
41
- redirect_uri: params[:redirect_uris].is_a?(Array) ? params[:redirect_uris].join("\n") : params[:redirect_uris],
42
- scopes: 'mcp',
43
- confidential: params[:token_endpoint_auth_method] != 'none',
44
- }
45
- end
46
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class WellKnownController < ApplicationController
4
- # skip_before_action :authenticate_user!
5
- before_action :set_base_url
6
-
7
- def oauth_protected_resource
8
- render json: {
9
- resource: "#{@base_url}/mcp",
10
- authorization_servers: [@base_url],
11
- }
12
- end
13
-
14
- def oauth_authorization_server
15
- render json: authorization_server_metadata
16
- end
17
-
18
- private
19
-
20
- def authorization_server_metadata # rubocop:disable Metrics/MethodLength
21
- {
22
- issuer: @base_url,
23
- authorization_endpoint: "#{@base_url}/oauth/authorize",
24
- token_endpoint: "#{@base_url}/oauth/token",
25
- registration_endpoint: "#{@base_url}/oauth/register",
26
- revocation_endpoint: "#{@base_url}/oauth/revoke",
27
- introspection_endpoint: "#{@base_url}/oauth/introspect",
28
- scopes_supported: ['mcp'],
29
- response_types_supported: ['code'],
30
- grant_types_supported: %w[authorization_code client_credentials refresh_token],
31
- token_endpoint_auth_methods_supported: %w[none client_secret_basic client_secret_post],
32
- code_challenge_methods_supported: ['S256'],
33
- }
34
- end
35
-
36
- def set_base_url
37
- @base_url = request.base_url
38
- end
39
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prompts
4
- class Sample < ::MCP::Prompt
5
- prompt_name 'sample_prompt'
6
- title 'Sample Prompt'
7
- description 'Sample prompt description'
8
-
9
- class << self
10
- def template(_args, _server_context: nil)
11
- ::MCP::Prompt::Result.new(
12
- description: 'Sample prompt result description',
13
- messages: [
14
- ::MCP::Prompt::Message.new(
15
- role: 'assistant',
16
- content: ::MCP::Content::Text.new(instructions_text),
17
- ),
18
- ],
19
- )
20
- end
21
-
22
- private
23
-
24
- def instructions_text
25
- <<~TEXT
26
- This is a sample prompt.
27
-
28
- MCP prompts can return instructions for the agent, which can be used to guide the agent's behavior.
29
- For example, you might include instructions on how to query a specific resource or use a specific tool.
30
- Think of it like a system prompt in a conversational agent, but it can be dynamically generated based on the
31
- context of the request.
32
- TEXT
33
- end
34
- end
35
- end
36
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Resources
4
- class Controller
5
- include ActiveModel::Attributes
6
- include ActiveModel::API
7
-
8
- attribute :server_context
9
- attribute :path, :string
10
-
11
- validates :path, presence: { message: 'is required' } # rubocop:disable Rails/I18nLocaleTexts
12
-
13
- class << self
14
- def mime_type(mime_type = nil)
15
- @mime_type = mime_type if mime_type
16
- @mime_type
17
- end
18
-
19
- def schema(schema = nil)
20
- @schema = schema if schema
21
- @schema
22
- end
23
-
24
- def serves?(uri)
25
- uri.start_with?(schema)
26
- end
27
-
28
- def call(params, server_context)
29
- controller = new(params[:uri].sub(schema, ''), server_context)
30
-
31
- unless controller.valid?
32
- raise ::MCP::Server::RequestHandlerError.new(
33
- controller.errors.full_messages.join(', '),
34
- params,
35
- error_type: :invalid_params
36
- )
37
- end
38
-
39
- [{ uri: params[:uri], mimeType: mime_type, text: controller.serve }]
40
- end
41
- end
42
-
43
- def initialize(path, server_context = nil)
44
- super()
45
- self.path = path
46
- self.server_context = server_context
47
- end
48
-
49
- private
50
-
51
- def no_extra_path_parts
52
- return if @extra.blank?
53
-
54
- errors.add(:base, "Too many uri parts: #{@extra.join('/')}.")
55
- end
56
- end
57
- end
@@ -1,4 +0,0 @@
1
- # Hello World
2
-
3
- This is a sample doc that the MCP can return as a resource.
4
- It's URI will be doc://SAMPLE_DOC.md as set in the handler.