-
Notifications
You must be signed in to change notification settings - Fork 375
feat: expose a2a agents via mcp in controller http server and migrate to mcp go-sdk #1210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
136619a to
fa6f830
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR extends the MCP server functionality to the Go controller's HTTP server by exposing A2A agents via a new /api/mcp endpoint. The implementation builds on PR #1201 which added CLI-based MCP serving, now bringing the same capabilities to the HTTP server with support for streamable HTTP transport.
Changes:
- Added MCP handler bridging MCP protocol to A2A endpoints with session-based context management
- Integrated MCP handler into HTTP server with new
/api/mcproute - Added comprehensive E2E tests for MCP endpoint functionality including agent listing, invocation, and error handling
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| go/internal/mcp/mcp_handler.go | Implements MCP-to-A2A bridge with list_agents and invoke_agent tools, session management, and context tracking |
| go/pkg/app/app.go | Instantiates MCP handler and wires it into the HTTP server configuration |
| go/internal/httpserver/server.go | Adds /api/mcp route and conditionally mounts MCP handler |
| go/test/e2e/invoke_mcp_test.go | E2E tests validating MCP endpoint with agent listing, invocation, and error handling |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
EItanya
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overally looks good to me, just some minor changes
go/internal/httpserver/server.go
Outdated
| APIPathMemories = "/api/memories" | ||
| APIPathNamespaces = "/api/namespaces" | ||
| APIPathA2A = "/api/a2a" | ||
| APIPathMCP = "/api/mcp" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically the path for streamable HTTP mcp servers is /mcp, any reason we can't do that?
go/internal/mcp/mcp_handler.go
Outdated
| httpServer *mcpserver.StreamableHTTPServer | ||
| lock sync.RWMutex | ||
| // Map to store context IDs per session and agent | ||
| contextBySessionAndAgent sync.Map |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth adding a TODO that this will not work across replicas. We may be able to store any necessary data in an external store if need be. Not sure we have any data specific to MCP here though, since the A2A data is already stored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This map is only for session "routing" so the agent using this MCP tool will have proper conversation continuation with the A2A agent by tracking the MCP context ID and the A2A context ID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed this session caching map since it won't work across replicas. The same functionality is achieved by just returning the context ID to the MCP client and most agents using it are capable of remembering the proper context id to call the A2A agent if it wants to continue the conversation. This makes the code simpler and avoids needing storage on server side.
Yes the changes make sense, I haven't had the time to update them will do that now |
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
fa6f830 to
61a0978
Compare
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| s.router.PathPrefix(APIPathMCP).Handler(s.config.MCPHandler) | ||
| } | ||
|
|
||
| // Use middleware for common functionality |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MCP handler route is registered before the authentication middleware is applied (line 237). In gorilla/mux, middleware added via Use() applies to routes registered before the Use() call, but the order can be confusing. While the current code appears correct (middleware applies to all routes including MCP), this pattern is fragile. Consider either: (1) explicitly documenting that routes must be registered before middleware, or (2) applying middleware before route registration for clarity and to prevent future security issues.
| // Use middleware for common functionality | |
| // Use middleware for common functionality. | |
| // | |
| // NOTE: gorilla/mux applies middleware added via Use() to all routes | |
| // registered on this router, including those registered *before* the Use() | |
| // call. To avoid confusion and potential security issues where routes might | |
| // accidentally be registered without authentication, all routes for this | |
| // server MUST be registered above this middleware block. Do not add new | |
| // routes below these Use() calls. |
| ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
| defer cancel() |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The context with timeout is created and immediately deferred for cancellation, but then the session remains open beyond this function's scope via t.Cleanup. This means the context expires after 30 seconds while the session might still be in use. Consider using the test's context or removing the timeout to avoid premature context cancellation during test execution.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Jet Chiang <jetjiang.ez@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Jet Chiang <jetjiang.ez@gmail.com>
The previous PR #1201 adds a CLI option to serve A2A agents via stdio / http transport MCP server. This PR extends this feature to the Go controller's HTTP server by adding a new
/mcproute and handler for Streamable HTTP server. The MCP bridge exposes thelist_agentsandinvoke_agenttools.e2e test has been added using a MCP client. Manual verification was done using MCP inspector and MCP clients like Cursor as well.
This PR also migrates all usages of
mark3labs/mcp-goto the new officialgo-sdklibrary from Anthropic for MCP protocol and server / client implementation. Most changes are direct and syntax-only. The only exception is that I cannot find a built-in option to set headers for transport interfaces (SSE/Streamable HTTP) ingo-sdk, so I had to create a custom HTTP client to make sure these headers are set inreconciler.go