ActiveadminMcp
Status: Experimental / work in progress
activeadmin_mcp turns the resources you have already registered with
ActiveAdmin into a
Model Context Protocol (MCP) server, so AI
assistants such as Claude Code can list, query, and update your admin data —
while respecting the exact same forms, permitted parameters, and authorization
rules as your ActiveAdmin UI.
The server is a Rails engine mounted inside your application (by default at
/mcp) and speaks MCP over HTTP (JSON-RPC 2.0, protocol revision
2025-06-18).
How it works
- Nothing new to describe. The engine reads your existing ActiveAdmin registrations, so the resources, attributes, and permitted fields it exposes are the ones you have already configured.
- Queries use Ransack. The
querytool passes its arguments straight to Ransack, the same search library ActiveAdmin uses for filtering. - Writes go through ActiveAdmin. The
updatetool only writes fields allowed by the resource'spermit_params, refuses resources that don't register theupdateaction, and runs every change through your authorization adapter (CanCanCan, Pundit, etc.) as the authenticated MCP user. - Authentication is optional but built in. Enable Bearer-token auth and the installer adds an "MCP Tokens" management page to your ActiveAdmin panel.
Requirements
- Ruby >= 3.0
- Rails >= 6.1
- ActiveAdmin >= 2.0
Installation
Add the gem to your Gemfile:
gem "activeadmin_mcp"
Install it and run the generator:
bundle install
rails generate activeadmin_mcp:install
The MCP server is mounted at /mcp automatically. That's all you need for a
read/query setup without authentication.
Available tools
| Tool | Description |
|---|---|
list_resources |
List every ActiveAdmin resource along with its attributes. |
query |
Query a resource using Ransack syntax (limit defaults to 25, capped at 100). |
update |
Update an existing record, honouring ActiveAdmin's permitted params and authorization. |
Query examples
Query users whose email contains "example.com"
→ query(resource: "User", q: { email_cont: "example.com" })
Find active posts created since the start of the month
→ query(resource: "Post", q: { status_eq: "active", created_at_gt: "2026-08-01" })
Updating records
Update a user's name
→ update(resource: "User", id: 42, attributes: { name: "New name" })
The update tool applies the same rules as the ActiveAdmin UI:
- Editable resources only — resources registered without the
updateaction (e.g.actions :index, :show) are refused. - Authorization — the change runs through the resource namespace's authorization adapter for the authenticated MCP user, so it can only update what that user is allowed to update in admin.
- Permitted fields only — attributes are filtered through the resource's
permit_params; fields the admin form doesn't accept are silently dropped.
Connecting a client
activeadmin_mcp has been tested with Claude Code (Anthropic) over the
HTTP transport.
claude mcp add --transport http my-app http://localhost:3000/mcp/
Or add it to your .mcp.json:
{
"mcpServers": {
"my-app": {
"type": "http",
"url": "http://localhost:3000/mcp/"
}
}
}
Authentication
To protect the MCP endpoint with API-token authentication, run the installer
with the devise_token strategy and migrate:
rails generate activeadmin_mcp:install --auth devise_token
rails db:migrate
This will:
- Create the
mcp_api_tokenstable. - Add an "MCP Tokens" page to your ActiveAdmin panel (
app/admin/by default). - Enable token authentication in the initializer.
Generator options
| Option | Default | Description |
|---|---|---|
--auth |
none | Authentication method to use (e.g. devise_token). |
--admin-path |
app/admin |
Directory for the ActiveAdmin page file. |
Example with a custom admin path:
rails generate activeadmin_mcp:install --auth devise_token --admin-path app/admin/mcp
Managing tokens
- Log in to your ActiveAdmin panel (
/admin). - Navigate to MCP Tokens (or Settings > MCP Tokens if you set a
menu_parent). - Create a token and copy it — it is only shown once.
Connecting with a token
claude mcp add --transport http \
--header 'Authorization: Bearer YOUR_TOKEN' \
my-app http://localhost:3000/mcp/
Or in .mcp.json:
{
"mcpServers": {
"my-app": {
"type": "http",
"url": "http://localhost:3000/mcp/",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}
Custom auth header
If your application sits behind a reverse proxy that strips the standard
Authorization header (e.g. AWS Verified Access), configure a custom header
name and pass the token through it instead:
ActiveadminMcp.configure do |config|
config.authentication_method = :devise_token
config.auth_header_name = "X-MCP-Authorization"
end
{
"mcpServers": {
"my-app": {
"type": "http",
"url": "https://admin.example.com/admin/mcp/",
"headers": {
"X-MCP-Authorization": "Bearer YOUR_TOKEN"
}
}
}
}
Configuration
The generator writes an initializer to
config/initializers/activeadmin_mcp.rb:
ActiveadminMcp.configure do |config|
config.authentication_method = :devise_token
config.user_class = "User" # your Devise model class
end
| Option | Default | Description |
|---|---|---|
authentication_method |
nil |
Set to :devise_token to enable Bearer-token auth. |
user_class |
"User" |
The Devise model class name. |
current_user_method |
:current_admin_user |
Controller method returning the current user. |
menu_parent |
nil |
Parent menu for the MCP Tokens page (e.g. "Settings"). |
mount_path |
"/mcp" |
Path where the MCP server is mounted. |
mount_strategy |
:prepend |
Route mounting strategy: :prepend, :append, or :none. |
auth_header_name |
"Authorization" |
HTTP header to read the Bearer token from. |
Route mounting
By default the engine prepends its route to the top of your application's route table. This suits most setups, but can cause problems when your admin routes use constraints (e.g. hostname-based routing), because a prepended mount sits outside any constraint blocks.
| Strategy | Behaviour |
|---|---|
:prepend |
(default) Mounts at the top of the route table via routes.prepend. |
:append |
Mounts at the bottom of the route table via routes.append. |
:none |
Skips automatic mounting — you mount the engine yourself. |
To mount inside a constraint block, set mount_strategy to :none and mount
the engine manually:
# config/initializers/activeadmin_mcp.rb
ActiveadminMcp.configure do |config|
config.mount_path = "/admin/mcp"
config.mount_strategy = :none
end
# config/routes.rb (or a drawn route file)
constraints AdminConstraint.new do
ActiveAdmin.routes(self)
mount ActiveadminMcp::Engine => ActiveadminMcp.config.mount_path
end
Development
After checking out the repo, install dependencies and run the test suite:
bundle install
bundle exec rspec
Contributing
Bug reports and pull requests are welcome on GitHub.
Credits
This project was forked from betacraft/active_admin_mcp, originally created by harunkumars, and has been extended from there.
License
Released under the MIT License.