2
.
09
.
2025
2
.
09
.
2025
Ruby on Rails
LLM

MCP Template for Rails applications

Paweł Strzałkowski
Chief Technology Officer
MCP Template for Rails applications by Paweł Strzałkowski

After building several MCP servers with Rails using different approaches in my previous posts in this series, I started noticing a pattern. Every time I wanted to create a new MCP-enabled Rails application, I had to go through the same setup steps: add the gem, create the controller, configure routes, set up tool autoloading, prepare MCP response formatting. It was becoming repetitive.

This repetition got me thinking about one of Rails core principles: Convention over Configuration. What if integrating MCP with Rails could be as simple as scaffolding a regular Rails application?

The Rails Way of Thinking

Rails has always been about reducing boilerplate and focusing on the essential parts of your application. When you scaffold a model, you get the controller, views, routes, and tests automatically generated. The framework handles the repetitive parts so you can focus on your business logic.

The same principle should apply to MCP integration. But rather than just automating setup, what if we could enhance Rails scaffolding to generate AI tools alongside the standard files?

Enhancing Rails Scaffolding with MCP Tools

The premise isn't just about automation - it's about extending Rails capabilities. What if rails generate scaffold could create not only controllers and views, but also MCP tools that make your models immediately accessible to AI assistants?

I built a Rails application template that does exactly this. The template is available at https://github.com/pstrzalk/mcp-on-rails and you can use it to create a new Rails application like this:

$ git clone https://github.com/pstrzalk/mcp-on-rails.git
$ rails new myapp -m mcp-on-rails/mcp

This creates a Rails application that serves MCP requests at /mcp using streamable HTTP transport, but more importantly, it enhances the scaffolding process to automatically generate MCP tools.

Alternatively, you may apply this template to an existing application:

$ git clone https://github.com/pstrzalk/mcp-on-rails.git
$ cd your-project/
$ rails app:template LOCATION=../mcp-on-rails/mcp

Generator Hooks: Extending Rails Functionality

The key insight was using Rails generator hook system to extend existing functionality rather than replacing it. This is the same pattern used by gems like jbuilder - they enhance the scaffold generator without overriding it.

When you run rails generate scaffold Post title:string content:text, the enhanced scaffolding process creates the standard Rails files plus five MCP tools:

  • Show tool for retrieving individual records
  • Index tool with filtering and listing the last N records
  • Create tool with validation
  • Update tool with partial updates
  • Delete tool

The beauty of this approach is that it feels like a natural extension of Rails. You're not learning a new workflow - you're using the same scaffolding commands you already know, just with enhanced output.

Smart Tool Generation

The generated tools include intelligent features based on your model structure. For models with references, the tools automatically handle relationships. If you scaffold Comment post:references content:text, the create tool will require a post_id parameter, and the index tool will allow filtering by post.

Type mapping is handled automatically. String fields become type: "string" in the JSON schema, integers become type: "integer", and so on.

Error handling is comprehensive, covering validation errors, not found errors, and general exceptions with meaningful messages.

Generate MCP Tools with ActiveRecord models

Let's say you're building a blog application. After creating the Rails app with the MCP template, you scaffold your models:

$ rails generate scaffold Post title:string content:text author:string
$ rails generate scaffold Comment post:references content:text author:string

You now have a fully functional blog, but also a complete set of MCP tools. An AI assistant can immediately:

  • List the most recent posts (with configurable count)
  • Create new posts with validation
  • Filter comments by post
  • Update existing content
  • Delete records

All without writing a single line of MCP-specific code. The scaffolding process handled both the web interface and the AI interface.

Scaffolded MCP Tools

The full list of MCP Tools generated alongside the ActiveRecord objects is:

app/tools/posts/
  - create_tool.rb
  - delete_tool.rb
  - index_tool.rb
  - show_tool.rb
  - update_tool.rb
app/tools/comments/
  - create_tool.rb
  - delete_tool.rb
  - index_tool.rb
  - show_tool.rb
  - update_tool.rb

Let’s take a look at some example implementations. Remember, those have been scaffolded automatically!

# app/tools/posts/create_tool.rb

module Posts
  class CreateTool < MCP::Tool
    tool_name "post-create-tool"
    description "Create a new Post entity"

    input_schema(
      properties: {
        title: { type: "string" },
        body: { type: "string" }
      },
      required: []
    )

    def self.call(title: nil, body: nil, server_context:)
      post = Post.new(
        title: title,
        body: body
      )

      if post.save
        MCP::Tool::Response.new([ { type: "text", text: "Created #{post.to_mcp_response}" } ])
      else
        MCP::Tool::Response.new([ { type: "text", text: "Post was not created due to the following errors: #{post.errors.full_messages.join(', ')}" } ])
      end
    rescue StandardError => e
      MCP::Tool::Response.new([ { type: "text", text: "An error occurred, what happened was #{e.message}" } ])
    end
  end
end

Additionally, when you look at the comments-related tools, you may see that they include the post_id parameter, as the underlying ActiveRecord object does.

module Comments
  class IndexTool < MCP::Tool
    tool_name "comment-index-tool"
    description "List the last count of Comments entities. The count parameter is an integer and defaults to 10. post_id property may be used to filter by integer identifier of the related Post entity."

    input_schema(
      properties: {
        count: { type: "integer" },
        post_id: { type: "integer" }
      },
      required: []
    )

    def self.call(count: 10, post_id: nil, server_context:)
      comments = Comment.all
      comments = comments.where(post_id: post_id) if post_id.present?
      comments = comments.last(count)

      response = comments.map(&:to_mcp_response).join("\n")
      response = "Nothing was found" unless response.present?

      MCP::Tool::Response.new([ { type: "text", text: response } ])
    rescue StandardError => e
      MCP::Tool::Response.new([ { type: "text", text: "An error occurred, what happened was #{e.message}" } ])
    end
  end
end

Should tools implement CRUD actions?

While the template generates CRUD tools automatically, it's important to understand that effective MCP tools often need to go beyond simple database operations. LLMs aren't particularly good at following long procedural lists of small partial steps. They work better when given tools accomplish bigger, more meaningful tasks.

For example, instead of having an AI assistant call three separate tools to create a post, add tags, and notify subscribers, you might want a single "publish post" tool that handles all these operations together. Similarly, instead of multiple calls to search, filter, and format results, a "generate report" tool could handle the entire workflow.

This is where the custom tool generator becomes valuable. While scaffolding gives you the basic CRUD operations, real-world applications often need tools that:

  • Aggregate multiple database operations into business workflows
  • Handle complex validation and business rules
  • Integrate with external services as part of larger processes
  • Provide domain-specific functionality that maps to how users actually think about the problem

The CRUD tools serve as building blocks, but the most effective MCP integrations often involve creating higher-level tools that encapsulate complete user intentions rather than exposing low-level data operations.

Custom tool generator

The template also includes a generator for custom tools when you need functionality beyond basic CRUD:

$ rails generate mcp_tool EmailSender recipient:string subject:string body:text

This creates a tool with the proper structure and type mapping, ready for you to implement your custom logic.

Scaffolding as a learning exercise

It's worth noting that having Active Record scaffolding generate MCP tools serves an important educational purpose. Just as early Ruby on Rails taught developers about CRUD operations in web development - lessons that helped us progress to thinking at scale and building more complex products - this approach teaches Rails developers how to think about MCP integration. The scaffolded tools provide a solid foundation for understanding how AI assistants can interact with your data, even if your production tools end up being more sophisticated.

The future of MCP in Rails ecosystem

The Model Context Protocol is still evolving, and so is the Rails ecosystem around it. This template represents one approach to enhancing Rails built-in capabilities, but there's room for many more patterns and tools.

What interests me most is how this changes the development workflow. When every scaffolded model automatically becomes available to AI assistants, it opens up new possibilities for how we build and interact with web applications.

The MCP Rails template is available at https://github.com/pstrzalk/mcp-on-rails. I'm looking forward to seeing how the Rails community adopts and evolves these enhanced scaffolding patterns.

Articles in this series

Paweł Strzałkowski
Chief Technology Officer

Check my Twitter

Check my Linkedin

Did you like it? 

Sign up To VIsuality newsletter

READ ALSO

N+1 in Ruby on Rails

11
.
06
.
2025
Katarzyna Melon-Markowska
Ruby on Rails
Ruby
Backend

Turbo Streams and current user

11
.
06
.
2025
Mateusz Bilski
Hotwire
Ruby on Rails
Backend
Frontend

Showing progress of background jobs with Turbo

11
.
06
.
2025
Michał Łęcicki
Ruby on Rails
Ruby
Hotwire
Frontend
Backend

Table partitioning in Rails, part 1 - Postgres Stories

11
.
06
.
2025
Jarosław Kowalewski
Postgresql
Backend
Ruby on Rails

Indexing partitioned table - Postgres Stories

11
.
06
.
2025
Jarosław Kowalewski
Backend
Postgresql

Table partitioning types - Postgres Stories

11
.
06
.
2025
Jarosław Kowalewski
Postgresql
Backend
SQL Views in Ruby on Rails

SQL views in Ruby on Rails

11
.
06
.
2025
Jan Grela
Backend
Ruby
Ruby on Rails
Postgresql
Design your bathroom in React

Design your bathroom in React

11
.
06
.
2025
Bartosz Bazański
Frontend
React
Lazy Attributes in Ruby - Krzysztof Wawer

Lazy attributes in Ruby

11
.
06
.
2025
Krzysztof Wawer
Ruby
Software

Exporting CSV files using COPY - Postgres Stories

11
.
06
.
2025
Jarosław Kowalewski
Postgresql
Ruby
Ruby on Rails
Michał Łęcicki - From Celluloid to Concurrent Ruby

From Celluloid to Concurrent Ruby: Practical Examples Of Multithreading Calls

11
.
06
.
2025
Michał Łęcicki
Backend
Ruby
Ruby on Rails
Software

Super Slide Me - Game Written in React

11
.
06
.
2025
Antoni Smoliński
Frontend
React
Jarek Kowalewski - ILIKE vs LIKE/LOWER - Postgres Stories

ILIKE vs LIKE/LOWER - Postgres Stories

11
.
06
.
2025
Jarosław Kowalewski
Ruby
Ruby on Rails
Postgresql

A look back at Friendly.rb 2023

11
.
06
.
2025
Cezary Kłos
Conferences
Ruby

Debugging Rails - Ruby Junior Chronicles

11
.
06
.
2025
Piotr Witek
Ruby on Rails
Backend
Tutorial

GraphQL in Ruby on Rails: How to Extend Connections

11
.
06
.
2025
Cezary Kłos
Ruby on Rails
GraphQL
Backend
Tutorial

Tetris on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Backend
Frontend
Hotwire

EURUKO 2023 - here's what you've missed

11
.
06
.
2025
Michał Łęcicki
Ruby
Conferences

Easy introduction to Connection Pool in ruby

11
.
06
.
2025
Michał Łęcicki
Ruby on Rails
Backend
Ruby
Tutorial

When crazy ideas bring great time or how we organized our first Conference!

11
.
06
.
2025
Alexander Repnikov
Ruby on Rails
Conferences
Visuality

Stacey Matrix & Takeaways - why does your IT project suck?

11
.
06
.
2025
Wiktor De Witte
Project Management
Business

A simple guide to pessimistic locking in Rails

11
.
06
.
2025
Michał Łęcicki
Ruby on Rails
Backend
Ruby
Tutorial

Poltrax design - story of POLTRAX (part 3)

11
.
06
.
2025
Mateusz Wodyk
Startups
Business
Design

Writing Chrome Extensions Is (probably) Easier Than You Think

11
.
06
.
2025
Antoni Smoliński
Tutorial
Frontend
Backend