actionmcp 0.111.0 → 0.200.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -18
  3. data/Rakefile +3 -0
  4. data/app/controllers/action_mcp/application_controller.rb +166 -109
  5. data/app/controllers/action_mcp/mcp_app_renderer.rb +1 -0
  6. data/app/helpers/action_mcp/apps_helper.rb +48 -0
  7. data/app/jobs/action_mcp/tool_execution_job.rb +67 -35
  8. data/app/models/action_mcp/session/message.rb +8 -3
  9. data/app/models/action_mcp/session/task.rb +15 -5
  10. data/app/models/action_mcp/session.rb +93 -19
  11. data/config/routes.rb +1 -1
  12. data/lib/action_mcp/apps/javascript/ext_apps.LICENSE +216 -0
  13. data/lib/action_mcp/apps/javascript/ext_apps.js +78 -0
  14. data/lib/action_mcp/apps/view_manifest.rb +170 -0
  15. data/lib/action_mcp/apps.rb +17 -2
  16. data/lib/action_mcp/configuration.rb +14 -30
  17. data/lib/action_mcp/content/audio.rb +4 -1
  18. data/lib/action_mcp/content/base.rb +3 -1
  19. data/lib/action_mcp/content/image.rb +4 -1
  20. data/lib/action_mcp/content/resource.rb +15 -14
  21. data/lib/action_mcp/content/resource_link.rb +15 -5
  22. data/lib/action_mcp/content/text.rb +2 -1
  23. data/lib/action_mcp/content/validation.rb +253 -0
  24. data/lib/action_mcp/content.rb +3 -1
  25. data/lib/action_mcp/dev/runner.rb +203 -0
  26. data/lib/action_mcp/engine.rb +24 -2
  27. data/lib/action_mcp/json_rpc_handler_base.rb +7 -12
  28. data/lib/action_mcp/logging/logger.rb +16 -11
  29. data/lib/action_mcp/logging.rb +34 -1
  30. data/lib/action_mcp/middleware/origin_validation.rb +9 -13
  31. data/lib/action_mcp/output_schema_builder.rb +2 -0
  32. data/lib/action_mcp/prompt_response.rb +12 -2
  33. data/lib/action_mcp/protocol_validator.rb +513 -0
  34. data/lib/action_mcp/renderable.rb +4 -4
  35. data/lib/action_mcp/resource.rb +15 -11
  36. data/lib/action_mcp/resource_template.rb +105 -90
  37. data/lib/action_mcp/resource_templates_registry.rb +31 -97
  38. data/lib/action_mcp/schema_validator.rb +43 -0
  39. data/lib/action_mcp/server/base_session.rb +134 -28
  40. data/lib/action_mcp/server/base_session_store.rb +1 -1
  41. data/lib/action_mcp/server/capabilities.rb +10 -30
  42. data/lib/action_mcp/server/client_notifications.rb +63 -0
  43. data/lib/action_mcp/server/completions.rb +68 -0
  44. data/lib/action_mcp/server/elicitation.rb +47 -25
  45. data/lib/action_mcp/server/elicitation_request.rb +136 -51
  46. data/lib/action_mcp/server/handlers/logging_handler.rb +7 -5
  47. data/lib/action_mcp/server/handlers/prompt_handler.rb +12 -1
  48. data/lib/action_mcp/server/handlers/resource_handler.rb +41 -7
  49. data/lib/action_mcp/server/handlers/task_handler.rb +26 -0
  50. data/lib/action_mcp/server/handlers/tool_handler.rb +8 -0
  51. data/lib/action_mcp/server/json_rpc_handler.rb +5 -21
  52. data/lib/action_mcp/server/messaging_service.rb +3 -1
  53. data/lib/action_mcp/server/prompts.rb +5 -3
  54. data/lib/action_mcp/server/resources.rb +35 -2
  55. data/lib/action_mcp/server/response_collector.rb +5 -1
  56. data/lib/action_mcp/server/roots.rb +15 -2
  57. data/lib/action_mcp/server/sampling.rb +36 -1
  58. data/lib/action_mcp/server/sampling_request.rb +317 -55
  59. data/lib/action_mcp/server/tasks.rb +27 -45
  60. data/lib/action_mcp/server/tool_result.rb +27 -0
  61. data/lib/action_mcp/server/tools.rb +13 -8
  62. data/lib/action_mcp/server/transport_handler.rb +3 -0
  63. data/lib/action_mcp/server/url_elicitation_request.rb +13 -0
  64. data/lib/action_mcp/tool.rb +133 -199
  65. data/lib/action_mcp/tool_response.rb +35 -4
  66. data/lib/action_mcp/tools_registry.rb +1 -1
  67. data/lib/action_mcp/version.rb +1 -1
  68. data/lib/action_mcp.rb +2 -5
  69. data/lib/generators/action_mcp/identifier/identifier_generator.rb +1 -1
  70. data/lib/generators/action_mcp/identifier/templates/identifier.rb.erb +1 -1
  71. data/lib/generators/action_mcp/install/templates/bin/mcp +16 -4
  72. data/lib/generators/action_mcp/install/templates/mcp/config.ru.tt +2 -2
  73. data/lib/generators/action_mcp/install/templates/mcp.yml +4 -0
  74. data/lib/generators/action_mcp/resource_template/resource_template_generator.rb +18 -1
  75. data/lib/generators/action_mcp/resource_template/templates/resource_template_ui.rb.erb +27 -0
  76. data/lib/generators/action_mcp/tool/templates/tool.rb.erb +6 -0
  77. data/lib/generators/action_mcp/tool/tool_generator.rb +2 -0
  78. data/lib/generators/action_mcp/view/templates/resource_template.rb.erb +16 -0
  79. data/lib/generators/action_mcp/view/templates/view.html.erb +27 -0
  80. data/lib/generators/action_mcp/view/view_generator.rb +57 -0
  81. data/lib/tasks/action_mcp_apps.rake +17 -0
  82. data/lib/tasks/action_mcp_tasks.rake +135 -74
  83. metadata +47 -17
@@ -9,8 +9,8 @@
9
9
  #
10
10
  # Start this server with:
11
11
  # bin/mcp # Uses Falcon (recommended)
12
- # bundle exec falcon serve --bind http://0.0.0.0:62770 mcp/config.ru
13
- # bundle exec rails s -c mcp/config.ru -p 62770 # Uses Puma (fallback)
12
+ # bundle exec falcon serve --bind http://127.0.0.1:62770 mcp/config.ru
13
+ # bundle exec rails s -c mcp/config.ru -b 127.0.0.1 -p 62770 # Uses Puma (fallback)
14
14
  #
15
15
  # Port 62770 = MCPS0 on a phone keypad (MCP Server, instance 0)
16
16
  #
@@ -24,6 +24,10 @@ shared:
24
24
  # io.modelcontextprotocol/ui with text/html;profile=mcp-app support.
25
25
  # mcp_apps_enabled: true
26
26
 
27
+ # Directory holding JavaScript view bundles compiled from JavaScript or
28
+ # TypeScript by @action-mcp/vite-plugin. Relative to Rails.root.
29
+ # mcp_apps_views_path: .action_mcp/views
30
+
27
31
  # MCP capability profiles
28
32
  profiles:
29
33
  primary:
@@ -11,8 +11,12 @@ module ActionMCP
11
11
 
12
12
  argument :name, type: :string, required: true, banner: "ResourceTemplateName"
13
13
 
14
+ class_option :ui, type: :boolean, default: false,
15
+ desc: "Generate an MCP Apps UI template (ui:// URI, :mcp_app mime type, render_ui)"
16
+
14
17
  def create_resource_template_file
15
- template "resource_template.rb.erb", "app/mcp/resource_templates/#{file_name}.rb"
18
+ source = options[:ui] ? "resource_template_ui.rb.erb" : "resource_template.rb.erb"
19
+ template source, "app/mcp/resource_templates/#{file_name}.rb"
16
20
  end
17
21
 
18
22
  private
@@ -25,6 +29,19 @@ module ActionMCP
25
29
  base = name.underscore
26
30
  base.end_with?("_template") ? base : "#{base}_template"
27
31
  end
32
+
33
+ # Name without the Template suffix, for view paths and ui:// URIs.
34
+ def base_name
35
+ name.camelize.delete_suffix("Template")
36
+ end
37
+
38
+ def view_name
39
+ base_name.underscore
40
+ end
41
+
42
+ def uri_name
43
+ view_name.dasherize
44
+ end
28
45
  end
29
46
  end
30
47
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %> < ApplicationMCPResTemplate
4
+ description "Interactive UI view for <%= base_name.titleize.downcase %>"
5
+ uri_template "ui://views/<%= uri_name %>"
6
+ mime_type :mcp_app
7
+
8
+ # Declare CSP origins and host preferences for the embedded view (MCP Apps / SEP-1865).
9
+ # Only origins listed here can be reached from inside the view iframe.
10
+ # ui csp: { connectDomains: %w[https://api.example.com] },
11
+ # prefersBorder: true
12
+
13
+ def resolve
14
+ # Inline HTML keeps this template self-contained. To render an ERB view
15
+ # instead, run `bin/rails generate action_mcp:view <%= base_name %>` or
16
+ # create app/views/mcp/ui/<%= view_name %>.html.erb and use:
17
+ # render_ui(template: "mcp/ui/<%= view_name %>")
18
+ render_ui(text: <<~HTML)
19
+ <!doctype html>
20
+ <html>
21
+ <body>
22
+ <h1><%= base_name.titleize %></h1>
23
+ </body>
24
+ </html>
25
+ HTML
26
+ end
27
+ end
@@ -23,6 +23,12 @@ class <%= class_name %> < ApplicationMCPTool
23
23
  annotate(:<%= k %>, <%= v.inspect %>)
24
24
  <% end %>
25
25
  <% end %>
26
+ <% if options[:ui] %>
27
+ renders_ui "<%= options[:ui] %>"
28
+ <% else %>
29
+ # Link this tool to an MCP Apps UI view (generate one with `bin/rails g action_mcp:view MyView`):
30
+ # renders_ui "ui://views/my-view", visibility: %i[model app]
31
+ <% end %>
26
32
 
27
33
  <% if properties.empty? %>
28
34
  property :input, type: "string", description: "Input", required: true
@@ -14,6 +14,8 @@ module ActionMCP
14
14
  class_option :destructive, type: :boolean, default: false
15
15
  class_option :category, type: :string, default: nil
16
16
  class_option :properties, type: :array, default: [], banner: "name:type:description:required"
17
+ class_option :ui, type: :string, default: nil, banner: "ui://views/my-view",
18
+ desc: "Link this tool to an MCP Apps UI view via renders_ui"
17
19
 
18
20
  def create_tool_file
19
21
  template "tool.rb.erb", "app/mcp/tools/#{file_name}.rb"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %> < ApplicationMCPResTemplate
4
+ description "Interactive UI view for <%= base_name.titleize.downcase %>"
5
+ uri_template "ui://views/<%= uri_name %>"
6
+ mime_type :mcp_app
7
+
8
+ # Declare CSP origins and host preferences for the embedded view (MCP Apps / SEP-1865).
9
+ # Only origins listed here can be reached from inside the view iframe.
10
+ # ui csp: { connectDomains: %w[https://api.example.com] },
11
+ # prefersBorder: true
12
+
13
+ def resolve
14
+ render_ui(template: "mcp/ui/<%= view_name %>")
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <style>
6
+ /* Styles must be inline or served from origins declared in the ui csp metadata. */
7
+ body { font-family: system-ui, sans-serif; margin: 1rem; }
8
+ </style>
9
+ </head>
10
+ <body>
11
+ <h1><%= base_name.titleize %></h1>
12
+ <pre id="output">Waiting for tool result…</pre>
13
+ <%%= mcp_app_bridge_tag %>
14
+ <script type="module">
15
+ // Register handlers before connecting; handlers set after connect() can miss events.
16
+ const app = await ActionMCP.connect({
17
+ handlers: {
18
+ ontoolresult: (result) => {
19
+ document.getElementById("output").textContent =
20
+ JSON.stringify(result.structuredContent ?? result, null, 2);
21
+ }
22
+ }
23
+ });
24
+ // app.callServerTool({ name: "my-tool", arguments: { ... } }) calls back into the server.
25
+ </script>
26
+ </body>
27
+ </html>
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module ActionMCP
6
+ module Generators
7
+ class ViewGenerator < Rails::Generators::NamedBase
8
+ namespace "action_mcp:view"
9
+ source_root File.expand_path("templates", __dir__)
10
+ desc "Creates an MCP Apps UI view: a ui:// ResourceTemplate (in app/mcp/resource_templates) " \
11
+ "paired with an ERB view (in app/views/mcp/ui)"
12
+
13
+ argument :name, type: :string, required: true, banner: "ViewName"
14
+
15
+ def create_resource_template_file
16
+ template "resource_template.rb.erb", "app/mcp/resource_templates/#{file_name}.rb"
17
+ end
18
+
19
+ def create_view_file
20
+ template "view.html.erb", "app/views/mcp/ui/#{view_name}.html.erb"
21
+ end
22
+
23
+ def show_instructions
24
+ say <<~INSTRUCTIONS
25
+
26
+ MCP Apps view created. Next steps:
27
+ 1. Enable MCP Apps in config/mcp.yml: mcp_apps_enabled: true
28
+ 2. Link a tool to this view: renders_ui "ui://views/#{uri_name}"
29
+ 3. Edit app/views/mcp/ui/#{view_name}.html.erb
30
+ INSTRUCTIONS
31
+ end
32
+
33
+ private
34
+
35
+ def class_name
36
+ "#{base_name.camelize}Template"
37
+ end
38
+
39
+ def file_name
40
+ "#{view_name}_template"
41
+ end
42
+
43
+ # Name without a trailing Template suffix, used for the view path and URI.
44
+ def base_name
45
+ name.camelize.delete_suffix("Template")
46
+ end
47
+
48
+ def view_name
49
+ base_name.underscore
50
+ end
51
+
52
+ def uri_name
53
+ view_name.dasherize
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :action_mcp do
4
+ namespace :apps do
5
+ desc "Print registered tool schemas as JSON (consumed by @action-mcp/vite-plugin for types.d.ts)"
6
+ task schema: :environment do
7
+ Rails.application.eager_load!
8
+
9
+ tools = ActionMCP::ToolsRegistry.non_abstract.sort_by(&:name).map do |tool|
10
+ tool.klass.to_h(protocol_version: ActionMCP.configuration.protocol_version)
11
+ .slice(:name, :description, :inputSchema, :outputSchema)
12
+ end
13
+
14
+ puts JSON.generate({ tools: tools })
15
+ end
16
+ end
17
+ end
@@ -9,10 +9,13 @@ namespace :action_mcp do
9
9
 
10
10
  puts "\e[34mACTION MCP TOOLS\e[0m" # Blue
11
11
  puts "\e[34m---------------\e[0m" # Blue
12
- ActionMCP::Tool.descendants.each do |tool|
13
- next if tool.abstract?
14
-
15
- puts "\e[34m#{tool.capability_name}:\e[0m #{tool.description}" # Blue name
12
+ tools = ActionMCP::ToolsRegistry.non_abstract.sort_by(&:name)
13
+ if tools.any?
14
+ tools.each do |tool|
15
+ puts "\e[34m#{tool.name}:\e[0m #{tool.description}" # Blue name
16
+ end
17
+ else
18
+ puts " No tools registered"
16
19
  end
17
20
  puts "\n"
18
21
  end
@@ -25,10 +28,13 @@ namespace :action_mcp do
25
28
 
26
29
  puts "\e[32mACTION MCP PROMPTS\e[0m" # Green
27
30
  puts "\e[32m-----------------\e[0m" # Green
28
- ActionMCP::Prompt.descendants.each do |prompt|
29
- next if prompt.abstract?
30
-
31
- puts "\e[32m#{prompt.capability_name}:\e[0m #{prompt.description}" # Green name
31
+ prompts = ActionMCP::PromptsRegistry.non_abstract.sort_by(&:name)
32
+ if prompts.any?
33
+ prompts.each do |prompt|
34
+ puts "\e[32m#{prompt.name}:\e[0m #{prompt.description}" # Green name
35
+ end
36
+ else
37
+ puts " No prompts registered"
32
38
  end
33
39
  puts "\n"
34
40
  end
@@ -41,11 +47,70 @@ namespace :action_mcp do
41
47
 
42
48
  puts "\e[33mACTION MCP RESOURCES\e[0m" # Yellow
43
49
  puts "\e[33m--------------------\e[0m" # Yellow
44
- ActionMCP::ResourceTemplate.descendants.each do |resource|
45
- next if resource.abstract?
50
+ resources = ActionMCP::ResourceTemplatesRegistry.non_abstract.sort_by(&:name)
51
+ if resources.any?
52
+ resources.each do |resource|
53
+ puts "\e[33m#{resource.name}:\e[0m #{resource.description} : #{resource.klass.uri_template}" # Yellow name
54
+ end
55
+ else
56
+ puts " No resources registered"
57
+ end
58
+ puts "\n"
59
+ end
60
+
61
+ # bin/rails action_mcp:list_widgets
62
+ desc "List MCP Apps UI widgets and their linked tools"
63
+ task list_widgets: :environment do
64
+ Rails.application.eager_load!
65
+
66
+ widgets = ActionMCP::ResourceTemplatesRegistry.non_abstract
67
+ .select { |resource| resource.klass.mime_type == ActionMCP::Apps::MIME_TYPE }
68
+ .sort_by(&:name)
69
+ widget_classes = widgets.map(&:klass)
70
+ tools_by_widget = Hash.new { |linked, widget| linked[widget] = [] }
71
+ missing_resources = Hash.new { |uris, uri| uris[uri] = [] }
72
+
73
+ ActionMCP::ToolsRegistry.non_abstract.sort_by(&:name).each do |tool|
74
+ descriptor = tool.klass.to_h(protocol_version: ActionMCP.configuration.protocol_version)
75
+ meta = descriptor[:_meta] || descriptor["_meta"]
76
+ ui = meta && (meta[:ui] || meta["ui"])
77
+ next unless ui.is_a?(Hash)
78
+
79
+ resource_uri = ui[:resourceUri] || ui["resourceUri"]
80
+ next unless resource_uri
81
+
82
+ visibility = ui[:visibility] || ui["visibility"]
83
+ widget = ActionMCP::ResourceTemplatesRegistry.find_template_for_uri(resource_uri, templates: widget_classes)
84
+ destination = widget ? tools_by_widget[widget] : missing_resources[resource_uri]
85
+ destination << [ tool.name, Array(visibility) ]
86
+ end
87
+
88
+ puts "\e[36mACTION MCP UI WIDGETS\e[0m" # Cyan
89
+ puts "\e[36m---------------------\e[0m" # Cyan
90
+
91
+ if widgets.any?
92
+ widgets.each do |widget|
93
+ uri = widget.klass.uri_template
94
+ linked_tools = tools_by_widget.fetch(widget.klass, []).map do |tool_name, visibility|
95
+ visibility.any? ? "#{tool_name} [visibility: #{visibility.join(', ')}]" : tool_name
96
+ end
97
+
98
+ puts "\e[36m#{widget.name}:\e[0m #{uri}"
99
+ puts " #{widget.description}" if widget.description.present?
100
+ puts " Linked tools: #{linked_tools.any? ? linked_tools.join(', ') : 'None'}"
101
+ end
102
+ else
103
+ puts " No MCP Apps UI widgets registered"
104
+ end
46
105
 
47
- puts "\e[33m#{resource.capability_name}:\e[0m #{resource.description} : #{resource.uri_template}" # Yellow name
106
+ if missing_resources.any?
107
+ puts "\n\e[31mMISSING UI WIDGET RESOURCES\e[0m" # Red
108
+ puts "\e[31m---------------------------\e[0m" # Red
109
+ missing_resources.sort.each do |uri, linked_tools|
110
+ puts " #{uri}: #{linked_tools.map(&:first).join(', ')}"
111
+ end
48
112
  end
113
+
49
114
  puts "\n"
50
115
  end
51
116
 
@@ -81,9 +146,8 @@ namespace :action_mcp do
81
146
  profiles = ActionMCP.configuration.profiles
82
147
 
83
148
  unless profiles.key?(profile_name)
84
- puts "\e[31mProfile '#{profile_name}' not found!\e[0m"
85
- puts "Available profiles: #{profiles.keys.join(', ')}"
86
- next
149
+ abort "\e[31mProfile '#{profile_name}' not found!\e[0m\n" \
150
+ "Available profiles: #{profiles.keys.join(', ')}"
87
151
  end
88
152
 
89
153
  # Temporarily activate this profile to show what would be included
@@ -142,73 +206,70 @@ namespace :action_mcp do
142
206
  puts "\n"
143
207
  end
144
208
 
145
- desc "List all tools, prompts, resources and available profiles"
146
- task list: %i[list_tools list_prompts list_resources list_profiles] do
147
- # This task lists all tools, prompts, resources and profiles
209
+ desc "List all tools, prompts, resources, UI widgets and available profiles"
210
+ task list: %i[list_tools list_prompts list_resources list_widgets list_profiles] do
211
+ # This task lists all registered MCP components and profiles.
148
212
  end
149
213
 
150
214
  # bin/rails action_mcp:info
151
- # bin/rails action_mcp:info[test]
152
- desc "Display ActionMCP configuration for current or specified environment"
215
+ desc "Display the live ActionMCP configuration for the current Rails environment"
153
216
  task :info, [ :env ] => :environment do |_t, args|
154
- env = args[:env] || Rails.env
155
-
156
- # Load configuration for the specified environment
157
- original_env = Rails.env
158
- Rails.env = env.to_s
217
+ requested_env = args[:env]&.to_s
218
+ if requested_env && requested_env != Rails.env
219
+ abort "ActionMCP configuration is initialized when Rails boots. " \
220
+ "Run `RAILS_ENV=#{requested_env} bin/rails action_mcp:info` instead."
221
+ end
159
222
 
160
- # Reload configuration to get the environment-specific settings
161
- config = ActionMCP::Configuration.new
162
- config.load_profiles
223
+ env = Rails.env.to_s
224
+ config = ActionMCP.configuration
163
225
 
164
226
  puts "\e[35mActionMCP Configuration (#{env})\e[0m"
165
227
  puts "\e[35m#{'=' * (25 + env.length)}\e[0m"
166
228
 
167
- # Basic Information
168
229
  puts "\n\e[36mBasic Information:\e[0m"
169
230
  puts " Name: #{config.name}"
170
231
  puts " Version: #{config.version}"
171
232
  puts " Protocol Version: #{config.protocol_version}"
172
233
  puts " Active Profile: #{config.active_profile}"
173
234
 
174
- # Session Storage
235
+ puts "\n\e[36mTransport:\e[0m"
236
+ puts " Base Path: #{config.base_path}"
237
+ puts " Allowed Origins: #{config.allowed_origins.join(', ')}"
238
+ puts " Pagination Page Size: #{config.pagination_page_size || 'disabled'}"
239
+
175
240
  puts "\n\e[36mSession Storage:\e[0m"
176
241
  puts " Session Store Type: #{config.session_store_type}"
177
242
  puts " Client Session Store: #{config.client_session_store_type || 'default'}"
178
243
  puts " Server Session Store: #{config.server_session_store_type || 'default'}"
179
244
 
180
- # Transport Configuration
181
- puts "\n\e[36mTransport Configuration:\e[0m"
182
- puts " Protocol Version: #{config.protocol_version}"
183
-
184
- # Pub/Sub Adapter
185
- puts "\n\e[36mPub/Sub Adapter:\e[0m"
186
- puts " Adapter: #{config.adapter || 'not configured'}"
187
- if config.adapter
188
- puts " Polling Interval: #{config.polling_interval}" if config.polling_interval
189
- puts " Min Threads: #{config.min_threads}" if config.min_threads
190
- puts " Max Threads: #{config.max_threads}" if config.max_threads
191
- puts " Max Queue: #{config.max_queue}" if config.max_queue
192
- end
245
+ puts "\n\e[36mThread Pool:\e[0m"
246
+ puts " Min Threads: #{config.min_threads || 'default'}"
247
+ puts " Max Threads: #{config.max_threads || 'default'}"
248
+ puts " Max Queue: #{config.max_queue || 'default'}"
193
249
 
194
- # Authentication
195
250
  puts "\n\e[36mAuthentication:\e[0m"
196
- puts " Methods: #{config.authentication_methods.join(', ')}"
197
- if config.oauth_config&.any?
198
- puts " OAuth Provider: #{config.oauth_config['provider']}"
199
- puts " OAuth Scopes: #{config.oauth_config['scopes_supported']&.join(', ')}"
200
- end
251
+ methods = config.authentication_methods
252
+ puts " Methods: #{methods.any? ? methods.join(', ') : 'none'}"
253
+ puts " Allowed Identity Keys: #{config.allowed_identity_keys.join(', ')}"
254
+
255
+ puts "\n\e[36mMCP Apps:\e[0m"
256
+ puts " Enabled: #{config.mcp_apps_enabled}"
257
+ puts " Views Path: #{config.mcp_apps_views_path}"
258
+ puts " Widget MIME Type: #{ActionMCP::Apps::MIME_TYPE}"
259
+
260
+ puts "\n\e[36mTasks:\e[0m"
261
+ puts " Enabled: #{config.tasks_enabled}"
262
+ puts " List Enabled: #{config.tasks_list_enabled}"
263
+ puts " Cancel Enabled: #{config.tasks_cancel_enabled}"
264
+ puts " Result Poll Interval: #{config.tasks_result_poll_interval}"
201
265
 
202
- # Logging
203
266
  puts "\n\e[36mLogging:\e[0m"
204
267
  puts " Logging Enabled: #{config.logging_enabled}"
205
268
  puts " Logging Level: #{config.logging_level}"
206
269
 
207
- # Gateway
208
270
  puts "\n\e[36mGateway:\e[0m"
209
- puts " Gateway Class: #{config.gateway_class}"
271
+ puts " Gateway Class: #{config.gateway_class || 'not configured'}"
210
272
 
211
- # Capabilities
212
273
  puts "\n\e[36mEnabled Capabilities:\e[0m"
213
274
  capabilities = config.capabilities
214
275
  if capabilities.any?
@@ -219,15 +280,11 @@ namespace :action_mcp do
219
280
  puts " None"
220
281
  end
221
282
 
222
- # Available Profiles
223
283
  puts "\n\e[36mAvailable Profiles:\e[0m"
224
284
  config.profiles.each_key do |profile_name|
225
285
  puts " - #{profile_name}"
226
286
  end
227
287
 
228
- # Restore original environment
229
- Rails.env = original_env
230
-
231
288
  puts "\n"
232
289
  end
233
290
 
@@ -237,15 +294,25 @@ namespace :action_mcp do
237
294
  puts "\e[35mActionMCP Statistics\e[0m"
238
295
  puts "\e[35m===================\e[0m"
239
296
 
240
- # Debug database connection
241
- puts "\n\e[36mDatabase Debug:\e[0m"
297
+ database_path = nil
298
+
299
+ puts "\n\e[36mDatabase:\e[0m"
242
300
  puts " Rails Environment: #{Rails.env}"
243
301
  puts " Rails Root: #{Rails.root}"
244
- puts " Database Config: #{ActionMCP::ApplicationRecord.connection_db_config.configuration_hash.inspect}"
245
- if ActionMCP::ApplicationRecord.connection.adapter_name.downcase.include?("sqlite")
246
- db_path = ActionMCP::ApplicationRecord.connection_db_config.database
247
- puts " Database Path: #{db_path}"
248
- puts " Database Exists?: #{File.exist?(db_path)}" if db_path
302
+ begin
303
+ connection = ActionMCP::ApplicationRecord.connection
304
+ db_config = ActionMCP::ApplicationRecord.connection_db_config
305
+ puts " Adapter: #{connection.adapter_name}"
306
+ puts " Database: #{db_config.database}"
307
+
308
+ if connection.adapter_name.downcase.include?("sqlite") && db_config.database != ":memory:"
309
+ configured_path = Pathname.new(db_config.database.to_s)
310
+ database_path = configured_path.absolute? ? configured_path : Rails.root.join(configured_path)
311
+ puts " Database Path: #{database_path}"
312
+ puts " Database Exists?: #{database_path.file?}"
313
+ end
314
+ rescue StandardError => e
315
+ puts " Could not inspect database connection: #{e.message}"
249
316
  end
250
317
 
251
318
  # Session Statistics
@@ -332,19 +399,13 @@ namespace :action_mcp do
332
399
  # Storage Information
333
400
  puts "\n\e[36mStorage Information:\e[0m"
334
401
  puts " Session Store Type: #{ActionMCP.configuration.session_store_type}"
335
- puts " Database Adapter: #{ActionMCP::ApplicationRecord.connection.adapter_name}"
336
402
 
337
- # Database size (if SQLite)
338
403
  begin
339
- if ActionMCP::ApplicationRecord.connection.adapter_name.downcase.include?("sqlite")
340
- db_config = Rails.application.config.database_configuration[Rails.env]
341
- if db_config && db_config["database"]
342
- db_file = db_config["database"]
343
- if File.exist?(db_file)
344
- size_mb = (File.size(db_file) / 1024.0 / 1024.0).round(2)
345
- puts " Database Size: #{size_mb} MB"
346
- end
347
- end
404
+ connection = ActionMCP::ApplicationRecord.connection
405
+ puts " Database Adapter: #{connection.adapter_name}"
406
+ if database_path&.file?
407
+ size_mb = (database_path.size / 1024.0 / 1024.0).round(2)
408
+ puts " Database Size: #{size_mb} MB"
348
409
  end
349
410
  rescue StandardError => e
350
411
  puts " Could not determine database size: #{e.message}"