action_mcp
Rails Engine with MCP compliant Spec.
Documentation
ActionMCP
ActionMCP is a Ruby gem focused on providing Model Context Protocol (MCP) capability to Ruby on Rails applications, specifically as a server.
ActionMCP is designed for production Rails environments and does not support STDIO transport. STDIO is not included because it is not production-ready and is only suitable for desktop or script-based use cases. Instead, ActionMCP is built for robust, network-based deployments.
The client functionality in ActionMCP is intended to connect to remote MCP servers, not to local processes via STDIO.
It offers base classes and helpers for creating MCP applications, making it easier to integrate your Ruby/Rails application with the MCP standard.
With ActionMCP, you can focus on your app's logic while it handles the boilerplate for MCP compliance.
Introduction
Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to large language models (LLMs).
Think of it as a universal interface for connecting AI assistants to external data sources and tools.
MCP allows AI systems to plug into various resources in a consistent, secure way, enabling two-way integration between your data and AI-powered applications.
This means an AI (like an LLM) can request information or actions from your application through a well-defined protocol, and your app can provide context or perform tasks for the AI in return.
ActionMCP is targeted at developers building MCP-enabled Rails applications. It simplifies the process of integrating Ruby and Rails apps with the MCP standard by providing a set of base classes and an easy-to-use server interface.
Protocol Support
ActionMCP targets the released MCP 2025-11-25 protocol. Older protocol versions and unreleased draft versions are not accepted. Core handling includes:
- JSON-RPC 2.0 transport layer
- Capability negotiation during initialization
- Error handling with proper error codes (-32601 for method not found, -32002 for consent required)
- Stateful session management with explicit termination
- Tasks, tools, prompts, resources, completion, logging, sampling, roots, and elicitation message handling
The built-in Streamable HTTP endpoint returns one `application/json` message for
requests and intentionally returns HTTP 405 for GET because it does not provide
SSE streams. That behavior is allowed by the transport specification. Outbound
server requests and notifications are retained in session message storage, but
an application needs an SSE or custom transport to push those messages to a
client outside the response to an active request.
For a detailed (and entertaining) breakdown of protocol versions, features, and our design decisions, see The Hitchhiker's Guide to MCP.
*Don't Panic: The guide contains everything you need to know about surviving MCP protocol versions.*
> Note: STDIO transport is not supported in ActionMCP. This gem is focused on production-ready, network-based deployments. STDIO is only suitable for desktop or script-based experimentation and is intentionally excluded.
Instead of implementing MCP support from scratch, you can subclass and configure the provided Prompt, Tool, and ResourceTemplate classes to expose your app's functionality to LLMs.
ActionMCP handles the underlying MCP message format and routing, so you can adhere to the open standard with minimal effort.
In short, ActionMCP helps you build an MCP server (the component that exposes capabilities to AI) more quickly and with fewer mistakes.
> Client connections: The client part of ActionMCP is meant to connect to remote MCP servers only. Connecting to local processes (such as via STDIO) is not supported.
Requirements
- Ruby: 3.4.8+ or 4.0.0+
- Rails: 8.1.1+
- Database: PostgreSQL, MySQL, or SQLite3
ActionMCP is tested against Ruby 3.4.8 and 4.0.0 with Rails 8.1.1+.
Installation
To start using ActionMCP, add it to your project:
# Add gem to your Gemfile
$ bundle add actionmcp
# Install dependencies
bundle install
# Copy migrations from the engine
bin/rails action_mcp:install:migrations
# Generate base classes and configuration
bin/rails generate action_mcp:install
# Create necessary database tables
bin/rails db:migrateThe `action_mcp:install` generator will:
- Create base application classes (ApplicationGateway, ApplicationMCPTool, etc.)
- Generate the MCP configuration file (`config/mcp.yml`)
- Set up the basic directory structure for MCP components (`app/mcp/`)
Database migrations are copied separately using `bin/rails action_mcp:install:migrations`.
Core Components
ActionMCP provides three core abstractions to streamline MCP server development:
ActionMCP::Prompt
`ActionMCP::Prompt` enables you to create reusable prompt templates that can be discovered and used by LLMs. Each prompt is defined as a Ruby class that inherits from `ApplicationMCPPrompt`.
Key features:
- Define expected arguments with descriptions and validation rules
- Build multi-step conversations with mixed content types
- Support for text, images, audio, and resource attachments
- Add messages with different roles (user/assistant)
Example:
class AnalyzeCodePrompt 1000
report_error("Warning: Sum exceeds recommended limit")
end
# Or even images
render(image: generate_visualization(a, b), mime_type: "image/png")
end
private
def generate_visualization(a, b)
# Implementation to create a visualization as base64
end
endConsent Management
For tools that perform sensitive operations (file system access, database modifications, external API calls), you can require explicit user consent:
class FileSystemTool **Note:** Not all MCP clients support both resource endpoints. Claude Code (as of v2.1.50) only calls `resources/list`, and Codex stubs resource methods entirely. Implement `self.list` on your templates to ensure resources are visible to all clients. Crush and VS Code support both endpoints.
**Example:**class ProductResourceTemplate π‘ Pro Tip: Start with the component-specific guides (TOOLS.MD, PROMPTS.MD, RESOURCE_TEMPLATES.md) for hands-on development, then reference the Hitchhiker's Guide for protocol details and CLIENTUSAGE.MD for integration patterns.
Configuration
ActionMCP is configured via `config.action_mcp` in your Rails application.
By default, the name is set to your application's name and the version defaults to "0.0.1" unless your app has a version file.
You can override these settings in your configuration (e.g., in `config/application.rb`):
module Tron
class Application **WARNING: Do NOT mount ActionMCP::Engine in your `routes.rb`.** ActionMCP is a standalone Rack application that runs on its own port via `mcp/config.ru`. Mounting it as a Rails engine route will not work correctly.
When you use `run ActionMCP.server` in your `mcp/config.ru`, the MCP endpoint is available at the root path (`/`) by default and can be configured via `config.action_mcp.base_path`. Always use `ActionMCP.server` (not `ActionMCP::Engine` directly) β it initializes required subsystems.
### Installing ActionMCP
ActionMCP includes generators to help you set up your project quickly. The install generator creates all necessary base classes and configuration files:Install ActionMCP with base classes and configuration
bin/rails generate action_mcp:install
This will create:
- `app/mcp/prompts/application_mcp_prompt.rb` - Base prompt class
- `app/mcp/tools/application_mcp_tool.rb` - Base tool class
- `app/mcp/resource_templates/application_mcp_res_template.rb` - Base resource template class
- `app/mcp/application_gateway.rb` - Gateway for authentication
- `config/mcp.yml` - Configuration file with example settings for all environments
- `mcp/config.ru` - Standalone Rack server configuration
- `bin/mcp` - Server binstub (prefers Falcon, falls back to Puma)
> **Note:** Authentication and authorization are not included. You are responsible for securing the endpoint.
## Authentication with Gateway
ActionMCP provides a Gateway system for handling authentication. The Gateway allows you to authenticate users and make them available throughout your MCP components. For the full gateway reference including identifier classes, session persistence, profile switching, and production hardening tips, see **[GATEWAY.md](GATEWAY.md)**.
ActionMCP uses a Gateway pattern with pluggable identifiers for authentication. You can implement custom authentication strategies using session-based auth, API keys, bearer tokens, or integrate with existing authentication systems like Warden, Devise, or external OAuth providers.
> **Note:** When a Gateway is configured, it authenticates every MCP HTTP request, including `initialize`, GET, and DELETE. Authentication failures return HTTP 401 with a `WWW-Authenticate: Bearer` challenge and a JSON-RPC error body.
### Creating an ApplicationGateway
When you run the install generator, it creates an `ApplicationGateway` class:app/mcp/application_gateway.rb
class ApplicationGateway user.id, "tenant_id" => user.tenant_id }
end
end
Tools access it via `session_data["user_id"]`. See [GATEWAY.md](GATEWAY.md) for details.
### 1. Create `mcp/config.ru`
The install generator (`rails generate action_mcp:install`) creates this automatically. If you need to create it manually:Load the full Rails environment to access models, DB, Redis, etc.
require_relative "../config/environment"
$stdout.sync = true
Eager load so all tools, prompts, and resources are registered.
Rails.application.eager_load!
IMPORTANT: Use ActionMCP.server β it initializes required subsystems.
Do NOT use ActionMCP::Engine directly.
run ActionMCP.server
### 2. Start the serverbin/mcp # Uses Falcon (recommended)
bundle exec rails s -c mcp/config.ru -p 62770 # Uses Puma (fallback)
### Dealing with Middleware Conflicts
If your Rails application uses middleware that interferes with MCP server operation (like Devise, Warden, Ahoy, Rack::Cors, etc.), use `mcp_vanilla.ru` instead:mcp_vanilla.ru - A minimal Rack app with only essential middleware
This avoids conflicts with authentication, tracking, and other web-specific middleware
See the file for detailed documentation on when and why to use it
bundle exec rails s -c mcp_vanilla.ru -p 62770
Or with Falcon:
bundle exec falcon serve --bind http://127.0.0.1:62770 --config mcp_vanilla.ru
Common middleware that can cause issues:
- **Devise/Warden** - Expects cookies and sessions, throws `Devise::MissingWarden` errors
- **Ahoy** - Analytics tracking that intercepts requests
- **Rack::Attack** - Rate limiting designed for web traffic
- **Rack::Cors** - CORS headers meant for browsers
- Any middleware assuming HTML responses or cookie-based authentication
An example of a minimal `mcp_vanilla.ru` file is located in the dummy app : test/dummy/mcp_vanilla.ru.
This file is a minimal Rack application that only includes the essential middleware needed for MCP server operation, avoiding conflicts with web-specific middleware.
But remember to add any instrumentation or logging middleware you need, as the minimal setup will not include them by default.Production Deployment of MCPS0
In production, MCPS0 (the MCP server) is a standard Rack application. You can run it using any Rack-compatible server (such as Puma, Unicorn, or Passenger).
> **For best performance and concurrency, it is highly recommended to use a modern, synchronous server like Falcon**. Falcon is optimized for streaming and concurrent workloads, making it ideal for MCP servers. You can still use Puma, Unicorn, or Passenger, but Falcon will generally provide superior throughput and responsiveness for real-time and streaming use cases.
You have several main options for exposing the server:
1. Dedicated Port
Run MCPS0 on its own TCP port (commonly `62770`):
With Falcon:
bundle exec falcon serve --bind http://0.0.0.0:62770 --config mcp/config.ruWith Puma:
bundle exec rails s -c mcp/config.ru -p 62770With Passenger:
passenger start --rackup mcp/config.ru --port 62770Then, use your web server (Nginx, Apache, etc.) to reverse proxy requests to this port.
2. Unix Socket
Alternatively, you can run MCPS0 on a Unix socket for improved performance and security (especially when the web server and app server are on the same machine):
With Falcon:
bundle exec falcon serve --bind unix:/tmp/mcps0.sock mcp/config.ruWith Puma:
bundle exec puma -C config/puma.rb -b unix:///tmp/mcps0.sock -c mcp/config.ruWith Passenger:
passenger start --rackup mcp/config.ru --socket /tmp/mcps0.sockAnd configure your web server to proxy to the socket:
location /mcp/ {
proxy_pass http://unix:/tmp/mcps0.sock:;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}3. Nginx With Passenger
You can run both the main app and the MCP app using Passenger processes within Nginx.
location / {
root /path/to/current/public;
passenger_app_root /path/to/current;
passenger_enabled on;
# ... additional configuration for the main Rails app
}
location ~* ^/mcp {
root /path/to/current/public;
passenger_app_root /path/to/current;
passenger_enabled on;
passenger_startup_file mcp/config.ru;
passenger_app_group_name mcp;
}You must set the `config.action_mcp.base_path` to match the above Nginx configuration, i.e. `config.action_mcp.base_path = '/mcp'`.
Key Points:
- MCPS0 is a standalone Rack appβrun it separately from your main Rails server.
- You can expose it via a TCP port (e.g., 62770) or a Unix socket.
- Use a reverse proxy (Nginx, Apache, etc.) to route requests to MCPS0 as needed.
- This separation ensures reliability and scalability for both your main app and MCP services.
Generators
ActionMCP includes Rails generators to help you quickly set up your MCP server components.
First, install ActionMCP to create base classes and configuration:
bin/rails action_mcp:install:migrations # to copy the migrations
bin/rails generate action_mcp:installThis will create the base application classes, configuration file, authentication gateway, `mcp/config.ru` rackup file, and `bin/mcp` binstub in your app directory.
Generate a New Prompt
bin/rails generate action_mcp:prompt AnalyzeCodeGenerate a New Tool
bin/rails generate action_mcp:tool CalculateSumTesting with TestHelper
ActionMCP provides a `TestHelper` module to simplify testing of tools and prompts:
require "test_helper"
require "action_mcp/test_helper"
class ToolTest < ActiveSupport::TestCase
include ActionMCP::TestHelper
test "CalculateSumTool returns the correct sum" do
assert_mcp_tool_findable("calculate_sum")
result = execute_mcp_tool("calculate_sum", a: 5, b: 10)
assert_mcp_tool_output("15.0", result)
end
test "AnalyzeCodePrompt returns the correct analysis" do
assert_mcp_prompt_findable("analyze_code")
result = execute_mcp_prompt("analyze_code", language: "Ruby", code: "def hello; puts 'Hello, world!'; end")
assert_mcp_prompt_output("Analyzing Ruby code: def hello; puts 'Hello, world!'; end", result)
end
endThe TestHelper provides several assertion and execution methods:
Tools:
- `assert_mcp_tool_findable(name)` - Verifies a tool exists and is registered
- `execute_mcp_tool(name, **args)` - Executes a tool with arguments and asserts success
- `execute_mcp_tool_with_error(name, **args)` - Executes a tool without asserting success (for testing error cases)
- `assert_mcp_tool_output(expected, response)` - Asserts tool output matches expected content
Prompts:
- `assert_mcp_prompt_findable(name)` - Verifies a prompt exists and is registered
- `execute_mcp_prompt(name, **args)` - Executes a prompt with arguments
- `assert_mcp_prompt_output(expected, response)` - Asserts prompt output matches expected content
Resource Templates:
- `assert_mcp_resource_template_findable(name)` - Verifies a resource template exists and is registered
- `resolve_mcp_resource(uri)` - Resolves a resource URI and asserts success
- `resolve_mcp_resource_with_error(uri)` - Resolves a resource URI without asserting success (for testing error cases)
General:
- `assert_mcp_error_code(code, response)` - Asserts a specific JSON-RPC error code
Testing Resource Templates
require "test_helper"
require "action_mcp/test_helper"
class ProductResourceTest < ActiveSupport::TestCase
include ActionMCP::TestHelper
test "product template is registered" do
assert_mcp_resource_template_findable("products")
end
test "resolves a product resource by URI" do
resp = resolve_mcp_resource("ecommerce://products/1")
assert resp.success?
assert_not_empty resp.contents
assert_equal "application/json", resp.contents.first.mime_type
end
test "returns error for nonexistent product" do
resp = resolve_mcp_resource_with_error("ecommerce://products/0")
assert resp.is_error
end
endInspecting Your MCP Server
You can use the MCP Inspector to test your server implementation:
# Start your MCP server
bundle exec rails s -c mcp/config.ru -p 62770
# In another terminal, run the inspector
npx @modelcontextprotocol/inspector --url http://localhost:62770The MCP Inspector provides an interactive interface to:
- Test tool executions with custom arguments
- Validate prompt responses
- Inspect resource templates and their outputs
- Debug protocol compliance and error handling
Development Commands
ActionMCP includes several rake tasks for development and debugging:
# List all MCP components
bundle exec rails action_mcp:list
# List specific component types
bundle exec rails action_mcp:list_tools
bundle exec rails action_mcp:list_prompts
bundle exec rails action_mcp:list_resources
bundle exec rails action_mcp:list_widgets
bundle exec rails action_mcp:list_profiles
# Show configuration and statistics
bundle exec rails action_mcp:info
bundle exec rails action_mcp:stats
# Show profile configuration
bundle exec rails action_mcp:show_profile[profile_name]Linting
RuboCop runs as a git pre-commit hook on staged Ruby files. Enable it once per clone:
git config core.hooksPath .githooksError Handling and Troubleshooting
ActionMCP provides comprehensive error handling following the JSON-RPC 2.0 specification:
Error Codes
- -32601: Method not found - The requested method doesn't exist
- -32002: Consent required - Tool requires user consent to execute
- -32603: Internal error - Server encountered an unexpected error
- -32600: Invalid request - The request is malformed
Context-Aware Error Messages
Tools should return clear error messages to the LLM using the `render` method:
class MyTool < ApplicationMCPTool
def perform
# Check for error conditions and return clear messages
if some_error_condition?
report_error("Clear error message for the LLM")
return
end
# Normal processing
render(text: "Success message")
end
endCommon Issues
1. Session not found: Ensure sessions are properly created and saved in the session store
2. Tool not registered: Verify tools are properly defined and inherit from ApplicationMCPTool
3. Consent required: Grant consent using `session.grant_consent(tool_name)`
4. Middleware conflicts: Use `mcp_vanilla.ru` to avoid web-specific middleware
Debugging Tips
- Check server logs for detailed error information
- Use `bundle exec rails action_mcp:info` to verify configuration
- Test with MCP Inspector to isolate protocol issues
- Ensure proper session management in production environments
Profiles
ActionMCP supports a flexible profile system that allows you to selectively expose tools, prompts, and resources based on different usage scenarios. This is particularly useful for applications that need different MCP capabilities for different contexts (e.g., public API vs. admin interface).
Understanding Profiles
Profiles are named configurations that define:
- Which tools are available
- Which prompts are accessible
- Which resources can be accessed
- Configuration options like logging level and change notifications
By default, ActionMCP includes two profiles:
- `primary`: Exposes all tools, prompts, and resources
- `minimal`: Exposes no tools, prompts, or resources by default
Configuring Profiles
Profiles are configured via a `config/mcp.yml` file in your Rails application. If this file doesn't exist, ActionMCP will use default settings from the gem.
Example configuration:
default:
tools:
- all # Include all tools
prompts:
- all # Include all prompts
resources:
- all # Include all resources
options:
list_changed: false
logging_enabled: true
logging_level: info
resources_subscribe: false
api_only:
tools:
- calculator
- weather
prompts: [] # No prompts for API
resources:
- user_profile
options:
list_changed: false
logging_level: warn
admin:
tools:
- all
options:
logging_level: debug
list_changed: true
resources_subscribe: trueEach profile can specify:
- `tools`: Array of tool names to include (use `all` to include all tools)
- `prompts`: Array of prompt names to include (use `all` to include all prompts)
- `resources`: Array of resource names to include (use `all` to include all resources)
- `options`: Additional configuration options:
- `list_changed`: Whether to send change notifications
- `logging_enabled`: Whether to enable logging
- `logging_level`: The logging level to use
- `resources_subscribe`: Whether to enable resource subscriptions
Switching Profiles
You can switch between profiles programmatically in your code:
# Permanently switch to a different profile
ActionMCP.configuration.use_profile(:only_tools) # Switch to a profile named "only_tools"
# Temporarily use a profile for a specific operation
ActionMCP.with_profile(:minimal) do
# Code here uses the minimal profile
# After the block, reverts to the previous profile
endThis makes it easy to control which MCP capabilities are available in different contexts of your application.
Inspecting Profiles
ActionMCP includes rake tasks to help you manage and inspect your profiles:
# List all available profiles with their configurations
bin/rails action_mcp:list_profiles
# Show detailed information about a specific profile
bin/rails action_mcp:show_profile[admin]
# List all tools, prompts, resources, UI widgets, and profiles
bin/rails action_mcp:listThe profile inspection tasks will highlight any issues, such as configured tools, prompts, or resources that don't actually exist in your application.
Use Cases
Profiles are particularly useful for:
1. Multi-tenant applications: Use different profiles for different customer tiers with Dorp or other gems
2. Access control: Create profiles for different user roles (admin, staff, public)
3. Performance optimization: Use a minimal profile for high-traffic endpoints
4. Testing environments: Use specific test profiles in your test environment
5. Progressive enhancement: Start with a minimal profile and gradually add capabilities
By leveraging profiles, you can maintain a single ActionMCP codebase while providing tailored MCP capabilities for different contexts.
Client Usage
ActionMCP includes a client for connecting to remote MCP servers. The client handles session management, protocol negotiation, and provides a simple API for interacting with MCP servers.
For comprehensive client documentation, including examples, session management, transport configuration, and API usage, see CLIENTUSAGE.md.
Production Considerations
Security
- Never expose sensitive data through MCP components
- Use authentication via Gateway for production deployments
- Implement proper authorization in your tools and prompts
- Validate all inputs using property definitions and Rails validations
- Use consent management for sensitive operations
- Protect against DNS rebinding: ActionMCP validates the `Origin` header on every request without trusting the request's `Host` header. Loopback origins (`localhost`, `127.0.0.1`, and `::1`) are allowed by default. Other browser origins receive HTTP 403 unless their host is explicitly listed in `allowed_origins`. Non-browser clients that omit `Origin` are unaffected:
# config/initializers/action_mcp.rb
ActionMCP.configure do |config|
config.allowed_origins = ["app.example.com", "api.example.com"]
endFor defence-in-depth, also configure Rails' `ActionDispatch::HostAuthorization` to restrict which `Host` headers are accepted (a separate check against host-header injection):
# config/environments/production.rb
config.hosts = ["api.example.com"]Performance
- Configure appropriate thread pools for high-traffic scenarios
- Use Redis or SolidMCP for production pub/sub
- Choose ActiveRecord session store for session persistence
- Monitor session cleanup to prevent memory leaks
- Use profiles to limit exposed capabilities
Monitoring
- Enable logging and configure appropriate log levels
- Monitor session statistics using `action_mcp:stats`
- Track tool usage and performance metrics
- Set up alerts for error rates and response times
Deployment
- Use Falcon for optimal performance with streaming workloads
- Deploy on dedicated ports or Unix sockets
- Use reverse proxies (Nginx, Apache) for SSL termination
- Implement health checks for your MCP endpoints
- Use `mcp_vanilla.ru` to avoid middleware conflicts
Frequently asked questions
What is action_mcp?
action_mcp is Rails Engine with MCP compliant Spec.
How do I install action_mcp?
Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.
Is action_mcp open source?
Yes β it is hosted on GitHub at https://github.com/seuros/action_mcp and has 76 stars.
Related MCP tools
A text-based user interface (TUI) client for interacting with MCP servers using Ollama. Features include multi-server, dynamic model switching, streaming res...
Expose your FastAPI endpoints as Model Context Protocol (MCP) tools, with Auth! Python-based implementation. Trusted by 11000+ developers.
An MCP client for Neovim that seamlessly integrates MCP servers into your editing workflow with an intuitive interface for managing, testing, and using MCP s...
A Unified MCP Server Management App TypeScript-based implementation. Trusted by 800+ developers. Trusted by 800+ developers.
Plugin for JADX to integrate MCP server Java-based implementation. Trusted by 600+ developers. Trusted by 600+ developers.
π€ The Semantic Engine for Model Context Protocol(MCP) Clients and AIΒ Agents π₯ Java-based implementation.
Run your own MCP server? See who uses it and what to fix.
Measure it with TrackMCP