fast-mcp-annotations 1.5.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 +7 -0
- data/CHANGELOG.md +115 -0
- data/LICENSE +21 -0
- data/README.md +440 -0
- data/lib/fast_mcp.rb +189 -0
- 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 +42 -0
- data/lib/generators/fast_mcp/install/templates/sample_resource.rb +12 -0
- data/lib/generators/fast_mcp/install/templates/sample_tool.rb +23 -0
- data/lib/mcp/logger.rb +32 -0
- data/lib/mcp/railtie.rb +45 -0
- data/lib/mcp/resource.rb +210 -0
- data/lib/mcp/server.rb +499 -0
- data/lib/mcp/server_filtering.rb +80 -0
- data/lib/mcp/tool.rb +867 -0
- data/lib/mcp/transports/authenticated_rack_transport.rb +71 -0
- data/lib/mcp/transports/base_transport.rb +40 -0
- data/lib/mcp/transports/rack_transport.rb +627 -0
- data/lib/mcp/transports/stdio_transport.rb +62 -0
- data/lib/mcp/version.rb +5 -0
- metadata +151 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'rack_transport'
|
4
|
+
|
5
|
+
module FastMcp
|
6
|
+
module Transports
|
7
|
+
class AuthenticatedRackTransport < RackTransport
|
8
|
+
def initialize(app, server, options = {})
|
9
|
+
super
|
10
|
+
|
11
|
+
@auth_token = options[:auth_token]
|
12
|
+
@auth_header_name = options[:auth_header_name] || 'Authorization'
|
13
|
+
@auth_exempt_paths = options[:auth_exempt_paths] || []
|
14
|
+
@auth_enabled = !@auth_token.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle_mcp_request(request, env)
|
18
|
+
if auth_enabled? && !exempt_from_auth?(request.path)
|
19
|
+
auth_header = request.env["HTTP_#{@auth_header_name.upcase.gsub('-', '_')}"]
|
20
|
+
token = auth_header&.gsub('Bearer ', '')
|
21
|
+
|
22
|
+
return unauthorized_response(request) unless valid_token?(token)
|
23
|
+
end
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def auth_enabled?
|
31
|
+
@auth_enabled
|
32
|
+
end
|
33
|
+
|
34
|
+
def exempt_from_auth?(path)
|
35
|
+
@auth_exempt_paths.any? { |exempt_path| path.start_with?(exempt_path) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid_token?(token)
|
39
|
+
token == @auth_token
|
40
|
+
end
|
41
|
+
|
42
|
+
def unauthorized_response(request)
|
43
|
+
@logger.error('Unauthorized request: Invalid or missing authentication token')
|
44
|
+
body = JSON.generate(
|
45
|
+
{
|
46
|
+
jsonrpc: '2.0',
|
47
|
+
error: {
|
48
|
+
code: -32_000,
|
49
|
+
message: 'Unauthorized: Invalid or missing authentication token'
|
50
|
+
},
|
51
|
+
id: extract_request_id(request)
|
52
|
+
}
|
53
|
+
)
|
54
|
+
|
55
|
+
[401, { 'Content-Type' => 'application/json' }, [body]]
|
56
|
+
end
|
57
|
+
|
58
|
+
def extract_request_id(request)
|
59
|
+
return nil unless request.post?
|
60
|
+
|
61
|
+
begin
|
62
|
+
body = request.body.read
|
63
|
+
request.body.rewind
|
64
|
+
JSON.parse(body)['id']
|
65
|
+
rescue StandardError
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FastMcp
|
4
|
+
module Transports
|
5
|
+
# Base class for all MCP transports
|
6
|
+
# This defines the interface that all transports must implement
|
7
|
+
class BaseTransport
|
8
|
+
attr_reader :server, :logger
|
9
|
+
|
10
|
+
def initialize(server, logger: nil)
|
11
|
+
@server = server
|
12
|
+
@logger = logger || server.logger
|
13
|
+
end
|
14
|
+
|
15
|
+
# Start the transport
|
16
|
+
# This method should be implemented by subclasses
|
17
|
+
def start
|
18
|
+
raise NotImplementedError, "#{self.class} must implement #start"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Stop the transport
|
22
|
+
# This method should be implemented by subclasses
|
23
|
+
def stop
|
24
|
+
raise NotImplementedError, "#{self.class} must implement #stop"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Send a message to the client
|
28
|
+
# This method should be implemented by subclasses
|
29
|
+
def send_message(message)
|
30
|
+
raise NotImplementedError, "#{self.class} must implement #send_message"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Process an incoming message
|
34
|
+
# This is a helper method that can be used by subclasses
|
35
|
+
def process_message(message, headers: {})
|
36
|
+
server.handle_request(message, headers: headers)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|