A Rails application template that integrates the Model Context Protocol (MCP) with Ruby on Rails. During setup, the template asks whether to add OAuth 2.
Overview
A Rails application template that integrates the Model Context Protocol (MCP) with Ruby on Rails. During setup, the template asks whether to add OAuth 2.
README
MCP on Rails (with optional OAuth)
A Rails application template that integrates the Model Context Protocol (MCP) with Ruby on Rails. During setup, the template asks whether to add OAuth 2.1 protection using Devise and Doorkeeper — so one template supports both plain MCP and fully authenticated setups.
Quick Start
git clone https://github.com/pstrzalk/mcp-on-rails.git
rails new myapp -m mcp-on-rails/mcp
cd myapp
rails db:migrate
rails server
The template will prompt:
Add Devise + Doorkeeper OAuth 2.1 authentication? (y/n)
Answer n for a plain MCP server or y for full OAuth protection.
Adding the MCP template to an existing Rails application
You may just as easily apply this template to an existing Rails app.
git clone https://github.com/pstrzalk/mcp-on-rails.git
cd your-project/
rails app:template LOCATION=../mcp-on-rails/mcp
Plain MCP (answer “n”)
Creates a Rails app with an open MCP server — no authentication required.
What you get
mcpgem added to GemfileMcpControllerat/mcp— inheritsActionController::API, handles MCP protocol- MCP routes —
POST /mcp,GET /mcp - Scaffold hook —
rails generate scaffoldautomatically creates MCP tools - Custom tool generator —
rails generate mcp_tool ToolName field:type - Custom prompt generator —
rails generate mcp_prompt PromptName arg arg:required to_mcp_responseon ApplicationRecord for consistent text formattingrake mcp:toolsandrake mcp:promptsto list all registered tools and prompts (usemcp:tools:verbose/mcp:prompts:verbosefor full details)
Usage
rails new myapp -m mcp-on-rails/mcp # answer n
cd myapp && rails db:migrate
rails generate scaffold Post title:string body:text
rails db:migrate
rails server
Test it:
# MCP initialize
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# List tools
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
Project structure (plain mode)
app/
├── controllers/
│ └── mcp_controller.rb # Open MCP endpoint (no auth)
├── models/
│ └── application_record.rb # Extended with to_mcp_response
├── prompts/ # MCP prompts (created via generator)
└── tools/ # MCP tools (auto-generated per scaffold)
config/
├── initializers/
│ └── mcp.rb # MCP tool and prompt autoloading
└── routes.rb # MCP routes
lib/
├── generators/ # MCP tool and prompt generators
└── tasks/
└── mcp.rake # rake mcp:tools, rake mcp:prompts (and verbose variants)
Connecting AI assistants (plain mode)
{
"name": "my-rails-app",
"type": "StreamableHttp",
"url": "http://localhost:3000/mcp"
}
No authentication needed — the /mcp endpoint is open.
OAuth MCP (answer “y”)
Creates a Rails app with an OAuth 2.1-protected MCP server, including PKCE, dynamic client registration, and resource indicator support — everything needed for MCP’s OAuth authorization flow.
What you get
Everything from plain mode, plus:
devise+doorkeepergems added to Gemfile- Devise user authentication (sign-up, sign-in, password reset)
- Doorkeeper OAuth 2.1 provider with PKCE enforcement (S256)
McpControllerprotected bydoorkeeper_authorize!with token audience validation (RFC 8707)- Dynamic client registration at
POST /oauth/register(RFC 7591) - Protected resource metadata at
GET /.well-known/oauth-protected-resource(RFC 9728) - Authorization server metadata at
GET /.well-known/oauth-authorization-server(RFC 8414) - Resource indicators — tokens are scoped to the
/mcpresource (RFC 8707)
Usage
rails new myapp -m mcp-on-rails/mcp # answer y
cd myapp && rails db:migrate
rails generate scaffold Post title:string body:text
rails db:migrate
rails server
The /mcp endpoint now requires a Bearer token — unauthenticated requests return 401.
OAuth flow
The full authorization flow follows MCP’s OAuth specification:
- Discovery — Client fetches
GET /.well-known/oauth-protected-resourceto find the authorization server - Server metadata — Client fetches
GET /.well-known/oauth-authorization-serverfor endpoints and capabilities - Client registration —
POST /oauth/registerwith client metadata (RFC 7591) - Authorization —
GET /oauth/authorizewith PKCEcode_challenge(S256) andresourceparameter - User authentication — Devise handles sign-in/sign-up
- Token exchange —
POST /oauth/tokenwithcode_verifierandresourceparameter - MCP requests —
POST /mcpwithAuthorization: Bearer
Supported RFCs
| RFC | Description | Endpoint |
|---|---|---|
| OAuth 2.1 + PKCE | Authorization with Proof Key for Code Exchange (S256) | /oauth/authorize, /oauth/token |
| RFC 7591 | Dynamic Client Registration | POST /oauth/register |
| RFC 8414 | Authorization Server Metadata | GET /.well-known/oauth-authorization-server |
| RFC 8707 | Resource Indicators | resource parameter in auth + token requests |
| RFC 9728 | Protected Resource Metadata | GET /.well-known/oauth-protected-resource |
Project structure (OAuth mode)
app/
├── controllers/
│ ├── mcp_controller.rb # OAuth-protected MCP endpoint
│ ├── oauth_client_registration_controller.rb # RFC 7591
│ └── oauth_authorization_server_metadata_controller.rb # RFC 8414 + 9728
├── models/
│ ├── user.rb # Devise user with OAuth associations
│ ├── oauth_application.rb
│ ├── oauth_access_token.rb
│ └── oauth_access_grant.rb
├── prompts/ # MCP prompts (created via generator)
├── tools/ # MCP tools (auto-generated per scaffold)
└── views/
└── devise/ # Customizable auth views
config/
├── initializers/
│ ├── doorkeeper.rb # OAuth + PKCE config
│ ├── devise.rb # User auth config
│ └── mcp.rb # MCP tool autoloading
└── routes.rb # All OAuth + MCP routes
db/migrate/
├── *_devise_create_users.rb
├── *_create_doorkeeper_tables.rb
├── *_enable_pkce.rb
└── *_add_resource_to_oauth_tables.rb
Connecting AI assistants (OAuth mode)
{
"name": "my-rails-app",
"type": "StreamableHttp",
"url": "http://localhost:3000/mcp"
}
The client must complete the OAuth PKCE flow before making MCP requests — the /mcp endpoint returns 401 without a valid Bearer token.
Common Features (both modes)
Scaffolding models with MCP tools
rails generate scaffold Post title:string content:text
rails db:migrate
This creates standard Rails files plus 5 MCP tools in app/tools/posts/:
show_tool.rb— Retrieve a single post by IDindex_tool.rb— List posts with paginationcreate_tool.rb— Create new postsupdate_tool.rb— Update existing postsdelete_tool.rb— Delete posts
Creating custom MCP tools
rails generate mcp_tool WeatherCheck location:string
Creating custom MCP prompts
rails generate mcp_prompt hotel_finder location:required check_in_date:required adults price_max
This creates app/prompts/hotel_finder.rb with a prompt class inheriting from MCP::Prompt. Arguments are optional by default — append :required to make them required.
Prompts are automatically loaded from app/prompts/ and registered with the MCP server. Unlike tools, prompts are not auto-generated during scaffolding — they are created explicitly via the generator.
Listing available tools and prompts
rake mcp:tools # compact one-line-per-tool summary
rake mcp:tools:verbose # full details with schema
rake mcp:prompts # compact one-line-per-prompt summary
rake mcp:prompts:verbose # full details with arguments
License
MIT License
Install
This server does not publish a one-line install command.
Open the repository installation guide