Securely Authenticating MCP Requests in Ruby with FastMCP and API Keys

2025-07-13 15:00:41 -0300

🧠 MCP (Model Context Protocol) is quickly becoming the go-to way to structure LLM requests—but it still feels like the Wild West when it comes to securing them.

As developers start adopting MCP to streamline and standardize how language models consume context, one key question often goes unanswered:

  • How do we authenticate and authorize MCP requests safely and cleanly in production?

While the protocol itself is powerful, practical guidance is limited—and official docs are still evolving. In this post, I’ll walk you through a clear and pragmatic approach to securing your MCP endpoints, using Ruby and FastMCP.

What We’re Going to Build

In this guide, we’ll walk through how to authenticate MCP requests using API tokens in a clean and maintainable way. While you could build a simple token system from scratch, I recommend using the excellent api_keys gem—it handles common concerns like token hashing and lookup for you, saving time and reducing risk.

We’ll also use the fast_mcp gem to build the MCP itself. The official docs do include a basic authentication example—but their approach uses a single shared token, almost like a global secret. That works for simple use cases, but it lacks a critical feature: token ownership.

In this post, you’ll learn how to tet up API tokens that belong to real users or systems (with ownership) and integrate with the mcp protocol.

Let’s get started by configuring API tokens in your Rails app.

🚀 Setting Up API Tokens

First, we need to install the api_keys gem.

Installation

Add this line to your Gemfile:

gem "api_keys"

Then run:

bundle install
rails g api_keys:install
rails db:migrate

After the migration is done, add the following line to the model that should own the API tokens. In my case, it’s the Company model:


class Company < ApplicationRecord
  has_api_keys
end

💡 I chose not to use the built-in dashboard because I prefer to build a custom interface tailored to my application.


🔐 Authenticating Tools with API Tokens

The api_keys gem is still relatively new, and it doesn’t yet expose a public method for authentication outside of Rails controllers. That means you’ll need to dig into its internals a bit.

Fortunately, we can use ApiKeys::Services::Authenticator.find_and_verify_key to manually authenticate a token. Here’s how I do it in a shared base class for my tools:

# frozen_string_literal: true

# app/tools/application_tool.rb
class ApplicationTool < ActionTool::Base
  def extract_api_key!
    token_from_header = headers["authorization"]
    token_from_header = token_from_header.split(" ").last if token_from_header # strip "Bearer"

    api_key = ApiKeys::Services::Authenticator.find_and_verify_key(
      token_from_header,
      ApiKeys.configuration
    )

    # Make a better error handler for you app
    raise "Token Invalid" if api_key.nil? || !api_key.active? # Optionally check scope here

    api_key
  end
end

Then in a tool, you can access the API token and its owner like this:

# frozen_string_literal: true

# app/tools/list_team_tool.rb
class ListTeamTool < ApplicationTool
  description "List all users in the company team"

  def call
    token = extract_api_key!
    company = token.owner

    users = company.users.active.where(role: [:super_admin, :owner, :member])
    users = serialize(users, AssigneeSerializer)

    users.as_json
  end
end

📡 Connecting MCP Client with Authorization

To authenticate your fast_mcp server with a client like Windsurf, simply send the Authorization header with your token:

{
  "mcpServers": {
    "switch-kanban": {
      "command": "npx",
      "args": [
        "-y",
        "supergateway",
        "--sse",
        "http://localhost:3000/mcp/sse",
        "--header",
        "Authorization: Bearer YOUR_TOKEN"
      ]
    }
  }
}

Conclusion

Adding authentication to your MCP setup doesn’t need to be complicated. With fast_mcp and the api_keys gem, you can secure your tools with minimal code and clear ownership. This approach keeps your MCP endpoints safe, scoped, and ready for production.

If you have questions, suggestions, or want to share how you’re using MCP in production, I’d love to hear from you—feel free to drop a comment or reach out on LinkedIn or GitHub.