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
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators/base'
|
4
|
+
|
5
|
+
module FastMcp
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
9
|
+
|
10
|
+
desc 'Creates a FastMcp initializer for Rails applications'
|
11
|
+
|
12
|
+
def copy_initializer
|
13
|
+
template 'fast_mcp_initializer.rb', 'config/initializers/fast_mcp.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_directories
|
17
|
+
empty_directory 'app/tools'
|
18
|
+
empty_directory 'app/resources'
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy_application_tool
|
22
|
+
template 'application_tool.rb', 'app/tools/application_tool.rb'
|
23
|
+
end
|
24
|
+
|
25
|
+
def copy_application_resource
|
26
|
+
template 'application_resource.rb', 'app/resources/application_resource.rb'
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy_sample_tool
|
30
|
+
template 'sample_tool.rb', 'app/tools/sample_tool.rb'
|
31
|
+
end
|
32
|
+
|
33
|
+
def copy_sample_resource
|
34
|
+
template 'sample_resource.rb', 'app/resources/sample_resource.rb'
|
35
|
+
end
|
36
|
+
|
37
|
+
def display_post_install_message
|
38
|
+
say "\n========================================================="
|
39
|
+
say 'FastMcp was successfully installed! 🎉'
|
40
|
+
say "=========================================================\n"
|
41
|
+
say 'You can now create:'
|
42
|
+
say ' • Tools in app/tools/'
|
43
|
+
say ' • Resources in app/resources/'
|
44
|
+
say "\n"
|
45
|
+
say 'Check config/initializers/fast_mcp.rb to configure the middleware.'
|
46
|
+
say "=========================================================\n"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# FastMcp - Model Context Protocol for Rails
|
4
|
+
# This initializer sets up the MCP middleware in your Rails application.
|
5
|
+
#
|
6
|
+
# In Rails applications, you can use:
|
7
|
+
# - ActionTool::Base as an alias for FastMcp::Tool
|
8
|
+
# - ActionResource::Base as an alias for FastMcp::Resource
|
9
|
+
#
|
10
|
+
# All your tools should inherit from ApplicationTool which already uses ActionTool::Base,
|
11
|
+
# and all your resources should inherit from ApplicationResource which uses ActionResource::Base.
|
12
|
+
|
13
|
+
# Mount the MCP middleware in your Rails application
|
14
|
+
# You can customize the options below to fit your needs.
|
15
|
+
require 'fast_mcp'
|
16
|
+
|
17
|
+
FastMcp.mount_in_rails(
|
18
|
+
Rails.application,
|
19
|
+
name: Rails.application.class.module_parent_name.underscore.dasherize,
|
20
|
+
version: '1.0.0',
|
21
|
+
path_prefix: '/mcp', # This is the default path prefix
|
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
|
24
|
+
# Add allowed origins below, it defaults to Rails.application.config.hosts
|
25
|
+
# allowed_origins: ['localhost', '127.0.0.1', 'example.com', /.*\.example\.com/],
|
26
|
+
# authenticate: true, # Uncomment to enable authentication
|
27
|
+
# auth_token: 'your-token', # Required if authenticate: true
|
28
|
+
) do |server|
|
29
|
+
Rails.application.config.after_initialize do
|
30
|
+
# FastMcp will automatically discover and register:
|
31
|
+
# - All classes that inherit from ApplicationTool (which uses ActionTool::Base)
|
32
|
+
# - All classes that inherit from ApplicationResource (which uses ActionResource::Base)
|
33
|
+
server.register_tools(*ApplicationTool.descendants)
|
34
|
+
server.register_resources(*ApplicationResource.descendants)
|
35
|
+
# alternatively, you can register tools and resources manually:
|
36
|
+
# server.register_tool(MyTool)
|
37
|
+
# server.register_resource(MyResource)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SampleResource < ApplicationResource
|
4
|
+
uri 'examples/users'
|
5
|
+
resource_name 'Users'
|
6
|
+
description 'A user resource for demonstration'
|
7
|
+
mime_type 'application/json'
|
8
|
+
|
9
|
+
def content
|
10
|
+
JSON.generate(User.all.as_json)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SampleTool < ApplicationTool
|
4
|
+
description 'Greet a user'
|
5
|
+
|
6
|
+
arguments do
|
7
|
+
required(:id).filled(:integer).description('ID of the user to greet')
|
8
|
+
optional(:prefix).filled(:string).description('Prefix to add to the greeting')
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(id:, prefix: 'Hey')
|
12
|
+
user = User.find(id)
|
13
|
+
|
14
|
+
"#{prefix} #{user.first_name} !"
|
15
|
+
end
|
16
|
+
end
|
data/lib/mcp/logger.rb
CHANGED
@@ -1,33 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# This class is not used yet.
|
4
|
-
module
|
4
|
+
module FastMcp
|
5
5
|
class Logger < Logger
|
6
|
-
def initialize
|
6
|
+
def initialize(transport: :stdio)
|
7
7
|
@client_initialized = false
|
8
|
-
@transport =
|
8
|
+
@transport = transport
|
9
9
|
|
10
|
-
|
10
|
+
# we don't want to log to stdout if we're using the stdio transport
|
11
|
+
super($stdout) unless stdio_transport?
|
11
12
|
end
|
12
13
|
|
13
14
|
attr_accessor :transport, :client_initialized
|
14
|
-
|
15
|
-
def client_initialized?
|
16
|
-
client_initialized
|
17
|
-
end
|
15
|
+
alias client_initialized? client_initialized
|
18
16
|
|
19
17
|
def stdio_transport?
|
20
18
|
transport == :stdio
|
21
19
|
end
|
22
20
|
|
21
|
+
def add(severity, message = nil, progname = nil, &block)
|
22
|
+
return if stdio_transport? # we don't want to log to stdout if we're using the stdio transport
|
23
|
+
|
24
|
+
# TODO: implement logging as the specification requires
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
23
28
|
def rack_transport?
|
24
29
|
transport == :rack
|
25
30
|
end
|
26
|
-
|
27
|
-
# def add(severity, message = nil, progname = nil, &block)
|
28
|
-
# # return unless client_initialized? && rack_transport?
|
29
|
-
|
30
|
-
# super
|
31
|
-
# end
|
32
31
|
end
|
33
32
|
end
|
data/lib/mcp/railtie.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'fileutils'
|
5
|
+
require_relative '../mcp/server'
|
6
|
+
|
7
|
+
# Create ActionTool and ActionResource modules at load time
|
8
|
+
unless defined?(ActionTool)
|
9
|
+
module ::ActionTool
|
10
|
+
Base = FastMcp::Tool
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
unless defined?(ActionResource)
|
15
|
+
module ::ActionResource
|
16
|
+
Base = FastMcp::Resource
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module FastMcp
|
21
|
+
# Railtie for integrating Fast MCP with Rails applications
|
22
|
+
class Railtie < Rails::Railtie
|
23
|
+
# Add tools and resources directories to autoload paths
|
24
|
+
initializer 'fast_mcp.setup_autoload_paths' do |app|
|
25
|
+
app.config.autoload_paths += %W[
|
26
|
+
#{app.root}/app/tools
|
27
|
+
#{app.root}/app/resources
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Auto-register all tools and resources after the application is fully loaded
|
32
|
+
config.after_initialize do
|
33
|
+
# Load all files in app/tools and app/resources directories
|
34
|
+
Dir[Rails.root.join('app', 'tools', '**', '*.rb')].each { |f| require f }
|
35
|
+
Dir[Rails.root.join('app', 'resources', '**', '*.rb')].each { |f| require f }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add rake tasks
|
39
|
+
rake_tasks do
|
40
|
+
# Path to the tasks directory in the gem
|
41
|
+
path = File.expand_path('../tasks', __dir__)
|
42
|
+
Dir.glob("#{path}/**/*.rake").each { |f| load f }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/mcp/resource.rb
CHANGED
@@ -5,11 +5,13 @@ require 'base64'
|
|
5
5
|
require 'mime/types'
|
6
6
|
require 'singleton'
|
7
7
|
|
8
|
-
module
|
8
|
+
module FastMcp
|
9
9
|
# Resource class for MCP Resources feature
|
10
10
|
# Represents a resource that can be exposed to clients
|
11
11
|
class Resource
|
12
12
|
class << self
|
13
|
+
attr_accessor :server
|
14
|
+
|
13
15
|
# Define URI for this resource
|
14
16
|
# @param value [String, nil] The URI for this resource
|
15
17
|
# @return [String] The URI for this resource
|
@@ -74,7 +76,7 @@ module MCP
|
|
74
76
|
end
|
75
77
|
|
76
78
|
# Override content method to load from file
|
77
|
-
define_method :
|
79
|
+
define_method :content do
|
78
80
|
if binary?
|
79
81
|
File.binread(file_path)
|
80
82
|
else
|
@@ -87,13 +89,6 @@ module MCP
|
|
87
89
|
|
88
90
|
include Singleton
|
89
91
|
|
90
|
-
attr_accessor :content
|
91
|
-
|
92
|
-
# Initialize a resource singleton instance
|
93
|
-
def initialize
|
94
|
-
@content = default_content
|
95
|
-
end
|
96
|
-
|
97
92
|
# URI of the resource - delegates to class method
|
98
93
|
# @return [String, nil] The URI for this resource
|
99
94
|
def uri
|
@@ -120,7 +115,7 @@ module MCP
|
|
120
115
|
|
121
116
|
# Method to be overridden by subclasses to dynamically generate content
|
122
117
|
# @return [String, nil] Generated content for this resource
|
123
|
-
def
|
118
|
+
def content
|
124
119
|
raise NotImplementedError, 'Subclasses must implement content'
|
125
120
|
end
|
126
121
|
|
data/lib/mcp/server.rb
CHANGED
@@ -9,9 +9,9 @@ require_relative 'transports/rack_transport'
|
|
9
9
|
require_relative 'transports/authenticated_rack_transport'
|
10
10
|
require_relative 'logger'
|
11
11
|
|
12
|
-
module
|
12
|
+
module FastMcp
|
13
13
|
class Server
|
14
|
-
attr_reader :name, :version, :tools, :resources, :
|
14
|
+
attr_reader :name, :version, :tools, :resources, :capabilities
|
15
15
|
|
16
16
|
DEFAULT_CAPABILITIES = {
|
17
17
|
resources: {
|
@@ -23,7 +23,7 @@ module MCP
|
|
23
23
|
}
|
24
24
|
}.freeze
|
25
25
|
|
26
|
-
def initialize(name:, version:, logger:
|
26
|
+
def initialize(name:, version:, logger: FastMcp::Logger.new, capabilities: {})
|
27
27
|
@name = name
|
28
28
|
@version = version
|
29
29
|
@tools = {}
|
@@ -32,13 +32,17 @@ module MCP
|
|
32
32
|
@logger = logger
|
33
33
|
@logger.level = Logger::INFO
|
34
34
|
@request_id = 0
|
35
|
+
@transport_klass = nil
|
35
36
|
@transport = nil
|
36
37
|
@capabilities = DEFAULT_CAPABILITIES.dup
|
37
38
|
|
38
39
|
# Merge with provided capabilities
|
39
40
|
@capabilities.merge!(capabilities) if capabilities.is_a?(Hash)
|
40
41
|
end
|
42
|
+
attr_accessor :transport, :transport_klass, :logger
|
41
43
|
|
44
|
+
# Register multiple tools at once
|
45
|
+
# @param tools [Array<Tool>] Tools to register
|
42
46
|
def register_tools(*tools)
|
43
47
|
tools.each do |tool|
|
44
48
|
register_tool(tool)
|
@@ -49,6 +53,7 @@ module MCP
|
|
49
53
|
def register_tool(tool)
|
50
54
|
@tools[tool.tool_name] = tool
|
51
55
|
@logger.info("Registered tool: #{tool.tool_name}")
|
56
|
+
tool.server = self
|
52
57
|
end
|
53
58
|
|
54
59
|
# Register multiple resources at once
|
@@ -63,7 +68,7 @@ module MCP
|
|
63
68
|
def register_resource(resource)
|
64
69
|
@resources[resource.uri] = resource
|
65
70
|
@logger.info("Registered resource: #{resource.name} (#{resource.uri})")
|
66
|
-
|
71
|
+
resource.server = self
|
67
72
|
# Notify subscribers about the list change
|
68
73
|
notify_resource_list_changed if @transport
|
69
74
|
|
@@ -93,7 +98,8 @@ module MCP
|
|
93
98
|
@logger.info("Available resources: #{@resources.keys.join(', ')}")
|
94
99
|
|
95
100
|
# Use STDIO transport by default
|
96
|
-
@
|
101
|
+
@transport_klass = FastMcp::Transports::StdioTransport
|
102
|
+
@transport = @transport_klass.new(self, logger: @logger)
|
97
103
|
@transport.start
|
98
104
|
end
|
99
105
|
|
@@ -104,7 +110,8 @@ module MCP
|
|
104
110
|
@logger.info("Available resources: #{@resources.keys.join(', ')}")
|
105
111
|
|
106
112
|
# Use Rack transport
|
107
|
-
|
113
|
+
transport_klass = FastMcp::Transports::RackTransport
|
114
|
+
@transport = transport_klass.new(app, self, options.merge(logger: @logger))
|
108
115
|
@transport.start
|
109
116
|
|
110
117
|
# Return the transport as middleware
|
@@ -117,7 +124,8 @@ module MCP
|
|
117
124
|
@logger.info("Available resources: #{@resources.keys.join(', ')}")
|
118
125
|
|
119
126
|
# Use Rack transport
|
120
|
-
|
127
|
+
transport_klass = FastMcp::Transports::AuthenticatedRackTransport
|
128
|
+
@transport = transport_klass.new(app, self, options.merge(logger: @logger))
|
121
129
|
@transport.start
|
122
130
|
|
123
131
|
# Return the transport as middleware
|
@@ -180,47 +188,6 @@ module MCP
|
|
180
188
|
end
|
181
189
|
end
|
182
190
|
|
183
|
-
# Register a callback for resource updates
|
184
|
-
def on_resource_update(&block)
|
185
|
-
@resource_update_callbacks ||= []
|
186
|
-
callback_id = SecureRandom.uuid
|
187
|
-
@resource_update_callbacks << { id: callback_id, callback: block }
|
188
|
-
callback_id
|
189
|
-
end
|
190
|
-
|
191
|
-
# Remove a resource update callback
|
192
|
-
def remove_resource_update_callback(callback_id)
|
193
|
-
@resource_update_callbacks ||= []
|
194
|
-
@resource_update_callbacks.reject! { |cb| cb[:id] == callback_id }
|
195
|
-
end
|
196
|
-
|
197
|
-
# Update a resource and notify subscribers
|
198
|
-
def update_resource(uri, content)
|
199
|
-
return false unless @resources.key?(uri)
|
200
|
-
|
201
|
-
resource = @resources[uri]
|
202
|
-
resource.instance.content = content
|
203
|
-
|
204
|
-
# Notify subscribers
|
205
|
-
notify_resource_updated(uri) if @transport && @resource_subscriptions.key?(uri)
|
206
|
-
|
207
|
-
# Notify resource update callbacks
|
208
|
-
if @resource_update_callbacks && !@resource_update_callbacks.empty?
|
209
|
-
@resource_update_callbacks.each do |cb|
|
210
|
-
cb[:callback].call(
|
211
|
-
{
|
212
|
-
uri: uri,
|
213
|
-
name: resource.name,
|
214
|
-
mime_type: resource.mime_type,
|
215
|
-
content: content
|
216
|
-
}
|
217
|
-
)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
true
|
222
|
-
end
|
223
|
-
|
224
191
|
# Read a resource directly
|
225
192
|
def read_resource(uri)
|
226
193
|
resource = @resources[uri]
|
@@ -229,13 +196,32 @@ module MCP
|
|
229
196
|
resource
|
230
197
|
end
|
231
198
|
|
199
|
+
# Notify subscribers about a resource update
|
200
|
+
def notify_resource_updated(uri)
|
201
|
+
@logger.warn("Notifying subscribers about resource update: #{uri}, #{@resource_subscriptions.inspect}")
|
202
|
+
return unless @client_initialized && @resource_subscriptions.key?(uri)
|
203
|
+
|
204
|
+
resource = @resources[uri]
|
205
|
+
notification = {
|
206
|
+
jsonrpc: '2.0',
|
207
|
+
method: 'notifications/resources/updated',
|
208
|
+
params: {
|
209
|
+
uri: uri,
|
210
|
+
name: resource.name,
|
211
|
+
mimeType: resource.mime_type
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
@transport.send_message(notification)
|
216
|
+
end
|
217
|
+
|
232
218
|
private
|
233
219
|
|
234
220
|
PROTOCOL_VERSION = '2024-11-05'
|
235
221
|
|
236
222
|
def handle_initialize(params, id)
|
237
|
-
|
238
|
-
client_capabilities = params['capabilities'] || {}
|
223
|
+
# Store client capabilities for later use
|
224
|
+
@client_capabilities = params['capabilities'] || {}
|
239
225
|
client_info = params['clientInfo'] || {}
|
240
226
|
|
241
227
|
# Log client information
|
@@ -254,9 +240,6 @@ module MCP
|
|
254
240
|
|
255
241
|
@logger.info("Server response: #{response.inspect}")
|
256
242
|
|
257
|
-
# Store client capabilities for later use
|
258
|
-
@client_capabilities = client_capabilities
|
259
|
-
|
260
243
|
send_result(response, id)
|
261
244
|
end
|
262
245
|
|
@@ -290,8 +273,9 @@ module MCP
|
|
290
273
|
# The client is now ready for normal operation
|
291
274
|
# No response needed for notifications
|
292
275
|
@client_initialized = true
|
293
|
-
@logger.set_client_initialized
|
294
276
|
@logger.info('Client initialized, beginning normal operation')
|
277
|
+
|
278
|
+
send_result({}, nil)
|
295
279
|
end
|
296
280
|
|
297
281
|
# Handle tools/list request
|
@@ -324,7 +308,7 @@ module MCP
|
|
324
308
|
|
325
309
|
# Format and send the result
|
326
310
|
send_formatted_result(result, id)
|
327
|
-
rescue
|
311
|
+
rescue FastMcp::Tool::InvalidArgumentsError => e
|
328
312
|
@logger.error("Invalid arguments for tool #{tool_name}: #{e.message}")
|
329
313
|
send_error_result(e.message, id)
|
330
314
|
rescue StandardError => e
|
@@ -410,24 +394,6 @@ module MCP
|
|
410
394
|
send_result({ unsubscribed: true }, id)
|
411
395
|
end
|
412
396
|
|
413
|
-
# Notify subscribers about a resource update
|
414
|
-
def notify_resource_updated(uri)
|
415
|
-
return unless @client_initialized && @resource_subscriptions.key?(uri)
|
416
|
-
|
417
|
-
resource = @resources[uri]
|
418
|
-
notification = {
|
419
|
-
jsonrpc: '2.0',
|
420
|
-
method: 'notifications/resources/updated',
|
421
|
-
params: {
|
422
|
-
uri: uri,
|
423
|
-
name: resource.name,
|
424
|
-
mimeType: resource.mime_type
|
425
|
-
}
|
426
|
-
}
|
427
|
-
|
428
|
-
@transport.send_message(notification)
|
429
|
-
end
|
430
|
-
|
431
397
|
# Notify clients about resource list changes
|
432
398
|
def notify_resource_list_changed
|
433
399
|
return unless @client_initialized
|
@@ -470,10 +436,10 @@ module MCP
|
|
470
436
|
def send_response(response)
|
471
437
|
if @transport
|
472
438
|
@logger.info("Sending response: #{response.inspect}")
|
473
|
-
@logger.info("Transport: #{@transport.inspect}")
|
474
439
|
@transport.send_message(response)
|
475
440
|
else
|
476
441
|
@logger.warn("No transport available to send response: #{response.inspect}")
|
442
|
+
@logger.warn("Transport: #{@transport.inspect}, transport_klass: #{@transport_klass.inspect}")
|
477
443
|
end
|
478
444
|
end
|
479
445
|
|
data/lib/mcp/tool.rb
CHANGED
@@ -62,12 +62,14 @@ module Dry
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
module
|
65
|
+
module FastMcp
|
66
66
|
# Main Tool class that represents an MCP Tool
|
67
67
|
class Tool
|
68
68
|
class InvalidArgumentsError < StandardError; end
|
69
69
|
|
70
70
|
class << self
|
71
|
+
attr_accessor :server
|
72
|
+
|
71
73
|
def arguments(&block)
|
72
74
|
@input_schema = Dry::Schema.JSON(&block)
|
73
75
|
end
|
@@ -100,6 +102,10 @@ module MCP
|
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
105
|
+
def notify_resource_updated(uri)
|
106
|
+
self.class.server.notify_resource_updated(uri)
|
107
|
+
end
|
108
|
+
|
103
109
|
def call_with_schema_validation!(**args)
|
104
110
|
arg_validation = self.class.input_schema.call(args)
|
105
111
|
raise InvalidArgumentsError, arg_validation.errors.to_h.to_json if arg_validation.errors.any?
|
@@ -788,7 +794,7 @@ module MCP
|
|
788
794
|
end
|
789
795
|
|
790
796
|
# Example
|
791
|
-
# class ExampleTool <
|
797
|
+
# class ExampleTool < FastMcp::Tool
|
792
798
|
# description 'An example tool'
|
793
799
|
|
794
800
|
# arguments do
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
require_relative 'rack_transport'
|
4
4
|
|
5
|
-
module
|
5
|
+
module FastMcp
|
6
6
|
module Transports
|
7
7
|
class AuthenticatedRackTransport < RackTransport
|
8
|
-
def initialize(
|
8
|
+
def initialize(app, server, options = {})
|
9
9
|
super
|
10
10
|
|
11
11
|
@auth_token = options[:auth_token]
|