fix(mcp): include SandboxAgents in MCP agent listing#2204
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes MCP agent visibility and invocation for substrate-backed SandboxAgent resources by listing them alongside declarative Agent CRs and by recognizing both deployment- and workload-ready condition reasons, so MCP list_agents / kagent://agents / invoke_agent behave consistently with the REST/UI paths.
Changes:
- Extend MCP-ready agent enumeration to include both
AgentandSandboxAgentobjects. - Treat
Ready=Truewith reasonDeploymentReadyorWorkloadReadyas MCP-invokable (whenAccepted=True). - Add
AgentClientRegistry.SendMessagefallback to resolve sandbox-registered route keys, plus focused unit tests.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| go/core/internal/mcp/mcp_handler.go | Lists both Agent and SandboxAgent CRs and applies unified Accepted+Ready filtering for MCP visibility. |
| go/core/internal/mcp/mcp_handler_test.go | Adds a regression test ensuring Ready/Accepted SandboxAgent entries appear in MCP listReadyAgents. |
| go/core/internal/a2a/agent_client_registry.go | Falls back to sandbox route keys when resolving clients during SendMessage. |
| go/core/internal/a2a/agent_client_registry_test.go | Adds a regression test verifying the sandbox-route fallback behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| called := false | ||
| server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| called = true | ||
| var rpcReq map[string]any | ||
| require.NoError(t, json.NewDecoder(r.Body).Decode(&rpcReq)) |
There was a problem hiding this comment.
Updated in 0b81aba: called is now an atomic.Bool, with the handler using called.Store(true) so the test no longer has an unsynchronized cross-goroutine write/read. Verified with go test -race ./internal/a2a -run TestAgentClientRegistrySendMessageFallsBackToSandboxRoute -count=1 and go test -race ./internal/a2a -count=1.
| &a2atype.SendMessageRequest{Message: a2atype.NewMessage(a2atype.MessageRoleUser, a2atype.NewTextPart("hello"))}, | ||
| ) | ||
| require.NoError(t, err) | ||
| require.True(t, called) |
There was a problem hiding this comment.
Updated in 0b81aba: the assertion now reads the atomic value via called.Load(). I also ran the focused test and the full internal/a2a package with -race.
35bee8d to
0b81aba
Compare
EItanya
left a comment
There was a problem hiding this comment.
Unfortunately this only masks the problem that exists, and we really need to figure out how to really fix this. If an Agent exists with that Name, Namespace it will return that even if you intended to get the SandboxAgent. I think we need to start including the GroupKind in order to properly key the entries
|
@EItanya thanks, that makes sense. I agree the current fallback from Before pushing another revision, I want to confirm the shape I am thinking about:
Does that match what you had in mind, or would you prefer a different MCP input shape (for example encoding the kind directly into the |
1e15cae to
2d3b17b
Compare
|
@EItanya implemented the GroupKind-based routing in
I also checked that One remaining extension point: if a new Verified with |
| func (r *AgentClientRegistry) RegisterForGroupKind(groupKind, namespace, name string, c *a2aclient.Client) error { | ||
| key, err := routeKeyForGroupKind(groupKind, namespace, name) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| r.set(key, c) | ||
| return nil | ||
| } | ||
|
|
There was a problem hiding this comment.
I don't like adding functionality like this just for tests, and it seems to be only called by the tests
There was a problem hiding this comment.
Updated in a6e6be8: removed RegisterForGroupKind. The production registrar continues to register its workload-derived route key via set, and the A2A test uses that internal route setup directly, so no test-only registration API remains.
| } | ||
|
|
||
| // SendMessageForGroupKind invokes an agent directly via its cached A2A client. | ||
| func (r *AgentClientRegistry) SendMessageForGroupKind(ctx context.Context, groupKind, namespace, name string, req *a2atype.SendMessageRequest) (a2atype.SendMessageResult, error) { |
There was a problem hiding this comment.
This is a classic claudism. Let's just break the SendMessage API and add the new param
There was a problem hiding this comment.
Updated in a6e6be8: removed SendMessageForGroupKind and changed SendMessage to require groupKind directly. The MCP handler now calls the revised API. Verified with go test ./internal/a2a ./internal/mcp -count=1 and the focused A2A route test under -race.
Signed-off-by: lubingtan <bingtanlu@gmail.com>
Signed-off-by: lubingtan <bingtanlu@gmail.com>
Signed-off-by: lubingtan <bingtanlu@gmail.com>
d03f473 to
a6e6be8
Compare
|
Hi @EItanya would you pls take a look again? thanks! |
Summary
list_agentsandkagent://agentsDeploymentReadyandWorkloadReadyReady reasons when filtering MCP-visible agentsinvoke_agentresolve SandboxAgent clients registered under the sandbox route keyFixes #2123.
Reproduction
Added a focused MCP unit test that fails on main: a regular Agent is listed, but an Accepted/Ready SandboxAgent with
WorkloadReadyis omitted.Tests
GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/mcp -run TestListReadyAgentsIncludesSandboxAgents -count=1GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/a2a -run TestAgentClientRegistrySendMessageFallsBackToSandboxRoute -count=1GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/a2a -count=1GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/mcp -count=1