Skip to content

fix(mcp): include SandboxAgents in MCP agent listing#2204

Open
LuBingtan wants to merge 5 commits into
kagent-dev:mainfrom
LuBingtan:fix-mcp-sandboxagent-listing
Open

fix(mcp): include SandboxAgents in MCP agent listing#2204
LuBingtan wants to merge 5 commits into
kagent-dev:mainfrom
LuBingtan:fix-mcp-sandboxagent-listing

Conversation

@LuBingtan

Copy link
Copy Markdown

Summary

  • include Ready/Accepted SandboxAgent resources in MCP list_agents and kagent://agents
  • accept both DeploymentReady and WorkloadReady Ready reasons when filtering MCP-visible agents
  • let MCP invoke_agent resolve SandboxAgent clients registered under the sandbox route key

Fixes #2123.

Reproduction

Added a focused MCP unit test that fails on main: a regular Agent is listed, but an Accepted/Ready SandboxAgent with WorkloadReady is omitted.

Tests

  • GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/mcp -run TestListReadyAgentsIncludesSandboxAgents -count=1
  • GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/a2a -run TestAgentClientRegistrySendMessageFallsBackToSandboxRoute -count=1
  • GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/a2a -count=1
  • GOCACHE=/tmp/go-build GOFLAGS=-buildvcs=false go test ./internal/mcp -count=1

Copilot AI review requested due to automatic review settings July 10, 2026 14:09
@LuBingtan
LuBingtan requested a review from a team as a code owner July 10, 2026 14:09
@github-actions github-actions Bot added the bug Something isn't working label Jul 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 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 Agent and SandboxAgent objects.
  • Treat Ready=True with reason DeploymentReady or WorkloadReady as MCP-invokable (when Accepted=True).
  • Add AgentClientRegistry.SendMessage fallback 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.

Comment on lines +16 to +20
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))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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.

@LuBingtan
LuBingtan force-pushed the fix-mcp-sandboxagent-listing branch 2 times, most recently from 35bee8d to 0b81aba Compare July 10, 2026 14:20

@EItanya EItanya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

@LuBingtan

Copy link
Copy Markdown
Author

@EItanya thanks, that makes sense. I agree the current fallback from namespace/name to the sandbox route can select the wrong target when an Agent and SandboxAgent share the same namespace/name.

Before pushing another revision, I want to confirm the shape I am thinking about:

  1. list_agents / kagent://agents should return each entry with both ref and groupKind, for example:
    • ref: "default/my-agent", groupKind: "Agent.kagent.dev"
    • ref: "default/my-agent", groupKind: "SandboxAgent.kagent.dev"
  2. invoke_agent should accept an optional groupKind argument. If omitted, it defaults to Agent.kagent.dev for backward compatibility with existing clients.
  3. AgentClientRegistry should route by (groupKind, namespace, name) instead of falling back from the regular Agent key to the SandboxAgent key.
  4. Tests should cover the same namespace/name collision case and assert that groupKind: SandboxAgent.kagent.dev invokes the SandboxAgent backend, not the regular Agent backend.

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 agent ref instead of a separate groupKind field)?

@LuBingtan
LuBingtan force-pushed the fix-mcp-sandboxagent-listing branch from 1e15cae to 2d3b17b Compare July 13, 2026 04:11
@LuBingtan

LuBingtan commented Jul 13, 2026

Copy link
Copy Markdown
Author

@EItanya implemented the GroupKind-based routing in 2d3b17b4.

  • list_agents and kagent://agents now return groupKind.
  • invoke_agent accepts groupKind, defaulting to Agent.kagent.dev for backward compatibility.
  • The registry now selects the exact (groupKind, namespace, name) route and no longer falls back to a SandboxAgent route.
  • The regression test covers an Agent and SandboxAgent with the same namespace/name and verifies that the SandboxAgent backend is selected.

I also checked that routeKeyForGroupKind delegates to the existing routeKey, preserving the current route keys: namespace/name for Agent.kagent.dev and sandboxes/namespace/name for SandboxAgent.kagent.dev. This matches the A2A registrar, which derives the same keys from workload mode.

One remaining extension point: if a new AgentObject kind is added, or an existing kind can map to a different workload mode, its GroupKind-to-route mapping must be added explicitly. Unsupported kinds return an error instead of silently falling back to a potentially wrong target.

Verified with go test ./internal/a2a -count=1 and go test ./internal/mcp -count=1.

Comment on lines +43 to +51
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
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't like adding functionality like this just for tests, and it seems to be only called by the tests

@LuBingtan LuBingtan Jul 15, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a classic claudism. Let's just break the SendMessage API and add the new param

@LuBingtan LuBingtan Jul 15, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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>
@LuBingtan

Copy link
Copy Markdown
Author

Hi @EItanya would you pls take a look again? thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] MCP list_agents / invoke_agent exclude all SandboxAgents (only lists Agent CRs, hardcodes reason=="DeploymentReady")

3 participants