AI

MCP Ruby SDKとは:公式mcp gemでRubyのMCPサーバーを作る手順とTier 1の意味

MCP Ruby SDKとは:公式mcp gemでRubyのMCPサーバーを作る手順とTier 1の意味

MCP Ruby SDKは、Model Context Protocol(MCP)のサーバーとクライアントをRubyで書くための公式SDKで、RubyGemsではmcpという名前で配布されています。2026年10月30日の公式ブログで、TypeScript・Python・C#・Go・Rustに続く6番目のTier 1 SDKになったと発表されました。この記事では、gemの導入からstdioで動く最小サーバー、RailsへのStreamable HTTP組み込み、2026-07-28仕様への接続までを実装順に追い、非公式gemとの選び分けを判断します。MCPそのものの仕組みはMCP(Model Context Protocol)の仕組みとAPIとの違いで確認できます。

まとめ|Ruby製のMCPサーバーは公式mcp gemの1.9系で組む

2026年11月10日時点の最新版は1.9.0です。要求RubyはRuby 2.7以上、実行時の依存はjson_schemerだけで、既存のRailsアプリへ足しても依存関係はほとんど増えません。Tier 1は、適合性テストの合格率100%と、新しい仕様版への公開日までの対応を約束した区分です。Rubyで新しくMCPサーバーを作るなら、非公式gemを選ぶ理由は残っていません。

注意点は2つあります。Railsに組み込む場合、従来型の接続はセッションをメモリに持つため、Pumaのworkersを増やすと壊れます。もう1つは、1.x系のマイナー版でも仕様への準拠を理由に挙動が変わる点です。版を上げる前にCHANGELOGのChanged欄を読む運用を、導入時点で決めておいてください。

MCP Ruby SDK(mcp gem)の提供元とTier 1到達で変わった位置づけ

まず、どのgemが公式で、どの程度の品質が約束されているかを確かめます。

公式gemの名前mcpと1.9.0までの版履歴、要求Ruby 2.7以上

公式SDKのソースはGitHubのmodelcontextprotocol/ruby-sdkにあり、配布名はmcpです。mcp-sdk.rbやmcp_liteなど似た名前の別実装もあるので、Gemfileに書く名前を取り違えないようにします。RubyGemsのmcpのページとAPIで2026年11月10日に確認した版の推移は次のとおりです。

版 公開日 主な変化
1.0.0 2026年7月24日 初の安定版
1.2.0 2026年8月15日 2026-07-28仕様に対応
1.3.0 2026年8月21日 説明を専用サイトへ移設
1.8.0 2026年10月16日 OAuth 2.1のリソースサーバー機能
1.9.0 2026年11月3日 Skills拡張(SEP-2640)

1.9.0のメタデータでは、要求Rubyが2.7.0以上、ライセンスがApache-2.0です。7月の1.0.0から11月の1.9.0まで、約3か月半でマイナー版が9回出ています。更新の速さを前提に、版の固定と上げ方を決めておく必要があります。

Tier 1の5要件と3月のTier 3から9月のTier 1までの適合性スコア

MCP公式のSDK Tiersのページは、Tier 1に適合性テスト合格率100%、Issueの振り分け2営業日以内、重大バグの修正7日以内、新仕様版の公開前の対応、全機能を例つきで説明する文書を求めています。Tier 2は合格率80%、新機能への対応は6か月以内です。

MCP公式ブログのTier 1到達の発表には、評価ごとの数値が載っています。

評価時期 区分 評価時の版 サーバー適合性 クライアント適合性
2026年3月 Tier 3 0.8.0 25/30 未計測
2026年7月 Tier 2 1.0.0 30/30 24/24
2026年9月確定 Tier 1 1.2.0 67/67 50/50

7か月足らずでTier 3から上がったことになります。Tier 1はテストが4週間落ち続けるとTier 2へ戻される仕組みなので、ここに載った数字は一度きりの成績ではありません。次期仕様は2027年3月下旬が目標で、Ruby SDKもリリース日までに対応すると表明しています。

gemのインストールからstdioで動くMCPサーバーを試すまでの手順

ここからは手元で動かす手順です。stdioはMCPクライアントがサーバーを子プロセスとして起動する方式で、Claude Desktopのようなデスクトップアプリとの接続や動作確認に向きます。

Gemfileへの追加とHTTPクライアントやJWT検証で別途要るgem

サーバーをstdioで動かすだけならmcpだけで足ります。公式ガイドのInstallationによると、機能によって追加のgemが要ります。

  • HTTPクライアントとしてMCPサーバーへつなぐ:faraday(2.0以上)と、SSE応答を読むためのevent_stream_parser
  • サーバー側でJWTのアクセストークンを検証する:jwt(EdDSA署名ならjwt-eddsaも)
  • RFC 7662のトークンイントロスペクションで検証する:追加なし(標準ライブラリで動く)

SDK自身のサーバーは既定でSSEを返します。どんなサーバーにもつなぐクライアントを書くなら、faradayとevent_stream_parserの両方を入れておきます。

MCP::Toolを継承したツール定義とstdioトランスポートの起動コード

受注番号を受け取ってステータスを返す、読み取り専用のツールを1つだけ持つサーバーです。READMEのQuick Startと同じ構成で、題材だけ業務寄りに変えています。

# Gemfile
gem "mcp", "~> 1.9"

# server.rb
require "mcp"

class OrderStatusTool < MCP::Tool
  title "受注ステータス照会"
  description "受注番号から現在のステータスを返す"
  input_schema(
    properties: {
      order_id: { type: "string" },
    },
    required: ["order_id"]
  )
  annotations(read_only_hint: true, destructive_hint: false)

  class << self
    def call(order_id:, server_context:)
      statuses = { "A-100" => "出荷済み", "A-101" => "受注確認中" }
      status = statuses.fetch(order_id, "該当なし")
      MCP::Tool::Response.new([{ type: "text", text: "#{order_id}: #{status}" }])
    end
  end
end

server = MCP::Server.new(name: "order_server", version: "0.1.0", tools: [OrderStatusTool])
MCP::Server::Transports::StdioTransport.new(server).open

annotationsは省略できますが、公式ガイドのToolsではdestructive_hintの既定値がtrue、read_only_hintの既定値がfalseです。何も書かないと「破壊的な操作をするツール」としてクライアントに伝わるため、参照系のツールには明示しておきます。ツールの定義方法は、このクラス継承のほかMCP::Tool.defineとMCP::Server#define_toolのブロック形式も選べます。

標準入力からJSON-RPCでツール一覧と呼び出し結果を確認する手順

起動したら、標準入力へ1行に1リクエストずつJSON-RPCを貼り付けます。確認するメソッドはtools/listとtools/callです。MCPクライアントを用意しなくても、ツールの一覧と呼び出し結果を目で確認できます。

$ bundle exec ruby server.rb
{"jsonrpc":"2.0","id":"1","method":"ping"}
{"jsonrpc":"2.0","id":"2","method":"tools/list"}
{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"order_status_tool","arguments":{"order_id":"A-100"}}}

READMEの例では、ExampleToolクラスがexample_toolという名前で呼ばれています。同じ規則ならOrderStatusToolはorder_status_toolですが、実際の名前はtools/listの応答で確かめてからtools/callに書いてください。1行の上限は既定で4MiBで、StdioTransport.newのmax_line_bytes:で変えられます。

ツール引数がシンボルキーで届く仕様とテストで見落とす落とし穴

トランスポートは受け取ったJSONをsymbolize_names: trueで読むため、入れ子のオブジェクトもキーがシンボルになります。payload["subject"]と文字列で読むとnilが返ります。

厄介なのはテストです。ツールを直接MyTool.call(payload: { "subject" => "greet" }, server_context: nil)のように呼ぶと、本番では来ない文字列キーで通ってしまいます。公式ガイドは、引数を一度JSONに変換して戻してから呼ぶ書き方を勧めています。

delivered = JSON.parse(JSON.generate(arguments), symbolize_names: true)
MyTool.call(**delivered, server_context: nil)

RSpecのヘルパーにこの2行を入れておけば、テストは通るのに実際のクライアントからは失敗する、という食い違いを防げます。

RailsアプリにStreamable HTTPでMCPエンドポイントを組み込む実装

社内の業務システムにMCPの口を足す場合は、Streamable HTTPを使います。トランスポート自体の仕組みはStreamable HTTPとSSEの違いと2026-07-28仕様の変更点にまとめています。

Railsのroutes.rbでのmount構成と単一プロセスの制約

MCP::Server::Transports::StreamableHTTPTransportはRackアプリです。Rackアプリとして渡せる理由はRackの仕様とconfig.ruの書き方で補足しています。公式のexamples/railsのサンプルは、起動時にサーバーを1つ作り、routes.rbでmountします。

# config/routes.rb
server = MCP::Server.new(
  name: "order_server",
  version: "1.0.0",
  tools: [OrderStatusTool],
)
transport = MCP::Server::Transports::StreamableHTTPTransport.new(
  server,
  allowed_hosts: ["mcp.example.com"],
)

Rails.application.routes.draw do
  mount transport => "/mcp"
end

サーバーをinitializersではなくroutes.rbで作るのは、Zeitwerkの自動読み込みが整う前だとapp/toolsのクラスを参照できないためです。mountすればPOST・GET・DELETEをトランスポートが振り分けるので、ルートを個別に書く必要はありません。

ただし、公式ガイドのTransportsにあるとおり、従来のハンドシェイク型の接続ではセッションとSSEの状態をプロセスのメモリに持ちます。Pumaをworkers 0で動かすか、複数台ならロードバランサーでMcp-Session-Idごとのスティッキーセッションの設定が必要です。stateless: trueならこの制約は消えますが、進捗通知とサーバーからの問いかけが使えなくなります。

コントローラ方式でログイン利用者をツールへ渡す際の必須設定と理由

current_userをserver_contextへ渡し、ログイン中の利用者ごとに渡すツールや権限を変えたいときは、リクエストのたびにサーバーを作るコントローラ方式にします。

class McpController < ActionController::API
  def create
    server = MCP::Server.new(
      name: "order_server",
      version: "1.0.0",
      tools: [OrderStatusTool],
      server_context: { user_id: current_user.id },
    )
    transport = MCP::Server::Transports::StreamableHTTPTransport.new(
      server,
      stateless: true,
      serve_subscriptions_listen: false,
    )
    status, headers, body = transport.handle_request(request)
    render(json: body.first, status: status, headers: headers)
  end
end

stateless: trueとserve_subscriptions_listen: falseの2つは外せません。リクエストごとに作るトランスポートではセッションIDを引き継げず、renderは応答本文をまとめて返すため、通知用の開きっぱなしのストリームを扱えないからです。後者を付け忘れると、2026-07-28仕様のクライアントが通知ストリームを要求した時点で500になります。

server_contextに入れた値は、ツールのcallにserver_context:として渡ります。利用者IDに基づく絞り込みはツール側で行い、AIに渡すデータの範囲をアプリの権限と一致させます。

本番公開で403になるallowed_hostsとDNSリバインディング保護の設定

2025-11-25仕様に沿って、トランスポートは既定でHostとOriginのヘッダを検証します。許可されているHostは127.0.0.1・::1・localhostだけです。開発機では動いたのに、リバースプロキシの後ろに置いた途端にすべて403になる原因の多くはこれです。

公開用のホスト名をallowed_hosts:に、ブラウザから呼ぶ画面があるならそのオリジンをallowed_origins:に入れます。dns_rebinding_protection: falseで検証を切れますが、上流のプロキシで同じ検証をしている場合だけです。リクエスト本文の上限は既定で4MiB、initializeは64KiBで、超えると413を返します。

2026-07-28仕様の新しい接続方式とRubyクライアントからの扱い

2026-07-28仕様では、最初にinitializeで握手する手順がなくなり、リクエストごとに版を載せる方式が加わりました。仕様側の差分はMCP仕様2026-07-28版のステートレス化と移行手順で扱っています。

クライアントの自動接続判定とconnect(mode:)で固定する条件

公式ガイドのProtocol Versionsによると、ハンドシェイク型の既定の版は2025-11-25です。initializeで2026-07-28を求められても、サーバーは2025-11-25を返します。新方式はハンドシェイクでは選ばれず、クライアントがserver/discoverを送って判定します。

require "mcp"

transport = MCP::Client::HTTP.new(url: "https://mcp.example.com/mcp")
client = MCP::Client.new(transport: transport)

client.connect                                  # 既定:server/discoverを試し、非対応ならinitialize
# client.connect(mode: :modern)                 # 新方式を必須にする
# client.connect(mode: :legacy)                 # initializeでの握手を強制する
# client.connect(protocol_version: "2025-11-25") # 版を固定し、判定を省く

tools = client.tools
puts tools.map(&:name)

相手のサーバーが分からないなら既定の自動判定のままで構いません。社内で版をそろえたサーバー同士なら、mode:を固定しておくと、接続のたびの判定通信と、判定結果の違いによる挙動差を避けられます。HTTPクライアントには前述のfaradayが要ります。

Roots・Sampling・Loggingの非推奨化とstatelessで使えない機能

2026-07-28仕様では、SEP-2577によりRoots・Sampling・Loggingが非推奨になりました。ハンドシェイク型の版では3つとも引き続き動きます。新規の設計では、これらに頼らずツールの入出力で完結させるのが無難です。

stateless: trueの接続では、sampling/createMessage・roots/list・elicitation/createといったサーバーからの問いかけはエラーになり、進捗やログの通知も届きません。利用者に確認を取ってから処理を進めるツールを作るなら、mount方式を選ぶか、確認を2回のツール呼び出しに分けて設計します。

公式mcp gemとfast-mcp・ActionMCPを比べたRubyでの採用判断

公式gemの安定版が出る前から、RubyにはMCPの非公式実装がありました。どれを使うかを判断の材料ごとに並べます。

3つのgemの最新版・要求Ruby・依存gemを並べた2026年11月時点の比較

各gemのRubyGems APIを2026年11月10日に照会した結果です。表のactionmcpが依存するrailties・activerecordは、8.1以上が条件です。

gem 最新版(公開日) 要求Ruby 主な実行時依存
mcp(公式) 1.9.0(2026年11月3日) 2.7.0以上 json_schemerのみ
fast-mcp 1.6.0(2025年9月28日) 3.0.0以上 dry-schema・rackなど6本
actionmcp 0.201.3(2026年10月27日) 指定なし railties・activerecord等

fast-mcpはDry-Schemaで引数を検証するDSLが特徴ですが、最後のリリースから1年以上新版が出ていません。ActionMCPはRails 8.1とActive Recordを前提にしたRails専用の実装で、版は0.x系のままです。

新規開発で公式gemを選ぶ条件とfast-mcpを使い続けてよい場面

新規に作るなら、公式gemを選びます。2026-07-28仕様、OAuth 2.1のリソースサーバー、クライアント機能までそろっていて、Tier 1の要件により次期仕様への追従も約束されています。非公式gemには、この追従を誰も約束していません。

fast-mcpで動いているサーバーを急いで書き換える必要はありません。社内の限られたクライアントからだけ使っていて、2026-07-28仕様の新方式で接続する予定がないなら、そのまま運用を続けて構いません。書き換えに着手すべきなのは、新仕様のクライアントへの対応を求められたとき、または外部へ公開してOAuthで保護する必要が出たときです。ActionMCPは、Rails 8.1以上でジョブやデータベース連携まで同じ枠組みに載せたい場合に限って検討します。

マイナー版でも挙動が変わるVERSIONING方針とGemfileの固定方法

公式gemはVERSIONING.mdで、MCP仕様への準拠の修正、セキュリティ対応、明確な欠陥の修正であれば、互換性のない変更をマイナー版で出すと明記しています。実際に1.8.1ではidがnullのリクエストを拒否するようになり、1.9.0ではオブジェクトでない_metaを不正な引数として返すようになりました。

Gemfileは"~> 1.9"とし、Gemfile.lockで実際の版を固定します。上げるときはCHANGELOG.mdのChanged欄だけを先に読み、自社のクライアントや連携先が影響を受けるかを確認してからbundle updateします。依存の自動更新ツールでmcpを無条件に上げる運用は避けてください。

既存RailsシステムにMCPを足す前に棚卸しする権限とプロセス構成

MCPサーバーを足すと、AIエージェントが業務データに触れる経路が1本増えます。着手前に確かめるのは、公開するツールの一覧と各ツールが読み書きする範囲、利用者ごとの権限をどうserver_contextへ渡すか、そしてPumaのworkers数とロードバランサーの構成です。最後の点はmount方式とコントローラ方式のどちらを選ぶかを決めます。

この棚卸しで、AIに渡すデータの範囲や認可の設計が決まらない場合は、エージェント側の設計と合わせて検討したほうが手戻りは減ります。業務システムとAIエージェントの接続設計は一創のAIエージェント開発でも相談を受けています。

よくある質問

MCP Ruby SDKを導入する前によく出る疑問をまとめました。

MCP Ruby SDKは商用のシステムで無料で使えますか?

使えます。RubyGemsに登録された1.9.0のライセンスはApache-2.0です。リポジトリのREADMEでは、新しい貢献分がApache License 2.0、既存のコードがMITと説明されています。どちらも商用利用を認めるライセンスなので、社内システムに組み込んで提供しても問題ありません。再配布するときは、LICENSEファイルの表示義務を確認してください。

対応しているRubyのバージョンはどこまで古くて大丈夫ですか?

gemの要求はRuby 2.7.0以上です。ただしRuby 2.7はすでに保守が終わった系列なので、MCPサーバーを新しく置く環境では現行版を使ってください。現行の版とサポート期限はRubyの最新バージョンとサポート期限で確認できます。古いRubyで動く業務システムに組み込む場合も、MCPサーバーだけ別プロセスに切り出す選択肢があります。

SSEだけを使う旧式のトランスポートには対応していますか?

対応していません。公式ブログによると、旧来のSSEトランスポートは仕様で非推奨になっているため、SDKは意図的に実装していません。Streamable HTTPの中でSSEによる応答は使えるので、古いクライアントが旧式のSSEエンドポイントしか話せない場合に限って問題になります。その場合はクライアント側の更新を先に検討します。

MCPクライアントもRubyで書けますか?

書けます。MCP::Client::Stdioはサーバーを子プロセスとして起動し、MCP::Client::HTTPはStreamable HTTPのサーバーへ接続します。connectで接続方式を決めたあと、toolsで一覧を取得し、call_toolで呼び出す流れです。stdioの子プロセスは既定で一部の環境変数しか引き継がないため、APIキーなどはenv:で明示して渡します。

Tier 1になると、利用者にとって何が変わりますか?

次の仕様版が出たときに、Ruby SDKが置いていかれない保証が付きました。Tier 1は新仕様の公開日までの対応、重大バグの7日以内の修正を求められ、適合性テストが4週間落ち続けると格下げされます。2027年3月下旬が目標の次期仕様にも、リリース日までに対応すると表明されています。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.09 テックブログ エディオン不正アクセス第2報:カード番号の対象確認とログ・決済画面の改ざん対策
  2. 2025.04.30 テックブログ IMSIキャッチャー(偽基地局)とは?2Gフォールバックの手口と日本での対策
  3. 2026.11.10 テックブログ ニトリの情報漏洩:最大3,762万件の対象確認となりすまし連絡の見分け方
  4. 2026.11.10 テックブログ Claude Fable 5.2とは:料金・破壊的変更4点とFable 5.1からの移行手順【2026年11月】
  5. 2024.07.11 コラム Sakana AI創業者3人の経歴と会社概要|伊藤錬・デイビッド ハ・ライオン ジョーンズ

RELATED POSTS 関連記事

目次