MCP Lite
[](https://badge.fury.io/rb/mcp_lite) [](LICENSE) The Lightweight implementation of Model Context Protocol (MCP) in Ruby.Caution
This is an early version of MCP Lite. The API may change in future releases. Please check the CHANGELOG for updates.
Active MCP is deprecated. This library is a lightweight implementation of the Model Context Protocol (MCP) in Ruby, designed to be easy to use and integrate into your applications.
๐ Table of Contents
- ๐ Table of Contents
- โจ Features
- ๐ฆ Installation
- ๐ Setup
- ๐ MCP Connection
- ๐งฐ Creating MCP Tools
- ๐ Input Schema
- ๐ Authorization & Authentication
- ๐ฆ MCP Resources
- ๐ฆ MCP Resource Templates
- ๐ฌ MCP Prompts
- ๐ก Best Practices
- ๐งช Development
- ๐ฅ Contributing
- ๐ License
๐ฆ Installation
Add this line to your application's Gemfile:
gem 'mcp_lite'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install mcp_lite
๐ Setup
class CreateNoteTool < McpLite::Tool::Base
tool_name "create_note"
description "Create Note"
argument :title, :string, required: true
argument :content, :string, required: true
argument :published_at, :string, required: true,
visible: ->(context) { context[:role] == "admin" }
def call(title:, content:, context:)
note = Note.create(title: title, content: content)
[{ type: "text", text: "Created note with ID: #{note.id}" }]
end
end
class MySchema < McpLite::Schema::Base
tools CreateNoteTool
end
class MyMcpController < McpLite::BaseController
def index
render json: MySchema.execute(param:, context:)
end
end
Rails.application.routes.draw do
post "/mcp", to: "my_mcp#index"
# Your other routes
end
๐ MCP Connection Methods
Start a dedicated MCP server that communicates with your Ruby app:
# script/mcp_server.rb
server = McpLite::Server.new(
name: "My App MCP Server",
uri: 'https://your-app.example.com/mcp'
)
server.start
Then configure your MCP client:
{
"mcpServers": {
"my-rails-app": {
"command": "/path/to/ruby",
"args": ["/path/to/script/mcp_server.rb"]
}
}
}
๐งฐ Creating MCP Tools
MCP tools are Ruby classes that inherit from McpLite::Tool::Base and define an interface for AI to interact with your application:
class SearchUsersTool < McpLite::Tool::Base
tool_name "Search Users"
description 'Search users by criteria'
argument :email, :string, required: false, description: 'Email to search for'
argument :name, :string, required: false, description: 'Name to search for'
argument :limit, :integer, required: false, description: 'Maximum number of records to return'
def call(email: nil, name: nil, limit: 10, context: {})
criteria = {}
criteria[:email] = email if email.present?
criteria[:name] = name if name.present?
users = User.where(criteria).limit(limit)
[{ type: "text", text: users.attributes.to_json }]
end
end
๐ Input Schema
Define arguments for your tools using the argument method:
argument :name, :string, required: true, description: 'User name'
argument :age, :integer, required: false, description: 'User age'
argument :addresses, :array, required: false, description: 'User addresses'
argument :preferences, :object, required: false, description: 'User preferences'
Supported types:
| Type | Description |
|---|---|
:string |
Text values |
:integer |
Whole numbers |
:number |
Decimal numbers (float/decimal) |
:boolean |
True/false values |
:array |
Lists of values |
:object |
Hash/dictionary structures |
:null |
Null values |
๐ Authorization & Authentication
Authorization for Tools
Control access to tools by overriding the visible? class method:
class AdminOnlyTool < McpLite::Tool::Base
tool_name "admin_only_tool"
description "Admin-only tool"
argument :command, :string, required: true, description: "Admin command"
# Only allow admins to access this tool
def self.visible?(context:)
return false unless context
return false unless context[:current_user]
# Check if the token belongs to an admin
User.find_by_token(context[:current_user][:token])&.admin?
end
def call(command:, context: {})
# Tool implementation
end
end
Authentication Options
1. Server Configuration
server = McpLite::Server.new(
name: "My Secure MCP Server",
uri: 'http://localhost:3000/mcp',
auth: {
type: :bearer,
token: ENV['MCP_AUTH_TOKEN']
}
)
server.start
2. Token Verification in Tools
def call(resource_id:, context: {})
# Check if authentication is provided
unless context[:current_user].present?
raise "Authentication required"
end
# Proceed with authenticated operation
# ...
end
๐ฆ MCP Resources
MCP Resources allow you to share data and files with AI assistants. Resources have a URI, MIME type, and can return either text or binary data.
Creating Resources
Resources are Ruby classes **Resource:
class UserResource < McpLite::Resource::Base
mime_type "application/json"
def initialize(id:)
@user = User.find(id)
end
def resource_name
@user.name
end
def uri
"data://localhost/users/#{@user.id}"
end
def description
@user.profile
end
def self.visible?(context:)
# Your logic...
end
def text
# Return JSON data
{
id: @user.id,
name: @user.name,
email: @user.email,
created_at: @user.created_at
}
end
end
class MySchema < McpLite::Schema::Base
resource UserResource, items: User.all.each do |user|
{ id: user.id }
end
end
Resource Types
Resources can return two types of content:
- Text Content - Use the
textmethod to return structured data:
def text
# Return strings, arrays, hashes, or any JSON-serializable object
{ items: Product.all.map(&:attributes) }
end
- Binary Content - Use the
blobmethod to return binary files:
class ImageResource < McpLite::Resource::Base
mime_type "image/png"
def resource_name
"profile_image"
end
def uri
"data://localhost/image"
end
def description
"Profile image"
end
def blob
# Return binary file content
File.read(Rails.root.join("public", "profile.png"))
end
end
Resources can be protected using the same authorization mechanism as tools:
def visible?(context: {})
return false unless context
return false unless context[:current_user]
# Check if the token belongs to an admin
User.find_by_token(context[:current_user][:token])&.admin?
end
๐ฆ MCP Resource Templates
MCP Resource Teamplates allow you to define template of resources.
Creating Resource Templates
Resource teamplates are Ruby classes **Resource:
class UserResource < McpLite::Resource::Base
resource_template_name "users"
uri_template "data://localhost/users/{id}"
mime_type "application/json"
description "This is a test."
argument :id, complete: ->(value, context) do
User.all.pluck(:id).filter { _1.match(value) }
end
def self.visible?(context:)
# Your logic...
end
def initialize(id:)
@user = User.find(id)
end
def resource_name
@user.name
end
def description
@user.profile
end
def uri
"data://localhost/users/#{@user.name}"
end
def text
{ name: @user.name }
end
end
class MySchema < McpLite::Schema::Base
resource UserResource, items: User.all.each do |user|
{ id: user.id }
end
end
๐ฌ MCP Prompts
MCP Prompts allow you to define prompt set.
Creating Prompt
Resources are Ruby classes **Prompt:
class HelloPrompt < McpLite::Prompt::Base
prompt_name "hello"
description "This is a test."
argument :name, required: true, description: "User name", complete: ->(value, context) do
User.all.pluck(:name).filter { _1.match(value) }
end
def self.visible?(context:)
# Your logic...
end
def (name:)
[
McpLite::Message::Text.new(
role: "user",
text: "Hello! #{name}"
),
McpLite::Message::Image.new(
role: "assistant",
data: File.read(file),
mime_type: "image/png"
),
McpLite::Message::Audio.new(
role: "user",
data: File.read(file),
mime_type: "audio/mpeg"
),
McpLite::Message::Resource.new(
role: "assistant",
resource: UserResource.new(name: @name)
)
]
end
end
class MySchema < McpLite::Schema::Base
prompt HelloPrompt
end
๐ก Best Practices
1. Create Specific Tool Classes
Create dedicated tool classes for each model or operation instead of generic tools:
# โ
GOOD: Specific tool for a single purpose
class SearchUsersTool < McpLite::Tool::Base
# ...specific implementation
end
# โ BAD: Generic tool that dynamically loads models
class GenericSearchTool < McpLite::Tool::Base
# Avoid this pattern - security and maintainability issues
end
2. Validate and Sanitize Inputs
Always validate and sanitize inputs in your tool implementations:
def call(user_id:, context: {})
# Validate input
unless user_id.is_a?(Integer) || user_id.to_s.match?(/^\d+$/)
raise "Invalid user ID format"
end
# Proceed with validated data
user = User.find_by(id: user_id)
# ...
end
3. Return Structured Responses
Return structured responses that are easy for AI to parse:
def call(query:, context: {})
results = User.search(query)
[{
type: "text",
text: {
content: results.to_json(only: [:id, :name, :email]),
metadata: {
count: results.size,
query: query
}
}.to_json
}]
end
๐งช Development
After checking out the repo, run bundle install to install dependencies. Then, run bundle exec rake to run the tests.
๐ฅ Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/moekiorg/mcp_lite. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
๐ License
The gem is available as open source under the terms of the MIT License.
MCP Lite
Model Context Protocol (MCP) implementation in Ruby.
Installation
Add this line to your application's Gemfile:
gem 'mcp_lite'
Usage
Basic Usage (Without Rails)
require 'mcp_lite'
class MySchema < McpLite::Schema::Base
tool SearchTool
resource UserResource, items: [
{name: "UserA"},
{name: "UserB"}
]
prompt HelloPrompt
end
# Execute MCP request
result = MySchema.execute(
params: {
method: "tools/list",
params: {},
jsonrpc: "2.0"
},
context: {
current_user: User.find_by_token(token)
}
)
Rails Integration
# app/controllers/mcp_controller.rb
class McpController < McpLite::BaseController
def index
render json: MySchema.execute(param:, context:)
end
end
# config/routes.rb
Rails.application.routes.draw do
post "/mcp", to: "mcp#index"
end
// ...existing documentation...