Claude Media
RubyでMCPサーバーを書く公式SDKの使い方

RubyでMCPサーバーを書く公式SDKの使い方

公式Ruby SDKでMCPサーバー・クライアントを実装し、RailsアプリにStreamable HTTPで組み込む手順をコード付きで解説します。

RubyでMCPサーバーを書くと何が手に入るか

MCP(Model Context Protocol)のRuby実装はmcp gemとして公式配布されています。Tier分類はTier2で、Tier1(TypeScript・Python・C#・Go・Rust)より1段階下ですが、同じTier2のJava SDKと並ぶ位置付けです。実運用で使う前に、必要な機能がRuby SDKで既にサポートされているかを個別に確認するのが安全です。

Tier2という位置付けを見て導入を迷う場合は、自分のユースケースが求める機能がカンフォーマンステストの対象に含まれているかを基準に考えると判断しやすくなります。単純なツール呼び出しやstdio通信だけを使うなら、Tier1との差を実運用で意識する場面は限られます。逆に、OAuth認可や複雑な進捗通知など仕様の細部に依存する機能を使う場合は、リポジトリのCI状況やissueで既知の制約を確認してから採用を決めるほうが安全です。

Ruby SDKの強みは、標準のstdio・Streamable HTTP(SSE含む)トランスポートに加えてRails統合を最初から持っている点です。サーバーへのリクエストからクライアントの通知・進捗・キャンセル・補完・ページングまで、プロトコルの全体をカバーします。

MCPの仕組み自体や他言語SDKとの比較はMCP実用ガイド、TypeScript・PythonでのMCPサーバー自作はMCPサーバー自作ガイドが扱っています。本記事はRuby SDK(mcp gem)固有の実装手順に絞ります。

導入からツール定義まで — 3ステップ

ステップ1: gemを追加する

Gemfileにmcpを追加してbundle installするだけです。

bundle add mcp

Gemfileに手で書いてからbundle installする経路でも、gem install mcpで単体インストールする経路でも動きます。使いたい機能(OAuth認可やRails統合など)によっては追加の依存gemが要る場合があるため、エラーが出たら該当機能のドキュメントで前提gemを確認してください。

ステップ2: ツールを1つ実装する

Ruby SDKではMCP::Toolを継承したクラスとしてツールを定義します。descriptionでツールの説明、input_schemaで引数のJSON Schemaを宣言し、クラスメソッドcallに実処理を書きます。

require "mcp"
 
class ExampleTool < MCP::Tool
  description "A simple example tool that echoes back its arguments"
  input_schema(
    properties: {
      message: { type: "string" },
    },
    required: ["message"]
  )
 
  class << self
    def call(message:, server_context:)
      MCP::Tool::Response.new([{
        type: "text",
        text: "Hello from example tool! Message: #{message}",
      }])
    end
  end
end
 
server = MCP::Server.new(
  name: "example_server",
  tools: [ExampleTool],
)
 
transport = MCP::Server::Transports::StdioTransport.new(server)
transport.open

Go SDKやRust SDKがフィールドタグ・マクロからスキーマを自動生成するのに対し、Ruby版はinput_schemaにハッシュを明示的に書くスタイルです。動的型付け言語らしく、スキーマの正確さは自分で担保する必要があります。

callメソッドが受け取るserver_contextは、リクエストごとの付随情報を渡す引数で、実処理の中で参照する値をここから取り出す窓口として使えます。class << selfでクラスメソッドとしてcallを定義しているのは、MCP::Toolのサブクラス自体をツール定義としてMCP::Serverに渡す設計になっているためで、インスタンスを生成せずにクラスのまま呼び出せる構造です。input_schemaで宣言したrequiredや型と、callが受け取る引数名が一致していないと呼び出し時に食い違いが表面化するため、ツールを増やすときはこの2箇所をセットで見直す癖をつけておくとよいでしょう。

ステップ3: stdioで起動しクライアントから呼ぶ

上のスクリプトをserver.rbとして保存して実行すると、標準入力からJSON-RPCリクエストを受け取れます。

ruby server.rb

動作確認だけなら、標準入力に直接JSON-RPCを流し込んで応答を見る方法が手早く試せます。pingtools/listtools/callの3つを送るだけで、初期化からツール呼び出しまでの一連の流れを確認できます。

クライアント側はMCP::Client::Stdioにサーバーの起動コマンドを渡して接続します。

stdio_transport = MCP::Client::Stdio.new(
  command: "bundle",
  args: ["exec", "ruby", "path/to/server.rb"],
  env: { "API_KEY" => "my_secret_key" },
  read_timeout: 30
)
client = MCP::Client.new(transport: stdio_transport)
 
client.connect
 
tools = client.tools
tools.each do |tool|
  puts "Tool: #{tool.name} - #{tool.description}"
end
 
response = client.call_tool(
  tool: tools.first,
  arguments: { message: "Hello, world!" }
)
 
stdio_transport.close

client.connectがMCPの初期化ハンドシェイクを行うため、これを呼ばずにいきなりtoolscall_toolを呼ぶと失敗します。Claude Codeなどのホスト側からこの種のサーバーバイナリを起動しstdin/stdoutで通信させる設定は、MCPサーバー自作ガイドのClaude Code接続手順と共通です。

RailsアプリにMCPサーバーを組み込む

Ruby SDKが他言語のSDKと最も違うのは、Railsアプリケーションへのマウントを最初から想定している点です。同じサーバー定義をStreamable HTTPトランスポートに切り替えれば、Railsのルーティングの中でMCPサーバーとして動かせます。単体プロセスでstdioサーバーを動かす構成と、既存のRailsアプリにMCPエンドポイントを1つ追加する構成のどちらを取るかは、社内向けツールか外部公開APIかで選び分けるとよいでしょう。Streamable HTTPへの切り替えに伴う実装の詳細は公式のServer Transportsガイドにまとまっています。

クライアント側もStreamable HTTPサーバーに接続するMCP::Client::HTTPが用意されており、MCP::Client::Stdioと同じインターフェースで使えます。stdio用に書いたクライアントコードのトランスポート部分だけを差し替えれば、リモートのMCPサーバーにもそのまま接続できます。

公式リポジトリのexamples/ディレクトリには、単体のサーバー・クライアント例に加えて、examples/railsという完全なRailsアプリケーションのサンプルが置かれています。ゼロからRails統合のコードを書き起こすより、このサンプルをたたき台にルーティングとMCPサーバーの結びつけ方を確認するほうが早く動くものにたどり着けます。APIリファレンスはrubydoc.infoでgem単位に公開されており、クラス・メソッド単位の詳細な引数や戻り値はそちらで確認できます。

どの構成を選ぶべきか

導入経路によって向き不向きが変わります。

用途構成向き不向き
Claude Codeからローカルで呼ぶだけ構成stdio + MCP::Tool継承クラス向き不向き◎。gem追加とクラス定義だけで完結する
既存のRailsアプリに1機能として追加構成Streamable HTTP + Railsルーティング統合向き不向き◎。新規プロセスを増やさずに済む
社外のRailsを持たないチームでのHTTP公開構成Streamable HTTP単体(Rack等)向き不向き○。Rails前提のドキュメントを読み替える手間がある
OAuth 2.1で保護したリモートAPIをツール化構成MCP::Client::HTTP + 認可設定向き不向き△。認可周りの実装コストが増える

プロトコル機能のカバー範囲

公式READMEが挙げる対応範囲は、標準トランスポート(stdio・Streamable HTTP)に加えて、サーバーからクライアントへのリクエスト・複数往復のリクエスト・各種通知・進捗報告・ロギング・キャンセル・補完(completions)・ページングまでカバーします。プロトコルのライフサイクルネゴシエーション(バージョンの合意)も自動で処理されるため、client.connectを呼ぶだけでハンドシェイクの詳細を意識せずに済みます。

これだけの範囲を1つのgemでカバーしているため、機能ごとに別のライブラリを組み合わせる必要がなく、依存関係の管理がシンプルになる点は運用面での利点です。反面、gem全体のバージョンが上がるとプロトコル対応範囲もまとめて変わるため、アップグレード前にはリリースノートで変更点を確認してから既存のツール実装に影響が無いかを見るのが無難です。

これだけの範囲を1つのgemでカバーしている点は、複数のパッケージに機能が分散しがちな他言語のTier1 SDKと比べても遜色ありません。

Ruby SDKでつまずきやすいところ

  • client.connectを呼び忘れる: MCPは接続時に初期化ハンドシェイク(バージョンネゴシエーション・capability交換)を行うプロトコルです。Ruby SDKではこれをclient.connectが担うため、呼ばずにtoolscall_toolを使うと未接続エラーになります
  • Tier2であることの意味を見誤る: Tier2は公式基準でカンフォーマンステスト80%以上のパス率などを満たしつつ、Tier1(100%パス率)の完全サポートに向けて取り組んでいる段階を指し、「まだ動かない」わけではありません。本番導入前に、自分が使う機能(OAuth・進捗通知・ページング等)がテストされているかをリポジトリのCI状況で確認しておくと安全です
  • stdio用のコードをそのままHTTPに流用する: MCP::Client::StdioMCP::Client::HTTPはインターフェースこそ共通ですが、接続先の起動・認証・タイムアウトの扱いはトランスポートごとに異なります。動作確認をstdioで済ませたあとHTTPに切り替えるときは、認証(OAuth 2.1)まわりの設定が新たに必要になることを前提に見積もってください
  • 必要な追加gemを見落とす: mcp gem単体はコア機能をカバーしますが、OAuth認可やRails統合など特定機能を使う場合は追加の依存gemが必要になることがあります。bundle add mcpだけ実行してエラーになったら、使いたい機能のドキュメントで前提gemを確認してください。特にRailsアプリへの組み込みでは、Railsのバージョンとの組み合わせで動作要件が変わる可能性があるため、examples/railsのGemfileを参照するのが確実です

GoやRustとの実装感覚の違い

Javaのアノテーション方式を含め、Tier1のSDKの多くは構造体やアノテーションからスキーマを自動生成する設計を採っていますが、Ruby SDKはinput_schemaにハッシュを直書きするスタイルです。コンパイル時の型チェックが無いぶん、スキーマと実装の食い違いは実行時まで気づきにくくなります。一方でRailsアプリへの統合を最初から想定している点はRuby版ならではの強みで、既存のRailsサービスに社内向けツールを1つ生やしたいだけなら、他言語SDKより導入の摩擦は小さくなります。複数言語のTier判定基準を横断的に比較するならMCP SDK Tierシステムが参考になります。

まとめ

Ruby SDK(mcp gem)はTier2のMCP公式実装で、MCP::Toolを継承したクラスとinput_schemaのハッシュ定義だけで最小サーバーが動きます。stdioでの動作確認からStreamable HTTPへの切り替え、そしてRailsアプリへのマウントまでを1つのSDKでカバーしている点が、他言語のSDKには無い強みです。Tier2という位置付けは機能不足を意味するわけではありませんが、複雑な相互運用が必要な場面では、使う機能のテストカバレッジを事前に確認してから採用してください。

すでにRailsで社内向けの管理画面やバックオフィスAPIを運用しているチームにとっては、新しい言語やランタイムを増やさずに済むこの構成が最も現実的な選択です。逆に、Rails資産を持たない環境で単にMCPサーバーを1つ立てたいだけなら、Tier1のGoやTypeScriptのSDKのほうが単体バイナリとしての取り回しは軽くなります。どちらを選ぶにせよ、まずはstdioの最小構成で疎通を確認してから、本番構成(Rails統合かHTTP単体か)を決めるのが遠回りに見えて一番早い進め方です。

この記事を共有:XはてブLinkedIn