package tools import ( "context" "github.com/mark3labs/mcp-go/mcp" "spark-mcp-go/internal/rm" ) const ListApplicationsName = "list_applications" // NewListApplicationsTool returns the schema for the list_applications MCP Tool. func NewListApplicationsTool() mcp.Tool { return mcp.NewTool(ListApplicationsName, mcp.WithDescription("List YARN applications from a cluster's ResourceManager. Returns the raw RM JSON response so the agent can inspect app IDs, states, and owners."), mcp.WithString("cluster_id", mcp.Required(), mcp.Description("ID of the configured cluster (from list_clusters)"), ), mcp.WithString("state", mcp.Description("Filter by application state. YARN accepts comma-separated states; common values: NEW, NEW_SAVING, SUBMITTED, ACCEPTED, RUNNING, FINISHED, FAILED, KILLED."), ), mcp.WithString("user", mcp.Description("Filter by submitting user."), ), ) } // ListApplicationsHandler queries the ResourceManager for applications. func (d *Deps) ListApplicationsHandler(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { clusterID, err := req.RequireString("cluster_id") if err != nil { return errResult("list_applications: " + err.Error()), nil } args := req.GetArguments() state := "" if v, ok := args["state"].(string); ok { state = v } user := "" if v, ok := args["user"].(string); ok { user = v } callLog := startToolCall(ctx, d.Logger, ListApplicationsName, map[string]any{ "cluster_id": clusterID, "state": state, "user": user, }) defer callLog.End() cl, err := d.ClusterRepo.Get(ctx, clusterID) if err != nil { callLog.WithError(err) return errResult("list_applications: cluster " + clusterID + ": " + err.Error()), nil } rmc := rm.New(d.HTTPClient, cl) raw, err := rmc.ListApps(ctx, state, user) if err != nil { callLog.WithError(err) return errResult("list_applications: " + err.Error()), nil } callLog.WithResult(map[string]any{"bytes": len(raw)}) return textResult(string(raw)), nil }