Skip to content

Conversation

@yarolegovich
Copy link
Member

@yarolegovich yarolegovich commented Dec 10, 2025

A prototyping attempt to address #129
Some notes:

  • Extension developer needs to provide server and client method definition. Only server definition contains the actual logic.
  • Extension developer attaches transport bindings that make it work with different transports and might require dependencies.
  • Extension methods run in the call context with access to interceptor-provided data like auth or logger.
  • Client-side integration has typed requests and responses.
  • Extension methods are discovered and registered by transport implementations through RequestHandler.

Still need to think of a good way for do it with gRPC.

image

@gemini-code-assist
Copy link

Summary of Changes

Hello @yarolegovich, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a foundational framework for method extensions within the a2a-go project. It enables developers to define and integrate custom methods that extend the core A2A protocol, facilitating more flexible and specialized interactions between agents. The changes encompass new client-side invocation mechanisms, server-side handling, and initial support for JSON-RPC transport bindings, allowing for a structured way to add new functionality.

Highlights

  • Method Extension Framework: Introduced a new a2aext package to define and manage method extensions, providing interfaces for method definitions, bindings, and server/client-side method creation.
  • Client-Side Extension Invocation: Added a generic Invoke function to the a2aclient package, allowing clients to call unary extension methods with proper context, interceptor handling, and typed request/response processing.
  • Server-Side Extension Handling: Updated the a2asrv.RequestHandler interface and its default implementation to support registration and invocation of server-side extension methods, including unary and streaming calls, with interceptor integration.
  • JSON-RPC Transport Integration: Implemented specific JSON-RPC bindings for both client and server extension methods, enabling custom methods to be exposed and consumed over the JSON-RPC transport protocol.
  • Task Search Example: Provided a comprehensive example (examples/tasksearch) demonstrating how to create, register, and invoke a custom 'Task Search' extension method using the new framework on both the client and server sides.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a well-designed mechanism for method extensions, allowing for custom RPC-style methods to be added to the A2A protocol. The changes are comprehensive, touching both client and server components, and include a clear example. The separation of concerns with Method, Binding, and transport-specific implementations is clean. My review includes a few suggestions to improve robustness, clarity, and error handling, such as preventing a potential panic with safer type assertions and improving diagnostic messages.

Comment on lines +84 to +86
func (m *unaryServerMethod[Arg, Res]) InvokeUnary(ctx context.Context, arg any) (any, error) {
return m.call(ctx, arg.(*Arg))
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The direct type assertion arg.(*Arg) will cause a panic if arg is not of the expected type *Arg. This could be triggered by a malformed request or a bug in a transport implementation, leading to a server crash. It's much safer to use the "comma ok" idiom to check the type and return an error if it's incorrect.

func (m *unaryServerMethod[Arg, Res]) InvokeUnary(ctx context.Context, arg any) (any, error) {
	typedArg, ok := arg.(*Arg)
	if !ok {
		return nil, errors.New("internal error: unexpected argument type")
	}
	return m.call(ctx, typedArg)
}

Comment on lines +383 to +386
typedBinding, ok := binding.(*jsonrpcExtensionBinding)
if !ok {
return nil, fmt.Errorf("method %s is not bound to JSON-RPC", method.Name())
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message here is identical to the one for the check above, which can be misleading. If the binding exists but has the wrong type, the error should reflect that instead of saying the method is not bound.

Suggested change
typedBinding, ok := binding.(*jsonrpcExtensionBinding)
if !ok {
return nil, fmt.Errorf("method %s is not bound to JSON-RPC", method.Name())
}
typedBinding, ok := binding.(*jsonrpcExtensionBinding)
if !ok {
return nil, fmt.Errorf("method %s has incorrect JSON-RPC binding type: got %T", method.Name(), binding)
}

GetAgentCard(ctx context.Context) (*a2a.AgentCard, error)

// Invoke calls the provided extension method.
Invoke(ctx context.Context, client a2aext.Method, arg any) (any, error)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For clarity and consistency, consider renaming the client parameter to method. The parameter is of type a2aext.Method, which represents a method definition, not a client instance. This change would align with its usage in implementations and call sites.

Suggested change
Invoke(ctx context.Context, client a2aext.Method, arg any) (any, error)
Invoke(ctx context.Context, method a2aext.Method, arg any) (any, error)

return nil, errNotImplemented
}

func (unimplementedTransport) Invoke(ctx context.Context, client a2aext.Method, arg any) (any, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with the Transport interface, please rename the client parameter to method here as well.

Suggested change
func (unimplementedTransport) Invoke(ctx context.Context, client a2aext.Method, arg any) (any, error) {
func (unimplementedTransport) Invoke(ctx context.Context, method a2aext.Method, arg any) (any, error) {

// GetAgentCard returns an extended a2a.AgentCard if configured.
OnGetExtendedAgentCard(ctx context.Context) (*a2a.AgentCard, error)

// GetAgentCard returns an extended a2a.AgentCard if configured.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This comment appears to be a copy-paste error from OnGetExtendedAgentCard. It should be updated to describe what GetExtensionMethods does.

Suggested change
// GetAgentCard returns an extended a2a.AgentCard if configured.
// GetExtensionMethods returns all registered extension methods.

Comment on lines +92 to +93
if !ok { // fail?
continue

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The // fail? comment indicates uncertainty. Silently continuing on a binding type mismatch during initialization can hide a configuration error. It would be better to log a warning to make developers aware of the potential issue, as this is part of the server setup.

Suggested change
if !ok { // fail?
continue
if !ok {
log.Warn(context.Background(), "extension method has incompatible JSON-RPC binding type", "method", method.Name())
continue
}

@yarolegovich yarolegovich changed the title Yarolegovich/method extensions feat: method extensions prototyping Dec 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants