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.
@@ -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