Phase 0: scheduleragent + mcp-server read-only agent framework
Complete the natural-language scheduler agent's Phase 0 read-only pipeline: Task #2 (API Key auth mechanism): - New store/apikey.go: APIKey store (hash, validate, expiry) - controllers/apikey.go: GenerateAPIKey finally persists + registers RBAC role - controllers/auth.go: GetUsernameFromToken recognizes xce_ak_ prefix - middleware/validatehandler.go: isValidToken handles API Key - config/rbac.yaml: mcp_readonly role (view-only permissions) - 6 new test files covering unit + integration full-chain Task #3 (schedulerclient Go library): - types.go: URM types (URMJob/URMQueue/URMNode/JobStatus/Metrics) - client.go: read-only Client interface (6 methods) - httpclient.go: resty-based HTTP implementation - statusmap.go + _test.go: TDD status mapping (33 tests) Task #4 (mcp-server binary): - cmd/main.go: entry, reads XCE_API_URL/XCE_API_KEY, registers 6 MCP tools - internal/app.go: tool handlers wiring schedulerclient to MCP stdio - Uses github.com/mark3labs/mcp-go@v0.57.0 for stdio transport All: go vet clean, go build clean, schedulerclient 33/33 tests pass. mcp-server binary ~11MB arm64, CGO_ENABLED=0. Architecture: Claude Code --stdio--> mcp-server --HTTP--> apiserver REST API --cgo--> C backend Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
98066db68d
commit
e4af0cac73
27 changed files with 1760 additions and 7 deletions
86
src/frontend/mcp-server/cmd/main.go
Normal file
86
src/frontend/mcp-server/cmd/main.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
mcps "github.com/mark3labs/mcp-go/server"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
|
||||
"mcp-server/internal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
baseURL := os.Getenv("XCE_API_URL")
|
||||
token := os.Getenv("XCE_API_KEY")
|
||||
|
||||
if baseURL == "" {
|
||||
baseURL = "https://localhost:8080"
|
||||
log.Printf("XCE_API_URL not set, defaulting to %s", baseURL)
|
||||
}
|
||||
if token == "" {
|
||||
log.Fatal("XCE_API_KEY environment variable is required")
|
||||
}
|
||||
|
||||
app, err := internal.NewApp(baseURL, token)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize: %v", err)
|
||||
}
|
||||
|
||||
s := mcps.NewMCPServer(
|
||||
"scheduler-agent",
|
||||
"0.1.0",
|
||||
mcps.WithResourceCapabilities(true, true),
|
||||
mcps.WithLogging(),
|
||||
)
|
||||
|
||||
// ── scheduler_list_jobs ──
|
||||
s.AddTool(mcp.NewTool("scheduler_list_jobs",
|
||||
mcp.WithDescription("List jobs in the cluster. Supports filtering by queue, owner, status, and pagination."),
|
||||
mcp.WithString("queue", mcp.Description("Filter by queue name")),
|
||||
mcp.WithString("owner", mcp.Description("Filter by job owner username")),
|
||||
mcp.WithString("status", mcp.Description("Filter by status: pending|running|succeeded|failed|cancelled|suspended|unknown")),
|
||||
mcp.WithNumber("limit", mcp.Description("Max results to return")),
|
||||
mcp.WithString("page_token", mcp.Description("Pagination token from a prior response")),
|
||||
), app.HandleListJobs)
|
||||
|
||||
// ── scheduler_get_job ──
|
||||
s.AddTool(mcp.NewTool("scheduler_get_job",
|
||||
mcp.WithDescription("Get detailed information about a specific job by its ID."),
|
||||
mcp.WithString("id",
|
||||
mcp.Description("Job ID"),
|
||||
mcp.Required(),
|
||||
),
|
||||
), app.HandleGetJob)
|
||||
|
||||
// ── scheduler_list_queues ──
|
||||
s.AddTool(mcp.NewTool("scheduler_list_queues",
|
||||
mcp.WithDescription("List all queues/partitions and their current state (active, inactive, draining)."),
|
||||
), app.HandleListQueues)
|
||||
|
||||
// ── scheduler_list_nodes ──
|
||||
s.AddTool(mcp.NewTool("scheduler_list_nodes",
|
||||
mcp.WithDescription("List all compute nodes with their resource usage and status."),
|
||||
), app.HandleListNodes)
|
||||
|
||||
// ── scheduler_job_logs ──
|
||||
s.AddTool(mcp.NewTool("scheduler_job_logs",
|
||||
mcp.WithDescription("Retrieve the output logs of a job."),
|
||||
mcp.WithString("id",
|
||||
mcp.Description("Job ID"),
|
||||
mcp.Required(),
|
||||
),
|
||||
mcp.WithNumber("tail", mcp.Description("Number of lines from the end to return (0 = all)")),
|
||||
mcp.WithNumber("offset_bytes", mcp.Description("Byte offset to start reading from")),
|
||||
), app.HandleJobLogs)
|
||||
|
||||
// ── scheduler_cluster_metrics ──
|
||||
s.AddTool(mcp.NewTool("scheduler_cluster_metrics",
|
||||
mcp.WithDescription("Get a snapshot of overall cluster resource usage and utilization."),
|
||||
), app.HandleClusterMetrics)
|
||||
|
||||
log.Printf("Starting scheduler-agent MCP server (base-url: %s)", baseURL)
|
||||
if err := mcps.ServeStdio(s); err != nil {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue