Class: FastMcp::Server
- Inherits:
-
Object
- Object
- FastMcp::Server
- Includes:
- ServerFiltering
- Defined in:
- lib/mcp/server.rb
Constant Summary collapse
- DEFAULT_CAPABILITIES =
{ resources: { subscribe: true, listChanged: true }, tools: { listChanged: true } }.freeze
Instance Attribute Summary collapse
-
#capabilities ⇒ Object
readonly
Returns the value of attribute capabilities.
-
#logger ⇒ Object
Returns the value of attribute logger.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#resources ⇒ Object
readonly
Returns the value of attribute resources.
-
#tools ⇒ Object
readonly
Returns the value of attribute tools.
-
#transport ⇒ Object
Returns the value of attribute transport.
-
#transport_klass ⇒ Object
Returns the value of attribute transport_klass.
-
#version ⇒ Object
readonly
Returns the value of attribute version.
Instance Method Summary collapse
-
#handle_json_request(request, headers: {}) ⇒ Object
Handle a JSON-RPC request and return the response as a JSON string.
-
#handle_request(json_str, headers: {}) ⇒ Object
Handle incoming JSON-RPC request.
-
#initialize(name:, version:, logger: FastMcp::Logger.new, capabilities: {}) ⇒ Server
constructor
A new instance of Server.
-
#notify_resource_updated(uri) ⇒ Object
Notify subscribers about a resource update.
- #read_resource(uri) ⇒ Object
-
#register_resource(resource) ⇒ Object
Register a resource with the server.
-
#register_resources(*resources) ⇒ Object
Register multiple resources at once.
-
#register_tool(tool) ⇒ Object
Register a tool with the server.
-
#register_tools(*tools) ⇒ Object
Register multiple tools at once.
-
#remove_resource(uri) ⇒ Object
Remove a resource from the server.
-
#start ⇒ Object
Start the server using stdio transport.
- #start_authenticated_rack(app, options = {}) ⇒ Object
-
#start_rack(app, options = {}) ⇒ Object
Start the server as a Rack middleware.
Methods included from ServerFiltering
#contains_filters?, #create_filtered_copy, #filter_resources, #filter_tools
Constructor Details
#initialize(name:, version:, logger: FastMcp::Logger.new, capabilities: {}) ⇒ Server
Returns a new instance of Server.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/mcp/server.rb', line 29 def initialize(name:, version:, logger: FastMcp::Logger.new, capabilities: {}) @name = name @version = version @tools = {} @resources = [] @resource_subscriptions = {} @logger = logger @request_id = 0 @transport_klass = nil @transport = nil @capabilities = DEFAULT_CAPABILITIES.dup @tool_filters = [] @resource_filters = [] # Merge with provided capabilities @capabilities.merge!(capabilities) if capabilities.is_a?(Hash) end |
Instance Attribute Details
#capabilities ⇒ Object (readonly)
Returns the value of attribute capabilities.
17 18 19 |
# File 'lib/mcp/server.rb', line 17 def capabilities @capabilities end |
#logger ⇒ Object
Returns the value of attribute logger.
46 47 48 |
# File 'lib/mcp/server.rb', line 46 def logger @logger end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
17 18 19 |
# File 'lib/mcp/server.rb', line 17 def name @name end |
#resources ⇒ Object (readonly)
Returns the value of attribute resources.
17 18 19 |
# File 'lib/mcp/server.rb', line 17 def resources @resources end |
#tools ⇒ Object (readonly)
Returns the value of attribute tools.
17 18 19 |
# File 'lib/mcp/server.rb', line 17 def tools @tools end |
#transport ⇒ Object
Returns the value of attribute transport.
46 47 48 |
# File 'lib/mcp/server.rb', line 46 def transport @transport end |
#transport_klass ⇒ Object
Returns the value of attribute transport_klass.
46 47 48 |
# File 'lib/mcp/server.rb', line 46 def transport_klass @transport_klass end |
#version ⇒ Object (readonly)
Returns the value of attribute version.
17 18 19 |
# File 'lib/mcp/server.rb', line 17 def version @version end |
Instance Method Details
#handle_json_request(request, headers: {}) ⇒ Object
Handle a JSON-RPC request and return the response as a JSON string
143 144 145 146 147 |
# File 'lib/mcp/server.rb', line 143 def handle_json_request(request, headers: {}) request_str = request.is_a?(String) ? request : JSON.generate(request) handle_request(request_str, headers: headers) end |
#handle_request(json_str, headers: {}) ⇒ Object
Handle incoming JSON-RPC request
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/mcp/server.rb', line 150 def handle_request(json_str, headers: {}) # rubocop:disable Metrics/MethodLength begin request = JSON.parse(json_str) rescue JSON::ParserError, TypeError return send_error(-32_600, 'Invalid Request', nil) end @logger.debug("Received request: #{request.inspect}") method = request['method'] params = request['params'] || {} id = request['id'] # Check if it's a valid JSON-RPC 2.0 request return send_error(-32_600, 'Invalid Request', id) unless request['jsonrpc'] == '2.0' case method when 'ping' send_result({}, id) when 'initialize' handle_initialize(params, id) when 'notifications/initialized' handle_initialized_notification when 'tools/list' handle_tools_list(id) when 'tools/call' handle_tools_call(params, headers, id) when 'resources/list' handle_resources_list(id) when 'resources/templates/list' handle_resources_templates_list(id) when 'resources/read' handle_resources_read(params, id) when 'resources/subscribe' handle_resources_subscribe(params, id) when 'resources/unsubscribe' handle_resources_unsubscribe(params, id) when nil # This is a notification response, we don't need to handle it nil else send_error(-32_601, "Method not found: #{method}", id) end rescue StandardError => e @logger.error("Error handling request: #{e.}, #{e.backtrace.join("\n")}") send_error(-32_600, "Internal error: #{e.}, #{e.backtrace.join("\n")}", id) end |
#notify_resource_updated(uri) ⇒ Object
Notify subscribers about a resource update
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/mcp/server.rb', line 199 def notify_resource_updated(uri) @logger.warn("Notifying subscribers about resource update: #{uri}, #{@resource_subscriptions.inspect}") return unless @client_initialized && @resource_subscriptions.key?(uri) resource = @resources[uri] notification = { jsonrpc: '2.0', method: 'notifications/resources/updated', params: { uri: uri, name: resource.name, mimeType: resource.mime_type } } @transport.(notification) end |
#read_resource(uri) ⇒ Object
217 218 219 |
# File 'lib/mcp/server.rb', line 217 def read_resource(uri) @resources.find { |r| r.match(uri) } end |
#register_resource(resource) ⇒ Object
Register a resource with the server
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/mcp/server.rb', line 72 def register_resource(resource) @resources << resource @logger.debug("Registered resource: #{resource.resource_name} (#{resource.uri})") resource.server = self # Notify subscribers about the list change notify_resource_list_changed if @transport resource end |
#register_resources(*resources) ⇒ Object
Register multiple resources at once
65 66 67 68 69 |
# File 'lib/mcp/server.rb', line 65 def register_resources(*resources) resources.each do |resource| register_resource(resource) end end |
#register_tool(tool) ⇒ Object
Register a tool with the server
57 58 59 60 61 |
# File 'lib/mcp/server.rb', line 57 def register_tool(tool) @tools[tool.tool_name] = tool @logger.debug("Registered tool: #{tool.tool_name}") tool.server = self end |
#register_tools(*tools) ⇒ Object
Register multiple tools at once
50 51 52 53 54 |
# File 'lib/mcp/server.rb', line 50 def register_tools(*tools) tools.each do |tool| register_tool(tool) end end |
#remove_resource(uri) ⇒ Object
Remove a resource from the server
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/mcp/server.rb', line 84 def remove_resource(uri) resource = @resources.find { |r| r.uri == uri } if resource @resources.delete(resource) @logger.debug("Removed resource: #{resource.name} (#{uri})") # Notify subscribers about the list change notify_resource_list_changed if @transport true else false end end |
#start ⇒ Object
Start the server using stdio transport
101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/mcp/server.rb', line 101 def start @logger.transport = :stdio @logger.info("Starting MCP server: #{@name} v#{@version}") @logger.info("Available tools: #{@tools.keys.join(', ')}") @logger.info("Available resources: #{@resources.map(&:resource_name).join(', ')}") # Use STDIO transport by default @transport_klass = FastMcp::Transports::StdioTransport @transport = @transport_klass.new(self, logger: @logger) @transport.start end |
#start_authenticated_rack(app, options = {}) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/mcp/server.rb', line 128 def start_authenticated_rack(app, = {}) @logger.info("Starting MCP server as Authenticated Rack middleware: #{@name} v#{@version}") @logger.info("Available tools: #{@tools.keys.join(', ')}") @logger.info("Available resources: #{@resources.map(&:resource_name).join(', ')}") # Use Rack transport transport_klass = FastMcp::Transports::AuthenticatedRackTransport @transport = transport_klass.new(app, self, .merge(logger: @logger)) @transport.start # Return the transport as middleware @transport end |
#start_rack(app, options = {}) ⇒ Object
Start the server as a Rack middleware
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/mcp/server.rb', line 114 def start_rack(app, = {}) @logger.info("Starting MCP server as Rack middleware: #{@name} v#{@version}") @logger.info("Available tools: #{@tools.keys.join(', ')}") @logger.info("Available resources: #{@resources.map(&:resource_name).join(', ')}") # Use Rack transport transport_klass = FastMcp::Transports::RackTransport @transport = transport_klass.new(app, self, .merge(logger: @logger)) @transport.start # Return the transport as middleware @transport end |