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.