stimulus_plumbers_mcp 0.4.4 → 0.4.8

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +120 -0
  3. data/bin/bump-version +26 -0
  4. data/bin/mcp-query +107 -0
  5. data/bin/server +8 -0
  6. data/bin/stimulus-plumbers-mcp +7 -0
  7. data/lib/stimulus_plumbers/mcp/cli.rb +13 -0
  8. data/lib/stimulus_plumbers/mcp/loaders/aria_loader.rb +31 -0
  9. data/lib/stimulus_plumbers/mcp/loaders/component_docs_loader.rb +38 -0
  10. data/lib/stimulus_plumbers/mcp/loaders/component_requirements.rb +41 -0
  11. data/lib/stimulus_plumbers/mcp/loaders/component_schema_loader.rb +49 -0
  12. data/lib/stimulus_plumbers/mcp/loaders/component_theme_loader.rb +41 -0
  13. data/lib/stimulus_plumbers/mcp/loaders/controller_docs_loader.rb +31 -0
  14. data/lib/stimulus_plumbers/mcp/loaders/controller_schema_loader.rb +49 -0
  15. data/lib/stimulus_plumbers/mcp/loaders/guide.md +48 -0
  16. data/lib/stimulus_plumbers/mcp/loaders/guide_loader.rb +40 -4
  17. data/lib/stimulus_plumbers/mcp/loaders/icons_loader.rb +34 -0
  18. data/lib/stimulus_plumbers/mcp/loaders/support/docs_table_parser.rb +112 -0
  19. data/lib/stimulus_plumbers/mcp/loaders/support/gem_vendor_path.rb +18 -0
  20. data/lib/stimulus_plumbers/mcp/loaders/tailwind_loader.rb +35 -0
  21. data/lib/stimulus_plumbers/mcp/loaders/versions_loader.rb +110 -0
  22. data/lib/stimulus_plumbers/mcp/plugins/aria.rb +34 -0
  23. data/lib/stimulus_plumbers/mcp/plugins/base.rb +40 -31
  24. data/lib/stimulus_plumbers/mcp/plugins/component_docs.rb +96 -0
  25. data/lib/stimulus_plumbers/mcp/plugins/component_schema.rb +133 -0
  26. data/lib/stimulus_plumbers/mcp/plugins/component_theme.rb +90 -0
  27. data/lib/stimulus_plumbers/mcp/plugins/controller_docs.rb +91 -0
  28. data/lib/stimulus_plumbers/mcp/plugins/controller_schema.rb +81 -0
  29. data/lib/stimulus_plumbers/mcp/plugins/guide.rb +70 -16
  30. data/lib/stimulus_plumbers/mcp/plugins/icons.rb +42 -0
  31. data/lib/stimulus_plumbers/mcp/plugins/tailwind.rb +69 -45
  32. data/lib/stimulus_plumbers/mcp/plugins/versions.rb +44 -0
  33. data/lib/stimulus_plumbers/mcp/server.rb +26 -9
  34. data/lib/stimulus_plumbers/mcp/version.rb +1 -1
  35. data/lib/stimulus_plumbers_mcp.rb +32 -11
  36. metadata +36 -13
  37. data/lib/stimulus_plumbers/mcp/loaders/component_controller_map.rb +0 -38
  38. data/lib/stimulus_plumbers/mcp/loaders/docs_loader.rb +0 -122
  39. data/lib/stimulus_plumbers/mcp/loaders/schema_loader.rb +0 -45
  40. data/lib/stimulus_plumbers/mcp/loaders/stimulus_manifest.rb +0 -23
  41. data/lib/stimulus_plumbers/mcp/loaders/tailwind_theme_loader.rb +0 -29
  42. data/lib/stimulus_plumbers/mcp/loaders/theme_loader.rb +0 -97
  43. data/lib/stimulus_plumbers/mcp/plugins/docs.rb +0 -82
  44. data/lib/stimulus_plumbers/mcp/plugins/schema.rb +0 -110
  45. data/lib/stimulus_plumbers/mcp/plugins/stimulus.rb +0 -66
  46. data/lib/stimulus_plumbers/mcp/plugins/theme.rb +0 -66
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module MCP
5
- module Plugins
6
- module Docs
7
- extend Base
8
-
9
- LOADER_KEY = :docs
10
- LOADER = DocsLoader
11
-
12
- STATIC_RESOURCES = [].freeze
13
- DYNAMIC_RESOURCE_TEMPLATES = [
14
- ::MCP::ResourceTemplate.new(
15
- uri_template: "docs://components/{name}",
16
- name: "component-docs",
17
- description: "Full markdown documentation and ERB examples for a component",
18
- mime_type: "text/markdown"
19
- ),
20
- ::MCP::ResourceTemplate.new(
21
- uri_template: "helper://components/{name}",
22
- name: "component-helper-signature",
23
- description: "Full sp_ helper option surface: keyword options with defaults and slot methods",
24
- mime_type: "application/json"
25
- )
26
- ].freeze
27
-
28
- def self.read(uri, store)
29
- docs = store[:docs]
30
-
31
- case uri
32
- when %r{\Adocs://components/(.+)\z}
33
- doc = docs[Regexp.last_match(1).to_sym]
34
- doc ? text_resource(uri, "text/markdown", doc[:content]) : missing(uri, Regexp.last_match(1))
35
- when %r{\Ahelper://components/(.+)\z}
36
- doc = docs[Regexp.last_match(1).to_sym]
37
- doc ? json_resource(uri, doc[:signature]) : missing(uri, Regexp.last_match(1))
38
- end
39
- end
40
-
41
- def self.missing(uri, name)
42
- json_resource(uri, { error: "no documentation for: #{name}" })
43
- end
44
- private_class_method :missing
45
-
46
- def self.register_tools(server, store)
47
- docs = store[:docs]
48
-
49
- text_tool(
50
- server,
51
- name: "list_docs",
52
- description: "Lists components that have markdown docs (docs://components/{name}) and " \
53
- "helper signatures (helper://components/{name})"
54
- ) do
55
- JSON.generate(docs.keys)
56
- end
57
-
58
- text_tool(
59
- server,
60
- name: "get_erb_examples",
61
- description: "Returns ERB usage examples for a component from the documentation",
62
- input_schema: { properties: { component: { type: "string" } }, required: ["component"] }
63
- ) do |component:|
64
- examples = docs[component.to_sym]&.dig(:examples) || []
65
- examples.empty? ? not_found("no examples for: #{component}") : examples.join("\n\n")
66
- end
67
-
68
- text_tool(
69
- server,
70
- name: "get_helper_signature",
71
- description: "Returns the full sp_ helper surface for a component: keyword options with " \
72
- "defaults plus slot methods (e.g. icon_leading, card.with_action)",
73
- input_schema: { properties: { component: { type: "string" } }, required: ["component"] }
74
- ) do |component:|
75
- doc = docs[component.to_sym]
76
- doc ? JSON.generate(doc[:signature]) : not_found("no documentation for: #{component}")
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module MCP
5
- module Plugins
6
- module Schema
7
- extend Base
8
-
9
- LOADER_KEY = :schema
10
- LOADER = SchemaLoader
11
-
12
- STATIC_RESOURCES = [
13
- ::MCP::Resource.new(
14
- uri: "schema://components",
15
- name: "components-index",
16
- description: "Index of all stimulus-plumbers component theme keys",
17
- mime_type: "application/json"
18
- ),
19
- ::MCP::Resource.new(
20
- uri: "schema://icons",
21
- name: "icons",
22
- description: "All available icon names from the active theme registry",
23
- mime_type: "application/json"
24
- ),
25
- ::MCP::Resource.new(
26
- uri: "schema://stimulus",
27
- name: "stimulus-wiring",
28
- description: "Mapping from Rails component name to the Stimulus controller identifiers it requires",
29
- mime_type: "application/json"
30
- )
31
- ].freeze
32
-
33
- DYNAMIC_RESOURCE_TEMPLATES = [
34
- ::MCP::ResourceTemplate.new(
35
- uri_template: "schema://components/{name}",
36
- name: "component-schema",
37
- description: "Params, valid values, defaults, and required controllers for a component",
38
- mime_type: "application/json"
39
- )
40
- ].freeze
41
-
42
- def self.read(uri, store)
43
- schema = store[:schema]
44
-
45
- case uri
46
- when "schema://components"
47
- json_resource(uri, schema[:components].keys)
48
- when "schema://icons"
49
- json_resource(uri, schema[:icons])
50
- when "schema://stimulus"
51
- json_resource(uri, schema[:stimulus])
52
- when %r{\Aschema://components/(.+)\z}
53
- key = Regexp.last_match(1).to_sym
54
- json_resource(uri, component_data(schema, key) || { error: "unknown component: #{key}" })
55
- end
56
- end
57
-
58
- def self.register_tools(server, store)
59
- schema = store[:schema]
60
- data_by_component = component_data_map(schema)
61
-
62
- text_tool(server, name: "list_components", description: "Lists all stimulus-plumbers component theme keys") do
63
- JSON.generate(schema[:components].keys)
64
- end
65
-
66
- text_tool(
67
- server,
68
- name: "get_component_schema",
69
- description: "Returns themed params (e.g. type/variant/size) with valid values, defaults, and " \
70
- "required Stimulus controllers. For the full helper surface (icon options, slots) " \
71
- "use get_helper_signature",
72
- input_schema: { properties: { component: { type: "string" } }, required: ["component"] }
73
- ) do |component:|
74
- data = data_by_component[component.to_sym]
75
- data ? JSON.generate(data) : not_found("unknown component: #{component}")
76
- end
77
-
78
- text_tool(
79
- server,
80
- name: "get_field_types",
81
- description: "Returns valid as: values for a form builder method",
82
- input_schema: {
83
- properties: { builder_method: { type: "string", enum: %w[field collection_field choice] } },
84
- required: ["builder_method"]
85
- }
86
- ) do |builder_method:|
87
- values = schema[:field_as][builder_method.to_sym]
88
- values ? JSON.generate(values) : not_found("unknown builder_method: #{builder_method}")
89
- end
90
- end
91
-
92
- # Resolve component_data at module scope; define_tool blocks run in another context.
93
- def self.component_data_map(schema)
94
- keys = (schema[:components].keys + schema[:stimulus].keys).uniq
95
- keys.to_h { |key| [key, component_data(schema, key)] }
96
- end
97
-
98
- def self.component_data(schema, key)
99
- params = schema[:components][key]
100
- wiring = schema[:stimulus]
101
- return nil unless params || wiring.key?(key)
102
-
103
- (params || {}).merge(controllers: wiring[key] || [])
104
- end
105
-
106
- private_class_method :component_data_map, :component_data
107
- end
108
- end
109
- end
110
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module MCP
5
- module Plugins
6
- module Stimulus
7
- extend Base
8
-
9
- LOADER_KEY = :stimulus
10
- LOADER = StimulusManifest
11
-
12
- STATIC_RESOURCES = [
13
- ::MCP::Resource.new(
14
- uri: "stimulus://controllers",
15
- name: "stimulus-controllers-index",
16
- description: "Index of all Stimulus controller identifiers in @stimulus-plumbers/controllers",
17
- mime_type: "application/json"
18
- )
19
- ].freeze
20
-
21
- DYNAMIC_RESOURCE_TEMPLATES = [
22
- ::MCP::ResourceTemplate.new(
23
- uri_template: "stimulus://controllers/{identifier}",
24
- name: "stimulus-controller-schema",
25
- description: "Targets, values, outlets, and classes for a Stimulus controller",
26
- mime_type: "application/json"
27
- )
28
- ].freeze
29
-
30
- def self.read(uri, store)
31
- controllers = store[:stimulus]
32
-
33
- case uri
34
- when "stimulus://controllers"
35
- json_resource(uri, controllers.keys)
36
- when %r{\Astimulus://controllers/(.+)\z}
37
- identifier = Regexp.last_match(1)
38
- json_resource(uri, controllers[identifier] || { error: "unknown controller: #{identifier}" })
39
- end
40
- end
41
-
42
- def self.register_tools(server, store)
43
- controllers = store[:stimulus]
44
-
45
- text_tool(
46
- server,
47
- name: "list_controllers",
48
- description: "Lists all Stimulus controller identifiers provided by @stimulus-plumbers/controllers"
49
- ) do
50
- JSON.generate(controllers.keys)
51
- end
52
-
53
- text_tool(
54
- server,
55
- name: "get_controller_schema",
56
- description: "Returns targets, values (with types and defaults), outlets, and classes for a Stimulus controller",
57
- input_schema: { properties: { controller: { type: "string" } }, required: ["controller"] }
58
- ) do |controller:|
59
- data = controllers[controller]
60
- data ? JSON.generate(data) : not_found("unknown controller: #{controller}")
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module MCP
5
- module Plugins
6
- module Theme
7
- extend Base
8
-
9
- LOADER_KEY = :theme
10
- LOADER = ThemeLoader
11
-
12
- STATIC_RESOURCES = [
13
- ::MCP::Resource.new(
14
- uri: "theme://base",
15
- name: "theme-base",
16
- description: "Guide for implementing a custom stimulus-plumbers theme: method convention, return format",
17
- mime_type: "text/markdown"
18
- ),
19
- ::MCP::Resource.new(
20
- uri: "theme://components",
21
- name: "theme-components-index",
22
- description: "Index of all component keys that can be implemented in a custom theme",
23
- mime_type: "application/json"
24
- )
25
- ].freeze
26
-
27
- DYNAMIC_RESOURCE_TEMPLATES = [
28
- ::MCP::ResourceTemplate.new(
29
- uri_template: "theme://components/{name}",
30
- name: "theme-component-interface",
31
- description: "Method name, param signature, and return contract for implementing a component in a custom theme",
32
- mime_type: "application/json"
33
- )
34
- ].freeze
35
-
36
- def self.read(uri, store)
37
- theme = store[:theme]
38
-
39
- case uri
40
- when "theme://base"
41
- text_resource(uri, "text/markdown", theme[:base_doc])
42
- when "theme://components"
43
- json_resource(uri, theme[:components].keys)
44
- when %r{\Atheme://components/(.+)\z}
45
- key = Regexp.last_match(1).to_sym
46
- json_resource(uri, theme[:components][key] || { error: "unknown component: #{key}" })
47
- end
48
- end
49
-
50
- def self.register_tools(server, store)
51
- theme = store[:theme]
52
-
53
- text_tool(
54
- server,
55
- name: "get_theme_interface",
56
- description: "Returns the method name, param signature, and return contract for a custom theme component",
57
- input_schema: { properties: { component: { type: "string" } }, required: ["component"] }
58
- ) do |component:|
59
- data = theme[:components][component.to_sym]
60
- data ? JSON.generate(data) : not_found("unknown component: #{component}")
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end