fast-mcp 0.1.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -2
- data/README.md +173 -107
- data/lib/fast_mcp.rb +123 -6
- data/lib/generators/fast_mcp/install/install_generator.rb +50 -0
- data/lib/generators/fast_mcp/install/templates/application_resource.rb +5 -0
- data/lib/generators/fast_mcp/install/templates/application_tool.rb +5 -0
- data/lib/generators/fast_mcp/install/templates/fast_mcp_initializer.rb +39 -0
- data/lib/generators/fast_mcp/install/templates/sample_resource.rb +12 -0
- data/lib/generators/fast_mcp/install/templates/sample_tool.rb +16 -0
- data/lib/mcp/logger.rb +13 -14
- data/lib/mcp/railtie.rb +45 -0
- data/lib/mcp/resource.rb +5 -10
- data/lib/mcp/server.rb +40 -74
- data/lib/mcp/tool.rb +8 -2
- data/lib/mcp/transports/authenticated_rack_transport.rb +2 -2
- data/lib/mcp/transports/base_transport.rb +1 -1
- data/lib/mcp/transports/rack_transport.rb +97 -21
- data/lib/mcp/transports/stdio_transport.rb +1 -1
- data/lib/mcp/version.rb +2 -3
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dde8305abcf47ffc314fc95df26467f737957e1b97eecae8c2c4a88b34c76017
|
4
|
+
data.tar.gz: 22715faf289cd3f45090480f59585220c3bb904b2a15c9669c5b93700a85e104
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eacade9a05c17a5032bbe2ed13e68bf094e7d018f272163a70f2457971c60ed79672488ab177fe3926dc9aa51cbf738ed2e3ba70bd9ab4c309599e593659ab97
|
7
|
+
data.tar.gz: 390cb3617e29219da28df5c49ff7da06c36b09d341068b5bc4c20883b64dd61f33c67e0542e03f368bf686129f1338ea5af3157bac28439c824520095250adfd
|
data/CHANGELOG.md
CHANGED
@@ -5,13 +5,57 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [1.1.0] - 2025-04-13
|
9
|
+
### Added
|
10
|
+
- Security enhancement: Added DNS rebinding protection by validating Origin headers [#32 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/32/files)
|
11
|
+
- Added configuration options for allowed origins in rack middleware [#32 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/32/files)
|
12
|
+
- Allow to change the SSE and Messages route [#23 @pedrofurtado](https://github.com/yjacquin/fast-mcp/pull/23)
|
13
|
+
- Fix invalid return value when processing notifications/initialized request [#31 @abMatGit](https://github.com/yjacquin/fast-mcp/pull/31)
|
14
|
+
|
15
|
+
|
16
|
+
## [1.0.0] - 2025-03-30
|
17
|
+
|
18
|
+
### Added
|
19
|
+
- Rails integration improvements via enhanced Railtie support
|
20
|
+
- Automatic tool and resource registration in Rails applications
|
21
|
+
- Extended Rails autoload paths for tools and resources directories
|
22
|
+
- Sample generator templates for resources and tools
|
23
|
+
- MCP Client configuration documentation as reported by [#8 @sivag-csod](https://github.com/yjacquin/fast-mcp/issues/8)
|
24
|
+
- Example Ruby on Rails app in the documentation
|
25
|
+
- `FastMcp.server` now exposes the MCP server to apps that may need it to access resources
|
26
|
+
- Automated Github Releases through Github Workflow
|
27
|
+
|
28
|
+
### Fixed
|
29
|
+
- Fixed bug with Rack middlewares not being initialized properly.
|
30
|
+
- Fixed bug with STDIO logging preventing a proper connection with clients [# 11 @cs3b](https://github.com/yjacquin/fast-mcp/issues/11)
|
31
|
+
- Fixed Rails SSE streaming detection and handling
|
32
|
+
- Improved error handling in client reconnection scenarios
|
33
|
+
- Namespace consistency correction (FastMCP -> FastMcp) throughout the codebase
|
34
|
+
|
35
|
+
### Improved
|
36
|
+
- ⚠️ [Breaking] Resource content declaration changes
|
37
|
+
- Now resources implement `content` over `default_content`
|
38
|
+
- `content` is dynamically called when calling a resource, this implies we can declare dynamic resource contents like:
|
39
|
+
```ruby
|
40
|
+
class HighestScoringUsersResource < FastMcp::Resource
|
41
|
+
...
|
42
|
+
def content
|
43
|
+
User.order(score: :desc).last(5).map(&:as_json)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
- More robust SSE connection lifecycle management
|
48
|
+
- Optimized test suite with faster execution times
|
49
|
+
- Better logging for debugging connection issues
|
50
|
+
- Documentation had outdated examples
|
51
|
+
|
8
52
|
## [0.1.0] - 2025-03-12
|
9
53
|
|
10
54
|
### Added
|
11
55
|
|
12
56
|
- Initial release of the Fast MCP library
|
13
|
-
-
|
14
|
-
-
|
57
|
+
- FastMcp::Tool class with multiple definition styles
|
58
|
+
- FastMcp::Server class with STDIO transport and HTTP / SSE transport
|
15
59
|
- Rack Integration with authenticated and standard middleware options
|
16
60
|
- Resource management with subscription capabilities
|
17
61
|
- Binary resource support
|
data/README.md
CHANGED
@@ -28,140 +28,169 @@ Fast MCP solves all these problems by providing a clean, Ruby-focused implementa
|
|
28
28
|
- 🛠️ **Tools API** - Let AI models call your Ruby functions securely, with in-depth argument validation through [Dry-Schema](https://github.com/dry-rb/dry-schema).
|
29
29
|
- 📚 **Resources API** - Share data between your app and AI models
|
30
30
|
- 🔄 **Multiple Transports** - Choose from STDIO, HTTP, or SSE based on your needs
|
31
|
-
- 🧩 **Framework Integration** - Works seamlessly with Rails, Sinatra
|
32
|
-
- 🔒 **Authentication Support** - Secure your AI endpoints with ease
|
31
|
+
- 🧩 **Framework Integration** - Works seamlessly with Rails, Sinatra or any Rack app.
|
32
|
+
- 🔒 **Authentication Support** - Secure your AI-powered endpoints with ease
|
33
33
|
- 🚀 **Real-time Updates** - Subscribe to changes for interactive applications
|
34
34
|
|
35
|
-
## 💎 What Makes FastMCP Great
|
36
35
|
|
36
|
+
## 💎 What Makes FastMCP Great
|
37
37
|
```ruby
|
38
38
|
# Define tools for AI models to use
|
39
|
-
server =
|
39
|
+
server = FastMcp::Server.new(name: 'popular-users', version: '1.0.0')
|
40
40
|
|
41
|
-
# Define a tool by inheriting from
|
42
|
-
class
|
43
|
-
description "
|
44
|
-
|
45
|
-
arguments do
|
41
|
+
# Define a tool by inheriting from FastMcp::Tool
|
42
|
+
class CreateUserTool < FastMcp::Tool
|
43
|
+
description "Create a user"
|
46
44
|
# These arguments will generate the needed JSON to be presented to the MCP Client
|
47
|
-
# And they will be
|
45
|
+
# And they will be validated at run time.
|
48
46
|
# The validation is based off Dry-Schema, with the addition of the description.
|
49
|
-
|
50
|
-
|
47
|
+
arguments do
|
48
|
+
required(:first_name).filled(:string).description("First name of the user")
|
49
|
+
optional(:age).filled(:integer).description("Age of the user")
|
50
|
+
required(:address).hash do
|
51
|
+
optional(:street).filled(:string)
|
52
|
+
optional(:city).filled(:string)
|
53
|
+
optional(:zipcode).filled(:string)
|
54
|
+
end
|
51
55
|
end
|
52
|
-
|
53
|
-
def call(
|
54
|
-
|
56
|
+
|
57
|
+
def call(first_name:, age: nil, address: {})
|
58
|
+
User.create!(first_name:, age:, address:)
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
58
62
|
# Register the tool with the server
|
59
|
-
server.register_tool(
|
63
|
+
server.register_tool(CreateUserTool)
|
60
64
|
|
61
|
-
# Share data resources with AI models by inheriting from
|
62
|
-
class
|
63
|
-
uri "
|
64
|
-
|
65
|
+
# Share data resources with AI models by inheriting from FastMcp::Resource
|
66
|
+
class PopularUsers < FastMcp::Resource
|
67
|
+
uri "file://popular_users.json"
|
68
|
+
resource_name "Popular Users"
|
65
69
|
mime_type "application/json"
|
66
|
-
|
67
|
-
def
|
68
|
-
JSON.generate(
|
70
|
+
|
71
|
+
def content
|
72
|
+
JSON.generate(User.popular.limit(5).as_json)
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
72
76
|
# Register the resource with the server
|
73
|
-
server.register_resource(
|
77
|
+
server.register_resource(PopularUsers)
|
74
78
|
|
75
79
|
# Accessing the resource through the server
|
76
|
-
server.read_resource(
|
80
|
+
server.read_resource(PopularUsers.uri)
|
77
81
|
|
78
|
-
#
|
79
|
-
server.
|
82
|
+
# Notify the resource content has been updated to clients
|
83
|
+
server.notify_resource_updated(PopularUsers.uri)
|
84
|
+
```
|
80
85
|
|
86
|
+
### 🚂 Fast Ruby on Rails implementation
|
87
|
+
```shell
|
88
|
+
bundle add fast-mcp
|
89
|
+
bin/rails generate fast_mcp:install
|
90
|
+
```
|
81
91
|
|
82
|
-
|
83
|
-
# config/application.rb (Rails)
|
84
|
-
config.middleware.use MCP::RackMiddleware.new(
|
85
|
-
name: 'recipe-ai',
|
86
|
-
version: '1.0.0'
|
87
|
-
) do |server|
|
88
|
-
# Register tools and resources here
|
89
|
-
server.register_tool(GetRecipesTool)
|
90
|
-
end
|
92
|
+
This will add a configurable `fast_mcp.rb` initializer
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
-
name: 'recipe-ai',
|
95
|
-
version: '1.0.0',
|
96
|
-
token: ENV['MCP_AUTH_TOKEN']
|
97
|
-
)
|
94
|
+
```ruby
|
95
|
+
require 'fast_mcp'
|
98
96
|
|
99
|
-
|
100
|
-
|
101
|
-
|
97
|
+
FastMcp.mount_in_rails(
|
98
|
+
Rails.application,
|
99
|
+
name: Rails.application.class.module_parent_name.underscore.dasherize,
|
100
|
+
version: '1.0.0',
|
101
|
+
path_prefix: '/mcp', # This is the default path prefix
|
102
|
+
messages_route: 'messages', # This is the default route for the messages endpoint
|
103
|
+
sse_route: 'sse', # This is the default route for the SSE endpoint
|
104
|
+
# Add allowed origins below, it defaults to Rails.application.config.hosts
|
105
|
+
# allowed_origins: ['localhost', '127.0.0.1', 'example.com', /.*\.example\.com/],
|
106
|
+
# authenticate: true, # Uncomment to enable authentication
|
107
|
+
# auth_token: 'your-token' # Required if authenticate: true
|
108
|
+
) do |server|
|
109
|
+
Rails.application.config.after_initialize do
|
110
|
+
# FastMcp will automatically discover and register:
|
111
|
+
# - All classes that inherit from ApplicationTool (which uses ActionTool::Base)
|
112
|
+
# - All classes that inherit from ApplicationResource (which uses ActionResource::Base)
|
113
|
+
server.register_tools(*ApplicationTool.descendants)
|
114
|
+
server.register_resources(*ApplicationResource.descendants)
|
115
|
+
# alternatively, you can register tools and resources manually:
|
116
|
+
# server.register_tool(MyTool)
|
117
|
+
# server.register_resource(MyResource)
|
118
|
+
end
|
102
119
|
end
|
103
120
|
```
|
121
|
+
The install script will also:
|
122
|
+
- add app/resources folder
|
123
|
+
- add app/tools folder
|
124
|
+
- add app/tools/sample_tool.rb
|
125
|
+
- add app/resources/sample_resource.rb
|
126
|
+
- add ApplicationTool to inherit from
|
127
|
+
- add ApplicationResource to inherit from as well
|
104
128
|
|
105
|
-
|
129
|
+
#### Rails-friendly class naming conventions
|
106
130
|
|
107
|
-
|
108
|
-
# In your Gemfile
|
109
|
-
gem 'fast-mcp'
|
131
|
+
For Rails applications, FastMCP provides Rails-style class names to better fit with Rails conventions:
|
110
132
|
|
111
|
-
|
112
|
-
|
133
|
+
- `ActionTool::Base` - An alias for `FastMcp::Tool`
|
134
|
+
- `ActionResource::Base` - An alias for `FastMcp::Resource`
|
113
135
|
|
114
|
-
|
115
|
-
gem install fast-mcp
|
116
|
-
```
|
136
|
+
These are automatically set up in Rails applications. You can use either naming convention in your code:
|
117
137
|
|
118
|
-
|
138
|
+
```ruby
|
139
|
+
# Using Rails-style naming:
|
140
|
+
class MyTool < ActionTool::Base
|
141
|
+
description "My awesome tool"
|
119
142
|
|
120
|
-
|
143
|
+
arguments do
|
144
|
+
required(:input).filled(:string)
|
145
|
+
end
|
121
146
|
|
122
|
-
|
123
|
-
|
124
|
-
|
147
|
+
def call(input:)
|
148
|
+
# Your implementation
|
149
|
+
end
|
150
|
+
end
|
125
151
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
```shell
|
131
|
-
npx @modelcontextprotocol/inspector examples/rack_middleware.rb
|
152
|
+
# Using standard FastMcp naming:
|
153
|
+
class AnotherTool < FastMcp::Tool
|
154
|
+
# Both styles work interchangeably in Rails apps
|
155
|
+
end
|
132
156
|
```
|
133
157
|
|
134
|
-
|
135
|
-
```shell
|
136
|
-
npx @modelcontextprotocol/inspector examples/authenticated_rack_middleware.rb
|
137
|
-
```
|
158
|
+
When creating new tools or resources, the generators will use the Rails naming convention by default:
|
138
159
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
160
|
+
```ruby
|
161
|
+
# app/tools/application_tool.rb
|
162
|
+
class ApplicationTool < ActionTool::Base
|
163
|
+
# Base methods for all tools
|
164
|
+
end
|
143
165
|
|
144
|
-
#
|
145
|
-
|
166
|
+
# app/resources/application_resource.rb
|
167
|
+
class ApplicationResource < ActionResource::Base
|
168
|
+
# Base methods for all resources
|
169
|
+
end
|
146
170
|
```
|
147
171
|
|
148
|
-
###
|
172
|
+
### Easy Sinatra setup
|
173
|
+
I'll let you check out the dedicated [sinatra integration docs](./docs/sinatra_integration.md).
|
174
|
+
|
175
|
+
## 🚀 Quick Start
|
176
|
+
|
177
|
+
### Create a Server with Tools and Resources and STDIO transport
|
149
178
|
|
150
179
|
```ruby
|
151
180
|
require 'fast_mcp'
|
152
181
|
|
153
182
|
# Create an MCP server
|
154
|
-
server =
|
183
|
+
server = FastMcp::Server.new(name: 'my-ai-server', version: '1.0.0')
|
155
184
|
|
156
|
-
# Define a tool by inheriting from
|
157
|
-
class SummarizeTool <
|
185
|
+
# Define a tool by inheriting from FastMcp::Tool
|
186
|
+
class SummarizeTool < FastMcp::Tool
|
158
187
|
description "Summarize a given text"
|
159
|
-
|
188
|
+
|
160
189
|
arguments do
|
161
190
|
required(:text).filled(:string).description("Text to summarize")
|
162
191
|
optional(:max_length).filled(:integer).description("Maximum length of summary")
|
163
192
|
end
|
164
|
-
|
193
|
+
|
165
194
|
def call(text:, max_length: 100)
|
166
195
|
# Your summarization logic here
|
167
196
|
text.split('.').first(3).join('.') + '...'
|
@@ -171,14 +200,14 @@ end
|
|
171
200
|
# Register the tool with the server
|
172
201
|
server.register_tool(SummarizeTool)
|
173
202
|
|
174
|
-
# Create a resource by inheriting from
|
175
|
-
class StatisticsResource <
|
203
|
+
# Create a resource by inheriting from FastMcp::Resource
|
204
|
+
class StatisticsResource < FastMcp::Resource
|
176
205
|
uri "data/statistics"
|
177
|
-
|
206
|
+
resource_name "Usage Statistics"
|
178
207
|
description "Current system statistics"
|
179
208
|
mime_type "application/json"
|
180
|
-
|
181
|
-
def
|
209
|
+
|
210
|
+
def content
|
182
211
|
JSON.generate({
|
183
212
|
users_online: 120,
|
184
213
|
queries_per_minute: 250,
|
@@ -188,30 +217,38 @@ class StatisticsResource < MCP::Resource
|
|
188
217
|
end
|
189
218
|
|
190
219
|
# Register the resource with the server
|
191
|
-
server.register_resource(StatisticsResource
|
220
|
+
server.register_resource(StatisticsResource)
|
192
221
|
|
193
222
|
# Start the server
|
194
223
|
server.start
|
195
224
|
```
|
196
225
|
|
197
|
-
|
226
|
+
## 🧪 Testing with the inspector
|
198
227
|
|
199
|
-
|
228
|
+
MCP has developed a very [useful inspector](https://github.com/modelcontextprotocol/inspector).
|
229
|
+
You can use it to validate your implementation. I suggest you use the examples I provided with this project as an easy boilerplate.
|
230
|
+
Clone this project, then give it a go !
|
200
231
|
|
201
|
-
```
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
232
|
+
```shell
|
233
|
+
npx @modelcontextprotocol/inspector examples/server_with_stdio_transport.rb
|
234
|
+
```
|
235
|
+
Or to test with an SSE transport using a rack middleware:
|
236
|
+
```shell
|
237
|
+
npx @modelcontextprotocol/inspector examples/rack_middleware.rb
|
238
|
+
```
|
239
|
+
|
240
|
+
Or to test over SSE with an authenticated rack middleware:
|
241
|
+
```shell
|
242
|
+
npx @modelcontextprotocol/inspector examples/authenticated_rack_middleware.rb
|
243
|
+
```
|
244
|
+
|
245
|
+
You can test your custom implementation with the official MCP inspector by using:
|
246
|
+
```shell
|
247
|
+
# Test with a stdio transport:
|
248
|
+
npx @modelcontextprotocol/inspector path/to/your_ruby_file.rb
|
249
|
+
|
250
|
+
# Test with an HTTP / SSE server. In the UI select SSE and input your address.
|
251
|
+
npx @modelcontextprotocol/inspector
|
215
252
|
```
|
216
253
|
|
217
254
|
#### Sinatra
|
@@ -221,7 +258,7 @@ end
|
|
221
258
|
require 'sinatra'
|
222
259
|
require 'fast_mcp'
|
223
260
|
|
224
|
-
use
|
261
|
+
use FastMcp::RackMiddleware.new(name: 'my-ai-server', version: '1.0.0') do |server|
|
225
262
|
# Register tools and resources here
|
226
263
|
server.register_tool(SummarizeTool)
|
227
264
|
end
|
@@ -250,6 +287,9 @@ Add your server to your Claude Desktop configuration at:
|
|
250
287
|
}
|
251
288
|
```
|
252
289
|
|
290
|
+
## How to add a MCP server to Claude, Cursor, or other MCP clients?
|
291
|
+
Please refer to [configuring_mcp_clients](docs/configuring_mcp_clients.md)
|
292
|
+
|
253
293
|
## 📊 Supported Specifications
|
254
294
|
|
255
295
|
| Feature | Status |
|
@@ -270,17 +310,43 @@ Add your server to your Claude Desktop configuration at:
|
|
270
310
|
- 📚 **Interactive Documentation**: Create AI-enhanced API documentation
|
271
311
|
- 💬 **Chatbots and Assistants**: Build AI assistants with access to your app's data
|
272
312
|
|
313
|
+
## 🔒 Security Features
|
314
|
+
|
315
|
+
Fast MCP includes built-in security features to protect your applications:
|
316
|
+
|
317
|
+
### DNS Rebinding Protection
|
318
|
+
|
319
|
+
The HTTP/SSE transport validates the Origin header on all incoming connections to prevent DNS rebinding attacks, which could allow malicious websites to interact with local MCP servers.
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
# Configure allowed origins (defaults to ['localhost', '127.0.0.1'])
|
323
|
+
FastMcp.rack_middleware(app,
|
324
|
+
allowed_origins: ['localhost', '127.0.0.1', 'your-domain.com', /.*\.your-domain\.com/],
|
325
|
+
# other options...
|
326
|
+
)
|
327
|
+
```
|
328
|
+
|
329
|
+
### Authentication
|
330
|
+
|
331
|
+
Fast MCP supports token-based authentication for all connections:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
# Enable authentication
|
335
|
+
FastMcp.authenticated_rack_middleware(app,
|
336
|
+
auth_token: 'your-secret-token',
|
337
|
+
# other options...
|
338
|
+
)
|
339
|
+
```
|
340
|
+
|
273
341
|
## 📖 Documentation
|
274
342
|
|
275
343
|
- [🚀 Getting Started Guide](docs/getting_started.md)
|
276
344
|
- [🧩 Integration Guide](docs/integration_guide.md)
|
277
345
|
- [🛤️ Rails Integration](docs/rails_integration.md)
|
278
346
|
- [🌐 Sinatra Integration](docs/sinatra_integration.md)
|
279
|
-
- [🌸 Hanami Integration](docs/hanami_integration.md)
|
280
347
|
- [📚 Resources](docs/resources.md)
|
281
348
|
- [🛠️ Tools](docs/tools.md)
|
282
|
-
- [
|
283
|
-
- [📘 API Reference](docs/api_reference.md)
|
349
|
+
- [🔒 Security](docs/security.md)
|
284
350
|
|
285
351
|
## 💻 Examples
|
286
352
|
|
data/lib/fast_mcp.rb
CHANGED
@@ -4,13 +4,20 @@
|
|
4
4
|
# https://modelcontextprotocol.io/introduction
|
5
5
|
|
6
6
|
# Define the MCP module
|
7
|
-
module
|
7
|
+
module FastMcp
|
8
|
+
class << self
|
9
|
+
attr_accessor :server
|
10
|
+
end
|
8
11
|
end
|
9
12
|
|
10
13
|
# Require the core components
|
11
14
|
require_relative 'mcp/tool'
|
12
15
|
require_relative 'mcp/server'
|
13
16
|
require_relative 'mcp/resource'
|
17
|
+
require_relative 'mcp/railtie' if defined?(Rails::Railtie)
|
18
|
+
|
19
|
+
# Load generators if Rails is available
|
20
|
+
require_relative 'generators/fast_mcp/install/install_generator' if defined?(Rails::Generators)
|
14
21
|
|
15
22
|
# Require all transport files
|
16
23
|
require_relative 'mcp/transports/base_transport'
|
@@ -22,28 +29,34 @@ end
|
|
22
29
|
require_relative 'mcp/version'
|
23
30
|
|
24
31
|
# Convenience method to create a Rack middleware
|
25
|
-
module
|
32
|
+
module FastMcp
|
26
33
|
# Create a Rack middleware for the MCP server
|
27
34
|
# @param app [#call] The Rack application
|
28
35
|
# @param options [Hash] Options for the middleware
|
29
36
|
# @option options [String] :name The name of the server
|
30
37
|
# @option options [String] :version The version of the server
|
31
38
|
# @option options [String] :path_prefix The path prefix for the MCP endpoints
|
39
|
+
# @option options [String] :messages_route The route for the messages endpoint
|
40
|
+
# @option options [String] :sse_route The route for the SSE endpoint
|
32
41
|
# @option options [Logger] :logger The logger to use
|
42
|
+
# @option options [Array<String,Regexp>] :allowed_origins List of allowed origins for DNS rebinding protection
|
33
43
|
# @yield [server] A block to configure the server
|
34
|
-
# @yieldparam server [
|
44
|
+
# @yieldparam server [FastMcp::Server] The server to configure
|
35
45
|
# @return [#call] The Rack middleware
|
36
46
|
def self.rack_middleware(app, options = {})
|
37
47
|
name = options.delete(:name) || 'mcp-server'
|
38
48
|
version = options.delete(:version) || '1.0.0'
|
39
49
|
logger = options.delete(:logger) || Logger.new
|
40
50
|
|
41
|
-
server =
|
51
|
+
server = FastMcp::Server.new(name: name, version: version, logger: logger)
|
42
52
|
yield server if block_given?
|
43
53
|
|
44
54
|
# Store the server in the Sinatra settings if available
|
45
55
|
app.settings.set(:mcp_server, server) if app.respond_to?(:settings) && app.settings.respond_to?(:mcp_server=)
|
46
56
|
|
57
|
+
# Store the server in the FastMcp module
|
58
|
+
self.server = server
|
59
|
+
|
47
60
|
server.start_rack(app, options)
|
48
61
|
end
|
49
62
|
|
@@ -53,17 +66,121 @@ module MCP
|
|
53
66
|
# @option options [String] :name The name of the server
|
54
67
|
# @option options [String] :version The version of the server
|
55
68
|
# @option options [String] :auth_token The authentication token
|
69
|
+
# @option options [Array<String,Regexp>] :allowed_origins List of allowed origins for DNS rebinding protection
|
56
70
|
# @yield [server] A block to configure the server
|
57
|
-
# @yieldparam server [
|
71
|
+
# @yieldparam server [FastMcp::Server] The server to configure
|
58
72
|
# @return [#call] The Rack middleware
|
59
73
|
def self.authenticated_rack_middleware(app, options = {})
|
60
74
|
name = options.delete(:name) || 'mcp-server'
|
61
75
|
version = options.delete(:version) || '1.0.0'
|
62
76
|
logger = options.delete(:logger) || Logger.new
|
63
77
|
|
64
|
-
server =
|
78
|
+
server = FastMcp::Server.new(name: name, version: version, logger: logger)
|
65
79
|
yield server if block_given?
|
66
80
|
|
81
|
+
# Store the server in the FastMcp module
|
82
|
+
self.server = server
|
83
|
+
|
67
84
|
server.start_authenticated_rack(app, options)
|
68
85
|
end
|
86
|
+
|
87
|
+
# Register a tool with the MCP server
|
88
|
+
# @param tool [FastMcp::Tool] The tool to register
|
89
|
+
# @return [FastMcp::Tool] The registered tool
|
90
|
+
def self.register_tool(tool)
|
91
|
+
self.server ||= FastMcp::Server.new(name: 'mcp-server', version: '1.0.0')
|
92
|
+
self.server.register_tool(tool)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Register multiple tools at once
|
96
|
+
# @param tools [Array<FastMcp::Tool>] The tools to register
|
97
|
+
# @return [Array<FastMcp::Tool>] The registered tools
|
98
|
+
def self.register_tools(*tools)
|
99
|
+
self.server ||= FastMcp::Server.new(name: 'mcp-server', version: '1.0.0')
|
100
|
+
self.server.register_tools(*tools)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Register a resource with the MCP server
|
104
|
+
# @param resource [FastMcp::Resource] The resource to register
|
105
|
+
# @return [FastMcp::Resource] The registered resource
|
106
|
+
def self.register_resource(resource)
|
107
|
+
self.server ||= FastMcp::Server.new(name: 'mcp-server', version: '1.0.0')
|
108
|
+
self.server.register_resource(resource)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Register multiple resources at once
|
112
|
+
# @param resources [Array<FastMcp::Resource>] The resources to register
|
113
|
+
# @return [Array<FastMcp::Resource>] The registered resources
|
114
|
+
def self.register_resources(*resources)
|
115
|
+
self.server ||= FastMcp::Server.new(name: 'mcp-server', version: '1.0.0')
|
116
|
+
self.server.register_resources(*resources)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Mount the MCP middleware in a Rails application
|
120
|
+
# @param app [Rails::Application] The Rails application
|
121
|
+
# @param options [Hash] Options for the middleware
|
122
|
+
# @option options [String] :name The name of the server
|
123
|
+
# @option options [String] :version The version of the server
|
124
|
+
# @option options [String] :path_prefix The path prefix for the MCP endpoints
|
125
|
+
# @option options [String] :messages_route The route for the messages endpoint
|
126
|
+
# @option options [String] :sse_route The route for the SSE endpoint
|
127
|
+
# @option options [Logger] :logger The logger to use
|
128
|
+
# @option options [Boolean] :authenticate Whether to use authentication
|
129
|
+
# @option options [String] :auth_token The authentication token
|
130
|
+
# @option options [Array<String,Regexp>] :allowed_origins List of allowed origins for DNS rebinding protection
|
131
|
+
# @yield [server] A block to configure the server
|
132
|
+
# @yieldparam server [FastMcp::Server] The server to configure
|
133
|
+
# @return [#call] The Rack middleware
|
134
|
+
def self.mount_in_rails(app, options = {})
|
135
|
+
# Default options
|
136
|
+
name = options.delete(:name) || app.class.module_parent_name.underscore.dasherize
|
137
|
+
version = options.delete(:version) || '1.0.0'
|
138
|
+
logger = options[:logger] || Rails.logger
|
139
|
+
path_prefix = options.delete(:path_prefix) || '/mcp'
|
140
|
+
messages_route = options.delete(:messages_route) || 'messages'
|
141
|
+
sse_route = options.delete(:sse_route) || 'sse'
|
142
|
+
authenticate = options.delete(:authenticate) || false
|
143
|
+
allowed_origins = options[:allowed_origins] || default_rails_allowed_origins(app)
|
144
|
+
|
145
|
+
options[:logger] = logger
|
146
|
+
options[:allowed_origins] = allowed_origins
|
147
|
+
|
148
|
+
# Create or get the server
|
149
|
+
self.server = FastMcp::Server.new(name: name, version: version, logger: logger)
|
150
|
+
yield self.server if block_given?
|
151
|
+
|
152
|
+
# Choose the right middleware based on authentication
|
153
|
+
self.server.transport_klass = if authenticate
|
154
|
+
FastMcp::Transports::AuthenticatedRackTransport
|
155
|
+
else
|
156
|
+
FastMcp::Transports::RackTransport
|
157
|
+
end
|
158
|
+
|
159
|
+
# Insert the middleware in the Rails middleware stack
|
160
|
+
app.middleware.use(
|
161
|
+
self.server.transport_klass,
|
162
|
+
self.server,
|
163
|
+
options.merge(path_prefix: path_prefix, messages_route: messages_route, sse_route: sse_route)
|
164
|
+
)
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.default_rails_allowed_origins(rail_app)
|
168
|
+
hosts = rail_app.config.hosts
|
169
|
+
|
170
|
+
hosts.map do |host|
|
171
|
+
if host.is_a?(String) && host.start_with?('.')
|
172
|
+
# Convert .domain to domain and *.domain
|
173
|
+
host_without_dot = host[1..]
|
174
|
+
[host_without_dot, Regexp.new(".*\.#{host_without_dot}")] # rubocop:disable Style/RedundantStringEscape
|
175
|
+
else
|
176
|
+
host
|
177
|
+
end
|
178
|
+
end.flatten.compact
|
179
|
+
end
|
180
|
+
|
181
|
+
# Notify the server that a resource has been updated
|
182
|
+
# @param uri [String] The URI of the resource
|
183
|
+
def self.notify_resource_updated(uri)
|
184
|
+
self.server.notify_resource_updated(uri)
|
185
|
+
end
|
69
186
|
end
|