Prechádzať zdrojové kódy

feat:mcp所有接口数据增加导出到文件的接口

Jerry Liang 2 mesiacov pred
rodič
commit
a466e62322

+ 2 - 1
service/cmd/mcp/main.go

@@ -151,4 +151,5 @@ const serverInstructions = `FranklinWH 工具箱 MCP Server — 内部工具平
 4. storage_file_* 支持读写,storage_minio_* 仅只读
 5. 查询 MongoDB 时注意 collection 白名单限制
 6. MinIO 操作受 bucket 白名单限制
-7. 描述中标注 [写操作] 的工具由 Agent 权限管控,执行前会请求用户确认`
+7. 描述中标注 [写操作] 的工具由 Agent 权限管控,执行前会请求用户确认
+8. 大结果落盘:当工具返回的 JSON 中包含 "spilled": true 时,表示完整结果已写入本地文件,请使用文件读取工具按返回的 "localPath" 读取完整内容;"preview" 字段仅为前部片段,不代表全部数据。落盘文件位于 /tmp/fwh-toolbox-agent/ 目录下`

+ 2 - 2
service/cmd/mcp/tools/data/emspara.go

@@ -46,7 +46,7 @@ func handleEmsparaVidInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 		return helper.EmptyResult(fmt.Sprintf("未找到 VID %d 的信息: %v", id, err)), nil
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"id":          id,
 		"type":        "VID",
 		"macro_name":  name,
@@ -70,7 +70,7 @@ func handleEmsparaPidInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 		return helper.EmptyResult(fmt.Sprintf("未找到 PID %d 的信息: %v", id, err)), nil
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"id":          id,
 		"type":        "PID",
 		"macro_name":  name,

+ 21 - 21
service/cmd/mcp/tools/data/fwh_api.go

@@ -234,7 +234,7 @@ func handleFwhQuerySiteBaseInfoList(ctx context.Context, req mcp.CallToolRequest
 	if err != nil {
 		return helper.ServiceError("查询站点基本信息列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQuerySiteList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -254,7 +254,7 @@ func handleFwhQuerySiteList(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 	if err != nil {
 		return helper.ServiceError("查询站点列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQuerySiteModeInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -267,7 +267,7 @@ func handleFwhQuerySiteModeInfo(ctx context.Context, req mcp.CallToolRequest) (*
 	if err != nil {
 		return helper.ServiceError("查询站点工作模式", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQuerySitePowerFlow(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -289,7 +289,7 @@ func handleFwhQuerySitePowerFlow(ctx context.Context, req mcp.CallToolRequest) (
 	if err != nil {
 		return helper.ServiceError("查询功率流", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQuerySiteAlarmList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -302,7 +302,7 @@ func handleFwhQuerySiteAlarmList(ctx context.Context, req mcp.CallToolRequest) (
 	if err != nil {
 		return helper.ServiceError("查询站点告警列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryEventAlarmList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -347,7 +347,7 @@ func handleFwhQueryEventAlarmList(ctx context.Context, req mcp.CallToolRequest)
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryAlarmSnapshot(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -373,7 +373,7 @@ func handleFwhQueryAlarmSnapshot(ctx context.Context, req mcp.CallToolRequest) (
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryAlarmStatistics(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -384,7 +384,7 @@ func handleFwhQueryAlarmStatistics(ctx context.Context, req mcp.CallToolRequest)
 	if err != nil {
 		return helper.ServiceError("查询实时告警统计", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryAlarmStatisticsFromCode(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -399,7 +399,7 @@ func handleFwhQueryAlarmStatisticsFromCode(ctx context.Context, req mcp.CallTool
 	if err != nil {
 		return helper.ServiceError("查询告警码统计", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryMqttLogList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -437,7 +437,7 @@ func handleFwhQueryMqttLogList(ctx context.Context, req mcp.CallToolRequest) (*m
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryShutdownList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -460,7 +460,7 @@ func handleFwhQueryShutdownList(ctx context.Context, req mcp.CallToolRequest) (*
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryEmsLogRecord(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -475,7 +475,7 @@ func handleFwhQueryEmsLogRecord(ctx context.Context, req mcp.CallToolRequest) (*
 		// 如果是"没有召唤日志"的错误,返回空结果
 		return helper.EmptyResult(fmt.Sprintf("未找到EMS日志记录: %v", err)), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryFaultRecordList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -500,7 +500,7 @@ func handleFwhQueryFaultRecordList(ctx context.Context, req mcp.CallToolRequest)
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryFaultRecordCallableList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -518,7 +518,7 @@ func handleFwhQueryFaultRecordCallableList(ctx context.Context, req mcp.CallTool
 	if len(data) == 0 {
 		return helper.EmptyResult("无可召唤的故障录波数据"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryFaultRecordCallRecordList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -530,7 +530,7 @@ func handleFwhQueryFaultRecordCallRecordList(ctx context.Context, req mcp.CallTo
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryFaultRecordSubList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -552,7 +552,7 @@ func handleFwhQueryFaultRecordSubList(ctx context.Context, req mcp.CallToolReque
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQuerySystemUpdateLog(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -569,7 +569,7 @@ func handleFwhQuerySystemUpdateLog(ctx context.Context, req mcp.CallToolRequest)
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQuerySystemUpdateLogDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -591,7 +591,7 @@ func handleFwhQuerySystemUpdateLogDetail(ctx context.Context, req mcp.CallToolRe
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryAHubUpdateLog(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -608,7 +608,7 @@ func handleFwhQueryAHubUpdateLog(ctx context.Context, req mcp.CallToolRequest) (
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryTouTemplateList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -620,7 +620,7 @@ func handleFwhQueryTouTemplateList(ctx context.Context, req mcp.CallToolRequest)
 	if err != nil {
 		return helper.ServiceError("查询TOU模板列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleFwhQueryTouTemplateDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -641,5 +641,5 @@ func handleFwhQueryTouTemplateDetail(ctx context.Context, req mcp.CallToolReques
 	if data == nil {
 		return helper.EmptyResult("模板不存在"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 2 - 2
service/cmd/mcp/tools/data/mongo.go

@@ -130,7 +130,7 @@ func handleMongoFind(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 		return helper.EmptyResult("查询结果为空"), nil
 	}
 
-	return helper.JSONResult(results), nil
+	return helper.JSONResultOrFileAuto(results), nil
 }
 
 func handleMongoCount(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -155,7 +155,7 @@ func handleMongoCount(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 		return helper.ServiceError("MongoDB统计", err), nil
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"collection": collection,
 		"count":      count,
 	}), nil

+ 2 - 2
service/cmd/mcp/tools/data/pgsql.go

@@ -41,7 +41,7 @@ func handlePgsqlQuerySnapshotFileRecord(ctx context.Context, req mcp.CallToolReq
 	if err != nil {
 		return helper.EmptyResult(fmt.Sprintf("未找到文件记录: %v", err)), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handlePgsqlQuerySnapshotData(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -67,5 +67,5 @@ func handlePgsqlQuerySnapshotData(ctx context.Context, req mcp.CallToolRequest)
 	if len(data) == 0 {
 		return helper.EmptyResult("查询结果为空"), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 38 - 22
service/cmd/mcp/tools/data/plane_v2_api_handlers.go

@@ -2,17 +2,33 @@ package data
 
 import (
 	"context"
+	"encoding/json"
 
 	"github.com/mark3labs/mcp-go/mcp"
 
 	"fwh-toolbox-server/cmd/mcp/tools/helper"
 )
 
+// planeV2RawResult 将 Plane V2 API 返回的原始 JSON bytes 转为 MCP 结果。
+// 结果较大时自动落盘到本地文件(/tmp/fwh-toolbox-agent/results/),
+// 返回 localPath + 预览,避免大数据占用 LLM 上下文。
+func planeV2RawResult(data []byte, err error, action string) (*mcp.CallToolResult, error) {
+	if err != nil {
+		return helper.ServiceError(action, err), nil
+	}
+	// 解析为 any 再格式化输出;解析失败则按原始文本走落盘判断
+	var parsed any
+	if jsonErr := json.Unmarshal(data, &parsed); jsonErr != nil {
+		return helper.BytesResultOrFile(data, "plane_v2_"+action), nil
+	}
+	return helper.JSONResultOrFile(parsed, "plane_v2_"+action), nil
+}
+
 // === Workspace Handlers ===
 
 func handlePlaneV2ListWorkspaces(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 	data, err := planeV2Svc.ListWorkspaces()
-	return planeRawResult(data, err, "列出工作空间")
+	return planeV2RawResult(data, err, "列出工作空间")
 }
 
 func handlePlaneV2GetWorkspace(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -21,7 +37,7 @@ func handlePlaneV2GetWorkspace(ctx context.Context, req mcp.CallToolRequest) (*m
 		return helper.ParamError("workspace_slug"), nil
 	}
 	data, apiErr := planeV2Svc.GetWorkspace(slug)
-	return planeRawResult(data, apiErr, "获取工作空间详情")
+	return planeV2RawResult(data, apiErr, "获取工作空间详情")
 }
 
 // === Project Handlers ===
@@ -32,7 +48,7 @@ func handlePlaneV2ListProjects(ctx context.Context, req mcp.CallToolRequest) (*m
 		return helper.ParamError("workspace_slug"), nil
 	}
 	data, apiErr := planeV2Svc.ListProjects(slug)
-	return planeRawResult(data, apiErr, "列出项目")
+	return planeV2RawResult(data, apiErr, "列出项目")
 }
 
 func handlePlaneV2GetProject(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -42,7 +58,7 @@ func handlePlaneV2GetProject(ctx context.Context, req mcp.CallToolRequest) (*mcp
 		return helper.ParamError("project_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetProject(slug, projectId)
-	return planeRawResult(data, apiErr, "获取项目详情")
+	return planeV2RawResult(data, apiErr, "获取项目详情")
 }
 
 // === Issue Handlers ===
@@ -51,7 +67,7 @@ func handlePlaneV2ListIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp
 	slug, _ := req.RequireString("workspace_slug")
 	projectId, _ := req.RequireString("project_id")
 	data, err := planeV2Svc.ListIssues(slug, projectId)
-	return planeRawResult(data, err, "列出工作项")
+	return planeV2RawResult(data, err, "列出工作项")
 }
 
 func handlePlaneV2GetIssue(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -62,7 +78,7 @@ func handlePlaneV2GetIssue(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
 		return helper.ParamError("issue_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetIssue(slug, projectId, issueId)
-	return planeRawResult(data, apiErr, "获取工作项详情")
+	return planeV2RawResult(data, apiErr, "获取工作项详情")
 }
 
 func handlePlaneV2GetSubIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -73,7 +89,7 @@ func handlePlaneV2GetSubIssues(ctx context.Context, req mcp.CallToolRequest) (*m
 		return helper.ParamError("issue_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetSubIssues(slug, projectId, issueId)
-	return planeRawResult(data, apiErr, "获取子工作项")
+	return planeV2RawResult(data, apiErr, "获取子工作项")
 }
 
 func handlePlaneV2GetIssueActivity(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -84,7 +100,7 @@ func handlePlaneV2GetIssueActivity(ctx context.Context, req mcp.CallToolRequest)
 		return helper.ParamError("issue_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetIssueActivity(slug, projectId, issueId)
-	return planeRawResult(data, apiErr, "获取工作项活动历史")
+	return planeV2RawResult(data, apiErr, "获取工作项活动历史")
 }
 
 // === Intake Handlers ===
@@ -94,7 +110,7 @@ func handlePlaneV2ListIntakeIssues(ctx context.Context, req mcp.CallToolRequest)
 	projectId, _ := req.RequireString("project_id")
 	status := req.GetString("status", "")
 	data, err := planeV2Svc.ListIntakeIssues(slug, projectId, status)
-	return planeRawResult(data, err, "列出收件箱工作项")
+	return planeV2RawResult(data, err, "列出收件箱工作项")
 }
 
 func handlePlaneV2GetIntakeIssue(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -105,7 +121,7 @@ func handlePlaneV2GetIntakeIssue(ctx context.Context, req mcp.CallToolRequest) (
 		return helper.ParamError("issue_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetIntakeIssue(slug, projectId, issueId)
-	return planeRawResult(data, apiErr, "获取收件箱工作项详情")
+	return planeV2RawResult(data, apiErr, "获取收件箱工作项详情")
 }
 
 // === Cycle Handlers ===
@@ -114,7 +130,7 @@ func handlePlaneV2ListCycles(ctx context.Context, req mcp.CallToolRequest) (*mcp
 	slug, _ := req.RequireString("workspace_slug")
 	projectId, _ := req.RequireString("project_id")
 	data, err := planeV2Svc.ListCycles(slug, projectId)
-	return planeRawResult(data, err, "列出周期")
+	return planeV2RawResult(data, err, "列出周期")
 }
 
 func handlePlaneV2GetCycle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -125,7 +141,7 @@ func handlePlaneV2GetCycle(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
 		return helper.ParamError("cycle_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetCycle(slug, projectId, cycleId)
-	return planeRawResult(data, apiErr, "获取周期详情")
+	return planeV2RawResult(data, apiErr, "获取周期详情")
 }
 
 // === Module Handlers ===
@@ -134,7 +150,7 @@ func handlePlaneV2ListModules(ctx context.Context, req mcp.CallToolRequest) (*mc
 	slug, _ := req.RequireString("workspace_slug")
 	projectId, _ := req.RequireString("project_id")
 	data, err := planeV2Svc.ListModules(slug, projectId)
-	return planeRawResult(data, err, "列出模块")
+	return planeV2RawResult(data, err, "列出模块")
 }
 
 func handlePlaneV2GetModule(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -145,7 +161,7 @@ func handlePlaneV2GetModule(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 		return helper.ParamError("module_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetModule(slug, projectId, moduleId)
-	return planeRawResult(data, apiErr, "获取模块详情")
+	return planeV2RawResult(data, apiErr, "获取模块详情")
 }
 
 // === Label Handlers ===
@@ -154,7 +170,7 @@ func handlePlaneV2ListLabels(ctx context.Context, req mcp.CallToolRequest) (*mcp
 	slug, _ := req.RequireString("workspace_slug")
 	projectId, _ := req.RequireString("project_id")
 	data, err := planeV2Svc.ListLabels(slug, projectId)
-	return planeRawResult(data, err, "列出标签")
+	return planeV2RawResult(data, err, "列出标签")
 }
 
 // === State Handlers ===
@@ -163,7 +179,7 @@ func handlePlaneV2ListStates(ctx context.Context, req mcp.CallToolRequest) (*mcp
 	slug, _ := req.RequireString("workspace_slug")
 	projectId, _ := req.RequireString("project_id")
 	data, err := planeV2Svc.ListStates(slug, projectId)
-	return planeRawResult(data, err, "列出状态")
+	return planeV2RawResult(data, err, "列出状态")
 }
 
 // === Page Handlers ===
@@ -172,7 +188,7 @@ func handlePlaneV2ListPages(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 	slug, _ := req.RequireString("workspace_slug")
 	projectId, _ := req.RequireString("project_id")
 	data, err := planeV2Svc.ListPages(slug, projectId)
-	return planeRawResult(data, err, "列出页面")
+	return planeV2RawResult(data, err, "列出页面")
 }
 
 func handlePlaneV2GetPage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -183,7 +199,7 @@ func handlePlaneV2GetPage(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 		return helper.ParamError("page_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetPage(slug, projectId, pageId)
-	return planeRawResult(data, apiErr, "获取页面详情")
+	return planeV2RawResult(data, apiErr, "获取页面详情")
 }
 
 func handlePlaneV2GetPageDescription(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -194,7 +210,7 @@ func handlePlaneV2GetPageDescription(ctx context.Context, req mcp.CallToolReques
 		return helper.ParamError("page_id"), nil
 	}
 	data, apiErr := planeV2Svc.GetPageDescription(slug, projectId, pageId)
-	return planeRawResult(data, apiErr, "获取页面描述内容")
+	return planeV2RawResult(data, apiErr, "获取页面描述内容")
 }
 
 // === Member Handlers ===
@@ -205,14 +221,14 @@ func handlePlaneV2ListWorkspaceMembers(ctx context.Context, req mcp.CallToolRequ
 		return helper.ParamError("workspace_slug"), nil
 	}
 	data, apiErr := planeV2Svc.ListWorkspaceMembers(slug)
-	return planeRawResult(data, apiErr, "列出工作空间成员")
+	return planeV2RawResult(data, apiErr, "列出工作空间成员")
 }
 
 func handlePlaneV2ListProjectMembers(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 	slug, _ := req.RequireString("workspace_slug")
 	projectId, _ := req.RequireString("project_id")
 	data, err := planeV2Svc.ListProjectMembers(slug, projectId)
-	return planeRawResult(data, err, "列出项目成员")
+	return planeV2RawResult(data, err, "列出项目成员")
 }
 
 // === Sticky Handlers ===
@@ -223,5 +239,5 @@ func handlePlaneV2ListStickies(ctx context.Context, req mcp.CallToolRequest) (*m
 		return helper.ParamError("workspace_slug"), nil
 	}
 	data, apiErr := planeV2Svc.ListStickies(slug)
-	return planeRawResult(data, apiErr, "列出便签")
+	return planeV2RawResult(data, apiErr, "列出便签")
 }

+ 5 - 5
service/cmd/mcp/tools/data/redis.go

@@ -45,7 +45,7 @@ func handleRedisGet(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 		if err != nil {
 			return helper.EmptyResult(fmt.Sprintf("key 不存在或读取失败: %s (%v)", key, err)), nil
 		}
-		return helper.JSONResult(map[string]any{
+		return helper.JSONResultOrFileAuto(map[string]any{
 			"key":   key,
 			"type":  "string",
 			"value": val,
@@ -59,7 +59,7 @@ func handleRedisGet(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 		if len(val) == 0 {
 			return helper.EmptyResult(fmt.Sprintf("key 不存在或为空: %s", key)), nil
 		}
-		return helper.JSONResult(map[string]any{
+		return helper.JSONResultOrFileAuto(map[string]any{
 			"key":   key,
 			"type":  "hash",
 			"value": val,
@@ -73,7 +73,7 @@ func handleRedisGet(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 		if len(val) == 0 {
 			return helper.EmptyResult(fmt.Sprintf("key 不存在或为空: %s", key)), nil
 		}
-		return helper.JSONResult(map[string]any{
+		return helper.JSONResultOrFileAuto(map[string]any{
 			"key":   key,
 			"type":  "list",
 			"count": len(val),
@@ -88,7 +88,7 @@ func handleRedisGet(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 		if len(val) == 0 {
 			return helper.EmptyResult(fmt.Sprintf("key 不存在或为空: %s", key)), nil
 		}
-		return helper.JSONResult(map[string]any{
+		return helper.JSONResultOrFileAuto(map[string]any{
 			"key":   key,
 			"type":  "set",
 			"count": len(val),
@@ -132,7 +132,7 @@ func handleRedisExists(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
 		ttlStr = "key 不存在"
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"key":    key,
 		"exists": true,
 		"ttl":    ttlStr,

+ 2 - 1
service/cmd/mcp/tools/data/register.go

@@ -9,7 +9,8 @@ func RegisterDataTools(s *server.MCPServer) {
 	registerPgsqlTools(s)
 	registerEmsparaTools(s)
 	registerFwhApiTools(s)
-	registerPlaneApiTools(s)
+	// 暂时停用Plane V1 Api
+	// registerPlaneApiTools(s)
 	registerPlaneV2ApiTools(s)
 	registerSimApiTools(s)
 }

+ 7 - 7
service/cmd/mcp/tools/data/sim_api.go

@@ -88,7 +88,7 @@ func handleSimSmartHubBatchInfo(ctx context.Context, req mcp.CallToolRequest) (*
 	if err != nil {
 		return helper.ServiceError("查询SmartHub SIM卡列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimSmartHubCardInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -101,7 +101,7 @@ func handleSimSmartHubCardInfo(ctx context.Context, req mcp.CallToolRequest) (*m
 	if err != nil {
 		return helper.ServiceError("查询SmartHub SIM卡详情", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimAcceleronixCardList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -112,7 +112,7 @@ func handleSimAcceleronixCardList(ctx context.Context, req mcp.CallToolRequest)
 	if err != nil {
 		return helper.ServiceError("查询Acceleronix SIM卡列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimAcceleronixCardInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -125,7 +125,7 @@ func handleSimAcceleronixCardInfo(ctx context.Context, req mcp.CallToolRequest)
 	if err != nil {
 		return helper.ServiceError("查询Acceleronix SIM卡详情", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimAcceleronixBatchDataInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -159,7 +159,7 @@ func handleSimAcceleronixBatchDataInfo(ctx context.Context, req mcp.CallToolRequ
 	if err != nil {
 		return helper.ServiceError("批量查询Acceleronix数据用量", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimAcceleronixDataPoolList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -170,7 +170,7 @@ func handleSimAcceleronixDataPoolList(ctx context.Context, req mcp.CallToolReque
 	if err != nil {
 		return helper.ServiceError("查询Acceleronix流量池列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimAcceleronixDataPoolSimInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -185,5 +185,5 @@ func handleSimAcceleronixDataPoolSimInfo(ctx context.Context, req mcp.CallToolRe
 	if err != nil {
 		return helper.ServiceError("查询流量池SIM详情", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 151 - 0
service/cmd/mcp/tools/helper/spill.go

@@ -0,0 +1,151 @@
+package helper
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"time"
+
+	"github.com/mark3labs/mcp-go/mcp"
+)
+
+// AgentBaseDir 是 MCP Agent 允许读写的本地根目录。
+// 输入侧 source_path 校验、输出侧结果落盘都限制在此目录及其子目录内。
+const AgentBaseDir = "/tmp/fwh-toolbox-agent"
+
+// spillDir 大结果落盘目录(AgentBaseDir 的子目录)
+var spillDir = filepath.Join(AgentBaseDir, "results")
+
+// InlineMaxBytes 结果内联返回的最大字节数,超过则落盘到文件。
+// 约 32KB,兼顾避免烧 token 与减少不必要的"读文件"往返。
+const InlineMaxBytes = 32 * 1024
+
+// previewMaxBytes 落盘时返回给 Agent 的预览内容最大字节数
+const previewMaxBytes = 2000
+
+// hintSanitizer 用于清洗文件名 hint,只保留安全字符
+var hintSanitizer = regexp.MustCompile(`[^a-zA-Z0-9_\-]+`)
+
+// JSONResultOrFile 将数据序列化为 JSON:
+//   - 小结果(<= InlineMaxBytes)直接内联返回文本
+//   - 大结果写入本地文件,返回包含 localPath + 预览 + 元信息的摘要
+//
+// hint 用于生成可读的文件名前缀(如 "plane_work_items")。
+func JSONResultOrFile(data any, hint string) *mcp.CallToolResult {
+	b, err := json.MarshalIndent(data, "", "  ")
+	if err != nil {
+		return mcp.NewToolResultError(fmt.Sprintf("序列化失败: %v", err))
+	}
+	return BytesResultOrFile(b, hint)
+}
+
+// JSONResultOrFileAuto 与 JSONResultOrFile 相同,但使用通用文件名前缀,
+// 便于对现有 helper.JSONResult 调用点做无侵入替换。
+func JSONResultOrFileAuto(data any) *mcp.CallToolResult {
+	return JSONResultOrFile(data, "result")
+}
+
+// TextResultOrFile 针对可能较大的纯文本返回值做落盘判断:
+//   - 小文本直接内联返回
+//   - 大文本写入本地文件,返回 localPath + 预览
+func TextResultOrFile(text string, hint string) *mcp.CallToolResult {
+	return BytesResultOrFile([]byte(text), hint)
+}
+
+// BytesResultOrFile 针对已经序列化好的 JSON/文本 bytes 做落盘判断。
+func BytesResultOrFile(b []byte, hint string) *mcp.CallToolResult {
+	if len(b) <= InlineMaxBytes {
+		return mcp.NewToolResultText(string(b))
+	}
+
+	path, werr := writeSpillFile(hint, b)
+	if werr != nil {
+		// 落盘失败则退化为直接内联返回,保证功能可用(仅牺牲上下文占用)
+		return mcp.NewToolResultText(string(b))
+	}
+
+	meta := map[string]any{
+		"spilled":   true,
+		"localPath": path,
+		"bytes":     len(b),
+		"preview":   truncate(string(b), previewMaxBytes),
+		"note":      "结果较大已写入本地文件,请使用文件读取工具按 localPath 读取完整内容;preview 仅为前部片段。",
+	}
+	mb, err := json.MarshalIndent(meta, "", "  ")
+	if err != nil {
+		return mcp.NewToolResultText(string(b))
+	}
+	return mcp.NewToolResultText(string(mb))
+}
+
+// writeSpillFile 将 bytes 写入落盘目录,返回绝对路径。
+func writeSpillFile(hint string, b []byte) (string, error) {
+	if err := os.MkdirAll(spillDir, 0755); err != nil {
+		return "", err
+	}
+	name := fmt.Sprintf("%s_%d.json", sanitizeHint(hint), time.Now().UnixNano())
+	path := filepath.Join(spillDir, name)
+	if err := os.WriteFile(path, b, 0644); err != nil {
+		return "", err
+	}
+	return path, nil
+}
+
+// sanitizeHint 清洗文件名前缀,避免路径穿越和非法字符
+func sanitizeHint(hint string) string {
+	hint = hintSanitizer.ReplaceAllString(hint, "_")
+	hint = strings.Trim(hint, "_")
+	if hint == "" {
+		hint = "result"
+	}
+	if len(hint) > 64 {
+		hint = hint[:64]
+	}
+	return hint
+}
+
+// truncate 截断字符串到指定字节数(按 rune 边界安全截断)
+func truncate(s string, max int) string {
+	if len(s) <= max {
+		return s
+	}
+	// 从 max 处向前回退到 rune 边界
+	end := max
+	for end > 0 && !utf8RuneStart(s[end]) {
+		end--
+	}
+	return s[:end] + "\n...(truncated)"
+}
+
+// utf8RuneStart 判断字节是否为 UTF-8 编码的起始字节
+func utf8RuneStart(b byte) bool {
+	// 0xxxxxxx (ASCII) 或 11xxxxxx (多字节起始);10xxxxxx 为续字节
+	return b&0xC0 != 0x80
+}
+
+// ValidateSourcePath 校验输入侧的本地文件路径:
+//   - 必须位于 AgentBaseDir(/tmp/fwh-toolbox-agent)目录或其子目录内
+//   - 清理 ".." 等路径穿越
+//
+// 返回清理后的绝对路径;非法路径返回 error。
+func ValidateSourcePath(path string) (string, error) {
+	if path == "" {
+		return "", fmt.Errorf("路径为空")
+	}
+
+	abs, err := filepath.Abs(filepath.Clean(path))
+	if err != nil {
+		return "", fmt.Errorf("路径解析失败: %v", err)
+	}
+
+	base := filepath.Clean(AgentBaseDir)
+	// 允许 base 本身或 base 的子路径;用分隔符前缀避免 /tmp/fwh-toolbox-agent-evil 误通过
+	if abs != base && !strings.HasPrefix(abs, base+string(os.PathSeparator)) {
+		return "", fmt.Errorf("路径不在允许目录内,仅允许 %s 及其子目录", AgentBaseDir)
+	}
+
+	return abs, nil
+}

+ 107 - 0
service/cmd/mcp/tools/helper/spill_test.go

@@ -0,0 +1,107 @@
+package helper
+
+import (
+	"encoding/json"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/mark3labs/mcp-go/mcp"
+)
+
+func TestJSONResultOrFile_SmallInline(t *testing.T) {
+	res := JSONResultOrFile(map[string]any{"a": 1}, "small")
+	if res == nil || len(res.Content) == 0 {
+		t.Fatal("empty result")
+	}
+	txt := textOf(t, res)
+	if strings.Contains(txt, "spilled") {
+		t.Fatalf("small result should be inline, got: %s", txt)
+	}
+	if !strings.Contains(txt, "\"a\": 1") {
+		t.Fatalf("unexpected inline content: %s", txt)
+	}
+}
+
+func TestJSONResultOrFile_LargeSpill(t *testing.T) {
+	// 构造超过 InlineMaxBytes 的数据
+	big := make([]map[string]string, 0, 5000)
+	for i := 0; i < 5000; i++ {
+		big = append(big, map[string]string{"k": "value-value-value-value"})
+	}
+	res := JSONResultOrFile(big, "large_test")
+	txt := textOf(t, res)
+
+	var meta map[string]any
+	if err := json.Unmarshal([]byte(txt), &meta); err != nil {
+		t.Fatalf("expected JSON meta, got: %s", txt)
+	}
+	if meta["spilled"] != true {
+		t.Fatalf("expected spilled=true, got: %v", meta["spilled"])
+	}
+	path, _ := meta["localPath"].(string)
+	if path == "" {
+		t.Fatal("missing localPath")
+	}
+	// 落盘路径必须在 AgentBaseDir 内
+	if !strings.HasPrefix(path, AgentBaseDir) {
+		t.Fatalf("spill path outside base dir: %s", path)
+	}
+	// 文件应真实存在且内容为完整 JSON
+	data, err := os.ReadFile(path)
+	if err != nil {
+		t.Fatalf("spill file not readable: %v", err)
+	}
+	var parsed []map[string]string
+	if err := json.Unmarshal(data, &parsed); err != nil {
+		t.Fatalf("spill file not valid JSON: %v", err)
+	}
+	if len(parsed) != 5000 {
+		t.Fatalf("spill file content incomplete: %d", len(parsed))
+	}
+	_ = os.Remove(path)
+}
+
+func TestValidateSourcePath(t *testing.T) {
+	okDir := filepath.Join(AgentBaseDir, "downloads")
+	_ = os.MkdirAll(okDir, 0755)
+
+	cases := []struct {
+		name    string
+		in      string
+		wantErr bool
+	}{
+		{"base itself", AgentBaseDir, false},
+		{"subdir file", filepath.Join(AgentBaseDir, "downloads", "a.json"), false},
+		{"nested subdir", filepath.Join(AgentBaseDir, "x", "y", "z.txt"), false},
+		{"traversal escape", filepath.Join(AgentBaseDir, "..", "etc", "passwd"), true},
+		{"outside abs", "/etc/passwd", true},
+		{"sibling prefix trick", "/tmp/fwh-toolbox-agent-evil/x", true},
+		{"empty", "", true},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			_, err := ValidateSourcePath(c.in)
+			if c.wantErr && err == nil {
+				t.Fatalf("expected error for %q", c.in)
+			}
+			if !c.wantErr && err != nil {
+				t.Fatalf("unexpected error for %q: %v", c.in, err)
+			}
+		})
+	}
+}
+
+func textOf(t *testing.T, res *mcp.CallToolResult) string {
+	t.Helper()
+	if res == nil || len(res.Content) == 0 {
+		t.Fatal("empty result content")
+	}
+	tc, ok := res.Content[0].(mcp.TextContent)
+	if !ok {
+		t.Fatalf("content is not text: %T", res.Content[0])
+	}
+	return tc.Text
+}

+ 5 - 5
service/cmd/mcp/tools/service/auto_update.go

@@ -109,7 +109,7 @@ func handleAutoUpdateGetOptions(ctx context.Context, req mcp.CallToolRequest) (*
 	if err != nil {
 		return helper.ServiceError("查询升级配置选项", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleAutoUpdateGetPackage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -122,7 +122,7 @@ func handleAutoUpdateGetPackage(ctx context.Context, req mcp.CallToolRequest) (*
 	if err != nil {
 		return helper.ServiceError("查询升级包信息", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleAutoUpdateCreatePackage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -188,7 +188,7 @@ func handleAutoUpdateGetAPowerVersion(ctx context.Context, req mcp.CallToolReque
 	if err != nil {
 		return helper.ServiceError("查询aPower版本列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleAutoUpdateGetConfig(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -201,7 +201,7 @@ func handleAutoUpdateGetConfig(ctx context.Context, req mcp.CallToolRequest) (*m
 	if err != nil {
 		return helper.ServiceError("查询升级配置", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleAutoUpdateUpdateConfig(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -245,7 +245,7 @@ func handleAutoUpdateFileList(ctx context.Context, req mcp.CallToolRequest) (*mc
 	if err != nil {
 		return helper.ServiceError("查询配置文件列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleAutoUpdateCreateFile(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {

+ 1 - 1
service/cmd/mcp/tools/service/background_task.go

@@ -29,5 +29,5 @@ func handleBackgroundTaskList(ctx context.Context, req mcp.CallToolRequest) (*mc
 		return helper.EmptyResult("当前无后台任务"), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 3 - 3
service/cmd/mcp/tools/service/batch_download.go

@@ -97,7 +97,7 @@ func handleBatchDownloadList(ctx context.Context, req mcp.CallToolRequest) (*mcp
 	if err != nil {
 		return helper.ServiceError("查询批量下载列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleBatchDownloadCreate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -223,7 +223,7 @@ func handleBatchDownloadPath(ctx context.Context, req mcp.CallToolRequest) (*mcp
 	if err != nil {
 		return helper.ServiceError("查询文件目录", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleBatchDownloadFileTree(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -236,5 +236,5 @@ func handleBatchDownloadFileTree(ctx context.Context, req mcp.CallToolRequest) (
 	if err != nil {
 		return helper.ServiceError("查询文件树", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 2 - 2
service/cmd/mcp/tools/service/dashboard.go

@@ -59,7 +59,7 @@ func handleDashboardShutdownDetail(ctx context.Context, req mcp.CallToolRequest)
 		return helper.ServiceError("查询停机详情", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleDashboardAlarmDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -75,5 +75,5 @@ func handleDashboardAlarmDetail(ctx context.Context, req mcp.CallToolRequest) (*
 		return helper.ServiceError("查询告警详情", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 2 - 2
service/cmd/mcp/tools/service/feedback.go

@@ -62,7 +62,7 @@ func handleFeedbackCreate(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 		return helper.ServiceError("创建反馈", err), nil
 	}
 
-	return helper.JSONResult(result), nil
+	return helper.JSONResultOrFileAuto(result), nil
 }
 
 func handleFeedbackHistory(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -71,5 +71,5 @@ func handleFeedbackHistory(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
 		return helper.ServiceError("查询反馈历史", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 2 - 2
service/cmd/mcp/tools/service/monitor.go

@@ -41,7 +41,7 @@ func handleAlarmStats(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 		return helper.ServiceError("查询告警统计", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleShutdownStats(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -57,5 +57,5 @@ func handleShutdownStats(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
 		return helper.ServiceError("查询停机统计", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 3 - 3
service/cmd/mcp/tools/service/sim.go

@@ -52,7 +52,7 @@ func handleSimQuery(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 		return helper.ServiceError("查询SIM卡信息", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimMonthlyData(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -70,7 +70,7 @@ func handleSimMonthlyData(ctx context.Context, req mcp.CallToolRequest) (*mcp.Ca
 		return helper.ServiceError("查询SIM卡月数据", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSimSearch(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -94,5 +94,5 @@ func handleSimSearch(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 		return helper.ServiceError("搜索SIM卡列表", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 3 - 3
service/cmd/mcp/tools/service/site.go

@@ -53,7 +53,7 @@ func handleSiteQueryInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
 		return helper.ServiceError("查询站点信息", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteSearch(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -72,7 +72,7 @@ func handleSiteSearch(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 		return helper.ServiceError("搜索站点列表", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSitePowerFlow(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -95,5 +95,5 @@ func handleSitePowerFlow(ctx context.Context, req mcp.CallToolRequest) (*mcp.Cal
 		return helper.ServiceError("查询功率流", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 3 - 3
service/cmd/mcp/tools/service/site_analysis.go

@@ -89,7 +89,7 @@ func handleSiteAnalysisList(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 	if err != nil {
 		return helper.ServiceError("查询站点分析列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteAnalysisGetConfig(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -107,7 +107,7 @@ func handleSiteAnalysisGetConfig(ctx context.Context, req mcp.CallToolRequest) (
 	if err != nil {
 		return helper.ServiceError("查询分析配置", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteAnalysisCreate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -199,5 +199,5 @@ func handleSiteAnalysisData(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 	if err != nil {
 		return helper.ServiceError("查询分析数据", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 2 - 2
service/cmd/mcp/tools/service/site_info.go

@@ -51,7 +51,7 @@ func handleSiteInfoUpdateStatus(ctx context.Context, req mcp.CallToolRequest) (*
 	if err != nil {
 		return helper.ServiceError("查询更新状态", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteInfoTriggerUpdate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -70,7 +70,7 @@ func handleSiteInfoTagsList(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 	if err != nil {
 		return helper.ServiceError("查询标签列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteInfoTagUpdate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {

+ 6 - 6
service/cmd/mcp/tools/service/site_monitor.go

@@ -97,7 +97,7 @@ func handleSiteMonitorConfigList(ctx context.Context, req mcp.CallToolRequest) (
 	if err != nil {
 		return helper.ServiceError("查询监控配置列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteMonitorNameList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -105,7 +105,7 @@ func handleSiteMonitorNameList(ctx context.Context, req mcp.CallToolRequest) (*m
 	if err != nil {
 		return helper.ServiceError("查询监控名称列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteMonitorConfigGet(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -118,7 +118,7 @@ func handleSiteMonitorConfigGet(ctx context.Context, req mcp.CallToolRequest) (*
 	if err != nil {
 		return helper.ServiceError("查询监控配置", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteMonitorConfigCreate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -219,7 +219,7 @@ func handleSiteMonitorBaseInfo(ctx context.Context, req mcp.CallToolRequest) (*m
 	if err != nil {
 		return helper.ServiceError("查询站点基本信息", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteMonitorModeInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -232,7 +232,7 @@ func handleSiteMonitorModeInfo(ctx context.Context, req mcp.CallToolRequest) (*m
 	if err != nil {
 		return helper.ServiceError("查询站点工作模式", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleSiteMonitorAlarmList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -245,5 +245,5 @@ func handleSiteMonitorAlarmList(ctx context.Context, req mcp.CallToolRequest) (*
 	if err != nil {
 		return helper.ServiceError("查询站点告警列表", err), nil
 	}
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 2 - 2
service/cmd/mcp/tools/service/tou_template.go

@@ -54,7 +54,7 @@ func handleTouTemplateList(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
 		return helper.ServiceError("查询TOU模版列表", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }
 
 func handleTouTemplateDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -68,5 +68,5 @@ func handleTouTemplateDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp
 		return helper.ServiceError("查询TOU模版详情", err), nil
 	}
 
-	return helper.JSONResult(data), nil
+	return helper.JSONResultOrFileAuto(data), nil
 }

+ 5 - 5
service/cmd/mcp/tools/storage/file.go

@@ -95,7 +95,7 @@ func handleFileRead(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 		return helper.ServiceError("读取文件", err), nil
 	}
 
-	return helper.TextResult(content), nil
+	return helper.TextResultOrFile(content, "file_read"), nil
 }
 
 func handleFileReadToLocal(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -129,7 +129,7 @@ func handleFileReadToLocal(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
 		return helper.ServiceError("写入本地文件", err), nil
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"message":   "文件已下载到本地",
 		"localPath": localPath,
 		"fileName":  fileName,
@@ -147,7 +147,7 @@ func handleFileList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 		return helper.EmptyResult("当前没有上传的文件"), nil
 	}
 
-	return helper.JSONResult(files), nil
+	return helper.JSONResultOrFileAuto(files), nil
 }
 
 func handleFileWrite(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -178,7 +178,7 @@ func handleFileWrite(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
 		return helper.ServiceError("写入文件", err), nil
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"message":  "文件写入成功",
 		"path":     record.Path,
 		"fileName": record.FileName,
@@ -237,7 +237,7 @@ func handleFileUpload(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
 		return helper.ServiceError("上传文件", err), nil
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"message":  "文件上传成功",
 		"path":     record.Path,
 		"fileName": record.FileName,

+ 2 - 2
service/cmd/mcp/tools/storage/minio.go

@@ -93,7 +93,7 @@ func handleMinioListObjects(ctx context.Context, req mcp.CallToolRequest) (*mcp.
 		return helper.EmptyResult(fmt.Sprintf("bucket %s 中无匹配对象 (prefix: %s)", bucket, prefix)), nil
 	}
 
-	return helper.JSONResult(map[string]any{
+	return helper.JSONResultOrFileAuto(map[string]any{
 		"bucket": bucket,
 		"prefix": prefix,
 		"count":  len(objects),
@@ -143,7 +143,7 @@ func handleMinioReadObject(ctx context.Context, req mcp.CallToolRequest) (*mcp.C
 		return helper.ServiceError("读取对象内容", err), nil
 	}
 
-	return helper.TextResult(string(data)), nil
+	return helper.TextResultOrFile(string(data), "minio_object"), nil
 }
 
 func isAllowedBucket(name string) bool {

+ 1 - 1
service/cmd/mcp/tools/util/format.go

@@ -79,5 +79,5 @@ func handleParseSN(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolR
 			productName, hwVersion, factory, year, week, serial),
 	}
 
-	return helper.JSONResult(result), nil
+	return helper.JSONResultOrFileAuto(result), nil
 }