Class: Legate::Mcp::Server::LegateAgentAdapter
- Inherits:
-
FastMcp::Tool
- Object
- FastMcp::Tool
- Legate::Mcp::Server::LegateAgentAdapter
- Defined in:
- lib/legate/mcp/server/legate_agent_adapter.rb
Overview
(Experimental) Adapter to expose an entire Legate::Agent (defined in GlobalDefinitionRegistry) as a single, simple tool via fast-mcp. The agent runs ephemerally for each call
Class Attribute Summary collapse
-
.agent_definition_name ⇒ Object
readonly
Returns the value of attribute agent_definition_name.
-
.session_service ⇒ Object
readonly
Returns the value of attribute session_service.
Class Method Summary collapse
-
.wrap(agent_definition_name, session_service_instance) ⇒ Class<LegateAgentAdapter>
Dynamically creates a new FastMcp::Tool subclass that wraps an Legate Agent definition.
Instance Method Summary collapse
-
#call(prompt:) ⇒ Any
Executes the wrapped Legate Agent for a single turn.
Class Attribute Details
.agent_definition_name ⇒ Object (readonly)
Returns the value of attribute agent_definition_name.
25 26 27 |
# File 'lib/legate/mcp/server/legate_agent_adapter.rb', line 25 def agent_definition_name @agent_definition_name end |
.session_service ⇒ Object (readonly)
Returns the value of attribute session_service.
25 26 27 |
# File 'lib/legate/mcp/server/legate_agent_adapter.rb', line 25 def session_service @session_service end |
Class Method Details
.wrap(agent_definition_name, session_service_instance) ⇒ Class<LegateAgentAdapter>
Dynamically creates a new FastMcp::Tool subclass that wraps an Legate Agent definition.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/legate/mcp/server/legate_agent_adapter.rb', line 34 def self.wrap(agent_definition_name, session_service_instance) raise ArgumentError, 'Agent definition name must be a non-empty String.' unless agent_definition_name.is_a?(String) && !agent_definition_name.empty? raise ArgumentError, 'Session service instance must inherit from Legate::SessionService::Base.' unless session_service_instance.is_a?(Legate::SessionService::Base) # Create the anonymous adapter class Class.new(LegateAgentAdapter) do # Store config on the generated class @agent_definition_name = agent_definition_name @session_service = session_service_instance # Set fast-mcp tool metadata # Use a modified tool name to avoid clashes if agent name = tool name tool_name "run_agent_#{agent_definition_name}" description "Runs the Legate Agent '#{agent_definition_name}' with the given prompt." # Define the single prompt argument arguments do required(:prompt).filled(:string).description('The user input/prompt for the agent') end Mcp.logger.info("Created fast-mcp adapter for Legate agent definition: '#{agent_definition_name}'") end end |
Instance Method Details
#call(prompt:) ⇒ Any
Executes the wrapped Legate Agent for a single turn. Loads definition, creates temp session, runs task, cleans up.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/legate/mcp/server/legate_agent_adapter.rb', line 64 def call(prompt:) # Retrieve config from the *class* instance variables agent_name = self.class.agent_definition_name session_service = self.class.session_service unless agent_name && session_service raise NotImplementedError, 'LegateAgentAdapter must be configured using .wrap first.' end Mcp.logger.info("Executing Legate Agent '#{agent_name}' via MCP adapter with prompt: '#{prompt}'") agent = nil temp_session = nil begin # 1. Load Agent Definition from GlobalDefinitionRegistry Mcp.logger.debug("Loading agent definition '#{agent_name}' from GlobalDefinitionRegistry...") agent_definition_object = Legate::GlobalDefinitionRegistry.find(agent_name.to_sym) # Try get_definition if available (expanded API from Phase 2) if !agent_definition_object && Legate::GlobalDefinitionRegistry.respond_to?(:get_definition) definition_hash = Legate::GlobalDefinitionRegistry.get_definition(agent_name) agent_definition_object = Legate::AgentDefinition.from_hash(definition_hash) if definition_hash end raise Legate::Mcp::Error, "Agent definition '#{agent_name}' not found in GlobalDefinitionRegistry." unless agent_definition_object Mcp.logger.debug("Agent definition loaded for '#{agent_name}'.") Mcp.logger.debug("AgentDefinition object ready: #{agent_definition_object.name}, Model=#{agent_definition_object.model_name}") # 2. Create Temporary Session Mcp.logger.debug('Creating temporary session...') temp_session = session_service.create_session(app_name: agent_name, user_id: "mcp_temp_#{SecureRandom.hex(4)}") Mcp.logger.debug("Temporary session created: #{temp_session.id}") # 3. Instantiate Agent Mcp.logger.debug("Instantiating agent '#{agent_definition_object.name}' with its definition object and session service...") agent = Legate::Agent.new( definition: agent_definition_object, session_service: session_service # Pass the session_service from adapter config ) # Tool loading is handled by Legate::Agent#initialize based on the definition object. # 4. Start Agent & Run Task Mcp.logger.debug('Starting ephemeral agent runtime...') agent.start Mcp.logger.debug("Running task in temp session #{temp_session.id}...") final_event = agent.run_task( session_id: temp_session.id, user_input: prompt, session_service: session_service ) Mcp.logger.debug("Agent run_task finished. Final event: #{final_event.inspect}") # 5. Process Result raise StandardError, "Agent task finished with unexpected event format: #{final_event.inspect}" unless final_event.is_a?(Legate::Event) && final_event.role == :agent && final_event.content.is_a?(Hash) result_content = final_event.content case result_content[:status] when :success result_content[:result] # Return result payload when :error err_msg = result_content[:error_message] || 'Agent execution failed.' Mcp.logger.error("Agent '#{agent_name}' execution failed: #{err_msg}") raise StandardError, "Agent Error: #{err_msg}" when :pending job_id = result_content[:job_id] # Assuming key is :job_id msg = result_content[:message] || 'Agent task resulted in a pending job.' Mcp.logger.warn("Agent '#{agent_name}' execution ended with pending status (Job: #{job_id}). Returning as structured data.") # Return pending structure similar to LegateToolAdapter for consistency { status: 'pending', job_id: job_id, message: msg } else raise StandardError, "Agent task finished with unknown status: #{result_content[:status]}" end rescue Legate::Mcp::Error, StandardError => e # Catch errors during setup or execution within the adapter Mcp.logger.error("Error during LegateAgentAdapter call for '#{agent_name}': #{e.class} - #{e.}") Mcp.logger.error(e.backtrace.join("\n")) # Let fast-mcp handle the error raise StandardError, "Failed to run agent '#{agent_name}': #{e.}" ensure # 6. Cleanup: Stop Agent & Delete Session if agent&.running? begin Mcp.logger.debug('Stopping ephemeral agent runtime...') agent.stop rescue StandardError => e Mcp.logger.error("Error stopping agent runtime during cleanup: #{e.}") end end if temp_session && session_service begin Mcp.logger.debug("Deleting temporary session: #{temp_session.id}") session_service.delete_session(session_id: temp_session.id) rescue StandardError => e Mcp.logger.error("Error deleting temporary session #{temp_session.id}: #{e.}") end end end end |