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

Bounded Context - DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

The origin of Poltrax development - story of POLTRAX (part 2)

11
.
06
.
2025
Stanisław Zawadzki
Ruby on Rails
Startups
Business
Backend

Ruby Meetups in 2022 - Summary

11
.
06
.
2025
Michał Łęcicki
Ruby on Rails
Visuality
Conferences

Repository - DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

Example Application - DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

How to launch a successful startup - story of POLTRAX (part 1)

11
.
06
.
2025
Michał Piórkowski
Ruby on Rails
Startups
Business

How to use different git emails for different projects

11
.
06
.
2025
Michał Łęcicki
Backend
Tutorial

Aggregate - DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

Visuality at wroc_love.rb 2022: It's back and it's good!

11
.
06
.
2025
Patryk Ptasiński
Ruby on Rails
Conferences
Ruby

Our journey to Event Storming

11
.
06
.
2025
Michał Łęcicki
Visuality
Event Storming

Should I use Active Record Callbacks?

11
.
06
.
2025
Mateusz Woźniczka
Ruby on Rails
Backend
Tutorial

How to rescue a transaction to roll back changes?

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

Safe navigation operator '&.' vs '.try' in Rails

11
.
06
.
2025
Mateusz Woźniczka
Ruby on Rails
Backend
Ruby
Tutorial

What does the ||= operator actually mean in Ruby?

11
.
06
.
2025
Mateusz Woźniczka
Ruby on Rails
Backend
Ruby
Tutorial

How to design an entity - DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

Entity - DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

Should I use instance variables in Rails views?

11
.
06
.
2025
Mateusz Woźniczka
Ruby on Rails
Frontend
Backend
Tutorial

Data Quality in Ruby on Rails

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

We started using Event Storming. Here’s why!

11
.
06
.
2025
Mariusz Kozieł
Event Storming
Visuality

First Miłośnicy Ruby Warsaw Meetup

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

Should I use Action Filters?

11
.
06
.
2025
Mateusz Woźniczka
Ruby on Rails
Backend
Tutorial

Value Object - DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

Introduction to DDD in Ruby on Rails

11
.
06
.
2025
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial