tools: 修 spark_submit 校验的两处小问题

c9e54a3 引入了 server-minted 校验, 但有两处需要调整:

1. 错误信息用 fmt.Errorf(... %w).Error() 构造, %w 包装语义被丢
   掉 (errResult 接收 string, Error() 之后 %w 已经不可达). 改用
   fmt.Sprintf 拼接, %q 引用路径, 错误信息保持不变但代码不再误
   导.

2. 校验放在 scriptPath 解析之后、queue 解析之前. 错误信息会按字
   段出现顺序报 (cluster_id, master, deploy_mode, script_path,
   queue, ...), 但当前顺序是 script_path 校验先报, 然后才报 queue
   缺失. 把校验挪到所有 RequireString 之后、parseStringMap 之前,
   LLM 看错误时字段顺序跟 schema 顺序一致.

nil-safe 校验逻辑不变 (d.UploadStore == nil 时跳过). 现有测试
全部通过:
  - TestSparkSubmit_RejectsNonMintedPath 仍通过 (校验位置不影响
    行为)
  - TestSparkSubmit_StructuredCommand 仍通过 (走 mint 路径)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tao.chen
2026-07-13 19:55:16 +08:00
co-authored by Claude
parent c9e54a3f27
commit 44c9f415b7
+15 -12
View File
@@ -80,26 +80,29 @@ func (d *Deps) SparkSubmitHandler(ctx context.Context, req mcp.CallToolRequest)
return errResult("spark_submit: " + err.Error()), nil return errResult("spark_submit: " + err.Error()), nil
} }
// Reject paths that didn't come through upload_file on this server. executorMemory, err := req.RequireString("executor_memory")
// Cluster-local paths and arbitrary host paths are unreachable from the MCP if err != nil {
// server's spark-submit, so we fail fast with a clear message rather than return errResult("spark_submit: " + err.Error()), nil
// letting spark-submit produce a confusing FileNotFoundException later.
if d.UploadStore != nil {
if _, err := d.UploadStore.Validate(scriptPath); err != nil {
return errResult(fmt.Errorf("spark_submit: script_path %q is not from upload_file on this server; upload the file first via upload_file and pass back the path field: %w", scriptPath, err).Error()), nil
}
} }
queue, err := req.RequireString("queue") queue, err := req.RequireString("queue")
if err != nil { if err != nil {
return errResult("spark_submit: " + err.Error()), nil return errResult("spark_submit: " + err.Error()), nil
} }
executorMemory, err := req.RequireString("executor_memory")
if err != nil {
return errResult("spark_submit: " + err.Error()), nil
}
args := req.GetArguments() args := req.GetArguments()
// Reject paths that didn't come through upload_file on this server.
// Cluster-local paths and arbitrary host paths are unreachable from the MCP
// server's spark-submit, so we fail fast with a clear message rather than
// letting spark-submit produce a confusing FileNotFoundException later.
// Placed after all required-string parses so error messages surface in
// field order (cluster_id, master, ..., script_path, queue, ...).
if d.UploadStore != nil {
if _, err := d.UploadStore.Validate(scriptPath); err != nil {
return errResult(fmt.Sprintf("spark_submit: script_path %q is not from upload_file on this server; upload the file first via upload_file and pass back the path field", scriptPath)), nil
}
}
// deploy_mode is required for forward compatibility but the builder does not // deploy_mode is required for forward compatibility but the builder does not
// emit --deploy-mode, matching the Python reference's actual behavior. If the // emit --deploy-mode, matching the Python reference's actual behavior. If the
// Go side needs --deploy-mode later, add it here and document the divergence. // Go side needs --deploy-mode later, add it here and document the divergence.