Add an MCP server to your Rails app

Serve the resources and actions you already defined in Avo to AI agents over the Model Context Protocol, under the Pundit policies you already wrote, so an agent sees and changes only what that user's role allows. You add one gem and one route, and your team connects Claude Code, Cursor, ChatGPT or any client that supports remote MCP servers.

01

What your team gets

Each connection acts as the person who approved it.

If they can't see a record in your Avo app, their agent can't either.

Ask the app from the assistant they already use

Support, ops and finance ask Claude or ChatGPT about your data in plain words, and get answers from your real records.

No second permission system

The policy that scopes records, hides fields and gates actions in your Avo app holds over MCP too. Write the rule once.

A consent screen in your own app

A teammate pastes one URL into their client, signs in to your app, and picks what the client may do. No token to copy into a config file.

You can see and stop every connection

Each one is a row you can narrow, revoke or read the log of.

02

Set it up

You need:

  • Avo 4.2 or newer.
  • A license with both the MCP and Authorization add-ons. The server refuses to run without Authorization, rather than serve calls with no policies.
  • Your Avo app reachable over HTTPS at a public URL.
  • An MCP client that supports remote servers with OAuth. There is no stdio transport.

Add the gem

# Gemfile
gem "avo-mcp_server", source: "https://packager.dev/avo-hq/"
bundle install

The paid gems come from Avo's private gem server. Set your Gem Server Token first: Gem server authentication.

Run the installer

bin/rails generate avo:mcp_server install
bin/rails db:migrate

This writes one migration (the connections, codes, tokens and the connection log) and adds a commented config block to config/initializers/avo.rb.

Mount the endpoints

# config/routes.rb
Rails.application.routes.draw do
  mount_avo_mcp_server

  authenticate :user do
    mount_avo
  end
end

Put it outside your authentication block and before mount_avo. Clients call it with a bearer token, not a browser session. Avo refuses to boot if either placement is wrong, and the message names the fix.

Turn it on

# config/initializers/avo.rb
Avo.configure do |config|
  config.mcp_server.enabled = true
end

The server is off until you do this. Your server's address is your app's origin plus /avo/mcp, for example https://app.example.com/avo/mcp.

Under explicit_authorization = true, Avo's default, it also waits for a use? answer from your policy:

# app/policies/avo/mcp_server/connection_policy.rb
class Avo::McpServer::ConnectionPolicy < ApplicationPolicy
  def use? = true
end

The docs have the rest of that policy: who sees and revokes which connections.

Connect a client

Paste the URL into the client as a remote MCP server. The client sends you to a consent page in your own app. Sign in, choose what it may do, approve.

Claude Code
claude mcp add --transport http acme-admin https://app.example.com/avo/mcp
# then, inside Claude Code: /mcp → acme-admin → Authenticate
Cursor
// ~/.cursor/mcp.json
{
  "mcpServers": {
    "acme-admin": {
      "url": "https://app.example.com/avo/mcp"
    }
  }
}
Codex CLI
# ~/.codex/config.toml
[mcp_servers.acme-admin]
url = "https://app.example.com/avo/mcp"

# then: codex mcp login acme-admin
VS Code
// .vscode/mcp.json
{
  "servers": {
    "acme-admin": {
      "type": "http",
      "url": "https://app.example.com/avo/mcp"
    }
  }
}
ChatGPT

ChatGPT has no config file. Open Settings, then Connectors, then Create, paste the URL and sign in when asked. It needs developer mode, under Settings, Apps & Connectors, Advanced.

Amp works too. The connect page in your Avo app shows a recipe for each client.

What the agent can do

Ten tools, grouped by what the person grants at consent:

GrantToolsTicked by default
Read list_resources, list_records, count_records, show_record, search_records, list_actions Yes
Read & write create_record, update_record, delete_record No
Run actions run_action No

The person can also grant read or write per resource. You can add your own tools with bin/rails generate avo:mcp_server:tool issue_invoice and register them in config.mcp_server.extra_tools. Full reference: MCP Server docs.

03

Avo or a hand‑rolled MCP server?

Most Rails MCP answers point to a gem you build on. They are good gems. The question is how much of the server you want to write.

OptionWhat it isWho decides what a user may seeBest for
Official Ruby SDK (mcp gem) The protocol, for servers and clients. stdio and Streamable HTTP, a Rails integration, OAuth 2.1 resource-server protection. You, in each tool you write. Any custom server.
fast-mcp A Ruby MCP implementation with tools, resources, and STDIO, HTTP or SSE transports. Rails generator included. You. It has a shared auth token and lets you filter tools by request. A handful of tools in a Rack or Rails app.
ActionMCP A Rails engine for production MCP servers over HTTP, no stdio. Needs Rails 8.1.1+ and Ruby 3.4.8+. You. You write the Gateway's authenticate!; its README says auth and authorization are not included. Teams who want an MCP framework shaped like Rails.
rails-mcp-server A standalone process that reads your project: routes, models, schema, Rails guides. Not a data server. Helping a coding agent understand your code.
Tidewave Rails Development tools for a running app: eval code, run SQL, read logs and docs. Raises in production, localhost only by default. Not a data server. Helping a coding agent while you build.
Avo MCP add-on A remote MCP server over your Avo resources and actions, with consent, revoke and a log per connection. Your Pundit policies and field visibility, for the person who approved the connection. Many models, permissions already written, a team that uses the app too.

Hand-rolling wins when

  • You need a few custom tools and no admin. A few tools in fast-mcp or the SDK is less than adding Avo.
  • You want dev-time help, not data access. Tidewave and rails-mcp-server answer a different question. Run one of them next to Avo; they don't compete.
  • Your client only speaks stdio, or the server can't be reached over public HTTPS. Avo's server is remote only.
  • Your data isn't in Active Record. Avo's tools refuse resources that aren't backed by Active Record.

Avo wins when

  • You have many models. Every Avo resource is reachable the day you turn the server on. You don't write a tool per model.
  • You already wrote the permissions. The policies that scope records, hide fields and gate actions in your Avo app bind every MCP call. A hand-rolled server needs that layer written again, per tool.
  • Your team uses the admin too. One set of resources serves the people in the app and the agents outside it. A change to a field shows up in both.
  • You need to answer "who did this?" Each connection has an owner, a log of every call, a grant you can narrow per resource, and a Revoke button.

Already on Avo and need one special tool? Write it as an Avo tool. It gets the same consent, policies and log as the ten that ship.

Frequently asked questions

Is it safe to let an agent into my Rails app?

It's as safe as the permissions you give the person, and no wider. Every call passes two gates: the grant the person approved at consent, then your policy for that person. A record outside their policy scope comes back as "not found", the same as a record that doesn't exist, so an agent can't probe for hidden data.

Write and Run actions start unticked at consent. Grant them with care. Over MCP, delete_record and run_action act immediately: approving the grant is the confirmation. Text inside a record can steer an agent, so keep untrusted clients read-only.

Tokens are short-lived and bound to your server's address. Each connection is limited to 300 tool calls a minute by default, and you can revoke it from your Avo app at any time.

Does it respect my Pundit policies?

Yes. The server looks up the person behind the connection on every request and runs the call through the same policies your Avo app uses for them. Demote someone to read-only and their connected client stops writing on its next call, with nothing to revoke.

Field visible: blocks apply to reads and writes. Credential-like columns (password, token, secret, digest) are never writable. There is no separate MCP redaction list: a field the person can see in the app, the agent can see too. To hide a field from agents only, check Avo::Current.interface == :mcp in your policy.

Which MCP clients work?

Any client that supports remote MCP servers with OAuth. The docs give recipes for Claude Code, Codex CLI, Cursor, Amp, VS Code and ChatGPT (developer mode). Clients that only speak stdio don't work.

What does it cost?

MCP is a paid add-on: $30 a month per app. It needs the Authorization add-on ($40 a month), so on its own it comes to $70 a month per app.

Two bundles include both: the AI Bundle, $139 a month, with Authorization, AI Chat, MCP, API and CLI; and Everything, $249 a month or $2,490 a year, with all 23 add-ons.

Prices are per app, with unlimited users and no per-seat fees. See pricing.

Do I need Avo's admin UI?

You need Avo installed, because the tools serve your Avo resources and the consent screen lives in your Avo app. The person who connects a client signs in there once to approve it. After that, your team can work from their assistant and rarely open the app. Avo's core is free.

Can I add my own tools?

Yes. bin/rails generate avo:mcp_server:tool <name> scaffolds one. You declare which grant unlocks it, and it runs under the same policies and log as the shipped tools. A tool written for Avo's AI Chat can be served over MCP as well.

Your policies already say who sees what.

Let them cover your team's agents too.