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.
- checksums.yaml +4 -4
- data/README.md +26 -18
- data/Rakefile +3 -0
- data/app/controllers/action_mcp/application_controller.rb +166 -109
- data/app/controllers/action_mcp/mcp_app_renderer.rb +1 -0
- data/app/helpers/action_mcp/apps_helper.rb +48 -0
- data/app/jobs/action_mcp/tool_execution_job.rb +67 -35
- data/app/models/action_mcp/session/message.rb +8 -3
- data/app/models/action_mcp/session/task.rb +15 -5
- data/app/models/action_mcp/session.rb +93 -19
- data/config/routes.rb +1 -1
- data/lib/action_mcp/apps/javascript/ext_apps.LICENSE +216 -0
- data/lib/action_mcp/apps/javascript/ext_apps.js +78 -0
- data/lib/action_mcp/apps/view_manifest.rb +170 -0
- data/lib/action_mcp/apps.rb +17 -2
- data/lib/action_mcp/configuration.rb +14 -30
- data/lib/action_mcp/content/audio.rb +4 -1
- data/lib/action_mcp/content/base.rb +3 -1
- data/lib/action_mcp/content/image.rb +4 -1
- data/lib/action_mcp/content/resource.rb +15 -14
- data/lib/action_mcp/content/resource_link.rb +15 -5
- data/lib/action_mcp/content/text.rb +2 -1
- data/lib/action_mcp/content/validation.rb +253 -0
- data/lib/action_mcp/content.rb +3 -1
- data/lib/action_mcp/dev/runner.rb +203 -0
- data/lib/action_mcp/engine.rb +24 -2
- data/lib/action_mcp/json_rpc_handler_base.rb +7 -12
- data/lib/action_mcp/logging/logger.rb +16 -11
- data/lib/action_mcp/logging.rb +34 -1
- data/lib/action_mcp/middleware/origin_validation.rb +9 -13
- data/lib/action_mcp/output_schema_builder.rb +2 -0
- data/lib/action_mcp/prompt_response.rb +12 -2
- data/lib/action_mcp/protocol_validator.rb +513 -0
- data/lib/action_mcp/renderable.rb +4 -4
- data/lib/action_mcp/resource.rb +15 -11
- data/lib/action_mcp/resource_template.rb +105 -90
- data/lib/action_mcp/resource_templates_registry.rb +31 -97
- data/lib/action_mcp/schema_validator.rb +43 -0
- data/lib/action_mcp/server/base_session.rb +134 -28
- data/lib/action_mcp/server/base_session_store.rb +1 -1
- data/lib/action_mcp/server/capabilities.rb +10 -30
- data/lib/action_mcp/server/client_notifications.rb +63 -0
- data/lib/action_mcp/server/completions.rb +68 -0
- data/lib/action_mcp/server/elicitation.rb +47 -25
- data/lib/action_mcp/server/elicitation_request.rb +136 -51
- data/lib/action_mcp/server/handlers/logging_handler.rb +7 -5
- data/lib/action_mcp/server/handlers/prompt_handler.rb +12 -1
- data/lib/action_mcp/server/handlers/resource_handler.rb +41 -7
- data/lib/action_mcp/server/handlers/task_handler.rb +26 -0
- data/lib/action_mcp/server/handlers/tool_handler.rb +8 -0
- data/lib/action_mcp/server/json_rpc_handler.rb +5 -21
- data/lib/action_mcp/server/messaging_service.rb +3 -1
- data/lib/action_mcp/server/prompts.rb +5 -3
- data/lib/action_mcp/server/resources.rb +35 -2
- data/lib/action_mcp/server/response_collector.rb +5 -1
- data/lib/action_mcp/server/roots.rb +15 -2
- data/lib/action_mcp/server/sampling.rb +36 -1
- data/lib/action_mcp/server/sampling_request.rb +317 -55
- data/lib/action_mcp/server/tasks.rb +27 -45
- data/lib/action_mcp/server/tool_result.rb +27 -0
- data/lib/action_mcp/server/tools.rb +13 -8
- data/lib/action_mcp/server/transport_handler.rb +3 -0
- data/lib/action_mcp/server/url_elicitation_request.rb +13 -0
- data/lib/action_mcp/tool.rb +133 -199
- data/lib/action_mcp/tool_response.rb +35 -4
- data/lib/action_mcp/tools_registry.rb +1 -1
- data/lib/action_mcp/version.rb +1 -1
- data/lib/action_mcp.rb +2 -5
- data/lib/generators/action_mcp/identifier/identifier_generator.rb +1 -1
- data/lib/generators/action_mcp/identifier/templates/identifier.rb.erb +1 -1
- data/lib/generators/action_mcp/install/templates/bin/mcp +16 -4
- data/lib/generators/action_mcp/install/templates/mcp/config.ru.tt +2 -2
- data/lib/generators/action_mcp/install/templates/mcp.yml +4 -0
- data/lib/generators/action_mcp/resource_template/resource_template_generator.rb +18 -1
- data/lib/generators/action_mcp/resource_template/templates/resource_template_ui.rb.erb +27 -0
- data/lib/generators/action_mcp/tool/templates/tool.rb.erb +6 -0
- data/lib/generators/action_mcp/tool/tool_generator.rb +2 -0
- data/lib/generators/action_mcp/view/templates/resource_template.rb.erb +16 -0
- data/lib/generators/action_mcp/view/templates/view.html.erb +27 -0
- data/lib/generators/action_mcp/view/view_generator.rb +57 -0
- data/lib/tasks/action_mcp_apps.rake +17 -0
- data/lib/tasks/action_mcp_tasks.rake +135 -74
- 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://
|
|
13
|
-
# bundle exec rails s -c mcp/config.ru -p 62770
|
|
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
|
-
|
|
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::
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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::
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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::
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
161
|
-
config = ActionMCP
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
puts "
|
|
182
|
-
puts "
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
puts "
|
|
248
|
-
puts " Database
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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}"
|