fast-mcp 1.5.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: 1a99f230c0ff73568287d0812e53c266dffca1bd46de3c0308000d5658b5b9a4
4
- data.tar.gz: 38fb4275c6cf123691ed02145687c70f5251be28e784f71b42a00c85b5fbee3d
3
+ metadata.gz: 37917d61ab02f9d3a42e8980b9007bdb0003f2b91847b5cfe6e6a5f0043550cd
4
+ data.tar.gz: 476fa4ea8a2c022c255edcf3e5b3d101748fddaa8643569d2bc999ef8ec74eee
5
5
  SHA512:
6
- metadata.gz: f3863d28ed426ef2274475d0ba39630dcc673ef91a416a3ae4b76833cdb87dac3d40d6f01806dddf32572e0d0fa7ab5f5b0a5dd0c5619c6c73202dcfbfa4ccf5
7
- data.tar.gz: 246ee983dc56d22708261681f5df1f0150e1376f19a18e373024ed5ecd12c1a54f592998a9738fb8d0aabb7194fe937ce98304a145f7bbf12efc1555d0b7d794
6
+ metadata.gz: f12f0d7bf4a4f36b4a5d6c642160c01ccf40d5b799020cbc2c16646c7332b51083bc525c867f38cd2d5a68b3757f51f2fb0f632c35e004c7fcd7a8cb0f571cdf
7
+ data.tar.gz: 559edc8f334fcf49c16b001c8e36e4ea7aa664b2f53e2fa5f83edd171442dfe927cdd42cbbfaeb5572a174347729b193ec6b000f6b43ffd6850021365967f913
data/CHANGELOG.md CHANGED
@@ -5,64 +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
+
8
33
  ## [1.5.0] - 2025-06-01
34
+
9
35
  ### Added
36
+
10
37
  - Handle filtering tools and resources [#85 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/85)
11
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)
12
39
  - Possibility to authorize requests before tool calls [#79 @EuanEdgar](https://github.com/yjacquin/fast-mcp/pull/79)
13
40
  - Possibility to read request headers in tool calls [#78 @EuanEdgar](https://github.com/yjacquin/fast-mcp/pull/78)
41
+
14
42
  ### Changed
43
+
15
44
  - Bump Dependencies [#86 @aothelal](https://github.com/yjacquin/fast-mcp/pull/86)
16
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
+
17
47
  ### Fixed
48
+
18
49
  - Stop overriding log level to info [#91 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/91)
19
50
  - Properly handle ping request responses from clients [#89 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/89)
20
51
  - Add Thread Safety to RackTransport sse_clients [#82 @Kevin-K](https://github.com/yjacquin/fast-mcp/pull/82)
21
52
 
22
53
  ## [1.4.0] - 2025-05-10
54
+
23
55
  ### Added
56
+
24
57
  - Conditionnally hidden properties for tool calls (#70) [#70 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/70)
25
58
  - Metadata to tool call results (#69) [#69 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/69)
26
59
  - Link to official Discord Server in README.md
27
60
 
28
61
  ## [1.3.2] - 2025-05-08
62
+
29
63
  ### Changed
64
+
30
65
  - Logs are now less verbose by default [#64 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/64)
66
+
31
67
  ### Fixed
68
+
32
69
  - Fix undefined method `call' for an instance of String error log [#61 @radwo](https://github.com/yjacquin/fast-mcp/pull/61)
33
70
 
34
71
  ## [1.3.1] - 2025-04-30
72
+
35
73
  ### Fixed
36
- - Allow ipv4 mapped to ipv6 (#56) [#56 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
37
- - Add items to array (#55) [#55 @josevalim](https://github.com/yjacquin/fast-mcp/pull/56)
38
- - 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)
39
78
 
40
79
  ## [1.3.0] - 2025-04-28
80
+
41
81
  ### Added
82
+
42
83
  - Added automatic forwarding of query params from to the messages endpoint [@yjacquin](https://github.com/yjacquin/fast-mcp/commit/011d968ac982d0b0084f7753dcac5789f66339ee)
43
84
 
44
85
  ### Fixed
86
+
45
87
  - Declare rack as an explicit dependency [#49 @subelsky](https://github.com/yjacquin/fast-mcp/pull/49)
46
88
  - Fix notifications/initialized response [#51 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/51)
47
89
 
48
90
  ## [1.2.0] - 2025-04-21
91
+
49
92
  ### Added
93
+
50
94
  - Security enhancement: Bing only to localhost by default [#44 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/44)
51
95
  - Prevent AuthenticatedRackMiddleware from blocking other rails routes[#35 @JulianPasquale](https://github.com/yjacquin/fast-mcp/pull/35)
52
96
  - Stop Forcing reconnections after 30 pings [#42 @zoedsoupe](https://github.com/yjacquin/fast-mcp/pull/42)
53
97
 
54
-
55
98
  ## [1.1.0] - 2025-04-13
99
+
56
100
  ### Added
101
+
57
102
  - Security enhancement: Added DNS rebinding protection by validating Origin headers [#32 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/32/files)
58
103
  - Added configuration options for allowed origins in rack middleware [#32 @yjacquin](https://github.com/yjacquin/fast-mcp/pull/32/files)
59
104
  - Allow to change the SSE and Messages route [#23 @pedrofurtado](https://github.com/yjacquin/fast-mcp/pull/23)
60
105
  - Fix invalid return value when processing notifications/initialized request [#31 @abMatGit](https://github.com/yjacquin/fast-mcp/pull/31)
61
106
 
62
-
63
107
  ## [1.0.0] - 2025-03-30
64
108
 
65
109
  ### Added
110
+
66
111
  - Rails integration improvements via enhanced Railtie support
67
112
  - Automatic tool and resource registration in Rails applications
68
113
  - Extended Rails autoload paths for tools and resources directories
@@ -73,6 +118,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
73
118
  - Automated Github Releases through Github Workflow
74
119
 
75
120
  ### Fixed
121
+
76
122
  - Fixed bug with Rack middlewares not being initialized properly.
77
123
  - Fixed bug with STDIO logging preventing a proper connection with clients [# 11 @cs3b](https://github.com/yjacquin/fast-mcp/issues/11)
78
124
  - Fixed Rails SSE streaming detection and handling
@@ -80,9 +126,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
80
126
  - Namespace consistency correction (FastMCP -> FastMcp) throughout the codebase
81
127
 
82
128
  ### Improved
129
+
83
130
  - ⚠️ [Breaking] Resource content declaration changes
131
+
84
132
  - Now resources implement `content` over `default_content`
85
133
  - `content` is dynamically called when calling a resource, this implies we can declare dynamic resource contents like:
134
+
86
135
  ```ruby
87
136
  class HighestScoringUsersResource < FastMcp::Resource
88
137
  ...
@@ -91,6 +140,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
91
140
  end
92
141
  end
93
142
  ```
143
+
94
144
  - More robust SSE connection lifecycle management
95
145
  - Optimized test suite with faster execution times
96
146
  - Better logging for debugging connection issues
data/README.md CHANGED
@@ -13,7 +13,7 @@
13
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
 
@@ -34,8 +34,8 @@ Fast MCP solves all these problems by providing a clean, Ruby-focused implementa
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
-
38
37
  ## 💎 What Makes FastMCP Great
38
+
39
39
  ```ruby
40
40
  # Define tools for AI models to use
41
41
  server = FastMcp::Server.new(name: 'popular-users', version: '1.0.0')
@@ -49,10 +49,10 @@ class CreateUserTool < FastMcp::Tool
49
49
  arguments do
50
50
  required(:first_name).filled(:string).description("First name of the user")
51
51
  optional(:age).filled(:integer).description("Age of the user")
52
- required(:address).hash do
53
- optional(:street).filled(:string)
54
- optional(:city).filled(:string)
55
- 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")
56
56
  end
57
57
  end
58
58
 
@@ -109,7 +109,7 @@ Control which tools and resources are available based on request context:
109
109
  class AdminTool < FastMcp::Tool
110
110
  tags :admin, :dangerous
111
111
  description "Perform admin operations"
112
-
112
+
113
113
  def call
114
114
  # Admin only functionality
115
115
  end
@@ -118,7 +118,7 @@ end
118
118
  # Filter tools based on user permissions
119
119
  server.filter_tools do |request, tools|
120
120
  user_role = request.params['role']
121
-
121
+
122
122
  case user_role
123
123
  when 'admin'
124
124
  tools # Admins see all tools
@@ -131,6 +131,7 @@ end
131
131
  ```
132
132
 
133
133
  ### 🚂 Fast Ruby on Rails implementation
134
+
134
135
  ```shell
135
136
  bundle add fast-mcp
136
137
  bin/rails generate fast_mcp:install
@@ -168,7 +169,9 @@ FastMcp.mount_in_rails(
168
169
  end
169
170
  end
170
171
  ```
172
+
171
173
  The install script will also:
174
+
172
175
  - add app/resources folder
173
176
  - add app/tools folder
174
177
  - add app/tools/sample_tool.rb
@@ -220,6 +223,7 @@ end
220
223
  ```
221
224
 
222
225
  ### Easy Sinatra setup
226
+
223
227
  I'll let you check out the dedicated [sinatra integration docs](./docs/sinatra_integration.md).
224
228
 
225
229
  ## 🚀 Quick Start
@@ -282,17 +286,21 @@ Clone this project, then give it a go !
282
286
  ```shell
283
287
  npx @modelcontextprotocol/inspector examples/server_with_stdio_transport.rb
284
288
  ```
289
+
285
290
  Or to test with an SSE transport using a rack middleware:
291
+
286
292
  ```shell
287
293
  npx @modelcontextprotocol/inspector examples/rack_middleware.rb
288
294
  ```
289
295
 
290
296
  Or to test over SSE with an authenticated rack middleware:
297
+
291
298
  ```shell
292
299
  npx @modelcontextprotocol/inspector examples/authenticated_rack_middleware.rb
293
300
  ```
294
301
 
295
302
  You can test your custom implementation with the official MCP inspector by using:
303
+
296
304
  ```shell
297
305
  # Test with a stdio transport:
298
306
  npx @modelcontextprotocol/inspector path/to/your_ruby_file.rb
@@ -321,6 +329,7 @@ end
321
329
  ### Integrating with Claude Desktop
322
330
 
323
331
  Add your server to your Claude Desktop configuration at:
332
+
324
333
  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
325
334
  - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
326
335
 
@@ -329,28 +338,27 @@ Add your server to your Claude Desktop configuration at:
329
338
  "mcpServers": {
330
339
  "my-great-server": {
331
340
  "command": "ruby",
332
- "args": [
333
- "/Users/path/to/your/awesome/fast-mcp/server.rb"
334
- ]
341
+ "args": ["/Users/path/to/your/awesome/fast-mcp/server.rb"]
335
342
  }
336
343
  }
337
344
  }
338
345
  ```
339
346
 
340
347
  ## How to add a MCP server to Claude, Cursor, or other MCP clients?
348
+
341
349
  Please refer to [configuring_mcp_clients](docs/configuring_mcp_clients.md)
342
350
 
343
351
  ## 📊 Supported Specifications
344
352
 
345
- | Feature | Status |
346
- |---------|--------|
347
- | ✅ **JSON-RPC 2.0** | Full implementation for communication |
348
- | ✅ **Tool Definition & Calling** | Define and call tools with rich argument types |
349
- | ✅ **Resource & Resource Templates Management** | Create, read, update, and subscribe to resources |
350
- | ✅ **Transport Options** | STDIO, HTTP, and SSE for flexible integration |
351
- | ✅ **Framework Integration** | Rails, Sinatra, Hanami, and any Rack-compatible framework |
352
- | ✅ **Authentication** | Secure your AI endpoints with token authentication |
353
- | ✅ **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 |
354
362
 
355
363
  ## 🗺️ Use Cases
356
364
 
@@ -406,6 +414,7 @@ FastMcp.authenticated_rack_middleware(app,
406
414
  Check out the [examples directory](examples) for more detailed examples:
407
415
 
408
416
  - **🔨 Basic Examples**:
417
+
409
418
  - [Simple Server](examples/server_with_stdio_transport.rb)
410
419
  - [Tool Examples](examples/tool_examples.rb)
411
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/server.rb CHANGED
@@ -39,6 +39,7 @@ module FastMcp
39
39
  @capabilities = DEFAULT_CAPABILITIES.dup
40
40
  @tool_filters = []
41
41
  @resource_filters = []
42
+ @on_error_result = nil
42
43
 
43
44
  # Merge with provided capabilities
44
45
  @capabilities.merge!(capabilities) if capabilities.is_a?(Hash)
@@ -80,6 +81,10 @@ module FastMcp
80
81
  resource
81
82
  end
82
83
 
84
+ def on_error_result(&block)
85
+ @on_error_result = block
86
+ end
87
+
83
88
  # Remove a resource from the server
84
89
  def remove_resource(uri)
85
90
  resource = @resources.find { |r| r.uri == uri }
@@ -112,27 +117,14 @@ module FastMcp
112
117
 
113
118
  # Start the server as a Rack middleware
114
119
  def start_rack(app, options = {})
115
- @logger.info("Starting MCP server as Rack middleware: #{@name} v#{@version}")
116
- @logger.info("Available tools: #{@tools.keys.join(', ')}")
117
- @logger.info("Available resources: #{@resources.map(&:resource_name).join(', ')}")
118
-
119
- # Use Rack transport
120
- transport_klass = FastMcp::Transports::RackTransport
121
- @transport = transport_klass.new(app, self, options.merge(logger: @logger))
122
- @transport.start
120
+ @transport_klass = options.delete(:transport) || FastMcp::Transports::RackTransport
121
+ transport_name = @transport_klass.name.split('::').last
123
122
 
124
- # Return the transport as middleware
125
- @transport
126
- end
127
-
128
- def start_authenticated_rack(app, options = {})
129
- @logger.info("Starting MCP server as Authenticated Rack middleware: #{@name} v#{@version}")
123
+ @logger.info("Starting MCP server with #{transport_name}: #{@name} v#{@version}")
130
124
  @logger.info("Available tools: #{@tools.keys.join(', ')}")
131
125
  @logger.info("Available resources: #{@resources.map(&:resource_name).join(', ')}")
132
126
 
133
- # Use Rack transport
134
- transport_klass = FastMcp::Transports::AuthenticatedRackTransport
135
- @transport = transport_klass.new(app, self, options.merge(logger: @logger))
127
+ @transport = @transport_klass.new(app, self, options.merge(logger: @logger))
136
128
  @transport.start
137
129
 
138
130
  # Return the transport as middleware
@@ -294,11 +286,25 @@ module FastMcp
294
286
  # Handle tools/list request
295
287
  def handle_tools_list(id)
296
288
  tools_list = @tools.values.map do |tool|
297
- {
289
+ tool_info = {
298
290
  name: tool.tool_name,
299
291
  description: tool.description || '',
300
292
  inputSchema: tool.input_schema_to_json || { type: 'object', properties: {}, required: [] }
301
293
  }
294
+
295
+ # Add annotations if they exist
296
+ annotations = tool.annotations
297
+ unless annotations.empty?
298
+ # Convert snake_case keys to camelCase for MCP protocol
299
+ camel_case_annotations = {}
300
+ annotations.each do |key, value|
301
+ camel_key = key.to_s.gsub(/_([a-z])/) { ::Regexp.last_match(1).upcase }.to_sym
302
+ camel_case_annotations[camel_key] = value
303
+ end
304
+ tool_info[:annotations] = camel_case_annotations
305
+ end
306
+
307
+ tool_info
302
308
  end
303
309
 
304
310
  send_result({ tools: tools_list }, id)
@@ -354,6 +360,8 @@ module FastMcp
354
360
 
355
361
  # Format and send error result
356
362
  def send_error_result(message, id)
363
+ @on_error_result&.call(message)
364
+
357
365
  # Format error according to the MCP specification
358
366
  error_result = {
359
367
  content: [{ type: 'text', text: "Error: #{message}" }],