Browse Source

feat:补充mcp操作工具(未测试)

Jerry Liang 2 tháng trước cách đây
mục cha
commit
fb02d21af8

+ 49 - 1
service/cmd/mcp/.kiro/agents/fwh-toolbox-agent.json

@@ -8,7 +8,55 @@
 	],
 	"allowedTools": [
 		"read", "grep", "glob", "code", "web_fetch", "web_search", "knowledge", "subagent", "todo_list", "introspect",
-		"@fwh-tools"
+		"@fwh-tools/service_site_query_info",
+		"@fwh-tools/service_site_search",
+		"@fwh-tools/service_site_power_flow",
+		"@fwh-tools/service_sim_query",
+		"@fwh-tools/service_sim_search",
+		"@fwh-tools/service_sim_monthly_data",
+		"@fwh-tools/service_monitor_alarm_stats",
+		"@fwh-tools/service_monitor_shutdown_stats",
+		"@fwh-tools/service_feedback_history",
+		"@fwh-tools/service_dashboard_shutdown_date",
+		"@fwh-tools/service_dashboard_shutdown_detail",
+		"@fwh-tools/service_dashboard_alarm_detail",
+		"@fwh-tools/service_background_task_list",
+		"@fwh-tools/service_tou_template_list",
+		"@fwh-tools/service_tou_template_detail",
+		"@fwh-tools/service_site_monitor_config_list",
+		"@fwh-tools/service_site_monitor_name_list",
+		"@fwh-tools/service_site_monitor_config_get",
+		"@fwh-tools/service_site_monitor_base_info",
+		"@fwh-tools/service_site_monitor_mode_info",
+		"@fwh-tools/service_site_monitor_alarm_list",
+		"@fwh-tools/service_site_info_update_status",
+		"@fwh-tools/service_site_info_tags_list",
+		"@fwh-tools/service_auto_update_get_options",
+		"@fwh-tools/service_auto_update_get_package",
+		"@fwh-tools/service_auto_update_get_apower_version",
+		"@fwh-tools/service_auto_update_get_config",
+		"@fwh-tools/service_auto_update_file_list",
+		"@fwh-tools/service_batch_download_list",
+		"@fwh-tools/service_batch_download_path",
+		"@fwh-tools/service_batch_download_file_tree",
+		"@fwh-tools/service_site_analysis_list",
+		"@fwh-tools/service_site_analysis_get_config",
+		"@fwh-tools/service_site_analysis_data",
+		"@fwh-tools/data_mongo_find",
+		"@fwh-tools/data_mongo_count",
+		"@fwh-tools/data_redis_get",
+		"@fwh-tools/data_redis_exists",
+		"@fwh-tools/data_pgsql_query_snapshot_file_record",
+		"@fwh-tools/data_pgsql_query_snapshot_data",
+		"@fwh-tools/data_fwh_*",
+		"@fwh-tools/data_sim_*",
+		"@fwh-tools/data_emspara_*",
+		"@fwh-tools/data_plane_*",
+		"@fwh-tools/data_plane_v2_*",
+		"@fwh-tools/storage_minio_*",
+		"@fwh-tools/storage_file_read",
+		"@fwh-tools/storage_file_list",
+		"@fwh-tools/util_*"
 	],
 	"toolsSettings": {
 		"read": {

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

@@ -150,4 +150,5 @@ const serverInstructions = `FranklinWH 工具箱 MCP Server — 内部工具平
 3. data_* 工具仅支持只读操作
 4. storage_file_* 支持读写,storage_minio_* 仅只读
 5. 查询 MongoDB 时注意 collection 白名单限制
-6. MinIO 操作受 bucket 白名单限制`
+6. MinIO 操作受 bucket 白名单限制
+7. 描述中标注 [写操作] 的工具由 Agent 权限管控,执行前会请求用户确认`

+ 256 - 0
service/cmd/mcp/tools/service/auto_update.go

@@ -0,0 +1,256 @@
+package service
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/domain"
+	"fwh-toolbox-server/services"
+)
+
+func registerAutoUpdateTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_auto_update_get_options",
+			mcp.WithDescription("查询自动升级配置选项(升级包列表和 aGate 版本列表)"),
+		),
+		handleAutoUpdateGetOptions,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_get_package",
+			mcp.WithDescription("查询指定升级包的详细信息"),
+			mcp.WithString("file_name", mcp.Required(), mcp.Description("升级包文件名")),
+		),
+		handleAutoUpdateGetPackage,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_create_package",
+			mcp.WithDescription("创建升级包信息 [写操作]"),
+			mcp.WithString("package_type", mcp.Required(), mcp.Description("包类型:system, aGate, subDevice, others")),
+			mcp.WithString("info", mcp.Required(), mcp.Description("升级包信息 JSON,包含 id(文件名)、title、invVersion[]")),
+		),
+		handleAutoUpdateCreatePackage,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_update_package",
+			mcp.WithDescription("更新升级包信息 [写操作]"),
+			mcp.WithString("file_name", mcp.Required(), mcp.Description("升级包文件名")),
+			mcp.WithString("info", mcp.Required(), mcp.Description("升级包信息 JSON,包含 id、title、invVersion[]")),
+		),
+		handleAutoUpdateUpdatePackage,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_delete_package",
+			mcp.WithDescription("删除升级包信息 [写操作]"),
+			mcp.WithString("package_type", mcp.Required(), mcp.Description("包类型:system, aGate, subDevice, others")),
+			mcp.WithString("file_name", mcp.Required(), mcp.Description("升级包文件名")),
+		),
+		handleAutoUpdateDeletePackage,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_get_apower_version",
+			mcp.WithDescription("查询 aPower 硬件版本列表"),
+		),
+		handleAutoUpdateGetAPowerVersion,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_get_config",
+			mcp.WithDescription("查询指定 aGate 版本的自动升级配置"),
+			mcp.WithString("agate_version", mcp.Required(), mcp.Description("aGate 硬件版本号")),
+		),
+		handleAutoUpdateGetConfig,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_update_config",
+			mcp.WithDescription("更新指定 aGate 版本的自动升级配置 [写操作]"),
+			mcp.WithString("agate_version", mcp.Required(), mcp.Description("aGate 硬件版本号")),
+			mcp.WithString("config", mcp.Required(), mcp.Description("配置 JSON,包含 config.defaultType 和 config.specialType")),
+		),
+		handleAutoUpdateUpdateConfig,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_delete_config",
+			mcp.WithDescription("删除指定 aGate 版本的自动升级配置 [写操作]"),
+			mcp.WithString("agate_version", mcp.Required(), mcp.Description("aGate 硬件版本号")),
+		),
+		handleAutoUpdateDeleteConfig,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_file_list",
+			mcp.WithDescription("查询自动升级配置文件列表(已生成的 Excel 文件)"),
+			mcp.WithNumber("page", mcp.Description("页码,从1开始,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认10")),
+		),
+		handleAutoUpdateFileList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_auto_update_create_file",
+			mcp.WithDescription("生成新的自动升级配置 Excel 文件 [写操作]"),
+		),
+		handleAutoUpdateCreateFile,
+	)
+}
+
+func handleAutoUpdateGetOptions(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := services.GetAutoUpdateConfigOption()
+	if err != nil {
+		return helper.ServiceError("查询升级配置选项", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleAutoUpdateGetPackage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	fileName, err := req.RequireString("file_name")
+	if err != nil {
+		return helper.ParamError("file_name"), nil
+	}
+
+	data, err := services.GetAutoUpdatePackageInfo(fileName)
+	if err != nil {
+		return helper.ServiceError("查询升级包信息", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleAutoUpdateCreatePackage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	packageType, err := req.RequireString("package_type")
+	if err != nil {
+		return helper.ParamError("package_type"), nil
+	}
+	infoStr, err := req.RequireString("info")
+	if err != nil {
+		return helper.ParamError("info"), nil
+	}
+
+	var info domain.AutoUpdatePackages
+	if err := json.Unmarshal([]byte(infoStr), &info); err != nil {
+		return helper.ParamInvalidError("info", "需要有效的JSON格式"), nil
+	}
+
+	if err := services.CreateAutoUpdatePackageInfo(packageType, info); err != nil {
+		return helper.ServiceError("创建升级包", err), nil
+	}
+	return helper.TextResult("升级包创建成功"), nil
+}
+
+func handleAutoUpdateUpdatePackage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	fileName, err := req.RequireString("file_name")
+	if err != nil {
+		return helper.ParamError("file_name"), nil
+	}
+	infoStr, err := req.RequireString("info")
+	if err != nil {
+		return helper.ParamError("info"), nil
+	}
+
+	var info domain.AutoUpdatePackages
+	if err := json.Unmarshal([]byte(infoStr), &info); err != nil {
+		return helper.ParamInvalidError("info", "需要有效的JSON格式"), nil
+	}
+
+	if err := services.UpdateAutoUpdatePackageInfo(fileName, info); err != nil {
+		return helper.ServiceError("更新升级包", err), nil
+	}
+	return helper.TextResult("升级包更新成功"), nil
+}
+
+func handleAutoUpdateDeletePackage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	packageType, err := req.RequireString("package_type")
+	if err != nil {
+		return helper.ParamError("package_type"), nil
+	}
+	fileName, err := req.RequireString("file_name")
+	if err != nil {
+		return helper.ParamError("file_name"), nil
+	}
+
+	if err := services.DeleteAutoUpdatePackageInfo(packageType, fileName); err != nil {
+		return helper.ServiceError("删除升级包", err), nil
+	}
+	return helper.TextResult("升级包删除成功"), nil
+}
+
+func handleAutoUpdateGetAPowerVersion(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := services.GetAutoUpdateAPowerVersion()
+	if err != nil {
+		return helper.ServiceError("查询aPower版本列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleAutoUpdateGetConfig(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	agateVersion, err := req.RequireString("agate_version")
+	if err != nil {
+		return helper.ParamError("agate_version"), nil
+	}
+
+	data, err := services.GetAutoUpdateConfig(agateVersion)
+	if err != nil {
+		return helper.ServiceError("查询升级配置", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleAutoUpdateUpdateConfig(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	agateVersion, err := req.RequireString("agate_version")
+	if err != nil {
+		return helper.ParamError("agate_version"), nil
+	}
+	configStr, err := req.RequireString("config")
+	if err != nil {
+		return helper.ParamError("config"), nil
+	}
+
+	var config domain.AutoUpdateConfig
+	if err := json.Unmarshal([]byte(configStr), &config); err != nil {
+		return helper.ParamInvalidError("config", "需要有效的JSON格式"), nil
+	}
+
+	if err := services.UpdateAutoUpdateConfig(agateVersion, config); err != nil {
+		return helper.ServiceError("更新升级配置", err), nil
+	}
+	return helper.TextResult("升级配置更新成功"), nil
+}
+
+func handleAutoUpdateDeleteConfig(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	agateVersion, err := req.RequireString("agate_version")
+	if err != nil {
+		return helper.ParamError("agate_version"), nil
+	}
+
+	if err := services.DeleteAutoUpdateConfig(agateVersion); err != nil {
+		return helper.ServiceError("删除升级配置", err), nil
+	}
+	return helper.TextResult("升级配置删除成功"), nil
+}
+
+func handleAutoUpdateFileList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 10))
+
+	data, err := services.GetAutoUpdateConfigFileInfo(page, size)
+	if err != nil {
+		return helper.ServiceError("查询配置文件列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleAutoUpdateCreateFile(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	if err := services.CreateAutoUpdateConfigFile(); err != nil {
+		return helper.ServiceError("生成配置文件", err), nil
+	}
+	return helper.TextResult("配置文件生成成功"), nil
+}

+ 33 - 0
service/cmd/mcp/tools/service/background_task.go

@@ -0,0 +1,33 @@
+package service
+
+import (
+	"context"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/services"
+)
+
+func registerBackgroundTaskTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_background_task_list",
+			mcp.WithDescription("查询当前后台任务列表(包括任务状态、重试次数、参数信息)"),
+		),
+		handleBackgroundTaskList,
+	)
+}
+
+func handleBackgroundTaskList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := services.GetBackgroundTaskInfoList()
+	if err != nil {
+		return helper.ServiceError("查询后台任务列表", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("当前无后台任务"), nil
+	}
+
+	return helper.JSONResult(data), nil
+}

+ 240 - 0
service/cmd/mcp/tools/service/batch_download.go

@@ -0,0 +1,240 @@
+package service
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/domain"
+	"fwh-toolbox-server/services"
+)
+
+func registerBatchDownloadTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_batch_download_list",
+			mcp.WithDescription("查询批量下载任务列表,支持按 SN 和进度筛选"),
+			mcp.WithNumber("page", mcp.Description("页码,从1开始,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认10")),
+			mcp.WithString("sn", mcp.Description("按 SN 过滤")),
+			mcp.WithString("progress", mcp.Description("按进度过滤:pending, running, success, failed")),
+		),
+		handleBatchDownloadList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_batch_download_create",
+			mcp.WithDescription("创建批量下载任务(自动入队执行)[写操作]"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN列表,JSON数组格式")),
+			mcp.WithString("time_period", mcp.Required(), mcp.Description("时间范围,JSON数组 [\"2024-01-01\",\"2024-01-31\"]")),
+			mcp.WithString("options", mcp.Required(), mcp.Description("下载选项 JSON,字段: powerFlow(bool), mqttLogs(string[]), eventAlarm(bool), alarmSnapshot(bool), emsLogs(string[]), faultRecord(bool), updateLog(bool), settings(bool)")),
+			mcp.WithString("description", mcp.Description("任务描述")),
+		),
+		handleBatchDownloadCreate,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_batch_download_update_desc",
+			mcp.WithDescription("更新批量下载任务的描述 [写操作]"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID(MongoDB ObjectID hex 字符串)")),
+			mcp.WithString("description", mcp.Required(), mcp.Description("新的描述")),
+		),
+		handleBatchDownloadUpdateDesc,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_batch_download_update_owner",
+			mcp.WithDescription("更新批量下载任务的 owner [写操作]"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID(MongoDB ObjectID hex 字符串)")),
+			mcp.WithString("owner", mcp.Required(), mcp.Description("owner 列表,JSON数组格式")),
+		),
+		handleBatchDownloadUpdateOwner,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_batch_download_delete",
+			mcp.WithDescription("删除批量下载任务 [写操作]"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID(MongoDB ObjectID hex 字符串)")),
+		),
+		handleBatchDownloadDelete,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_batch_download_path",
+			mcp.WithDescription("查询批量下载任务的文件目录结构"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID(MongoDB ObjectID hex 字符串)")),
+		),
+		handleBatchDownloadPath,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_batch_download_file_tree",
+			mcp.WithDescription("查询指定 tar.gz 文件的内部目录结构"),
+			mcp.WithString("file_path", mcp.Required(), mcp.Description("MinIO 中的文件路径")),
+		),
+		handleBatchDownloadFileTree,
+	)
+}
+
+func handleBatchDownloadList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 10))
+
+	var snPtr *string
+	if sn := req.GetString("sn", ""); sn != "" {
+		snPtr = &sn
+	}
+
+	var progressPtr *string
+	if progress := req.GetString("progress", ""); progress != "" {
+		progressPtr = &progress
+	}
+
+	data, err := services.GetBatchDownloadManageList(page, size, snPtr, progressPtr)
+	if err != nil {
+		return helper.ServiceError("查询批量下载列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleBatchDownloadCreate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	snStr, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	timePeriodStr, err := req.RequireString("time_period")
+	if err != nil {
+		return helper.ParamError("time_period"), nil
+	}
+	optionsStr, err := req.RequireString("options")
+	if err != nil {
+		return helper.ParamError("options"), nil
+	}
+
+	var snList []string
+	if err := json.Unmarshal([]byte(snStr), &snList); err != nil {
+		return helper.ParamInvalidError("sn", "需要JSON数组格式"), nil
+	}
+
+	var timePeriod [2]string
+	if err := json.Unmarshal([]byte(timePeriodStr), &timePeriod); err != nil {
+		return helper.ParamInvalidError("time_period", "需要 [\"start\",\"end\"] 格式"), nil
+	}
+
+	var options domain.BatchDownloadOptions
+	if err := json.Unmarshal([]byte(optionsStr), &options); err != nil {
+		return helper.ParamInvalidError("options", "需要有效的JSON格式"), nil
+	}
+
+	description := req.GetString("description", "")
+
+	config := domain.BatchDownloadConfigWrite{
+		Sn:          snList,
+		Description: description,
+		TimePeriod:  timePeriod,
+		Options:     options,
+	}
+
+	if err := services.InsertBatchDownloadConfig(config); err != nil {
+		return helper.ServiceError("创建批量下载任务", err), nil
+	}
+	return helper.TextResult("批量下载任务创建成功,已加入队列"), nil
+}
+
+func handleBatchDownloadUpdateDesc(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+	desc, err := req.RequireString("description")
+	if err != nil {
+		return helper.ParamError("description"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	if err := services.UpdateBatchDownloadDescription(id, desc); err != nil {
+		return helper.ServiceError("更新描述", err), nil
+	}
+	return helper.TextResult("描述更新成功"), nil
+}
+
+func handleBatchDownloadUpdateOwner(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+	ownerStr, err := req.RequireString("owner")
+	if err != nil {
+		return helper.ParamError("owner"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	var owner []string
+	if err := json.Unmarshal([]byte(ownerStr), &owner); err != nil {
+		return helper.ParamInvalidError("owner", "需要JSON数组格式"), nil
+	}
+
+	if err := services.UpdateBatchDownloadOwner(id, owner); err != nil {
+		return helper.ServiceError("更新owner", err), nil
+	}
+	return helper.TextResult("owner更新成功"), nil
+}
+
+func handleBatchDownloadDelete(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	if err := services.DeleteBatchDownloadConfig(id); err != nil {
+		return helper.ServiceError("删除批量下载任务", err), nil
+	}
+	return helper.TextResult("删除成功"), nil
+}
+
+func handleBatchDownloadPath(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	data, err := services.GetBatchDownloadPath(id)
+	if err != nil {
+		return helper.ServiceError("查询文件目录", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleBatchDownloadFileTree(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	filePath, err := req.RequireString("file_path")
+	if err != nil {
+		return helper.ParamError("file_path"), nil
+	}
+
+	data, err := services.GetBatchDownloadFileTree(filePath)
+	if err != nil {
+		return helper.ServiceError("查询文件树", err), nil
+	}
+	return helper.JSONResult(data), nil
+}

+ 79 - 0
service/cmd/mcp/tools/service/dashboard.go

@@ -0,0 +1,79 @@
+package service
+
+import (
+	"context"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/services"
+)
+
+func registerDashboardTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_dashboard_shutdown_date",
+			mcp.WithDescription("查询停机统计最新可用日期"),
+		),
+		handleDashboardShutdownDate,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_dashboard_shutdown_detail",
+			mcp.WithDescription("查询指定日期的停机详情数据(含各站点停机信息)"),
+			mcp.WithString("date", mcp.Description("统计日期,格式 yyyy-MM-dd(不传则查最新)")),
+		),
+		handleDashboardShutdownDetail,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_dashboard_alarm_detail",
+			mcp.WithDescription("查询指定日期的告警详情数据(含各站点告警码统计)"),
+			mcp.WithString("date", mcp.Description("统计日期,格式 yyyy-MM-dd(不传则查最新)")),
+		),
+		handleDashboardAlarmDetail,
+	)
+}
+
+func handleDashboardShutdownDate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	date, err := services.GetShutdownStatisticsDate()
+	if err != nil {
+		return helper.ServiceError("查询停机统计日期", err), nil
+	}
+	if date == nil {
+		return helper.EmptyResult("暂无停机统计数据"), nil
+	}
+	return helper.TextResult(*date), nil
+}
+
+func handleDashboardShutdownDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	date := req.GetString("date", "")
+
+	var datePtr *string
+	if date != "" {
+		datePtr = &date
+	}
+
+	data, err := services.GetShutdownInfoMap(datePtr)
+	if err != nil {
+		return helper.ServiceError("查询停机详情", err), nil
+	}
+
+	return helper.JSONResult(data), nil
+}
+
+func handleDashboardAlarmDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	date := req.GetString("date", "")
+
+	var datePtr *string
+	if date != "" {
+		datePtr = &date
+	}
+
+	data, err := services.GetAlarmStatisticsInfoMap(datePtr)
+	if err != nil {
+		return helper.ServiceError("查询告警详情", err), nil
+	}
+
+	return helper.JSONResult(data), nil
+}

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

@@ -14,7 +14,7 @@ import (
 func registerFeedbackTools(s *server.MCPServer) {
 	s.AddTool(
 		mcp.NewTool("service_feedback_create",
-			mcp.WithDescription("在 Plane 创建 Intake Issue(问题反馈或需求提交)"),
+			mcp.WithDescription("在 Plane 创建 Intake Issue(问题反馈或需求提交)[写操作]"),
 			mcp.WithString("title", mcp.Required(), mcp.Description("标题")),
 			mcp.WithString("type", mcp.Required(), mcp.Description("类型:bug 或 feature")),
 			mcp.WithString("priority", mcp.Description("优先级:urgent, high, medium, low, none(默认 medium)")),

+ 8 - 0
service/cmd/mcp/tools/service/register.go

@@ -8,4 +8,12 @@ func RegisterServiceTools(s *server.MCPServer) {
 	registerSimTools(s)
 	registerMonitorTools(s)
 	registerFeedbackTools(s)
+	registerDashboardTools(s)
+	registerBackgroundTaskTools(s)
+	registerTouTemplateTools(s)
+	registerSiteMonitorTools(s)
+	registerSiteInfoTools(s)
+	registerAutoUpdateTools(s)
+	registerBatchDownloadTools(s)
+	registerSiteAnalysisTools(s)
 }

+ 27 - 0
service/cmd/mcp/tools/service/sim.go

@@ -30,6 +30,15 @@ func registerSimTools(s *server.MCPServer) {
 		),
 		handleSimSearch,
 	)
+
+	s.AddTool(
+		mcp.NewTool("service_sim_monthly_data",
+			mcp.WithDescription("查询单张 SIM 卡指定月份的每日用量数据"),
+			mcp.WithString("giccid", mcp.Required(), mcp.Description("SIM 卡 GICCID(19位)")),
+			mcp.WithString("month", mcp.Required(), mcp.Description("月份,格式 yyyy-MM")),
+		),
+		handleSimMonthlyData,
+	)
 }
 
 func handleSimQuery(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
@@ -46,6 +55,24 @@ func handleSimQuery(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
 	return helper.JSONResult(data), nil
 }
 
+func handleSimMonthlyData(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	giccid, err := req.RequireString("giccid")
+	if err != nil {
+		return helper.ParamError("giccid"), nil
+	}
+	month, err := req.RequireString("month")
+	if err != nil {
+		return helper.ParamError("month"), nil
+	}
+
+	data, err := services.GetSimCardMonthlyData(month, giccid)
+	if err != nil {
+		return helper.ServiceError("查询SIM卡月数据", err), nil
+	}
+
+	return helper.JSONResult(data), nil
+}
+
 func handleSimSearch(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
 	page := uint(req.GetFloat("page", 1))
 	size := uint(req.GetFloat("size", 10))

+ 203 - 0
service/cmd/mcp/tools/service/site_analysis.go

@@ -0,0 +1,203 @@
+package service
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/domain"
+	"fwh-toolbox-server/services"
+)
+
+func registerSiteAnalysisTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_site_analysis_list",
+			mcp.WithDescription("查询站点分析任务列表,支持按 SN 和进度筛选"),
+			mcp.WithNumber("page", mcp.Description("页码,从1开始,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认10")),
+			mcp.WithString("sn", mcp.Description("按 SN 过滤")),
+			mcp.WithString("progress", mcp.Description("按进度过滤:pending, running, success, failed")),
+		),
+		handleSiteAnalysisList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_analysis_get_config",
+			mcp.WithDescription("查询指定站点分析任务的配置详情"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID(MongoDB ObjectID hex 字符串)")),
+		),
+		handleSiteAnalysisGetConfig,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_analysis_create",
+			mcp.WithDescription("创建站点分析任务(自动入队执行)[写操作]"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点 SN")),
+			mcp.WithString("time_period", mcp.Required(), mcp.Description("时间范围 [\"start\",\"end\"],格式 yyyy-MM-dd")),
+			mcp.WithBoolean("enable_timeline", mcp.Description("是否分析时间线,默认true")),
+			mcp.WithBoolean("enable_runtime", mcp.Description("是否分析运行时数据,默认true")),
+			mcp.WithString("description", mcp.Description("任务描述")),
+		),
+		handleSiteAnalysisCreate,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_analysis_update_desc",
+			mcp.WithDescription("更新站点分析任务的描述 [写操作]"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID")),
+			mcp.WithString("description", mcp.Required(), mcp.Description("新的描述")),
+		),
+		handleSiteAnalysisUpdateDesc,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_analysis_delete",
+			mcp.WithDescription("删除站点分析任务 [写操作]"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID")),
+		),
+		handleSiteAnalysisDelete,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_analysis_data",
+			mcp.WithDescription("查询站点分析任务的结果数据(timeline + runtime)"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("任务 ID")),
+		),
+		handleSiteAnalysisData,
+	)
+}
+
+func handleSiteAnalysisList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 10))
+
+	var snPtr *string
+	if sn := req.GetString("sn", ""); sn != "" {
+		snPtr = &sn
+	}
+
+	var progressPtr *string
+	if progress := req.GetString("progress", ""); progress != "" {
+		progressPtr = &progress
+	}
+
+	data, err := services.GetSiteAnalysisManageList(page, size, snPtr, nil, progressPtr, nil)
+	if err != nil {
+		return helper.ServiceError("查询站点分析列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteAnalysisGetConfig(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	data, err := services.GetSiteAnalysisConfig(id)
+	if err != nil {
+		return helper.ServiceError("查询分析配置", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteAnalysisCreate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	timePeriodStr, err := req.RequireString("time_period")
+	if err != nil {
+		return helper.ParamError("time_period"), nil
+	}
+
+	var timePeriod [2]string
+	if err := json.Unmarshal([]byte(timePeriodStr), &timePeriod); err != nil {
+		return helper.ParamInvalidError("time_period", "需要 [\"start\",\"end\"] 格式"), nil
+	}
+
+	enableTimeline := req.GetBool("enable_timeline", true)
+	enableRuntime := req.GetBool("enable_runtime", true)
+	description := req.GetString("description", "")
+
+	config := domain.SiteAnalysisConfigWrite{
+		Sn:          sn,
+		Description: description,
+		TimePeriod:  timePeriod,
+		Options: domain.SiteAnalysisOptions{
+			EnableTimeline: enableTimeline,
+			EnableRuntime:  enableRuntime,
+		},
+		User: "MCP-Agent",
+	}
+
+	if err := services.InsertSiteAnalysisConfig(config); err != nil {
+		return helper.ServiceError("创建站点分析任务", err), nil
+	}
+	return helper.TextResult("站点分析任务创建成功,已加入队列"), nil
+}
+
+func handleSiteAnalysisUpdateDesc(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+	desc, err := req.RequireString("description")
+	if err != nil {
+		return helper.ParamError("description"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	if err := services.UpdateSiteAnalysisDescription(id, desc); err != nil {
+		return helper.ServiceError("更新描述", err), nil
+	}
+	return helper.TextResult("描述更新成功"), nil
+}
+
+func handleSiteAnalysisDelete(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	if err := services.DeleteSiteAnalysisConfig(id); err != nil {
+		return helper.ServiceError("删除站点分析任务", err), nil
+	}
+	return helper.TextResult("删除成功"), nil
+}
+
+func handleSiteAnalysisData(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := primitive.ObjectIDFromHex(idStr)
+	if err != nil {
+		return helper.ParamInvalidError("id", "无效的 ObjectID"), nil
+	}
+
+	data, err := services.GetSiteAnalysisData(id)
+	if err != nil {
+		return helper.ServiceError("查询分析数据", err), nil
+	}
+	return helper.JSONResult(data), nil
+}

+ 91 - 0
service/cmd/mcp/tools/service/site_info.go

@@ -0,0 +1,91 @@
+package service
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/dto"
+	"fwh-toolbox-server/services"
+)
+
+func registerSiteInfoTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_site_info_update_status",
+			mcp.WithDescription("查询站点信息表的更新状态(最近一次更新时间和结果)"),
+		),
+		handleSiteInfoUpdateStatus,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_info_trigger_update",
+			mcp.WithDescription("触发站点信息表更新任务(从 FWH Pro 同步最新数据)[写操作]"),
+		),
+		handleSiteInfoTriggerUpdate,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_info_tags_list",
+			mcp.WithDescription("查询站点标签列表"),
+			mcp.WithNumber("page", mcp.Description("页码,从1开始,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认10")),
+			mcp.WithString("search", mcp.Description("搜索关键字")),
+		),
+		handleSiteInfoTagsList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_info_tag_update",
+			mcp.WithDescription("创建或更新站点标签 [写操作]"),
+			mcp.WithString("tag", mcp.Required(), mcp.Description("标签数据,JSON格式,包含 id(可选)、name、color、description 字段")),
+		),
+		handleSiteInfoTagUpdate,
+	)
+}
+
+func handleSiteInfoUpdateStatus(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := services.GetSiteInfoUpdateStatus()
+	if err != nil {
+		return helper.ServiceError("查询更新状态", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteInfoTriggerUpdate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	if err := services.UpdateSiteInfoDate(); err != nil {
+		return helper.ServiceError("触发站点信息更新", err), nil
+	}
+	return helper.TextResult("已触发站点信息更新任务"), nil
+}
+
+func handleSiteInfoTagsList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 10))
+	search := req.GetString("search", "")
+
+	data, err := services.GetSiteInfoTags(page, size, search)
+	if err != nil {
+		return helper.ServiceError("查询标签列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteInfoTagUpdate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	tagStr, err := req.RequireString("tag")
+	if err != nil {
+		return helper.ParamError("tag"), nil
+	}
+
+	var tag dto.SiteTagDTO
+	if err := json.Unmarshal([]byte(tagStr), &tag); err != nil {
+		return helper.ParamInvalidError("tag", "需要有效的JSON格式"), nil
+	}
+
+	if err := services.UpdateSiteInfoTag(tag); err != nil {
+		return helper.ServiceError("更新标签", err), nil
+	}
+	return helper.TextResult("标签更新成功"), nil
+}

+ 249 - 0
service/cmd/mcp/tools/service/site_monitor.go

@@ -0,0 +1,249 @@
+package service
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/domain"
+	"fwh-toolbox-server/dto"
+	"fwh-toolbox-server/services"
+)
+
+func registerSiteMonitorTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_config_list",
+			mcp.WithDescription("查询所有站点监控配置列表"),
+		),
+		handleSiteMonitorConfigList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_name_list",
+			mcp.WithDescription("查询所有站点监控配置的名称列表"),
+		),
+		handleSiteMonitorNameList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_config_get",
+			mcp.WithDescription("查询指定名称的站点监控配置"),
+			mcp.WithString("name", mcp.Required(), mcp.Description("监控配置名称")),
+		),
+		handleSiteMonitorConfigGet,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_config_create",
+			mcp.WithDescription("创建站点监控配置(包含监控名、描述、站点SN列表)[写操作]"),
+			mcp.WithString("name", mcp.Required(), mcp.Description("监控配置名称")),
+			mcp.WithString("description", mcp.Description("配置描述")),
+			mcp.WithString("site_list", mcp.Required(), mcp.Description("站点SN列表,JSON数组格式,如 [\"SN1\",\"SN2\"]")),
+		),
+		handleSiteMonitorConfigCreate,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_config_update",
+			mcp.WithDescription("更新指定名称的站点监控配置 [写操作]"),
+			mcp.WithString("name", mcp.Required(), mcp.Description("监控配置名称")),
+			mcp.WithString("description", mcp.Description("配置描述")),
+			mcp.WithString("site_list", mcp.Description("站点SN列表,JSON数组格式")),
+		),
+		handleSiteMonitorConfigUpdate,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_config_delete",
+			mcp.WithDescription("删除指定名称的站点监控配置 [写操作]"),
+			mcp.WithString("name", mcp.Required(), mcp.Description("监控配置名称")),
+		),
+		handleSiteMonitorConfigDelete,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_base_info",
+			mcp.WithDescription("查询指定站点列表的基本信息(激活状态、国家、版本等)"),
+			mcp.WithString("site_list", mcp.Required(), mcp.Description("站点SN列表,JSON数组格式")),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认10")),
+		),
+		handleSiteMonitorBaseInfo,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_mode_info",
+			mcp.WithDescription("查询站点工作模式信息"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点 SN 序列号")),
+		),
+		handleSiteMonitorModeInfo,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_site_monitor_alarm_list",
+			mcp.WithDescription("查询站点告警列表"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点 SN 序列号")),
+		),
+		handleSiteMonitorAlarmList,
+	)
+}
+
+func handleSiteMonitorConfigList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := services.GetSiteMonitorConfig()
+	if err != nil {
+		return helper.ServiceError("查询监控配置列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteMonitorNameList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := services.GetSiteMonitorNameList()
+	if err != nil {
+		return helper.ServiceError("查询监控名称列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteMonitorConfigGet(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	name, err := req.RequireString("name")
+	if err != nil {
+		return helper.ParamError("name"), nil
+	}
+
+	data, err := services.GetOneSiteMonitorConfig(name)
+	if err != nil {
+		return helper.ServiceError("查询监控配置", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteMonitorConfigCreate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	name, err := req.RequireString("name")
+	if err != nil {
+		return helper.ParamError("name"), nil
+	}
+	description := req.GetString("description", "")
+	siteListStr, err := req.RequireString("site_list")
+	if err != nil {
+		return helper.ParamError("site_list"), nil
+	}
+
+	var siteList []string
+	if err := json.Unmarshal([]byte(siteListStr), &siteList); err != nil {
+		return helper.ParamInvalidError("site_list", "需要JSON数组格式"), nil
+	}
+
+	config := domain.SitesMonitorConfig{
+		Name:        name,
+		Description: description,
+		SiteList:    siteList,
+	}
+
+	if err := services.InsertSiteMonitorConfig(config); err != nil {
+		return helper.ServiceError("创建监控配置", err), nil
+	}
+	return helper.TextResult("创建成功"), nil
+}
+
+func handleSiteMonitorConfigUpdate(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	name, err := req.RequireString("name")
+	if err != nil {
+		return helper.ParamError("name"), nil
+	}
+
+	// 先查现有配置
+	existing, err := services.GetOneSiteMonitorConfig(name)
+	if err != nil {
+		return helper.ServiceError("查询现有配置", err), nil
+	}
+	if len(existing) == 0 {
+		return helper.ServiceError("查询现有配置", fmt.Errorf("配置 %s 不存在", name)), nil
+	}
+
+	config := existing[0]
+
+	if desc := req.GetString("description", ""); desc != "" {
+		config.Description = desc
+	}
+	if siteListStr := req.GetString("site_list", ""); siteListStr != "" {
+		var siteList []string
+		if err := json.Unmarshal([]byte(siteListStr), &siteList); err != nil {
+			return helper.ParamInvalidError("site_list", "需要JSON数组格式"), nil
+		}
+		config.SiteList = siteList
+	}
+
+	if err := services.UpdateSiteMonitorConfig(name, config); err != nil {
+		return helper.ServiceError("更新监控配置", err), nil
+	}
+	return helper.TextResult("更新成功"), nil
+}
+
+func handleSiteMonitorConfigDelete(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	name, err := req.RequireString("name")
+	if err != nil {
+		return helper.ParamError("name"), nil
+	}
+
+	if err := services.DeleteSiteMonitorConfig(name); err != nil {
+		return helper.ServiceError("删除监控配置", err), nil
+	}
+	return helper.TextResult("删除成功"), nil
+}
+
+func handleSiteMonitorBaseInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	siteListStr, err := req.RequireString("site_list")
+	if err != nil {
+		return helper.ParamError("site_list"), nil
+	}
+
+	var siteList []string
+	if err := json.Unmarshal([]byte(siteListStr), &siteList); err != nil {
+		return helper.ParamInvalidError("site_list", "需要JSON数组格式"), nil
+	}
+
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 10))
+
+	para := dto.SiteBaseInfoPara{
+		SiteList: siteList,
+		PageSize: size,
+		PageNum:  page,
+	}
+
+	data, err := services.QuerySiteBaseInfoList(para)
+	if err != nil {
+		return helper.ServiceError("查询站点基本信息", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteMonitorModeInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+
+	data, err := services.QuerySiteModeInfo(sn)
+	if err != nil {
+		return helper.ServiceError("查询站点工作模式", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSiteMonitorAlarmList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+
+	data, err := services.QuerySiteAlarmList(sn)
+	if err != nil {
+		return helper.ServiceError("查询站点告警列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}

+ 72 - 0
service/cmd/mcp/tools/service/tou_template.go

@@ -0,0 +1,72 @@
+package service
+
+import (
+	"context"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/services"
+)
+
+func registerTouTemplateTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("service_tou_template_list",
+			mcp.WithDescription("查询 TOU 电价模版列表,支持按类型、状态筛选和关键字搜索"),
+			mcp.WithNumber("page", mcp.Description("页码,从1开始,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认10")),
+			mcp.WithNumber("type", mcp.Description("模版类型:0-系统模板 1-API模板 2-用户推荐")),
+			mcp.WithNumber("status", mcp.Description("发布状态:0-未发布 2-已发布")),
+			mcp.WithString("search", mcp.Description("搜索关键字")),
+		),
+		handleTouTemplateList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("service_tou_template_detail",
+			mcp.WithDescription("查询指定 TOU 电价模版的详细信息"),
+			mcp.WithNumber("id", mcp.Required(), mcp.Description("模版 ID")),
+		),
+		handleTouTemplateDetail,
+	)
+}
+
+func handleTouTemplateList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 10))
+	search := req.GetString("search", "")
+
+	var tempType *int
+	if v, err := req.RequireFloat("type"); err == nil {
+		t := int(v)
+		tempType = &t
+	}
+
+	var status *int
+	if v, err := req.RequireFloat("status"); err == nil {
+		s := int(v)
+		status = &s
+	}
+
+	data, err := services.GetTouTemplateProList(page, size, tempType, status, search)
+	if err != nil {
+		return helper.ServiceError("查询TOU模版列表", err), nil
+	}
+
+	return helper.JSONResult(data), nil
+}
+
+func handleTouTemplateDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireFloat("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	data, err := services.GetTouTemplateProDetailInfo(uint(id))
+	if err != nil {
+		return helper.ServiceError("查询TOU模版详情", err), nil
+	}
+
+	return helper.JSONResult(data), nil
+}