fast-mcp 1.4.0 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50141727429ea7f39887b8a0f6ec2da8515cc3c35803a1c3e328287931bebcae
4
- data.tar.gz: 518a481ba94b59f339b4e4957c54971866a76835e54833fd408efdb12e97c0e8
3
+ metadata.gz: 37917d61ab02f9d3a42e8980b9007bdb0003f2b91847b5cfe6e6a5f0043550cd
4
+ data.tar.gz: 476fa4ea8a2c022c255edcf3e5b3d101748fddaa8643569d2bc999ef8ec74eee
5
5
  SHA512:
6
- metadata.gz: 76f1f6136bc4f1041021f5a3522f230ae1f044bb19e2e0d8f0871249aac222740b3907229db4b7d482812396f03d492cd6ea414206008f3737361e4346cbb5ca
7
- data.tar.gz: 1ac14395b264d76b826feb2612a0ceae18933c8b37695ba8e5c673c6c5b29045f241617a2196409267f1c4c58b1fef0b3ff9865d92fae759306a6b17e2795db2
6
+ metadata.gz: f12f0d7bf4a4f36b4a5d6c642160c01ccf40d5b799020cbc2c16646c7332b51083bc525c867f38cd2d5a68b3757f51f2fb0f632c35e004c7fcd7a8cb0f571cdf
7
+ data.tar.gz: 559edc8f334fcf49c16b001c8e36e4ea7aa664b2f53e2fa5f83edd171442dfe927cdd42cbbfaeb5572a174347729b193ec6b000f6b43ffd6850021365967f913
data/CHANGELOG.md CHANGED
@@ -5,50 +5,109 @@ 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.6.0] - 2025-09-28
9
+
10
+ ### Added
11
+
12
+ - Tool annotations support for providing hints about tool behavior (readOnlyHint, destructiveHint, etc.) [#96 @pauloarruda](https://github.com/yjacquin/fast-mcp/pull/96)
13
+ - GitHub Discussions link in README [#139 @jeznicholson](https://github.com/yjacquin/fast-mcp/pull/139)
14
+ - GitHub Sponsors funding configuration [#3d5a7e5 @yjacquin](https://github.com/yjacquin/fast-mcp/commit/3d5a7e5)
15
+ - Server hook `on_error_result` for handling tool execution errors [#129 @yannickutard](https://github.com/yjacquin/fast-mcp/pull/129)
16
+ - Support for transport option in rack_middleware [#149 @josevalim](https://github.com/yjacquin/fast-mcp/pull/149)
17
+
18
+ ### Fixed
19
+
20
+ - Tool name validation and override for invalid names [#100 @abdelrahmanothman](https://github.com/yjacquin/fast-mcp/pull/100)
21
+ - Typo fixes in sinatra_integration.md [#131 @ilyakamenko](https://github.com/yjacquin/fast-mcp/pull/131) [#102 @anton](https://github.com/yjacquin/fast-mcp/pull/102)
22
+ - Add trailing comma to generated initializer template for better Ruby style [#123 @zachhaitz](https://github.com/yjacquin/fast-mcp/pull/123)
23
+ - Fix missing commas for params hash [#121 @FanaHOVA](https://github.com/yjacquin/fast-mcp/pull/121)
24
+ - RuboCop linting issues resolution [fa6af0b @yjacquin](https://github.com/yjacquin/fast-mcp/commit/fa6af0b)
25
+
26
+ ### Changed
27
+
28
+ - Updated GitHub Actions dependencies: checkout v4→v5, download-artifact v4→v5 [#141 #142 @dependabot](https://github.com/yjacquin/fast-mcp/pull/141)
29
+ - Relax Rack version requirements for better compatibility (>= 2.0, < 4.0) [#133 @eproulx](https://github.com/yjacquin/fast-mcp/pull/133)
30
+ - Load generators only when necessary for improved performance [#153 @josevalim](https://github.com/yjacquin/fast-mcp/pull/153)
31
+ - Drop schema compiler to use Dry's built-in functionality [#152 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/152), thanks @redox for the help !
32
+
33
+ ## [1.5.0] - 2025-06-01
34
+
35
+ ### Added
36
+
37
+ - Handle filtering tools and resources [#85 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/85)
38
+ - Support for resource templates 🥳 Big thanks to @danielcooper for the contribution [#84 co-authored by @danielcooper and @yjacquin](https://github.com/yjacquin/fast-mcp/pull/84)
39
+ - Possibility to authorize requests before tool calls [#79 @EuanEdgar](https://github.com/yjacquin/fast-mcp/pull/79)
40
+ - Possibility to read request headers in tool calls [#78 @EuanEdgar](https://github.com/yjacquin/fast-mcp/pull/78)
41
+
42
+ ### Changed
43
+
44
+ - Bump Dependencies [#86 @aothelal](https://github.com/yjacquin/fast-mcp/pull/86)
45
+ - ⚠️ Resources are now stateless, meaning that in-memory resources won't work anymore, they require an external data source such as database, file to read and write too, etc. This was needed for a refactoring of the resource class for the [resource template PR](https://github.com/yjacquin/fast-mcp/pull/84)
46
+
47
+ ### Fixed
48
+
49
+ - Stop overriding log level to info [#91 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/91)
50
+ - Properly handle ping request responses from clients [#89 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/89)
51
+ - Add Thread Safety to RackTransport sse_clients [#82 @Kevin-K](https://github.com/yjacquin/fast-mcp/pull/82)
52
+
8
53
  ## [1.4.0] - 2025-05-10
54
+
9
55
  ### Added
56
+
10
57
  - Conditionnally hidden properties for tool calls (#70) [#70 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/70)
11
58
  - Metadata to tool call results (#69) [#69 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/69)
12
59
  - Link to official Discord Server in README.md
13
60
 
14
61
  ## [1.3.2] - 2025-05-08
62
+
15
63
  ### Changed
64
+
16
65
  - Logs are now less verbose by default [#64 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/64)
66
+
17
67
  ### Fixed
68
+
18
69
  - Fix undefined method `call' for an instance of String error log [#61 @radwo](https://github.com/yjacquin/fast-mcp/pull/61)
19
70
 
20
71
  ## [1.3.1] - 2025-04-30
72
+
21
73
  ### Fixed
22
- - Allow ipv4 mapped to ipv6 (#56) [#56 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
23
- - Add items to array (#55) [#55 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
24
- - Ping is a regular message event (#54) [#56 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
74
+
75
+ - Allow ipv4 mapped to ipv6 (#56) [#56 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
76
+ - Add items to array (#55) [#55 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
77
+ - Ping is a regular message event (#54) [#56 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
25
78
 
26
79
  ## [1.3.0] - 2025-04-28
80
+
27
81
  ### Added
82
+
28
83
  - Added automatic forwarding of query params from to the messages endpoint [@yjacquin](https://github.com/yjacquin/fast-mcp/commit/011d968ac982d0b0084f7753dcac5789f66339ee)
29
84
 
30
85
  ### Fixed
86
+
31
87
  - Declare rack as an explicit dependency [#49 @subelsky](https://github.com/yjacquin/fast-mcp/pull/49)
32
88
  - Fix notifications/initialized response [#51 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/51)
33
89
 
34
90
  ## [1.2.0] - 2025-04-21
91
+
35
92
  ### Added
93
+
36
94
  - Security enhancement: Bing only to localhost by default [#44 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/44)
37
95
  - Prevent AuthenticatedRackMiddleware from blocking other rails routes[#35 @JulianPasquale](https://github.com/yjacquin/fast-mcp/pull/35)
38
96
  - Stop Forcing reconnections after 30 pings [#42 @zoedsoupe](https://github.com/yjacquin/fast-mcp/pull/42)
39
97
 
40
-
41
98
  ## [1.1.0] - 2025-04-13
99
+
42
100
  ### Added
101
+
43
102
  - Security enhancement: Added DNS rebinding protection by validating Origin headers [#32 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/32/files)
44
103
  - Added configuration options for allowed origins in rack middleware [#32 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/32/files)
45
104
  - Allow to change the SSE and Messages route [#23 @pedrofurtado](https://github.com/yjacquin/fast-mcp/pull/23)
46
105
  - Fix invalid return value when processing notifications/initialized request [#31 @abMatGit](https://github.com/yjacquin/fast-mcp/pull/31)
47
106
 
48
-
49
107
  ## [1.0.0] - 2025-03-30
50
108
 
51
109
  ### Added
110
+
52
111
  - Rails integration improvements via enhanced Railtie support
53
112
  - Automatic tool and resource registration in Rails applications
54
113
  - Extended Rails autoload paths for tools and resources directories
@@ -59,6 +118,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
59
118
  - Automated Github Releases through Github Workflow
60
119
 
61
120
  ### Fixed
121
+
62
122
  - Fixed bug with Rack middlewares not being initialized properly.
63
123
  - Fixed bug with STDIO logging preventing a proper connection with clients [# 11 @cs3b](https://github.com/yjacquin/fast-mcp/issues/11)
64
124
  - Fixed Rails SSE streaming detection and handling
@@ -66,9 +126,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66
126
  - Namespace consistency correction (FastMCP -> FastMcp) throughout the codebase
67
127
 
68
128
  ### Improved
129
+
69
130
  - ⚠️ [Breaking] Resource content declaration changes
131
+
70
132
  - Now resources implement `content` over `default_content`
71
133
  - `content` is dynamically called when calling a resource, this implies we can declare dynamic resource contents like:
134
+
72
135
  ```ruby
73
136
  class HighestScoringUsersResource < FastMcp::Resource
74
137
  ...
@@ -77,6 +140,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
140
  end
78
141
  end
79
142
  ```
143
+
80
144
  - More robust SSE connection lifecycle management
81
145
  - Optimized test suite with faster execution times
82
146
  - Better logging for debugging connection issues
data/README.md CHANGED
@@ -10,10 +10,10 @@
10
10
  <a href="https://github.com/yjacquin/fast-mcp/workflows/CI/badge.svg"><img src="https://github.com/yjacquin/fast-mcp/workflows/CI/badge.svg" alt="CI Status" /></a>
11
11
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
12
12
  <a href="code_of_conduct.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant" /></a>
13
- <a href="https://discord.gg/ayu8ZKvz"><img src = "https://dcbadge.limes.pink/api/server/https://discord.gg/ayu8ZKvz?style=flat" alt="Discord invite link" /></a>
13
+ <a href="https://discord.gg/9HHfAtY3HF"><img src = "https://dcbadge.limes.pink/api/server/https://discord.gg/9HHfAtY3HF?style=flat" alt="Discord invite link" /></a>
14
14
  </p>
15
15
 
16
- ## 🌟 Interface your Servers with LLMs in minutes !
16
+ ## 🌟 Interface your Servers with LLMs in minutes
17
17
 
18
18
  AI models are powerful, but they need to interact with your applications to be truly useful. Traditional approaches mean wrestling with:
19
19
 
@@ -32,9 +32,10 @@ Fast MCP solves all these problems by providing a clean, Ruby-focused implementa
32
32
  - 🧩 **Framework Integration** - Works seamlessly with Rails, Sinatra or any Rack app.
33
33
  - 🔒 **Authentication Support** - Secure your AI-powered endpoints with ease
34
34
  - 🚀 **Real-time Updates** - Subscribe to changes for interactive applications
35
-
35
+ - 🎯 **Dynamic Filtering** - Control tool/resource access based on request context (permissions, API versions, etc.)
36
36
 
37
37
  ## 💎 What Makes FastMCP Great
38
+
38
39
  ```ruby
39
40
  # Define tools for AI models to use
40
41
  server = FastMcp::Server.new(name: 'popular-users', version: '1.0.0')
@@ -48,10 +49,10 @@ class CreateUserTool < FastMcp::Tool
48
49
  arguments do
49
50
  required(:first_name).filled(:string).description("First name of the user")
50
51
  optional(:age).filled(:integer).description("Age of the user")
51
- required(:address).hash do
52
- optional(:street).filled(:string)
53
- optional(:city).filled(:string)
54
- optional(:zipcode).filled(:string)
52
+ required(:address).description("The shipping address").hash do
53
+ required(:street).filled(:string).description("Street address")
54
+ optional(:city).filled(:string).description("City name")
55
+ optional(:zipcode).maybe(:string).description("Postal code")
55
56
  end
56
57
  end
57
58
 
@@ -65,7 +66,7 @@ server.register_tool(CreateUserTool)
65
66
 
66
67
  # Share data resources with AI models by inheriting from FastMcp::Resource
67
68
  class PopularUsers < FastMcp::Resource
68
- uri "file://popular_users.json"
69
+ uri "myapp:///users/popular"
69
70
  resource_name "Popular Users"
70
71
  mime_type "application/json"
71
72
 
@@ -74,17 +75,63 @@ class PopularUsers < FastMcp::Resource
74
75
  end
75
76
  end
76
77
 
78
+ class User < FastMcp::Resource
79
+ uri "myapp:///users/{id}" # This is a resource template
80
+ resource_name "user"
81
+ mime_type "application/json"
82
+
83
+ def content
84
+ id = params[:id] # params are computed from the uri pattern
85
+
86
+ JSON.generate(User.find(id).as_json)
87
+ end
88
+ end
89
+
77
90
  # Register the resource with the server
78
- server.register_resource(PopularUsers)
91
+ server.register_resources(PopularUsers, User)
79
92
 
80
93
  # Accessing the resource through the server
81
94
  server.read_resource(PopularUsers.uri)
82
95
 
83
96
  # Notify the resource content has been updated to clients
84
- server.notify_resource_updated(PopularUsers.uri)
97
+ server.notify_resource_updated(PopularUsers.variabilized_uri)
98
+
99
+ # Notifiy the content of a resource from a template has been updated to clients
100
+ server.notify_resource_updated(User.variabilized_uri(id: 1))
101
+ ```
102
+
103
+ ### 🎯 Dynamic Tool Filtering
104
+
105
+ Control which tools and resources are available based on request context:
106
+
107
+ ```ruby
108
+ # Tag your tools for easy filtering
109
+ class AdminTool < FastMcp::Tool
110
+ tags :admin, :dangerous
111
+ description "Perform admin operations"
112
+
113
+ def call
114
+ # Admin only functionality
115
+ end
116
+ end
117
+
118
+ # Filter tools based on user permissions
119
+ server.filter_tools do |request, tools|
120
+ user_role = request.params['role']
121
+
122
+ case user_role
123
+ when 'admin'
124
+ tools # Admins see all tools
125
+ when 'user'
126
+ tools.reject { |t| t.tags.include?(:admin) }
127
+ else
128
+ tools.select { |t| t.tags.include?(:public) }
129
+ end
130
+ end
85
131
  ```
86
132
 
87
133
  ### 🚂 Fast Ruby on Rails implementation
134
+
88
135
  ```shell
89
136
  bundle add fast-mcp
90
137
  bin/rails generate fast_mcp:install
@@ -122,7 +169,9 @@ FastMcp.mount_in_rails(
122
169
  end
123
170
  end
124
171
  ```
172
+
125
173
  The install script will also:
174
+
126
175
  - add app/resources folder
127
176
  - add app/tools folder
128
177
  - add app/tools/sample_tool.rb
@@ -174,6 +223,7 @@ end
174
223
  ```
175
224
 
176
225
  ### Easy Sinatra setup
226
+
177
227
  I'll let you check out the dedicated [sinatra integration docs](./docs/sinatra_integration.md).
178
228
 
179
229
  ## 🚀 Quick Start
@@ -236,17 +286,21 @@ Clone this project, then give it a go !
236
286
  ```shell
237
287
  npx @modelcontextprotocol/inspector examples/server_with_stdio_transport.rb
238
288
  ```
289
+
239
290
  Or to test with an SSE transport using a rack middleware:
291
+
240
292
  ```shell
241
293
  npx @modelcontextprotocol/inspector examples/rack_middleware.rb
242
294
  ```
243
295
 
244
296
  Or to test over SSE with an authenticated rack middleware:
297
+
245
298
  ```shell
246
299
  npx @modelcontextprotocol/inspector examples/authenticated_rack_middleware.rb
247
300
  ```
248
301
 
249
302
  You can test your custom implementation with the official MCP inspector by using:
303
+
250
304
  ```shell
251
305
  # Test with a stdio transport:
252
306
  npx @modelcontextprotocol/inspector path/to/your_ruby_file.rb
@@ -275,6 +329,7 @@ end
275
329
  ### Integrating with Claude Desktop
276
330
 
277
331
  Add your server to your Claude Desktop configuration at:
332
+
278
333
  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
279
334
  - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
280
335
 
@@ -283,28 +338,27 @@ Add your server to your Claude Desktop configuration at:
283
338
  "mcpServers": {
284
339
  "my-great-server": {
285
340
  "command": "ruby",
286
- "args": [
287
- "/Users/path/to/your/awesome/fast-mcp/server.rb"
288
- ]
341
+ "args": ["/Users/path/to/your/awesome/fast-mcp/server.rb"]
289
342
  }
290
343
  }
291
344
  }
292
345
  ```
293
346
 
294
347
  ## How to add a MCP server to Claude, Cursor, or other MCP clients?
348
+
295
349
  Please refer to [configuring_mcp_clients](docs/configuring_mcp_clients.md)
296
350
 
297
351
  ## 📊 Supported Specifications
298
352
 
299
- | Feature | Status |
300
- |---------|--------|
301
- | ✅ **JSON-RPC 2.0** | Full implementation for communication |
302
- | ✅ **Tool Definition & Calling** | Define and call tools with rich argument types |
303
- | ✅ **Resource Management** | Create, read, update, and subscribe to resources |
304
- | ✅ **Transport Options** | STDIO, HTTP, and SSE for flexible integration |
305
- | ✅ **Framework Integration** | Rails, Sinatra, Hanami, and any Rack-compatible framework |
306
- | ✅ **Authentication** | Secure your AI endpoints with token authentication |
307
- | ✅ **Schema Support** | Full JSON Schema for tool arguments with validation |
353
+ | Feature | Status |
354
+ | ----------------------------------------------- | --------------------------------------------------------- |
355
+ | ✅ **JSON-RPC 2.0** | Full implementation for communication |
356
+ | ✅ **Tool Definition & Calling** | Define and call tools with rich argument types |
357
+ | ✅ **Resource & Resource Templates Management** | Create, read, update, and subscribe to resources |
358
+ | ✅ **Transport Options** | STDIO, HTTP, and SSE for flexible integration |
359
+ | ✅ **Framework Integration** | Rails, Sinatra, Hanami, and any Rack-compatible framework |
360
+ | ✅ **Authentication** | Secure your AI endpoints with token authentication |
361
+ | ✅ **Schema Support** | Full JSON Schema for tool arguments with validation |
308
362
 
309
363
  ## 🗺️ Use Cases
310
364
 
@@ -353,12 +407,14 @@ FastMcp.authenticated_rack_middleware(app,
353
407
  - [📚 Resources](docs/resources.md)
354
408
  - [🛠️ Tools](docs/tools.md)
355
409
  - [🔒 Security](docs/security.md)
410
+ - [🎯 Dynamic Filtering](docs/filtering.md)
356
411
 
357
412
  ## 💻 Examples
358
413
 
359
414
  Check out the [examples directory](examples) for more detailed examples:
360
415
 
361
416
  - **🔨 Basic Examples**:
417
+
362
418
  - [Simple Server](examples/server_with_stdio_transport.rb)
363
419
  - [Tool Examples](examples/tool_examples.rb)
364
420
 
data/lib/fast_mcp.rb CHANGED
@@ -16,9 +16,6 @@ require_relative 'mcp/server'
16
16
  require_relative 'mcp/resource'
17
17
  require_relative 'mcp/railtie' if defined?(Rails::Railtie)
18
18
 
19
- # Load generators if Rails is available
20
- require_relative 'generators/fast_mcp/install/install_generator' if defined?(Rails::Generators)
21
-
22
19
  # Require all transport files
23
20
  require_relative 'mcp/transports/base_transport'
24
21
  Dir[File.join(File.dirname(__FILE__), 'mcp/transports', '*.rb')].each do |file|
@@ -39,6 +36,7 @@ module FastMcp
39
36
  # @option options [String] :messages_route The route for the messages endpoint
40
37
  # @option options [String] :sse_route The route for the SSE endpoint
41
38
  # @option options [Logger] :logger The logger to use
39
+ # @option options [Class] :transport The transport class to use
42
40
  # @option options [Array<String,Regexp>] :allowed_origins List of allowed origins for DNS rebinding protection
43
41
  # @yield [server] A block to configure the server
44
42
  # @yieldparam server [FastMcp::Server] The server to configure
@@ -71,17 +69,8 @@ module FastMcp
71
69
  # @yieldparam server [FastMcp::Server] The server to configure
72
70
  # @return [#call] The Rack middleware
73
71
  def self.authenticated_rack_middleware(app, options = {})
74
- name = options.delete(:name) || 'mcp-server'
75
- version = options.delete(:version) || '1.0.0'
76
- logger = options.delete(:logger) || Logger.new
77
-
78
- server = FastMcp::Server.new(name: name, version: version, logger: logger)
79
- yield server if block_given?
80
-
81
- # Store the server in the FastMcp module
82
- self.server = server
83
-
84
- server.start_authenticated_rack(app, options)
72
+ options[:transport] ||= FastMcp::Transports::AuthenticatedRackTransport
73
+ rack_middleware(app, options)
85
74
  end
86
75
 
87
76
  # Register a tool with the MCP server
@@ -20,12 +20,12 @@ FastMcp.mount_in_rails(
20
20
  version: '1.0.0',
21
21
  path_prefix: '/mcp', # This is the default path prefix
22
22
  messages_route: 'messages', # This is the default route for the messages endpoint
23
- sse_route: 'sse' # This is the default route for the SSE endpoint
23
+ sse_route: 'sse', # This is the default route for the SSE endpoint
24
24
  # Add allowed origins below, it defaults to Rails.application.config.hosts
25
25
  # allowed_origins: ['localhost', '127.0.0.1', '[::1]', 'example.com', /.*\.example\.com/],
26
26
  # localhost_only: true, # Set to false to allow connections from other hosts
27
27
  # whitelist specific ips to if you want to run on localhost and allow connections from other IPs
28
- # allowed_ips: ['127.0.0.1', '::1']
28
+ # allowed_ips: ['127.0.0.1', '::1'],
29
29
  # authenticate: true, # Uncomment to enable authentication
30
30
  # auth_token: 'your-token', # Required if authenticate: true
31
31
  ) do |server|
@@ -3,6 +3,13 @@
3
3
  class SampleTool < ApplicationTool
4
4
  description 'Greet a user'
5
5
 
6
+ # Optional: Add annotations to provide hints about the tool's behavior
7
+ # annotations(
8
+ # title: 'User Greeting',
9
+ # read_only_hint: true, # This tool only reads data
10
+ # open_world_hint: false # This tool only accesses the local database
11
+ # )
12
+
6
13
  arguments do
7
14
  required(:id).filled(:integer).description('ID of the user to greet')
8
15
  optional(:prefix).filled(:string).description('Prefix to add to the greeting')
data/lib/mcp/railtie.rb CHANGED
@@ -41,5 +41,9 @@ module FastMcp
41
41
  path = File.expand_path('../tasks', __dir__)
42
42
  Dir.glob("#{path}/**/*.rake").each { |f| load f }
43
43
  end
44
+
45
+ generators do
46
+ require 'generators/fast_mcp/install/install_generator'
47
+ end
44
48
  end
45
49
  end
data/lib/mcp/resource.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'json'
4
4
  require 'base64'
5
5
  require 'mime/types'
6
- require 'singleton'
6
+ require 'addressable/template'
7
7
 
8
8
  module FastMcp
9
9
  # Resource class for MCP Resources feature
@@ -17,9 +17,62 @@ module FastMcp
17
17
  # @return [String] The URI for this resource
18
18
  def uri(value = nil)
19
19
  @uri = value if value
20
+
20
21
  @uri || (superclass.respond_to?(:uri) ? superclass.uri : nil)
21
22
  end
22
23
 
24
+ # Variabilize the URI with the given params
25
+ # @param params [Hash] The parameters to variabilize the URI with
26
+ # @return [String] The variabilized URI
27
+ def variabilized_uri(params = {})
28
+ addressable_template.partial_expand(params).pattern
29
+ end
30
+
31
+ # Get the Addressable::Template for this resource
32
+ # @return [Addressable::Template] The Addressable::Template for this resource
33
+ def addressable_template
34
+ @addressable_template ||= Addressable::Template.new(uri)
35
+ end
36
+
37
+ # Get the template variables for this resource
38
+ # @return [Array] The template variables for this resource
39
+ def template_variables
40
+ addressable_template.variables
41
+ end
42
+
43
+ # Check if this resource has a templated URI
44
+ # @return [Boolean] true if the URI contains template parameters
45
+ def templated?
46
+ template_variables.any?
47
+ end
48
+
49
+ # Check if this resource has a non-templated URI
50
+ # @return [Boolean] true if the URI does not contain template parameters
51
+ def non_templated?
52
+ !templated?
53
+ end
54
+
55
+ # Match the given URI against the resource's addressable template
56
+ # @param uri [String] The URI to match
57
+ # @return [Addressable::Template::MatchData, nil] The match data if the URI matches, nil otherwise
58
+ def match(uri)
59
+ addressable_template.match(uri)
60
+ end
61
+
62
+ # Initialize a new instance from the given URI
63
+ # @param uri [String] The URI to initialize from
64
+ # @return [Resource] A new resource instance
65
+ def initialize_from_uri(uri)
66
+ new(params_from_uri(uri))
67
+ end
68
+
69
+ # Get the parameters from the given URI
70
+ # @param uri [String] The URI to get the parameters from
71
+ # @return [Hash] The parameters from the URI
72
+ def params_from_uri(uri)
73
+ match(uri).mapping.transform_keys(&:to_sym)
74
+ end
75
+
23
76
  # Define name for this resource
24
77
  # @param value [String, nil] The name for this resource
25
78
  # @return [String] The name for this resource
@@ -28,6 +81,13 @@ module FastMcp
28
81
  @name || (superclass.respond_to?(:resource_name) ? superclass.resource_name : nil)
29
82
  end
30
83
 
84
+ alias original_name name
85
+ def name
86
+ return resource_name if resource_name
87
+
88
+ original_name
89
+ end
90
+
31
91
  # Define description for this resource
32
92
  # @param value [String, nil] The description for this resource
33
93
  # @return [String] The description for this resource
@@ -47,12 +107,21 @@ module FastMcp
47
107
  # Get the resource metadata (without content)
48
108
  # @return [Hash] Resource metadata
49
109
  def metadata
50
- {
51
- uri: uri,
52
- name: resource_name,
53
- description: description,
54
- mimeType: mime_type
55
- }.compact
110
+ if templated?
111
+ {
112
+ uriTemplate: uri,
113
+ name: resource_name,
114
+ description: description,
115
+ mimeType: mime_type
116
+ }.compact
117
+ else
118
+ {
119
+ uri: uri,
120
+ name: resource_name,
121
+ description: description,
122
+ mimeType: mime_type
123
+ }.compact
124
+ end
56
125
  end
57
126
 
58
127
  # Load content from a file (class method)
@@ -87,7 +156,11 @@ module FastMcp
87
156
  end
88
157
  end
89
158
 
90
- include Singleton
159
+ # Initialize with instance variables
160
+ # @param params [Hash] The parameters for this resource instance
161
+ def initialize(params = {})
162
+ @params = params
163
+ end
91
164
 
92
165
  # URI of the resource - delegates to class method
93
166
  # @return [String, nil] The URI for this resource
@@ -98,7 +171,7 @@ module FastMcp
98
171
  # Name of the resource - delegates to class method
99
172
  # @return [String, nil] The name for this resource
100
173
  def name
101
- self.class.name
174
+ self.class.resource_name
102
175
  end
103
176
 
104
177
  # Description of the resource - delegates to class method
@@ -113,6 +186,10 @@ module FastMcp
113
186
  self.class.mime_type
114
187
  end
115
188
 
189
+ # Get parameters from the URI template
190
+ # @return [Hash] The parameters extracted from the URI
191
+ attr_reader :params
192
+
116
193
  # Method to be overridden by subclasses to dynamically generate content
117
194
  # @return [String, nil] Generated content for this resource
118
195
  def content
@@ -129,25 +206,5 @@ module FastMcp
129
206
  mime_type == 'application/xml' ||
130
207
  mime_type == 'application/javascript')
131
208
  end
132
-
133
- # Get the resource contents
134
- # @return [Hash] Resource contents
135
- def contents
136
- result = {
137
- uri: uri,
138
- mimeType: mime_type
139
- }
140
-
141
- content_value = content
142
- if content_value
143
- if binary?
144
- result[:blob] = Base64.strict_encode64(content_value)
145
- else
146
- result[:text] = content_value
147
- end
148
- end
149
-
150
- result
151
- end
152
209
  end
153
210
  end