administrate-mcp 0.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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +61 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +267 -0
  5. data/app/controllers/administrate/mcp/json_rpc_controller.rb +83 -0
  6. data/app/controllers/administrate/mcp/o_auth_controller.rb +199 -0
  7. data/app/lib/administrate/mcp/actions.rb +170 -0
  8. data/app/lib/administrate/mcp/admin_dashboard_tool.rb +114 -0
  9. data/app/lib/administrate/mcp/authentication.rb +146 -0
  10. data/app/lib/administrate/mcp/base_tool.rb +93 -0
  11. data/app/lib/administrate/mcp/clean_old_feedbacks.rb +26 -0
  12. data/app/lib/administrate/mcp/dashboard_registry.rb +102 -0
  13. data/app/lib/administrate/mcp/fast_search.rb +47 -0
  14. data/app/lib/administrate/mcp/field_serializer.rb +284 -0
  15. data/app/lib/administrate/mcp/o_auth_service.rb +103 -0
  16. data/app/lib/administrate/mcp/report_improvement.rb +58 -0
  17. data/app/lib/administrate/mcp/server_builder.rb +70 -0
  18. data/app/lib/administrate/mcp/tools/admin_resource_list.rb +194 -0
  19. data/app/lib/administrate/mcp/tools/admin_resource_list_resources.rb +107 -0
  20. data/app/lib/administrate/mcp/tools/admin_resource_show.rb +130 -0
  21. data/app/lib/administrate/mcp/tools/report_improvement.rb +43 -0
  22. data/app/lib/administrate/mcp/tools/sidekiq_retries.rb +50 -0
  23. data/app/lib/administrate/mcp/tools/sidekiq_stats.rb +75 -0
  24. data/app/models/administrate/mcp/api_key.rb +59 -0
  25. data/app/models/administrate/mcp/application_record.rb +23 -0
  26. data/app/models/administrate/mcp/feedback.rb +20 -0
  27. data/app/models/administrate/mcp/o_auth_access_grant.rb +76 -0
  28. data/app/models/administrate/mcp/o_auth_access_token.rb +84 -0
  29. data/app/models/administrate/mcp/o_auth_application.rb +64 -0
  30. data/app/views/administrate/mcp/o_auth/authorize.html.erb +63 -0
  31. data/config/routes.rb +6 -0
  32. data/db/migrate/20260101000001_create_administrate_model_context_protocol_api_keys.rb +20 -0
  33. data/db/migrate/20260101000002_create_administrate_model_context_protocol_feedbacks.rb +19 -0
  34. data/db/migrate/20260101000003_create_administrate_model_context_protocol_authorization_tables.rb +52 -0
  35. data/docs/admin-integration.md +56 -0
  36. data/docs/authentication.md +116 -0
  37. data/docs/configuration.md +220 -0
  38. data/docs/dashboards.md +50 -0
  39. data/docs/development.md +19 -0
  40. data/docs/oauth.md +54 -0
  41. data/docs/routes.md +30 -0
  42. data/lib/administrate/mcp/authorization/base.rb +45 -0
  43. data/lib/administrate/mcp/authorization/permissive.rb +13 -0
  44. data/lib/administrate/mcp/authorization/pundit.rb +32 -0
  45. data/lib/administrate/mcp/cloudflare_access.rb +140 -0
  46. data/lib/administrate/mcp/configuration.rb +156 -0
  47. data/lib/administrate/mcp/dashboard_extension.rb +25 -0
  48. data/lib/administrate/mcp/engine.rb +21 -0
  49. data/lib/administrate/mcp/errors.rb +21 -0
  50. data/lib/administrate/mcp/loopback_uri.rb +18 -0
  51. data/lib/administrate/mcp/rack_attack.rb +39 -0
  52. data/lib/administrate/mcp/routes.rb +48 -0
  53. data/lib/administrate/mcp/version.rb +7 -0
  54. data/lib/administrate/mcp.rb +35 -0
  55. data/lib/administrate-mcp.rb +3 -0
  56. metadata +160 -0
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'administrate/mcp/errors'
4
+ require 'administrate/mcp/version'
5
+ require 'administrate/mcp/authorization/base'
6
+ require 'administrate/mcp/authorization/permissive'
7
+ require 'administrate/mcp/authorization/pundit'
8
+
9
+ module Administrate
10
+ module MCP
11
+ # Everything a host application supplies to the engine. Every entry has a default that works
12
+ # without the host, except the origins and `current_admin`, which the OAuth flow needs.
13
+ class Configuration
14
+ SKIPPED_FIELD_CLASSES = ['Administrate::Field::Password'].freeze
15
+
16
+ HAS_MANY_FIELD_CLASSES = ['Administrate::Field::HasMany'].freeze
17
+
18
+ FIELD_SERIALIZERS = {
19
+ 'Administrate::Field::HasMany' => :has_many,
20
+ 'Administrate::Field::Polymorphic' => :polymorphic,
21
+ 'Administrate::Field::BelongsTo' => :belongs_to,
22
+ 'Administrate::Field::HasOne' => :belongs_to,
23
+ 'Administrate::Field::DateTime' => :datetime,
24
+ 'Administrate::Field::Date' => :datetime,
25
+ 'Administrate::Field::Time' => :datetime,
26
+ 'Administrate::Field::Enum' => :enum,
27
+ 'Administrate::Field::Select' => :enum,
28
+ 'Administrate::Field::Shrine' => :attachment,
29
+ 'Administrate::Field::String' => :scalar,
30
+ 'Administrate::Field::Text' => :scalar,
31
+ 'Administrate::Field::Email' => :scalar,
32
+ 'Administrate::Field::Url' => :scalar,
33
+ 'Administrate::Field::Number' => :scalar,
34
+ 'Administrate::Field::Boolean' => :scalar
35
+ }.freeze
36
+
37
+ attr_accessor :server_name,
38
+ :server_version,
39
+ :issuer,
40
+ :admin_origin,
41
+ :current_admin,
42
+ :admin_active,
43
+ :identity_fallback,
44
+ :oauth,
45
+ :sign_in,
46
+ :admin_class_name,
47
+ :authorization,
48
+ :default_required_roles,
49
+ :tool_paths,
50
+ :dashboard_paths,
51
+ :instrument,
52
+ :on_tool_call,
53
+ :on_feedback,
54
+ :on_error,
55
+ :allow_localhost_redirects,
56
+ :default_client_name,
57
+ :sidekiq_stats_provider,
58
+ :admin_route_namespace,
59
+ :admin_url_options,
60
+ :skipped_field_classes,
61
+ :has_many_field_classes,
62
+ :field_serializers
63
+
64
+ attr_reader :api_key_token_prefix
65
+
66
+ def initialize
67
+ assign_identity
68
+ assign_hooks
69
+ assign_fields
70
+ end
71
+
72
+ def assign_identity
73
+ @server_name = 'administrate_mcp'
74
+ @server_version = VERSION
75
+ @issuer = nil
76
+ @admin_origin = nil
77
+ @current_admin = ->(_controller) {}
78
+ @admin_active = ->(_admin) { true }
79
+ @identity_fallback = ->(_request) {}
80
+ @oauth = true
81
+ @sign_in = nil
82
+ @admin_class_name = 'Administrator'
83
+ @authorization = default_authorization
84
+ @default_required_roles = []
85
+ @tool_paths = []
86
+ @dashboard_paths = nil
87
+ end
88
+
89
+ def assign_hooks
90
+ @instrument = ->(tool_name:, admin:, &block) { block.call } # rubocop:disable Lint/UnusedBlockArgument
91
+ @on_tool_call = ->(tool_name:, admin:, arguments:, scopes:) {}
92
+ @on_feedback = ->(feedback) {}
93
+ @on_error = ->(exception) {}
94
+ @allow_localhost_redirects = true
95
+ @api_key_token_prefix = 'amcp_'
96
+ @default_client_name = 'MCP Client'
97
+ @sidekiq_stats_provider = nil
98
+ end
99
+
100
+ def assign_fields
101
+ @admin_route_namespace = :admin
102
+ @admin_url_options = {}
103
+ @skipped_field_classes = SKIPPED_FIELD_CLASSES.dup
104
+ @has_many_field_classes = HAS_MANY_FIELD_CLASSES.dup
105
+ @field_serializers = FIELD_SERIALIZERS.dup
106
+ end
107
+
108
+ # The prefix is what tells a bearer token apart from an OAuth token, so a blank one would send
109
+ # every OAuth token down the API key path.
110
+ def api_key_token_prefix=(prefix)
111
+ raise ArgumentError, 'api_key_token_prefix cannot be blank' if prefix.blank?
112
+
113
+ @api_key_token_prefix = prefix.to_s
114
+ end
115
+
116
+ def register_field(class_name, as: nil, &serializer)
117
+ @field_serializers = @field_serializers.merge(class_name.to_s => as || serializer)
118
+ self
119
+ end
120
+
121
+ def skip_field(*class_names)
122
+ @skipped_field_classes |= class_names.map(&:to_s)
123
+ self
124
+ end
125
+
126
+ def register_has_many_field(*class_names)
127
+ @has_many_field_classes |= class_names.map(&:to_s)
128
+ self
129
+ end
130
+
131
+ def issuer_for(request = nil)
132
+ resolve_origin(@issuer, request)
133
+ end
134
+
135
+ def admin_origin_for(request = nil)
136
+ resolve_origin(@admin_origin, request)
137
+ end
138
+
139
+ def dashboard_directories
140
+ Array(@dashboard_paths.presence || Rails.root.join('app/dashboards')).map(&:to_s)
141
+ end
142
+
143
+ private
144
+
145
+ def resolve_origin(origin, request)
146
+ return origin.arity.zero? ? origin.call : origin.call(request) if origin.respond_to?(:call)
147
+
148
+ origin.to_s
149
+ end
150
+
151
+ def default_authorization
152
+ defined?(::Pundit) ? Authorization::Pundit.new : Authorization::Permissive.new
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'administrate/base_dashboard'
4
+
5
+ module Administrate
6
+ module MCP
7
+ # Adds the MCP declarations to every Administrate dashboard: the `mcp_action` macro and the
8
+ # `mcp_action_specs` store it writes to. `MCP_DESCRIPTION` and `MCP_BASE_SCOPE` stay plain
9
+ # constants, read by the registry.
10
+ module DashboardExtension
11
+ module ClassMethods
12
+ def mcp_action(name, **, &)
13
+ self.mcp_action_specs = [*mcp_action_specs, Administrate::MCP::Actions.build_spec(name, **, &)]
14
+ end
15
+ end
16
+
17
+ def self.install!
18
+ return if Administrate::BaseDashboard.respond_to?(:mcp_action_specs)
19
+
20
+ Administrate::BaseDashboard.class_attribute :mcp_action_specs, default: []
21
+ Administrate::BaseDashboard.singleton_class.prepend(ClassMethods)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Administrate
4
+ module MCP
5
+ # Mounts the models, controllers, tools and dashboard DSL into the host application.
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace Administrate::MCP
8
+
9
+ initializer 'administrate_mcp.inflection', before: :set_autoload_paths do
10
+ Rails.autoloaders.each { |loader| loader.inflector.inflect('mcp' => 'MCP') }
11
+ end
12
+
13
+ config.to_prepare do
14
+ require 'administrate/mcp/dashboard_extension'
15
+ Administrate::MCP::DashboardExtension.install!
16
+ Administrate::MCP::DashboardRegistry.reset!
17
+ Administrate::MCP::Actions.reset!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Administrate
4
+ module MCP
5
+ class Error < StandardError
6
+ end
7
+
8
+ # Raised when the caller may not perform the requested action on the resource.
9
+ class UnauthorizedError < Error
10
+ end
11
+
12
+ # Raised when the host asked for something the configuration cannot deliver.
13
+ class ConfigurationError < Error
14
+ end
15
+
16
+ # Raised when the caller passes an argument the resource cannot honour. Surfaced verbatim so the
17
+ # caller can correct the call, rather than as an "Internal error".
18
+ class InvalidArgumentError < Error
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Administrate
4
+ module MCP
5
+ # Recognises loopback redirect targets (RFC 8252). Subdomains of `.localhost` are used by
6
+ # parallel dev checkouts and are safe. Matches on the parsed host, never a string prefix —
7
+ # `localhost.attacker.com` is a normal, attacker-controlled DNS name and is rejected.
8
+ module LoopbackUri
9
+ HOSTS = %w[localhost 127.0.0.1 ::1].freeze
10
+
11
+ def self.localhost?(host)
12
+ return false if host.blank?
13
+
14
+ HOSTS.include?(host) || host.end_with?('.localhost')
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Administrate
4
+ module MCP
5
+ # Recommended Rack::Attack throttles for the OAuth endpoints. The gem does not depend on
6
+ # rack-attack: call this from the host's own Rack::Attack initializer.
7
+ module RackAttack
8
+ THROTTLES = [
9
+ { name: 'mcp_oauth/register/ip', limit: 5, period: 60, method: 'POST', path: '/oauth/register' },
10
+ { name: 'mcp_oauth/register/ip-day', limit: 50, period: 86_400, method: 'POST', path: '/oauth/register' },
11
+ { name: 'mcp_oauth/token/ip', limit: 10, period: 60, method: 'POST', path: '/oauth/token' }
12
+ ].freeze
13
+
14
+ AUTHORIZE_THROTTLE = {
15
+ name: 'mcp_oauth/authorize/ip',
16
+ limit: 20,
17
+ period: 60,
18
+ method: 'GET',
19
+ path: Routes::AUTHORIZE_PATH
20
+ }.freeze
21
+
22
+ # `host` is the MCP origin host, or a substring of it; the authorize throttle applies on the
23
+ # admin origin and is not host-scoped.
24
+ def self.throttles(host:)
25
+ THROTTLES.each do |rule|
26
+ ::Rack::Attack.throttle(rule[:name], limit: rule[:limit], period: rule[:period]) do |req|
27
+ req.remote_ip if req.request_method == rule[:method] && req.path == rule[:path] &&
28
+ req.host.include?(host)
29
+ end
30
+ end
31
+
32
+ rule = AUTHORIZE_THROTTLE
33
+ ::Rack::Attack.throttle(rule[:name], limit: rule[:limit], period: rule[:period]) do |req|
34
+ req.remote_ip if req.request_method == rule[:method] && req.path == rule[:path]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Administrate
4
+ module MCP
5
+ # Route helpers for hosts that serve the protocol endpoints and the consent screen on different
6
+ # origins. Call them from inside the host's own `constraints` blocks. With `config.oauth` false
7
+ # only the JSON-RPC endpoint is drawn.
8
+ #
9
+ # Endpoints are Rack lambdas rather than "controller#action" strings so that resolving them
10
+ # never depends on an `MCP` acronym being registered in the host's global inflections.
11
+ module Routes
12
+ AUTHORIZE_PATH = '/mcp/oauth/authorize'
13
+
14
+ class << self
15
+ def draw_mcp_origin(mapper)
16
+ if oauth?
17
+ mapper.get '/.well-known/oauth-protected-resource', to: o_auth(:resource_metadata)
18
+ mapper.get '/.well-known/oauth-authorization-server', to: o_auth(:server_metadata)
19
+ mapper.post '/oauth/register', to: o_auth(:register)
20
+ mapper.post '/oauth/token', to: o_auth(:token)
21
+ end
22
+
23
+ mapper.match '/', to: json_rpc, via: %i[get post delete]
24
+ end
25
+
26
+ # A no-op when the OAuth server is off, so a host needs no conditional around the call.
27
+ def draw_admin_origin(mapper, path: AUTHORIZE_PATH)
28
+ return unless oauth?
29
+
30
+ mapper.get path, to: o_auth(:authorize)
31
+ mapper.post path, to: o_auth(:approve)
32
+ end
33
+
34
+ def oauth?
35
+ Administrate::MCP.config.oauth
36
+ end
37
+
38
+ def json_rpc
39
+ ->(env) { Administrate::MCP::JsonRpcController.action(:handle).call(env) }
40
+ end
41
+
42
+ def o_auth(action)
43
+ ->(env) { Administrate::MCP::OAuthController.action(action).call(env) }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Administrate
4
+ module MCP
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mcp'
4
+ require 'administrate'
5
+
6
+ require 'administrate/mcp/version'
7
+ require 'administrate/mcp/errors'
8
+ require 'administrate/mcp/loopback_uri'
9
+ require 'administrate/mcp/configuration'
10
+ require 'administrate/mcp/routes'
11
+ require 'administrate/mcp/rack_attack'
12
+ require 'administrate/mcp/cloudflare_access'
13
+ require 'administrate/mcp/engine'
14
+
15
+ module Administrate
16
+ # Model Context Protocol server for Administrate dashboards.
17
+ module MCP
18
+ def self.table_name_prefix
19
+ 'administrate_mcp_'
20
+ end
21
+
22
+ def self.config
23
+ @config ||= Configuration.new
24
+ end
25
+
26
+ def self.configure
27
+ yield(config)
28
+ config
29
+ end
30
+
31
+ def self.reset_config!
32
+ @config = Configuration.new
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'administrate/mcp'
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: administrate-mcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sorare
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: administrate
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 1.0.0.beta3
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: 1.0.0.beta3
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '2'
32
+ - !ruby/object:Gem::Dependency
33
+ name: jwt
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '2.7'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '2.7'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mcp
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '1.5'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.5'
60
+ - !ruby/object:Gem::Dependency
61
+ name: rails
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '8.1'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '8.1'
74
+ description: Rails engine exposing Administrate dashboards over the Model Context
75
+ Protocol, with API key and OAuth 2.1 authentication.
76
+ email:
77
+ - engineering@sorare.com
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - CHANGELOG.md
83
+ - LICENSE.txt
84
+ - README.md
85
+ - app/controllers/administrate/mcp/json_rpc_controller.rb
86
+ - app/controllers/administrate/mcp/o_auth_controller.rb
87
+ - app/lib/administrate/mcp/actions.rb
88
+ - app/lib/administrate/mcp/admin_dashboard_tool.rb
89
+ - app/lib/administrate/mcp/authentication.rb
90
+ - app/lib/administrate/mcp/base_tool.rb
91
+ - app/lib/administrate/mcp/clean_old_feedbacks.rb
92
+ - app/lib/administrate/mcp/dashboard_registry.rb
93
+ - app/lib/administrate/mcp/fast_search.rb
94
+ - app/lib/administrate/mcp/field_serializer.rb
95
+ - app/lib/administrate/mcp/o_auth_service.rb
96
+ - app/lib/administrate/mcp/report_improvement.rb
97
+ - app/lib/administrate/mcp/server_builder.rb
98
+ - app/lib/administrate/mcp/tools/admin_resource_list.rb
99
+ - app/lib/administrate/mcp/tools/admin_resource_list_resources.rb
100
+ - app/lib/administrate/mcp/tools/admin_resource_show.rb
101
+ - app/lib/administrate/mcp/tools/report_improvement.rb
102
+ - app/lib/administrate/mcp/tools/sidekiq_retries.rb
103
+ - app/lib/administrate/mcp/tools/sidekiq_stats.rb
104
+ - app/models/administrate/mcp/api_key.rb
105
+ - app/models/administrate/mcp/application_record.rb
106
+ - app/models/administrate/mcp/feedback.rb
107
+ - app/models/administrate/mcp/o_auth_access_grant.rb
108
+ - app/models/administrate/mcp/o_auth_access_token.rb
109
+ - app/models/administrate/mcp/o_auth_application.rb
110
+ - app/views/administrate/mcp/o_auth/authorize.html.erb
111
+ - config/routes.rb
112
+ - db/migrate/20260101000001_create_administrate_model_context_protocol_api_keys.rb
113
+ - db/migrate/20260101000002_create_administrate_model_context_protocol_feedbacks.rb
114
+ - db/migrate/20260101000003_create_administrate_model_context_protocol_authorization_tables.rb
115
+ - docs/admin-integration.md
116
+ - docs/authentication.md
117
+ - docs/configuration.md
118
+ - docs/dashboards.md
119
+ - docs/development.md
120
+ - docs/oauth.md
121
+ - docs/routes.md
122
+ - lib/administrate-mcp.rb
123
+ - lib/administrate/mcp.rb
124
+ - lib/administrate/mcp/authorization/base.rb
125
+ - lib/administrate/mcp/authorization/permissive.rb
126
+ - lib/administrate/mcp/authorization/pundit.rb
127
+ - lib/administrate/mcp/cloudflare_access.rb
128
+ - lib/administrate/mcp/configuration.rb
129
+ - lib/administrate/mcp/dashboard_extension.rb
130
+ - lib/administrate/mcp/engine.rb
131
+ - lib/administrate/mcp/errors.rb
132
+ - lib/administrate/mcp/loopback_uri.rb
133
+ - lib/administrate/mcp/rack_attack.rb
134
+ - lib/administrate/mcp/routes.rb
135
+ - lib/administrate/mcp/version.rb
136
+ homepage: https://github.com/sorare/administrate-mcp
137
+ licenses:
138
+ - MIT
139
+ metadata:
140
+ source_code_uri: https://github.com/sorare/administrate-mcp
141
+ changelog_uri: https://github.com/sorare/administrate-mcp/blob/master/CHANGELOG.md
142
+ rubygems_mfa_required: 'true'
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '3.2'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubygems_version: 4.0.16
158
+ specification_version: 4
159
+ summary: Model Context Protocol server for Administrate dashboards
160
+ test_files: []