Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ group :development, :test do
gem 'rubocop-rspec', '~> 3.5'
end

group :test do
gem 'activerecord'
gem 'activesupport'
end

# Gems required for examples
group :examples do
gem 'puma'
Expand Down
61 changes: 46 additions & 15 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,46 @@ PATH
remote: .
specs:
fast-mcp (1.5.0)
activesupport (~> 7.0)
addressable (~> 2.8)
base64
dry-schema (~> 1.14)
json (~> 2.0)
mime-types (~> 3.4)
rack (~> 3.1)
rack (>= 2.2.4, < 4.0)

GEM
remote: https://rubygems.org/
specs:
activemodel (7.2.2.1)
activesupport (= 7.2.2.1)
activerecord (7.2.2.1)
activemodel (= 7.2.2.1)
activesupport (= 7.2.2.1)
timeout (>= 0.4.0)
activesupport (7.2.2.1)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3)
base64 (0.2.0)
bigdecimal (3.1.9)
base64 (0.3.0)
benchmark (0.4.1)
bigdecimal (3.2.2)
coderay (1.1.3)
concurrent-ruby (1.3.5)
connection_pool (2.5.3)
diff-lcs (1.6.2)
drb (2.2.3)
dry-configurable (1.3.0)
dry-core (~> 1.1)
zeitwerk (~> 2.6)
Expand Down Expand Up @@ -49,6 +71,8 @@ GEM
dry-inflector (~> 1.0)
dry-logic (~> 1.4)
zeitwerk (~> 2.6)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
json (2.12.2)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
Expand All @@ -57,7 +81,8 @@ GEM
mime-types (3.7.0)
logger
mime-types-data (~> 3.2025, >= 3.2025.0507)
mime-types-data (3.2025.0520)
mime-types-data (3.2025.0603)
minitest (5.25.5)
mustermann (3.0.3)
ruby2_keywords (~> 0.0.1)
nio4r (2.7.4)
Expand All @@ -73,7 +98,7 @@ GEM
puma (6.6.0)
nio4r (~> 2.0)
racc (1.8.1)
rack (3.1.15)
rack (3.1.16)
rack-protection (4.1.1)
base64 (>= 0.1.0)
logger (>= 1.6.0)
Expand All @@ -84,40 +109,41 @@ GEM
rackup (2.2.1)
rack (>= 3)
rainbow (3.1.1)
rake (13.2.1)
rake (13.3.0)
regexp_parser (2.10.0)
rspec (3.13.0)
rspec (3.13.1)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.3)
rspec-core (3.13.4)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.4)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.4)
rspec-mocks (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.3)
rubocop (1.75.7)
rspec-support (3.13.4)
rubocop (1.76.0)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-ast (>= 1.45.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.44.1)
rubocop-ast (1.45.0)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-rspec (3.6.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
securerandom (0.4.1)
sinatra (4.1.1)
logger (>= 1.6.0)
mustermann (~> 3.0)
Expand All @@ -126,16 +152,21 @@ GEM
rack-session (>= 2.0.0, < 3)
tilt (~> 2.0)
tilt (2.6.0)
timeout (0.4.3)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
zeitwerk (2.7.3)

PLATFORMS
arm64-darwin-23
arm64-darwin-24
ruby

DEPENDENCIES
activerecord
activesupport
fast-mcp!
json (~> 2.0)
mime-types (~> 3.4)
Expand Down
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,89 @@ class ApplicationResource < ActionResource::Base
end
```


### 🏷️ Tool Annotations
FastMCP supports the MCP tool annotation specification, which allows you to provide additional metadata about your tools' behavior:

```ruby
class WebSearchTool < FastMcp::Tool
tool_name 'web_search'
description 'Search the web for information'

# Tool annotations for better UI and behavior hints
title 'Web Search' # Human-friendly title for UI display
read_only true # Tool doesn't modify its environment
open_world true # Tool interacts with external entities

arguments do
required(:query).filled(:string).description('The search query')
optional(:max_results).filled(:integer, gt?: 0).description('Maximum number of results')
end

def call(query:, max_results: 10)
# Implementation...
end
end

class DeleteFileTool < FastMcp::Tool
tool_name 'delete_file'
description 'Delete a file from the filesystem'

# Tool annotations for destructive operations
title 'Delete File'
read_only false # Tool modifies its environment
destructive true # Tool performs destructive updates
idempotent true # Calling repeatedly with same args has no additional effect
open_world false # Tool doesn't interact with external entities

arguments do
required(:path).filled(:string).description('File path to delete')
end

def call(path:)
# Implementation...
end
end

### 🔄 ActiveRecord Auto-derive

FastMCP automatically discovers and registers ActiveRecord models that expose methods through the `expose_to_mcp` method:

```ruby
class User < ApplicationRecord
# Regular methods
def full_name
"#{first_name} #{last_name}"
end

# Predicate methods with ? are automatically handled
def is_admin?
role == 'admin'
end

# Expose methods to MCP
expose_to_mcp :full_name,
description: "Get the user's full name"

# Methods with special characters like ? are automatically sanitized
# The tool will be registered as "user_is_admin_is"
expose_to_mcp :is_admin?,
description: "Check if the user is an admin"

# You can also provide a custom tool name
expose_to_mcp :is_admin?,
description: "Check if the user is an admin",
tool_name: 'user_admin_check'
end
```

The library automatically handles special characters in method names:
- Methods with `?` are converted to have `_is` as a suffix (e.g., `is_admin?` → `is_admin_is`)
- Methods with `!` are converted to have `_bang` as a suffix
- Methods with `=` are converted to have `_equals` as a suffix

This ensures that the tool names are valid Ruby constants while preserving the original method behavior.

### Easy Sinatra setup
I'll let you check out the dedicated [sinatra integration docs](./docs/sinatra_integration.md).

Expand Down
Loading