mcp: scheduler_pend_reasons returns numeric id for round-trip

formatPendJob used job.HumanID ("1-0-1786764804-0") as the tool's `id`,
but the apiserver rejects dash-form job IDs as query input (only numeric is
accepted, utils.IsNumeric) — so an id copied out of scheduler_pend_reasons
could not be fed back into scheduler_get_job/cancel/hold/release (HTTP 400).

Use job.ID (numeric, same as scheduler_list_jobs' `id`) and keep `human_id`
for display. Round-trip verified on the cluster: pend_reasons id ->
scheduler_get_job succeeds.

Test: TestFormatPendJobIDConsistency (id is numeric, human_id preserved,
both with and without a pending_reason).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
lei.chen 2026-08-18 04:44:05 +00:00
parent 5753123798
commit bbe8ed3335
2 changed files with 39 additions and 2 deletions

View file

@ -149,11 +149,17 @@ func (a *App) HandlePendReasons(ctx context.Context, req mcp.CallToolRequest) (*
}
// formatPendJob renders one job's PEND reason, or notes it is not waiting.
//
// `id` is the numeric job ID (scheduler_list_jobs' `id` and the value the
// other per-job tools accept as their `id` param) so a caller can round-trip
// it into scheduler_get_job/cancel/hold/release. `human_id` is kept for
// display only — the apiserver rejects dash-form job IDs as query input.
func formatPendJob(job *sclient.URMJob) map[string]any {
pr := parsePendingReason(*job)
if pr == nil {
return map[string]any{
"id": job.HumanID,
"id": job.ID,
"human_id": job.HumanID,
"name": job.Name,
"queue": job.Queue,
"status": job.Status,
@ -161,7 +167,8 @@ func formatPendJob(job *sclient.URMJob) map[string]any {
}
}
return map[string]any{
"id": job.HumanID,
"id": job.ID,
"human_id": job.HumanID,
"name": job.Name,
"queue": job.Queue,
"status": job.Status,

View file

@ -71,3 +71,33 @@ func TestPendReasonSummary(t *testing.T) {
}
}
}
func TestFormatPendJobIDConsistency(t *testing.T) {
job := sclient.URMJob{
ID: "73931152729047040",
HumanID: "1-0-1786764804-0",
Name: "sleep 5",
Queue: "normal",
Status: "PEND",
}
// no pending_reason -> explanation branch
out := formatPendJob(&job)
if id, _ := out["id"].(string); id != job.ID {
t.Errorf("id = %q, want numeric %q (must round-trip into get/cancel/hold)", id, job.ID)
}
if hid, _ := out["human_id"].(string); hid != job.HumanID {
t.Errorf("human_id = %q, want %q", hid, job.HumanID)
}
// with pending_reason -> main branch
job.Extensions = map[string]any{
"pending_reason": `{"main_reason":3,"main_reason_name":"match_fail","host_reasons":[],"res_reasons":[]}`,
}
out = formatPendJob(&job)
if id, _ := out["id"].(string); id != job.ID {
t.Errorf("id (with reason) = %q, want numeric %q", id, job.ID)
}
if hid, _ := out["human_id"].(string); hid != job.HumanID {
t.Errorf("human_id (with reason) = %q, want %q", hid, job.HumanID)
}
}