DEV Community

James Miller
James Miller

Posted on

Local MCP Development with Ruby and Gemini CLI

These days, simply using AI isn't enough; the real power lies in learning how to build your own Model Context Protocol (MCP).

Python has long dominated the fields of AI and machine learning. However, a major advantage of the Model Context Protocol (MCP) is that its implementation details are language-agnostic. Not all projects are Python-based, and MCP allows developers to use the languages they are most familiar with to connect to the latest AI capabilities.

The goal of this article is to build a minimal viable Ruby MCP Stdio server and interact with it locally using the Gemini CLI.

Why Choose Ruby?

Ruby is a dynamic, general-purpose programming language known for its focus on simplicity and productivity. It is an open-source object-oriented language designed with an elegant syntax that is natural to read and easy to write. Through MCP, Ruby code can seamlessly interface with Large Language Models (LLMs), acting as a bridge between local data and AI.

Ruby Version Management

Due to Ruby's widespread deployment, some challenges can arise. Managing language versions across different platforms and maintaining supported versions can be difficult. Typically, developers use version management tools like RVM or rbenv.

However, the installation process for these tools often involves complex steps such as GPG key verification and source code compilation. For beginners or developers looking to get started quickly, this is often the first barrier to entry.

Using ServBay to Manage the Ruby Environment

To skip the tedious compilation and configuration steps, we recommend using ServBay. ServBay is an integrated development environment management tool that comes with a pre-compiled and well-maintained Ruby environment.

With ServBay, there is no need to deal with complex installation scripts. Simply download and install ServBay, and enable the Ruby module in the "Packages" section.

Verify if the Ruby environment is ready in the terminal:

Verify Ruby version

ruby -v
Enter fullscreen mode Exit fullscreen mode

Just like that, a Ruby environment is ready.

Gemini CLI

The Gemini CLI is a command-line tool provided by Google for interacting with source files and providing real-time assistance. In MCP development, it plays the role of the client.

Node Version Management

The Gemini CLI relies on a Node.js environment. Here, we also use ServBay to manage the Node.js version.

ServBay also includes a standard Node.js environment, making the installation of Node-based tools very direct. We don't need to install NVM separately; we can simply use the npm provided by ServBay.

Ensure ServBay is running and the Node module is enabled, then install the Gemini CLI:

npm install -g @google/gemini-cli
Enter fullscreen mode Exit fullscreen mode

Testing the Gemini CLI Environment

After installation and ensuring you have the correct Node.js version, you can test the Gemini CLI startup. You will need an API Key or Google account for authentication:

gemini auth
Enter fullscreen mode Exit fullscreen mode

After a successful login, running the gemini command will enter interactive mode.

Where to Start?

The strategy for MCP development adopts a step-by-step approach:

  1. Setup: Configure the basic development environment, set necessary system variables, and ensure the Gemini CLI is working.
  2. Build: Create a minimal "Hello World" style Ruby MCP server with stdio transport capabilities.
  3. Verify: Check the connection between the Gemini CLI and the local process via MCP.
  4. Extend: Expand the basic MCP server by adding more practical tools.

Hello World Using STDIO Transport

A primary function of the MCP library is to abstract various transport methods. Regardless of the low-level transport channel/method the MCP client uses to connect to the MCP server, the implementation of high-level tools remains the same.

The simplest transport method supported by the SDK is stdio (Standard Input/Output) transport, which connects locally running processes. The MCP client and MCP server must run in the same environment.

The logic for establishing the connection is as follows:

# Code Example
logger.info "Starting MCP Server..."
transport = MCP::Server::Transports::Stdio.new
server.run(transport)
Enter fullscreen mode Exit fullscreen mode

Project Configuration (Gemfile)

Create a Gemfile in the project directory to include the MCP SDK and logger library:

# frozen_string_literal: true

source 'https://rubygems.org'

# Use the Ruby version provided by ServBay
ruby '3.2.0'

gem 'logger'
gem 'mcp-sdk' # Note: Adjust this based on the actual available gem name
Enter fullscreen mode Exit fullscreen mode

Run the installation command:

bundle install
Enter fullscreen mode Exit fullscreen mode

Writing the Ruby Code

Create a file named server.rb in the project directory. This code will initialize the server, register tools, and start listening.

require 'mcp'
require 'logger'
require 'json'

# Set log output to standard error (stderr), as stdout is occupied by the protocol
$logger = Logger.new($stderr)
$logger.level = Logger::INFO

class SimpleRubyServer
  def initialize
    @mcp_server = MCP::Server.new("MyLocalRubyServer", "1.0.0")
    register_capabilities
  end

  def register_capabilities
    # Register a simple greeting tool
    @mcp_server.register_tool("say_hello", "Greet the user", {
      type: "object",
      properties: {
        username: { type: "string", description: "The user's name" }
      },
      required: ["username"]
    }) do |params|
      execute_hello(params)
    end
  end

  def execute_hello(params)
    user = params['username'] || "Anonymous"
    $logger.info "Received greeting request for: #{user}"

    # Return response in MCP protocol format
    { 
      content: [
        { 
          type: "text", 
          text: "Hello, #{user}! This is a Ruby MCP server running locally." 
        }
      ] 
    }
  end

  def start
    $logger.info "Server ready, listening on Stdio..."
    # Instantiate Stdio transport layer and run
    transport = MCP::Server::Transports::Stdio.new
    @mcp_server.run(transport)
  rescue => e
    $logger.error "Server runtime error: #{e.message}"
  end
end

# Entry point
if __FILE__ == $0
  SimpleRubyServer.new.start
end
Enter fullscreen mode Exit fullscreen mode

Running a Test

Before connecting to Gemini, try running the script to ensure there are no syntax errors:

ruby server.rb
Enter fullscreen mode Exit fullscreen mode

The program will hang waiting for input after starting, which is normal behavior.

Configuring Gemini CLI (settings.json)

The Gemini CLI needs to know how to start our Ruby server. Modify Gemini's configuration file:

{
  "mcpServers": {
    "ruby-demo": {
      "command": "ruby",
      "args": [
        "/absolute/path/to/your/project/server.rb"
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Reviewing the Project with Gemini CLI

Launch the Gemini CLI; it will read the configuration and attempt to connect to the server.

gemini
Enter fullscreen mode Exit fullscreen mode

In the interactive interface, we can check the server status:

> /mcp list

Configured MCP servers:
🟢 ruby-demo - Ready (1 tool)
  Tools:
  - say_hello
Enter fullscreen mode Exit fullscreen mode

Verification and Interaction

Now, interact with the Ruby code through the Gemini CLI.

Input the command:

Please use the ruby-demo server to say hello to "ServBay Developer".

Gemini will parse the request, call the local Ruby process, and output the result:

✦ I will call the say_hello tool with the username "ServBay Developer".

╭──────────────────────────────────────────────────────────────────╮
│ ✓  say_hello (ruby-demo MCP Server) {"username":"ServBay Developer"} │
│                                                                  │
│ Hello, ServBay Developer! This is a Ruby MCP server running locally. │
╰──────────────────────────────────────────────────────────────────╯
Enter fullscreen mode Exit fullscreen mode

Extending the Ruby MCP Server

The basic MCP functionality has been verified via the Gemini CLI. Next, we can extend the server by adding a tool to retrieve system information.

Add the new tool in the register_capabilities method of server.rb:

    # Register system info tool
    @mcp_server.register_tool("system_specs", "Get Ruby and system info of current environment", {}) do |_|
      get_specs
    end
Enter fullscreen mode Exit fullscreen mode

And add the corresponding handler method:

  def get_specs
    specs = {
      ruby_v: RUBY_VERSION,
      platform: RUBY_PLATFORM,
      servbay_env: ENV['SERVBAY_ROOT'] ? "Detected" : "Not Detected",
      time: Time.now.to_s
    }
    { content: [{ type: "text", text: JSON.pretty_generate(specs) }] }
  end
Enter fullscreen mode Exit fullscreen mode

After restarting the Gemini CLI, the new tool can be invoked.

Get information about the current system's Ruby environment.

Gemini will return JSON data containing the Ruby version and ServBay environment detection results, and answer your question based on that.

Summary

Through a step-by-step approach, we verified the feasibility of MCP development using Ruby and the Gemini CLI.

Thanks to ServBay, we avoided the complicated process of configuring the Ruby environment and Node.js environment, allowing us to focus directly on code implementation. By building a minimal Stdio transport server, we successfully connected local Ruby code with the Gemini Large Model. This method can be further extended to leverage Ruby's rich ecosystem for handling more complex local tasks.

Top comments (0)