1. Expose your first tool (read-only, end to end)
You have a Rails app and want one MCP tool that returns data. No auth subtleties yet.
Problem. You have a Rails app and want one MCP tool that returns data. No auth subtleties yet.
Solution. Three files: a handler (logic + schema), a tool wrapper (declaration), and the one-time config.
# app/service/workflows/fetch_latest_applicant.rb
module Workflows
class FetchLatestApplicant
include McpAuthorization::DSL
# @rbs type output = {
# applicant_id: String,
# name: String,
# current_stage: String
# }
def description
"Fetch the most recent applicant in the workflow."
end
#: (workflow_id: String) -> Hash[Symbol, untyped]
def call(workflow_id:)
{ applicant_id: "app-42", name: "Jane Doe", current_stage: "screening" }
end
end
end# app/mcp/workflows/fetch_latest_applicant_tool.rb
module Workflows
class FetchLatestApplicantTool < McpAuthorization::Tool
tool_name "fetch_latest_applicant"
read_only!
dynamic_contract Workflows::FetchLatestApplicant
end
end# config/initializers/mcp_authorization.rb
McpAuthorization.configure do |config|
config.server_name = "my-app"
config.context_builder = ->(request) {
user = User.authenticate(request.headers["Authorization"])
OpenStruct.new(current_user: user) # works for a sketch; prefer a real context class โ see recipe 14
}
endResult. Routes mount automatically at /mcp. POST /mcp now answers MCP tools/list and tools/call. The input schema comes from the #: line; the output schema from @rbs type output. You wrote no JSON Schema.
A handler must define
descriptionandcall, and declare@rbs type output. Miss one and the gem raises anArgumentErrorwith a worked example on first request.
Collected from COOKBOOK.md in the repository. Edit it there, not here.