Skip to content

feat(fleet): add fleet_query tool for fan-out MCP calls across all nodes#4

Merged
rthill91 merged 1 commit into
mainfrom
broadcast
May 13, 2026
Merged

feat(fleet): add fleet_query tool for fan-out MCP calls across all nodes#4
rthill91 merged 1 commit into
mainfrom
broadcast

Conversation

@rthill91

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

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 adds a new MCP tool (fleet_query) to fan out a tool call across every node in the current fleet (including self) and aggregate per-node results. It extends the tool dependency wiring so the tool can authenticate outbound MCP calls to peers using the shared bearer token already used for fleet membership and MCP access.

Changes:

  • Add fleet_query MCP tool that calls a specified tool on each fleet peer and returns aggregated results.
  • Extend tools.Deps with FleetToken and wire it from mcpserver.Config.Token.
  • Register fleet_query in the global tool list and tool registration flow.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
internal/tools/tools.go Adds FleetToken dependency and registers fleet_query in the tool catalogue.
internal/tools/fleet_query.go Implements the new fan-out tool and the peer MCP call helper (auth + timeout).
internal/mcpserver/server.go Wires the server’s bearer token into tool dependencies for outbound peer calls.
Comments suppressed due to low confidence (3)

internal/tools/fleet_query.go:58

  • This spawns one goroutine per roster entry with no upper bound. Since the registry has no peer-count limit, a large or poisoned roster can cause a burst of thousands of goroutines and outbound dials. Consider adding a concurrency limit (semaphore/worker pool) and still collecting per-node results.
		results := make([]fleetNodeResult, len(roster))
		var wg sync.WaitGroup

		for i, p := range roster {
			wg.Add(1)
			go func(idx int, advertiseURL, nodeID string) {
				defer wg.Done()

internal/tools/fleet_query.go:118

  • Endpoint is built via string concatenation (advertiseURL + "/mcp"), which can produce malformed URLs (double slashes, missing scheme/host) and breaks if AdvertiseURL includes a path prefix. Prefer robust URL joining/validation (e.g., the fleet.joinURL helper pattern) before dialing.
	transport := &mcp.StreamableClientTransport{
		Endpoint: advertiseURL + "/mcp",
		HTTPClient: &http.Client{

internal/tools/fleet_query.go:115

  • The docstring says a 30-second per-peer timeout is applied, but only the HTTP client's per-request Timeout is set. To ensure the entire Connect+CallTool operation is bounded/cancelled reliably, consider wrapping peerCallTool in context.WithTimeout(30s) and using that derived context for both Connect and CallTool.
// peerCallTool opens a short-lived MCP session to advertiseURL and calls the
// named tool, returning the raw result. A 30-second per-peer timeout is applied.
func peerCallTool(ctx context.Context, advertiseURL, token, toolName string, args map[string]any) (*mcp.CallToolResult, error) {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +45 to +47
if in.Tool == "" {
return textResult("tool name is required"), out, nil
}
@rthill91 rthill91 merged commit fc7d475 into main May 13, 2026
6 checks passed
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