Commit ea1bbc2
Changed files (3)
lib
elelem
lib/elelem/mcp_client.rb
@@ -6,7 +6,7 @@ module Elelem
def initialize(configuration, command = [])
@configuration = configuration
- @stdin, @stdout, @stderr, @worker_thread = Open3.popen3(*command, pgroup: true)
+ @stdin, @stdout, @stderr, @worker = Open3.popen3(*command, pgroup: true)
# 1. Send initialize request
send_request(
@@ -31,7 +31,16 @@ module Elelem
end
def connected?
- @worker_thread&.alive? && @stdin && !@stdin.closed?
+ return false unless @worker&.alive?
+ return false unless @stdin && !@stdin.closed?
+ return false unless @stdout && !@stdout.closed?
+
+ begin
+ Process.getpgid(@worker.pid)
+ true
+ rescue Errno::ESRCH
+ false
+ end
end
def call(name, arguments = {})
@@ -46,9 +55,11 @@ module Elelem
private
- attr_reader :stdin, :stdout, :stderr, :worker_thread, :configuration
+ attr_reader :stdin, :stdout, :stderr, :worker, :configuration
def send_request(method:, params: {})
+ return {} unless connected?
+
request = {
jsonrpc: "2.0",
id: Time.now.to_i,
@@ -56,14 +67,19 @@ module Elelem
}
request[:params] = params unless params.empty?
configuration.logger.debug(JSON.pretty_generate(request))
+
@stdin.puts(JSON.generate(request))
@stdin.flush
- response = JSON.parse(@stdout.gets.strip)
+ response_line = @stdout.gets&.strip
+ return {} if response_line.nil? || response_line.empty?
+
+ response = JSON.parse(response_line)
configuration.logger.debug(JSON.pretty_generate(response))
+
if response["error"]
configuration.logger.error(response["error"]["message"])
- {}
+ { error: response["error"]["message"] }
else
response["result"]
end
lib/elelem/state.rb
@@ -71,7 +71,15 @@ module Elelem
agent.say("\n\n", newline: false)
result = agent.execute(tool_call)
- agent.conversation.add(role: :tool, content: result)
+
+ if result.is_a?(Hash) && result[:success] == false
+ agent.say("\n", newline: false)
+ agent.complete_progress("#{tool_name} failed", colour: :red)
+ return Error.new(agent, result[:error])
+ end
+
+ output = result.is_a?(Hash) ? result[:output] : result
+ agent.conversation.add(role: :tool, content: output)
agent.say("\n", newline: false)
agent.complete_progress("#{tool_name} completed")
@@ -82,6 +90,19 @@ module Elelem
end
end
+ class Error < State
+ def initialize(agent, error_message)
+ super(agent)
+ @error_message = error_message
+ end
+
+ def process(_message)
+ agent.say("\nTool execution failed: #{@error_message}", colour: :red)
+ agent.say("Returning to idle state.\n\n", colour: :yellow)
+ Waiting.new(agent)
+ end
+ end
+
class Talking < State
def initialize(agent)
super(agent)
lib/elelem/tool.rb
@@ -90,10 +90,29 @@ module Elelem
end
def call(args)
+ unless client.connected?
+ error_msg = "MCP connection lost"
+ tui.say(error_msg, colour: :red)
+ return { success: false, output: "", error: error_msg }
+ end
+
result = client.call(name, args)
+
+ if result.nil? || result.empty?
+ error_msg = "Tool call failed: no response from MCP server"
+ tui.say(error_msg, colour: :red)
+ return { success: false, output: "", error: error_msg }
+ end
+
+ if result["error"]
+ error_msg = "Tool error: #{result["error"]}"
+ tui.say(error_msg, colour: :red)
+ return { success: false, output: "", error: error_msg }
+ end
+
output = result.dig("content", 0, "text") || result.to_s
tui.say(output)
- output
+ { success: true, output: output, error: nil }
end
end
end