Module: A2A::Server::Agent

Included in:
ExampleAgent
Defined in:
lib/a2a/server/agent.rb

Overview

Agent DSL for defining A2A-compatible methods and capabilities

This module provides a DSL for defining agent methods that can be called via the A2A protocol. It includes method registration, capability definition, parameter validation, and middleware support.

Examples:

Basic agent definition

class MyAgent
  include A2A::Server::Agent

  a2a_method "greet" do |params|
    { message: "Hello, #{params['name']}!" }
  end

  a2a_capability "greeting" do
    method "greet"
    description "Greet a user by name"
    input_schema type: "object", properties: { name: { type: "string" } }
    output_schema type: "object", properties: { message: { type: "string" } }
  end
end

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/a2a/server/agent.rb', line 31

def self.included(base)
  base.extend(ClassMethods)

  # Initialize class-level storage for A2A methods and capabilities
  if defined?(ActiveSupport) && base.respond_to?(:class_attribute)
    # Use ActiveSupport's class_attribute if available
    base.class_attribute :_a2a_methods, default: {}
    base.class_attribute :_a2a_capabilities, default: A2A::Protocol::CapabilityRegistry.new
    base.class_attribute :_a2a_config, default: {}
    base.class_attribute :_a2a_middleware, default: []
  else
    # Fallback implementation without ActiveSupport
    base.instance_variable_set(:@_a2a_methods, {})
    base.instance_variable_set(:@_a2a_capabilities, A2A::Protocol::CapabilityRegistry.new)
    base.instance_variable_set(:@_a2a_config, {})
    base.instance_variable_set(:@_a2a_middleware, [])

    # Define accessor methods
    base.define_singleton_method(:_a2a_methods) { @_a2a_methods }
    base.define_singleton_method(:_a2a_capabilities) { @_a2a_capabilities }
    base.define_singleton_method(:_a2a_config) { @_a2a_config }
    base.define_singleton_method(:_a2a_middleware) { @_a2a_middleware }
  end
end

Instance Method Details

#build_middleware_chain(_method_def, context) ⇒ Proc (private)

Build the middleware chain for method execution

Parameters:

  • method_def (Hash)

    The method definition

  • context (A2A::Server::Context)

    The request context

Returns:

  • (Proc)

    The middleware chain



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/a2a/server/agent.rb', line 324

def build_middleware_chain(_method_def, context)
  # Combine class-level and method-level middleware
  all_middleware = self.class._a2a_middleware.dup

  # Build the chain from the inside out
  chain = ->(_params, &block) { block.call }

  all_middleware.reverse_each do |middleware_def|
    middleware_class = middleware_def[:class]
    middleware_options = middleware_def[:options]

    # Create middleware instance
    middleware = middleware_class.new(**middleware_options)

    # Wrap the current chain
    current_chain = chain
    chain = lambda do |params, &block|
      middleware.call(params, context) do
        current_chain.call(params, &block)
      end
    end
  end

  chain
end

#execute_method(method_def, params, context) ⇒ Object

Execute the actual method implementation

Parameters:

  • method_def (Hash)

    The method definition

  • params (Hash)

    The method parameters

  • context (A2A::Server::Context)

    The request context

Returns:

  • (Object)

    The method result



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/a2a/server/agent.rb', line 273

def execute_method(method_def, params, context)
  handler = method_def[:handler]

  # Call the handler with appropriate parameters
  case handler.arity
  when 0
    instance_exec(&handler)
  when 1
    instance_exec(params, &handler)
  when 2
    instance_exec(params, context, &handler)
  else
    # For methods with more parameters, pass params and context
    instance_exec(params, context, &handler)
  end
end

#execute_with_middleware(method_def, params, context) ⇒ Object

Execute a method with the middleware chain

Parameters:

  • method_def (Hash)

    The method definition

  • params (Hash)

    The method parameters

  • context (A2A::Server::Context)

    The request context

Returns:

  • (Object)

    The method result



255
256
257
258
259
260
261
262
263
264
# File 'lib/a2a/server/agent.rb', line 255

def execute_with_middleware(method_def, params, context)
  # Build middleware chain
  middleware_chain = build_middleware_chain(method_def, context)

  # Execute the chain
  middleware_chain.call(params) do
    # Execute the actual method
    execute_method(method_def, params, context)
  end
end

#handle_a2a_request(request, context: nil) ⇒ Hash

Handle an A2A JSON-RPC request

Parameters:

Returns:

  • (Hash)

    The JSON-RPC response



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/a2a/server/agent.rb', line 210

def handle_a2a_request(request, context: nil)
  method_name = request.method
  method_def = self.class.a2a_method_definition(method_name)

  raise A2A::Errors::MethodNotFound, "Method '#{method_name}' not found" unless method_def

  # Create context if not provided
  context ||= A2A::Server::Context.new(request: request)

  # Validate security requirements
  validate_security_requirements(method_def, context)

  # Execute with middleware chain
  result = execute_with_middleware(method_def, request.params, context)

  # Build response
  A2A::Protocol::JsonRpc.build_response(result: result, id: request.id) unless request.notification?
rescue A2A::Errors::A2AError => e
  # Return error response for A2A errors
  unless request.notification?
    A2A::Protocol::JsonRpc.build_error_response(
      code: e.code,
      message: e.message,
      data: e.data,
      id: request.id
    )
  end
rescue StandardError => e
  # Convert other errors to internal errors
  unless request.notification?
    A2A::Protocol::JsonRpc.build_error_response(
      code: A2A::Protocol::JsonRpc::INTERNAL_ERROR,
      message: e.message,
      id: request.id
    )
  end
end

#validate_security_requirements(method_def, context) ⇒ Object (private)

Validate security requirements for a method

Parameters:

  • method_def (Hash)

    The method definition

  • context (A2A::Server::Context)

    The request context

Raises:



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/a2a/server/agent.rb', line 299

def validate_security_requirements(method_def, context)
  security_requirements = method_def[:security] || []
  return if security_requirements.empty?

  # Check if any required security scheme is satisfied
  satisfied = security_requirements.any? do |scheme|
    context.authenticated_with?(scheme)
  end

  return if satisfied

  if context.authenticated?
    raise A2A::Errors::AuthorizationFailed, "Method requires one of: #{security_requirements.join(', ')}"
  end

  raise A2A::Errors::AuthenticationRequired,
        "Method requires authentication with: #{security_requirements.join(', ')}"
end