---
title: "MCP Ruby SDKとは：公式mcp gemでRubyのMCPサーバーを作る手順とTier 1の意味"
url: "https://www.issoh.co.jp/tech/details/19565/"
published: 2026-11-10
updated: 2026-11-10
categories: ["AI"]
publisher: "株式会社一創"
---

# 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との違い](https://www.issoh.co.jp/tech/details/5736/)で確認できます。

## まとめ｜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](https://github.com/modelcontextprotocol/ruby-sdk)にあり、配布名は`mcp`です。`mcp-sdk.rb`や`mcp_lite`など似た名前の別実装もあるので、Gemfileに書く名前を取り違えないようにします。[RubyGemsのmcpのページ](https://rubygems.org/gems/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のページ](https://modelcontextprotocol.io/community/sdk-tiers)は、Tier 1に適合性テスト合格率100%、Issueの振り分け2営業日以内、重大バグの修正7日以内、新仕様版の公開前の対応、全機能を例つきで説明する文書を求めています。Tier 2は合格率80%、新機能への対応は6か月以内です。

[MCP公式ブログのTier 1到達の発表](https://blog.modelcontextprotocol.io/posts/ruby-sdk-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](https://ruby.sdk.modelcontextprotocol.io/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](https://ruby.sdk.modelcontextprotocol.io/server/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仕様の変更点](https://www.issoh.co.jp/tech/details/6580/)にまとめています。

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

`MCP::Server::Transports::StreamableHTTPTransport`はRackアプリです。Rackアプリとして渡せる理由は[Rackの仕様とconfig.ruの書き方](https://www.issoh.co.jp/tech/details/5598/)で補足しています。公式の[examples/railsのサンプル](https://github.com/modelcontextprotocol/ruby-sdk/tree/main/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](https://ruby.sdk.modelcontextprotocol.io/server/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版のステートレス化と移行手順](https://www.issoh.co.jp/tech/details/18245/)で扱っています。

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

[公式ガイドのProtocol Versions](https://ruby.sdk.modelcontextprotocol.io/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](https://github.com/yjacquin/fast-mcp)はDry-Schemaで引数を検証するDSLが特徴ですが、最後のリリースから1年以上新版が出ていません。[ActionMCP](https://github.com/seuros/action%5Fmcp)は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](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/VERSIONING.md)で、MCP仕様への準拠の修正、セキュリティ対応、明確な欠陥の修正であれば、互換性のない変更をマイナー版で出すと明記しています。実際に1.8.1ではidがnullのリクエストを拒否するようになり、1.9.0ではオブジェクトでない`_meta`を不正な引数として返すようになりました。

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

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

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

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

## よくある質問

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の最新バージョンとサポート期限](https://www.issoh.co.jp/tech/details/10592/)で確認できます。古い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月下旬が目標の次期仕様にも、リリース日までに対応すると表明されています。

## 関連記事

- [MCP（Model Context Protocol）とは？仕組み・APIとの違い・MCPサーバーの作り方](https://www.issoh.co.jp/tech/details/5736/)：MCPの基本と全体像
- [MCP Python SDK v2 移行：FastMCPからMCPServerへの書き換え手順と据え置き判断](https://www.issoh.co.jp/tech/details/18273/)：同じTier 1のPython版
- [MCP TypeScript SDK v2とは？パッケージ分割とステートレス化の移行要点](https://www.issoh.co.jp/tech/details/15905/)：TypeScript版の新仕様対応
- [MCP Go SDKの使い方｜v1.8.0でサーバーを作る手順とmcp-goとの違い](https://www.issoh.co.jp/tech/details/8272/)：公式と非公式を比べたGo版
- [MCP C# SDKの使い方｜v2.2でサーバー・クライアントを作る手順と1.xからの移行](https://www.issoh.co.jp/tech/details/11306/)：.NETでの実装

---

出典: [MCP Ruby SDKとは：公式mcp gemでRubyのMCPサーバーを作る手順とTier 1の意味](<https://www.issoh.co.jp/tech/details/19565/>)（株式会社一創）
