feat(mcp-server): server instructions + report-only status tool guidance
When a user asks to view cluster status (e.g. "看看机器情况"), the LLM saw non-OK values in the MCP response (a node UNAVAIL, a job PEND/EXIT) and treated them as incidents — proposing unsolicited diagnostic/repair actions and referencing "known bugs". The user only wanted a factual status report. Mitigation at the MCP layer: - Add server instructions (returned to the client in the initialize handshake) stating that the status tools return operational snapshots, that UNAVAIL/PEND/EXIT/UNKWN are normal state, and that the model must not speculate or propose actions unless the user explicitly asks. - Append report-only guidance to the scheduler_list_nodes / list_jobs / cluster_metrics descriptions. Verified: initialize response carries the 857-char instructions; tools/list shows the updated descriptions. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
057274094d
commit
3db216c628
1 changed files with 21 additions and 3 deletions
|
|
@ -10,6 +10,23 @@ import (
|
||||||
"mcp-server/internal"
|
"mcp-server/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// serverInstructions are returned to MCP clients (LLMs like Claude Code) in
|
||||||
|
// the initialize handshake and are the standard way to steer how the model
|
||||||
|
// uses the server. The key guidance: the status tools return operational
|
||||||
|
// snapshots, not incident triggers — a node reporting UNAVAIL or a job in
|
||||||
|
// PEND/EXIT/UNKWN is normal state. Without this, the model treats every
|
||||||
|
// non-"OK" value as a problem and floods the user with unsolicited
|
||||||
|
// diagnostic advice when they only asked to view status.
|
||||||
|
const serverInstructions = "XCE scheduler agent — cluster status, not an incident console.\n\n" +
|
||||||
|
"Status tools (scheduler_list_nodes, scheduler_list_jobs, scheduler_get_job, scheduler_cluster_metrics) " +
|
||||||
|
"return informational snapshots. A node showing UNAVAIL/offline, or a job in PEND/EXIT/UNKWN/TRANSIENT, is " +
|
||||||
|
"normal operational state, not an error condition.\n\n" +
|
||||||
|
"When the user asks to view or check status, report the data factually and concisely. Do NOT speculate about " +
|
||||||
|
"root causes, do NOT reference \"known bugs\", and do NOT propose diagnostic or repair actions (checking logs, " +
|
||||||
|
"restarting daemons, opening tickets, ssh'ing to nodes, etc.) unless the user explicitly asks for help or " +
|
||||||
|
"remediation. If you believe action may be warranted, say so in at most one line and let the user decide.\n\n" +
|
||||||
|
"Write tools (scheduler_submit/hold/release/cancel) are destructive and require the user's explicit intent."
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
baseURL := os.Getenv("XCE_API_URL")
|
baseURL := os.Getenv("XCE_API_URL")
|
||||||
token := os.Getenv("XCE_API_KEY")
|
token := os.Getenv("XCE_API_KEY")
|
||||||
|
|
@ -32,6 +49,7 @@ func main() {
|
||||||
"0.2.0",
|
"0.2.0",
|
||||||
mcps.WithResourceCapabilities(true, true),
|
mcps.WithResourceCapabilities(true, true),
|
||||||
mcps.WithLogging(),
|
mcps.WithLogging(),
|
||||||
|
mcps.WithInstructions(serverInstructions),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Read-only tools are annotated as such so the MCP client does not prompt
|
// Read-only tools are annotated as such so the MCP client does not prompt
|
||||||
|
|
@ -39,7 +57,7 @@ func main() {
|
||||||
|
|
||||||
// ── scheduler_list_jobs ──
|
// ── scheduler_list_jobs ──
|
||||||
s.AddTool(mcp.NewTool("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.WithDescription("List jobs in the cluster. Supports filtering by queue, owner, status, and pagination. Status snapshot: report results as-is; job states (PEND/RUN/EXIT/DONE/etc.) are informational, not errors to investigate unprompted."),
|
||||||
mcp.WithString("queue", mcp.Description("Filter by queue name")),
|
mcp.WithString("queue", mcp.Description("Filter by queue name")),
|
||||||
mcp.WithString("owner", mcp.Description("Filter by job owner username")),
|
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.WithString("status", mcp.Description("Filter by status: pending|running|succeeded|failed|cancelled|suspended|unknown")),
|
||||||
|
|
@ -69,7 +87,7 @@ func main() {
|
||||||
|
|
||||||
// ── scheduler_list_nodes ──
|
// ── scheduler_list_nodes ──
|
||||||
s.AddTool(mcp.NewTool("scheduler_list_nodes",
|
s.AddTool(mcp.NewTool("scheduler_list_nodes",
|
||||||
mcp.WithDescription("List all compute nodes with their resource usage and status."),
|
mcp.WithDescription("List all compute nodes with their resource usage and status. Status snapshot: report node states (OK/UNAVAIL/etc.) as-is — a non-OK node is informational, not an error; do not offer diagnostic/repair suggestions unless the user asks."),
|
||||||
mcp.WithReadOnlyHintAnnotation(true),
|
mcp.WithReadOnlyHintAnnotation(true),
|
||||||
mcp.WithDestructiveHintAnnotation(false),
|
mcp.WithDestructiveHintAnnotation(false),
|
||||||
), app.HandleListNodes)
|
), app.HandleListNodes)
|
||||||
|
|
@ -89,7 +107,7 @@ func main() {
|
||||||
|
|
||||||
// ── scheduler_cluster_metrics ──
|
// ── scheduler_cluster_metrics ──
|
||||||
s.AddTool(mcp.NewTool("scheduler_cluster_metrics",
|
s.AddTool(mcp.NewTool("scheduler_cluster_metrics",
|
||||||
mcp.WithDescription("Get a snapshot of overall cluster resource usage and utilization."),
|
mcp.WithDescription("Get a snapshot of overall cluster resource usage and utilization. Status snapshot: report as-is; do not speculate about causes or propose actions unless the user asks."),
|
||||||
mcp.WithReadOnlyHintAnnotation(true),
|
mcp.WithReadOnlyHintAnnotation(true),
|
||||||
mcp.WithDestructiveHintAnnotation(false),
|
mcp.WithDestructiveHintAnnotation(false),
|
||||||
), app.HandleClusterMetrics)
|
), app.HandleClusterMetrics)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue