7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails×MCP】RailsのController#methodたちをMCPツールとして公開する変換レイヤーライブラリを作りました

7
Last updated at Posted at 2026-03-09

どうもこんにちは。
この度、monkey_mcpというライブラリを開発しました。

はじめに

このライブラリ(gem)は、Ruby on RailsでWebアプリケーションを開発している方がPythonやTypeScriptを使わずとも簡単にリモートMCPサーバを構築するためのライブラリです。

monkey_mcp の概要

項目 説明 備考
対応言語/フレームワーク Ruby on Rails Ruby対応バージョンは3.1以上
ライセンス MITライセンス -
用途 既存のRailsアプリケーションをサクっとMCPサーバ化するライブラリ -

命名由来

  • モンキーパッチのように「簡単にサクッと」「機能のちょい足しレベルで」MCPサーバを作れる
  • 開発者のアイコンが猿のイラスト

特徴

  • bundle installincludeと設定少々を行うだけでMCPサーバ化できる
  • Railsサーバ = MCPサーバ という関係性(同一のVM上で動作する)
  • コンテキスト圧迫改善のために通常MCPモード動的ツール取得モードを実装

開発契機

自分のタスクをエージェントから自然言語で管理したかった

最近、自分のやりたいことを管理するために最小限のタスク管理アプリを作って
ぶん回しているのですが、CopilotやCodexから自然言語でタスク確認や登録をしたいなぁと思ったのがきっかけで開発しました。

↓タスク管理アプリのリポジトリ↓
(すでにmonkey_mcpbundle installしているのですぐにお試しいただけます。)

ふと、既存のRailsアプリってMCPサーバ化しづらくね?と思った

また、Ruby on Railsで開発されているWebアプリケーションは、APIとしては提供されているものがある程度存在します。
しかし、APIをエージェントから呼び出すためにはMCPサーバSkillが必要になります。

APIをMCPサーバ化する方法としては、サービスとしてはAmazon Bedrock AgentCore Gatewayなどがありますが、Railsアプリケーションとは別サービスを使用してデプロイをする必要があります。
これだと、Railsアプリケーションの稼働料金に加えてMCPサーバの料金も発生することになります。よろしくないっすねぇ...

具体的に何を作ったのか

monkey_mcp がやっていることを一言で言うと、RailsのコントローラアクションをMCPツールとして公開する変換レイヤーです。

ポイントは次の3つです。

  1. POST /mcp のエンドポイントで JSON-RPC 2.0 を受ける
  2. tools/listtools/call を解釈して、Rails側の実処理に橋渡しする
  3. アクション定義から inputSchema を組み立てて、MCPクライアントに返す

要は「MCPサーバを別で1個立てる」のではなく、既存のRailsプロセスの中で完結させる設計です。

実際の利用イメージ

たとえば my_task_app 側でタスク一覧取得(tasks_controller.rbindex)アクションをすでに持っている場合、monkey_mcp ではそれをtask_indexツールとして公開できます。

エージェント側からは以下の流れで見えます。

  1. tools/list を呼ぶ
  2. task_index などのツール定義を受け取る
  3. tools/calltask_index を実行する
  4. Railsの既存処理結果を、そのままエージェントに返す

このとき、Railsアプリケーション側の既存アクションのロジックは維持されます。と同時に、MCPツールとして呼び出す場合にも、同じロジックが使用されます。

コンテキスト圧迫との戦い

MCPを触っていて地味にしんどかったのが、tools/list の返却量です。
ツール数が増えると、毎回のコンテキスト消費が重くなります。

そこで monkey_mcp ではモードを2つ用意しました。

モード tools/list の返却 想定用途
:full すべてのツールを返す 小規模、または互換性重視
:dynamic tool_search / call_proxy の2件のみ返す 中〜大規模、コンテキスト節約重視

dynamic ではまず tool_search で候補を絞り、その後 call_proxy で実行します。
つまり「最初から全部渡す」ではなく、必要なときに必要なツールだけ引く動きになります。

full と dynamic の動きの違い

ここでは、task_index task_show task_create task_update task_destroy など、ツールが30件ある前提で比較します。

full モード

1回目の tools/list で、30件分の name description inputSchema がすべて返ります。

  • メリット: 最初の1往復で「使えるツール全体」を把握できる
  • デメリット: 使わないツール情報まで毎回コンテキストに入りやすい

実行フローは次の2ステップです。

  1. tools/list(全件取得)
  2. tools/call(目的ツールを直接実行)

dynamic モード

1回目の tools/list では、tool_searchcall_proxy の2件だけ返ります。
実ツールは必要になった時点で検索して取得します。

  • メリット: 初期コンテキストを小さく保ちやすい
  • デメリット: 実行までに検索ステップが1つ増える

実行フローは次の3ステップです。

  1. tools/list(メタツール2件のみ)
  2. tool_search(例: query: "タスク一覧 取得"
  3. call_proxy(例: name: "task_index" で実行)

どちらを選ぶべきか

  • ツール数が少ない(目安10件以下): full のほうがシンプルで、会話の往復回数も減らしやすい
  • ツール数が多い(目安20件以上): dynamic のほうがノイズを抑えやすく、長い会話でも安定しやすい

使い分けとしては、ライブラリ導入初期は、fullモードで動作の全体像を掴んで、動作を理解して「ツールが多いからどうにかしたいなぁ」って思ったタイミングでdynamic に切り替えるのが良いかなと思います。

具体的にどれくらい嬉しいか

ざっとツールが20〜50件を超え始めると、tools/list の説明文やschemaがコンテキストを圧迫し始めてきます。
dynamic にすると初期会話での情報量を強制的に絞れるので、次のような効果があります。

  • 無関係なツール説明が会話コンテキストを圧迫しにくい
  • エージェントが「どのツールを使うべきか」を検索ベースで判断しやすい
  • 新規ツール追加時の副作用(一覧肥大化)が相対的に小さい

設計で意識したこと

1. Rails開発者の導入ハードルを上げない

「MCPのために別技術を学ぶ」より、今のRailsの延長で使えることを優先しました。
Gem導入と設定、必要なら公開対象アクションの調整で動く形に寄せています。

2. 既存システムとの責務分離を崩さない

monkey_mcp側にはロジックを持たせず、既存のRailsアプリ側に委譲するようにしています。
これにより、MCP対応の有無でビジネスロジックが分岐して壊れるリスクを抑えています。

3. 失敗時の挙動を JSON-RPC として明示する

入力不正は -32602 Invalid params でエラーを返すなど、MCPクライアント側が扱いやすい失敗形に揃えています。
Railsで発生する例外をそのまま投げないことで、エージェント側のリカバリもしやすくなります。

注意点

返却形式について

導入する場合、Railsアプリケーションのアクションの戻り値の形式をJSONにする必要があります。以下のように実装されている場合は、複数の形式で返却できるようにしておく必要があります。

NG実装

class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end
end

OK実装

class TasksController < ApplicationController
  def index
    @tasks = Task.all

    # json形式で返せるようにする
    respond_to do |format|
      format.html
      format.json { render json: { tasks: @tasks } }
    end
  end
end

複数のnamespaceで同名コントローラがある場合

例えば、Api/V1/TaskController#indexApi/V2/TaskController#indexが存在している場合、どちらの定義がツールとして定義されるかは、読み込み順に依存します。

この仕様については、今後改善の余地があるかなと思っています。

どんな人に向いているか

monkey_mcp は、次の条件に当てはまる人には相性がいいと思っています。

  • すでにRailsで動いている業務アプリがある
  • そのAPI/機能をエージェント経由で触りたい
  • MCPサーバを別運用するコストを増やしたくない

逆に「Railsとは独立したMCP基盤を最初から分離して運用したい」場合は、monkey_mcpの使用は向いていません。

最後に

今回作った monkey_mcp は、派手なことをするライブラリではありません。
ただ、Railsアプリを持っている人が「エージェントからRailsアプリのロジックを動かしてみる」ための最短距離としては、かなり実用的だと感じています。

特に、既存のWebアプリを使っている開発チームが、「MCP対応を試したいが、別サービス運用まではまだやりたくない」という段階で使うにはちょうどいい立ち位置です。

今後は検索精度、schema表現、運用時の監視ポイントも整理していく予定です。

また、改善点やバグ報告, ご意見については、GithubのIssueに起票いただけますと幸いです。よろしくお願いいたします。

以上

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?