Class: Encom::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/encom/server.rb,
lib/encom/server/tool.rb

Defined Under Namespace

Classes: Tool

Constant Summary collapse

LATEST_PROTOCOL_VERSION =
'2024-11-05'
SUPPORTED_PROTOCOL_VERSIONS =
[
  LATEST_PROTOCOL_VERSION
  # Add more supported versions as they're developed
].freeze

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Server

Returns a new instance of Server.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/encom/server.rb', line 31

def initialize(options = {})
  @message_id = 0
  @capabilities = options[:capabilities] || {
    roots: {
      listChanged: true
    },
    sampling: {},
    tools: {}
  }
  
  # Validate protocol version immediately
  protocol_version = options[:protocol_version] || LATEST_PROTOCOL_VERSION
  unless SUPPORTED_PROTOCOL_VERSIONS.include?(protocol_version)
    raise ArgumentError, "Unsupported protocol version: #{protocol_version}. Supported versions: #{SUPPORTED_PROTOCOL_VERSIONS.join(', ')}"
  end
  
  @protocol_version = protocol_version
  @transport = nil
end

Class Attribute Details

.toolsObject (readonly)

Returns the value of attribute tools.



26
27
28
# File 'lib/encom/server.rb', line 26

def tools
  @tools
end

Instance Attribute Details

#capabilitiesObject (readonly)

Returns the value of attribute capabilities.



29
30
31
# File 'lib/encom/server.rb', line 29

def capabilities
  @capabilities
end

#transportObject (readonly)

Returns the value of attribute transport.



29
30
31
# File 'lib/encom/server.rb', line 29

def transport
  @transport
end

Class Method Details

.name(server_name = nil) ⇒ Object



13
14
15
# File 'lib/encom/server.rb', line 13

def name(server_name = nil)
  @server_name ||= server_name
end

.tool(tool_name, description, schema, proc) ⇒ Object



21
22
23
24
# File 'lib/encom/server.rb', line 21

def tool(tool_name, description, schema, proc)
  @tools ||= []
  @tools << Tool.new(name: tool_name, description:, schema:, proc:)
end

.version(version = nil) ⇒ Object



17
18
19
# File 'lib/encom/server.rb', line 17

def version(version = nil)
  @version ||= version
end

Instance Method Details

#call_tool(name, arguments) ⇒ Object



63
64
65
66
67
# File 'lib/encom/server.rb', line 63

def call_tool(name, arguments)
  tool = tools.find { |t| t.name.to_s == name.to_s || t.name == name.to_sym }
  raise "Unknown tool: #{name}" unless tool
  tool.call(arguments)
end

#handle_initialize(message) ⇒ Object

Handle initialize request



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
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/encom/server.rb', line 164

def handle_initialize(message)
  client_protocol_version = message[:params][:protocolVersion]
  client_capabilities = message[:params][:capabilities]
  client_info = message[:params][:clientInfo]

  server_info = {
    name: name,
    version: version
  }

  # Debug log the received protocol version
  if @transport && @transport.respond_to?(:debug)
    @transport.debug "Received initialize request with protocol version: #{client_protocol_version}"
    @transport.debug "Supported protocol versions: #{SUPPORTED_PROTOCOL_VERSIONS.inspect}"
  end

  # Check if the requested protocol version is supported
  if SUPPORTED_PROTOCOL_VERSIONS.include?(client_protocol_version)
    # Use the requested version if supported
    protocol_version = client_protocol_version
    
    if @transport && @transport.respond_to?(:debug)
      @transport.debug "Protocol version #{protocol_version} is supported, sending success response"
    end
    
    # Send initialize response
    respond(message[:id], {
      protocolVersion: protocol_version,
      capabilities: @capabilities,
      serverInfo: server_info
    })
  else
    # Return an error for unsupported protocol versions
    if @transport && @transport.respond_to?(:debug)
      @transport.debug "Protocol version error: Client requested unsupported version #{client_protocol_version}"
    end
    
    respond_error(
      message[:id],
      Encom::ErrorCodes::PROTOCOL_ERROR,
      "Unsupported protocol version: #{client_protocol_version}",
      { supportedVersions: SUPPORTED_PROTOCOL_VERSIONS }
    )
  end
end

#handle_initialized(message) ⇒ Object

Handle initialized notification



211
212
213
# File 'lib/encom/server.rb', line 211

def handle_initialized(message)
  # No response needed for notifications
end

#handle_resources_list(message) ⇒ Object

Handle resources/list request



216
217
218
219
220
221
# File 'lib/encom/server.rb', line 216

def handle_resources_list(message)
  # Default implementation returns an empty list
  respond(message[:id], {
    resources: []
  })
end

#handle_roots_list(message) ⇒ Object

Handle roots/list request



224
225
226
227
228
229
# File 'lib/encom/server.rb', line 224

def handle_roots_list(message)
  # Default implementation returns an empty list
  respond(message[:id], {
    roots: []
  })
end

#handle_sampling_prepare(message) ⇒ Object

Handle sampling/prepare request



232
233
234
235
236
237
238
# File 'lib/encom/server.rb', line 232

def handle_sampling_prepare(message)
  # Default implementation returns a simple response
  respond(message[:id], {
    prepared: false,
    samplingId: nil
  })
end

#handle_sampling_sample(message) ⇒ Object

Handle sampling/sample request



241
242
243
244
245
246
247
# File 'lib/encom/server.rb', line 241

def handle_sampling_sample(message)
  # Default implementation returns a simple response
  respond(message[:id], {
    completion: "Sampling not implemented",
    completionId: nil
  })
end

#handle_shutdown(message) ⇒ Object

Handle JSON-RPC shutdown request



272
273
274
275
276
277
278
279
280
281
# File 'lib/encom/server.rb', line 272

def handle_shutdown(message)
  if message[:id]
    # If it's a request with ID, respond with a success result
    respond(message[:id], {
      shutdown: true
    })
  end
  # Initiate clean shutdown
  shutdown
end

#handle_tools_call(message) ⇒ Object

Handle tools/call request



259
260
261
262
263
264
265
266
267
268
269
# File 'lib/encom/server.rb', line 259

def handle_tools_call(message)
  tool_name = message[:params][:name]
  arguments = message[:params][:arguments] || {}
  
  begin
    result = call_tool(tool_name, arguments)
    respond(message[:id], result)
  rescue StandardError => e
    respond_error(message[:id], Encom::ErrorCodes::TOOL_EXECUTION_ERROR, "Tool execution error: #{e.message}")
  end
end

#handle_tools_list(message) ⇒ Object

Handle tools/list request



250
251
252
253
254
255
256
# File 'lib/encom/server.rb', line 250

def handle_tools_list(message)
  tool_definitions = tools ? tools.map(&:definition) : []
  
  respond(message[:id], {
    tools: tool_definitions
  })
end

#nameObject



51
52
53
# File 'lib/encom/server.rb', line 51

def name
  self.class.name
end

#process_message(message) ⇒ Object

Process incoming JSON-RPC message



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/encom/server.rb', line 80

def process_message(message)
  return unless message.is_a?(Hash)

  if @transport && @transport.respond_to?(:debug)
    @transport.debug "Processing message: #{message.inspect}"
  end

  # Check for jsonrpc version
  unless message[:jsonrpc] == '2.0'
    if message[:id]
      respond_error(message[:id], Encom::ErrorCodes::INVALID_REQUEST, 'Invalid JSON-RPC request')
    end
    return
  end

  # Process request by method
  case message[:method]
  when 'initialize'
    handle_initialize(message)
  when 'initialized'
    handle_initialized(message)
  when 'resources/list'
    handle_resources_list(message)
  when 'roots/list'
    handle_roots_list(message)
  when 'sampling/prepare'
    handle_sampling_prepare(message)
  when 'sampling/sample'
    handle_sampling_sample(message)
  when 'tools/list'
    handle_tools_list(message)
  when 'tools/call'
    handle_tools_call(message)
  when 'shutdown'
    handle_shutdown(message)
  else
    if message[:id]
      if @transport && @transport.respond_to?(:debug)
        @transport.debug "Unknown method: #{message[:method]}"
      end
      respond_error(message[:id], Encom::ErrorCodes::METHOD_NOT_FOUND, "Method not found: #{message[:method]}")
    end
  end
rescue StandardError => e
  if @transport && @transport.respond_to?(:debug)
    @transport.debug "Error processing message: #{e.message}\n#{e.backtrace.join("\n")}"
  end
  
  if message && message[:id]
    respond_error(message[:id], Encom::ErrorCodes::INTERNAL_ERROR, "Internal error: #{e.message}")
  end
end

#respond(id, result) ⇒ Object

Generate and send a JSON-RPC response



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/encom/server.rb', line 134

def respond(id, result)
  return unless @transport

  response = {
    jsonrpc: "2.0",
    id: id,
    result: result
  }

  @transport.send_message(response)
end

#respond_error(id, code, message, data = nil) ⇒ Object

Generate and send a JSON-RPC error response



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/encom/server.rb', line 147

def respond_error(id, code, message, data = nil)
  return unless @transport

  response = {
    jsonrpc: "2.0",
    id: id,
    error: {
      code: code,
      message: message
    }
  }
  response[:error][:data] = data if data

  @transport.send_message(response)
end

#run(transport_class, transport_options = {}) ⇒ Object

Run the server with the specified transport



70
71
72
73
74
75
76
77
# File 'lib/encom/server.rb', line 70

def run(transport_class, transport_options = {})
  @transport = transport_class.new(self, transport_options)
  @transport.start
rescue StandardError => e
  $stderr.puts "Error running server: #{e.message}"
  $stderr.puts e.backtrace.join("\n") if transport_options[:debug]
  raise
end

#shutdownObject

Shutdown the server and clean up



284
285
286
287
288
289
290
291
292
# File 'lib/encom/server.rb', line 284

def shutdown
  return if @shutting_down
  @shutting_down = true
  
  if @transport
    @transport.stop
    @transport = nil
  end
end

#toolsObject



59
60
61
# File 'lib/encom/server.rb', line 59

def tools
  self.class.tools
end

#versionObject



55
56
57
# File 'lib/encom/server.rb', line 55

def version
  self.class.version
end