Ver Fonte

feat:增加MCP server的data接口

Jerry Liang há 2 meses atrás
pai
commit
6729a0a46b

+ 119 - 23
service/cmd/mcp/DESIGN.md

@@ -7,7 +7,7 @@ FranklinWH MCP Server (`fwh-tools`) 是基于 [Model Context Protocol](https://m
 | 项目 | 说明 |
 |------|------|
 | 服务名 | `fwh-tools` |
-| 版本 | `2.0.0` |
+| 版本 | `2.1.0` |
 | 语言 | Go |
 | SDK | `github.com/mark3labs/mcp-go` |
 | 传输协议 | stdio (JSON-RPC 2.0 over stdin/stdout) |
@@ -43,12 +43,15 @@ FranklinWH MCP Server (`fwh-tools`) 是基于 [Model Context Protocol](https://m
 └───────┼──────────┼───────────┼────────────┼──────────┘
         │          │           │            │
         ▼          ▼           ▼            │ (纯函数)
-┌──────────┐ ┌──────────┐ ┌──────────┐     │
-│ services/│ │ MongoDB  │ │  MinIO   │     │
-│层(复用后 │ │ Redis    │ │          │     │
-│端业务代 │ │          │ │          │     │
-│码)       │ │          │ │          │     │
-└──────────┘ └──────────┘ └──────────┘
+┌──────────┐ ┌──────────────────────┐ ┌──────────┐
+│ services/│ │  repositories/       │ │  MinIO   │
+│层(复用后 │ │  ├─ mongo (MongoDB)  │ │          │
+│端业务代 │ │  ├─ redis (Redis)    │ │          │
+│码)       │ │  ├─ pgsql (PgSQL)   │ │          │
+│          │ │  ├─ api/fwh_pro     │ │          │
+│          │ │  ├─ api/plane_v1+v2 │ │          │
+│          │ │  └─ api/sim (SH/AC) │ │          │
+└──────────┘ └──────────────────────┘ └──────────┘
 ```
 
 ### 2.2 与现有后端的关系
@@ -80,8 +83,14 @@ service/cmd/mcp/
 │   │   └── feedback.go          # 反馈协作工具
 │   ├── data/                    # 数据直查层工具
 │   │   ├── register.go          # RegisterDataTools
-│   │   ├── mongo.go             # MongoDB 直查
-│   │   └── redis.go             # Redis 缓存读取
+│   │   ├── mongo.go             # MongoDB 通用直查
+│   │   ├── redis.go             # Redis 缓存读取
+│   │   ├── pgsql.go             # PostgreSQL 直查
+│   │   ├── emspara.go           # EMS 参数查询
+│   │   ├── fwh_api.go           # FWH Pro API 读操作
+│   │   ├── plane_api.go         # Plane API 读操作(工具注册)
+│   │   ├── plane_api_handlers.go # Plane API 读操作(handler 实现)
+│   │   └── sim_api.go           # SmartHub/Acceleronix SIM 卡 API
 │   ├── storage/                 # 对象存储层工具
 │   │   ├── register.go          # RegisterStorageTools
 │   │   ├── file.go              # 用户上传文件读取
@@ -103,7 +112,8 @@ service/cmd/mcp/
 │  调用后端 Service 层函数,包含业务校验和数据聚合      │
 ├───────────────────────────────────────────────────┤
 │  Layer 2: data_*     — 数据直查层(灵活查询)        │
-│  直接读取 DB/Cache,跳过业务逻辑,仅限只读           │
+│  直接调用 repositories 层,跳过业务逻辑,仅限只读     │
+│  覆盖: DB(Mongo/Redis/PgSQL) + 外部API(FWH/Plane/SIM) │
 ├───────────────────────────────────────────────────┤
 │  Layer 3: storage_*  — 对象存储层(文件操作)        │
 │  file_* 支持读写,minio_* 仅只读浏览和读取           │
@@ -115,7 +125,7 @@ service/cmd/mcp/
 
 **命名规范:** `{layer}_{domain}_{action}`
 
-示例:`service_site_query_info`、`data_mongo_find`、`storage_minio_list_objects`、`util_parse_sn`
+示例:`service_site_query_info`、`data_fwh_query_site_alarm_list`、`data_plane_list_work_items`、`storage_minio_list_objects`、`util_parse_sn`
 
 ---
 
@@ -137,12 +147,88 @@ service/cmd/mcp/
 
 ### 4.2 Data 层(数据直查)
 
+#### 4.2.1 通用数据库直查
+
 | 工具名 | 说明 | 参数 |
 |--------|------|------|
 | `data_mongo_find` | MongoDB 集合查询 | `collection` (必填), `filter`, `limit`, `skip`, `sort` |
 | `data_mongo_count` | MongoDB 文档计数 | `collection` (必填), `filter` |
 | `data_redis_get` | 读取 Redis key | `key` (必填), `type` |
 | `data_redis_exists` | 检查 key 存在性及 TTL | `key` (必填) |
+| `data_pgsql_query_snapshot_file_record` | 查询快照文件解析记录 | `file_name` (必填) |
+| `data_pgsql_query_snapshot_data` | 查询告警快照数据 | `sn` (必填), `date` (必填) |
+| `data_emspara_vid_info` | 查询 EMS VID 参数信息 | `id` (必填) |
+| `data_emspara_pid_info` | 查询 EMS PID 参数信息 | `id` (必填) |
+
+#### 4.2.2 FWH Pro API 直查
+
+| 工具名 | 说明 | 参数 |
+|--------|------|------|
+| `data_fwh_query_site_base_info_list` | 查询站点基本信息列表 | `sn`, `page`, `size` |
+| `data_fwh_query_site_list` | 查询站点列表 | `sn`, `page`, `size` |
+| `data_fwh_query_site_mode_info` | 查询站点工作模式 | `sn` (必填) |
+| `data_fwh_query_site_power_flow` | 查询站点功率流 | `sn` (必填), `date` (必填) |
+| `data_fwh_query_site_alarm_list` | 查询站点告警列表 | `sn` (必填) |
+| `data_fwh_query_event_alarm_list` | 查询事件/告警日志 | `sn` (必填), `start_time` (必填), `end_time` (必填), `type` |
+| `data_fwh_query_alarm_snapshot` | 查询告警快照 | `sn` (必填), `date` (必填) |
+| `data_fwh_query_alarm_statistics` | 查询实时告警统计 | `country`, `page` |
+| `data_fwh_query_alarm_statistics_from_code` | 按告警码查询站点 | `alarm_code` (必填), `country`, `page` |
+| `data_fwh_query_mqtt_log_list` | 查询 MQTT 日志 | `sn` (必填), `cmd_type` (必填), `start_time` (必填), `end_time` (必填) |
+| `data_fwh_query_shutdown_list` | 查询停机站点列表 | `date` (必填), `country` |
+| `data_fwh_query_ems_log_record` | 查询 EMS 日志记录 | `sn` (必填) |
+| `data_fwh_query_fault_record_list` | 查询故障录波列表 | `sn` (必填), `start_time` (必填), `end_time` (必填) |
+| `data_fwh_query_fault_record_callable_list` | 查询可召唤故障录波 | `sn` (必填), `dev_type` |
+| `data_fwh_query_fault_record_call_record_list` | 查询录波召唤记录 | (无参数) |
+| `data_fwh_query_fault_record_sub_list` | 查询录波子记录 | `fault_id` (必填) |
+| `data_fwh_query_system_update_log` | 查询系统升级记录 | `sn` (必填) |
+| `data_fwh_query_system_update_log_detail` | 查询升级记录详情 | `order_id` (必填) |
+| `data_fwh_query_ahub_update_log` | 查询 aHub 升级记录 | `sn` (必填) |
+| `data_fwh_query_tou_template_list` | 查询 TOU 模板列表 | `search`, `page`, `size` |
+| `data_fwh_query_tou_template_detail` | 查询 TOU 模板详情 | `id` (必填) |
+
+#### 4.2.3 Plane API 直查
+
+| 工具名 | 说明 | 参数 |
+|--------|------|------|
+| `data_plane_list_projects` | 列出项目 | (无参数) |
+| `data_plane_get_project` | 获取项目详情 | `project_id` (必填) |
+| `data_plane_get_project_summary` | 获取项目摘要 | `project_id` (必填), `fields` |
+| `data_plane_list_work_items` | 列出工作项 | `state`, `priority`, `assignee`, `label` |
+| `data_plane_get_work_item` | 获取工作项详情 | `work_item_id` (必填) |
+| `data_plane_get_work_item_by_identifier` | 通过标识符获取工作项 | `identifier` (必填), `sequence_id` (必填) |
+| `data_plane_search_work_items` | 搜索工作项 | `query` (必填), `limit` |
+| `data_plane_list_cycles` | 列出迭代 | (无参数) |
+| `data_plane_get_cycle` | 获取迭代详情 | `cycle_id` (必填) |
+| `data_plane_list_cycle_issues` | 列出迭代工作项 | `cycle_id` (必填) |
+| `data_plane_list_modules` | 列出模块 | (无参数) |
+| `data_plane_get_module` | 获取模块详情 | `module_id` (必填) |
+| `data_plane_list_module_issues` | 列出模块工作项 | `module_id` (必填) |
+| `data_plane_list_states` | 列出状态 | (无参数) |
+| `data_plane_list_labels` | 列出标签 | (无参数) |
+| `data_plane_list_workspace_members` | 列出工作空间成员 | (无参数) |
+| `data_plane_list_project_members` | 列出项目成员 | (无参数) |
+| `data_plane_list_work_item_comments` | 列出评论 | `work_item_id` (必填) |
+| `data_plane_list_work_item_activities` | 列出活动历史 | `work_item_id` (必填) |
+| `data_plane_list_work_item_links` | 列出链接 | `work_item_id` (必填) |
+| `data_plane_list_work_item_attachments` | 列出附件 | `work_item_id` (必填) |
+| `data_plane_list_work_item_relations` | 列出关系 | `work_item_id` (必填) |
+| `data_plane_get_intake_issue_list` | 查询 Intake 列表 | (无参数) |
+| `data_plane_get_intake_issue_detail` | 获取 Intake 详情 | `intake_id` (必填) |
+| `data_plane_list_pages` | 列出页面 (V2) | (无参数) |
+| `data_plane_get_page` | 获取页面详情 (V2) | `page_id` (必填) |
+| `data_plane_get_page_description` | 获取页面描述 (V2) | `page_id` (必填) |
+
+#### 4.2.4 SIM 卡 API 直查
+
+| 工具名 | 说明 | 参数 |
+|--------|------|------|
+| `data_sim_smarthub_batch_info` | SmartHub 批量查询 SIM 卡 | `page`, `size` |
+| `data_sim_smarthub_card_info` | SmartHub 单卡详情 | `giccid` (必填) |
+| `data_sim_acceleronix_card_list` | Acceleronix SIM 卡列表 | `page`, `size` |
+| `data_sim_acceleronix_card_info` | Acceleronix 单卡详情 | `iccid` (必填) |
+| `data_sim_acceleronix_batch_data_info` | Acceleronix 批量数据用量 | `iccid_list` (必填), `month` (必填) |
+| `data_sim_acceleronix_data_pool_list` | Acceleronix 流量池列表 | `page`, `size` |
+| `data_sim_acceleronix_data_pool_sim_info` | Acceleronix 流量池 SIM 详情 | `data_pool_id` (必填), `page`, `size` |
 
 ### 4.3 Storage 层(对象存储)
 
@@ -172,24 +258,31 @@ service/cmd/mcp/
 | 认证 | 无需(由 ACP 管理器控制进程生命周期) |
 | Collection 白名单 | MongoDB 查询限制在指定集合内 |
 | Bucket 白名单 | MinIO 访问限制在指定 bucket 内 |
-| 只读限制 | data_* 和 storage_* 层所有操作均为只读 |
+| 只读限制 | data_* 层所有操作均为只读(排除写、更新操作) |
+| 外部 API | data_fwh_*、data_plane_*、data_sim_* 仅暴露查询接口,不暴露写操作 |
 | 文件大小限制 | MinIO 读取限制 1MB,用户上传限制 2MB |
 | 查询结果限制 | MongoDB 单次最多返回 100 条 |
 | Redis 安全 | 不支持通配符 key,防止 KEYS * 类操作 |
+| SIM 卡批量限制 | Acceleronix 批量查询最多 50 个 ICCID |
 
 ### 5.2 白名单配置
 
 **MongoDB Collections:**
 ```go
 var mongoAllowedCollections = []string{
-    "site_info",
-    "sim_card_manage",
-    "batch_download",
-    "site_analysis",
-    "shutdown_statistics",
-    "alarm_statistics",
-    "sites_monitor_config",
-    "user_config",
+    "SiteInfoManageData",
+    "SiteInfoManageRecord",
+    "SimCardManage",
+    "SimCardMonthlyData",
+    "BatchDownloadConfig",
+    "SiteAnalysisConfig",
+    "StatisticsShutdown",
+    "StatisticsAlarm",
+    "SiteMonitorConfig",
+    "UserInfo",
+    "AutoUpdateConfigOption",
+    "AutoUpdateConfig",
+    "AiChatSessions",
 }
 ```
 
@@ -366,8 +459,9 @@ FranklinWH 工具箱 MCP Server — 内部工具平台的 AI 接口层。
 使用原则:
 1. 优先使用 service_* 工具,它们包含完整的业务逻辑
 2. 仅当 service 层无法满足时才使用 data_* 直查
-3. 所有 data_* 和 storage_* 工具仅支持只读操作
-4. 查询 MongoDB 时注意 collection 白名单限制
+3. data_* 层覆盖 repositories 全部只读操作(DB + 外部 API)
+4. 所有 data_* 和 storage_* 工具仅支持只读操作
+5. 查询 MongoDB 时注意 collection 白名单限制
 ```
 
 ---
@@ -475,7 +569,8 @@ kiro-cli chat
 - `fwh-toolbox-server/services` — 业务逻辑层
 - `fwh-toolbox-server/database` — 数据库连接
 - `fwh-toolbox-server/cache` — Redis 缓存
-- `fwh-toolbox-server/repositories` — 数据访问层
+- `fwh-toolbox-server/repositories` — 数据访问层(Mongo/Redis/PgSQL/API)
+- `fwh-toolbox-server/domain` — 领域对象定义
 - `fwh-toolbox-server/utils/filestore` — 文件存储
 - `fwh-toolbox-server/utils/minio` — MinIO 客户端
 - `fwh-toolbox-server/config` — 配置加载
@@ -489,3 +584,4 @@ kiro-cli chat
 |------|------|----------|
 | 1.0.0 | 2026-08-03 | 初版:6 个 service 层工具(site/sim/alarm/shutdown/plane) |
 | 2.0.0 | 2026-08-06 | 重构为四层架构,新增 data/storage/util 层,命名规范化 |
+| 2.1.0 | 2026-08-10 | data 层全面覆盖 repositories 只读操作:新增 fwh_api(21)、plane_api(25)、sim_api(7)、pgsql(2)、emspara(2) 共 57 个工具;修正 MongoDB 白名单为实际集合名 |

+ 79 - 0
service/cmd/mcp/tools/data/emspara.go

@@ -0,0 +1,79 @@
+package data
+
+import (
+	"context"
+	"fmt"
+	"strconv"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/repositories"
+)
+
+func registerEmsparaTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("data_emspara_vid_info",
+			mcp.WithDescription("查询 EMS 参数 VID 信息(通过 VID ID 获取宏名和描述)"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("VID ID(数字)")),
+		),
+		handleEmsparaVidInfo,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_emspara_pid_info",
+			mcp.WithDescription("查询 EMS 参数 PID 信息(通过 PID ID 获取宏名和描述)"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("PID ID(数字)")),
+		),
+		handleEmsparaPidInfo,
+	)
+}
+
+func handleEmsparaVidInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := strconv.ParseUint(idStr, 10, 64)
+	if err != nil {
+		return helper.ParamInvalidError("id", "必须是数字"), nil
+	}
+
+	name, desc, err := repositories.GetEmsparaVidInfo(uint(id))
+	if err != nil {
+		return helper.EmptyResult(fmt.Sprintf("未找到 VID %d 的信息: %v", id, err)), nil
+	}
+
+	return helper.JSONResult(map[string]any{
+		"id":          id,
+		"type":        "VID",
+		"macro_name":  name,
+		"description": desc,
+	}), nil
+}
+
+func handleEmsparaPidInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := strconv.ParseUint(idStr, 10, 64)
+	if err != nil {
+		return helper.ParamInvalidError("id", "必须是数字"), nil
+	}
+
+	name, desc, err := repositories.GetEmsparaPidInfo(uint(id))
+	if err != nil {
+		return helper.EmptyResult(fmt.Sprintf("未找到 PID %d 的信息: %v", id, err)), nil
+	}
+
+	return helper.JSONResult(map[string]any{
+		"id":          id,
+		"type":        "PID",
+		"macro_name":  name,
+		"description": desc,
+	}), nil
+}

+ 645 - 0
service/cmd/mcp/tools/data/fwh_api.go

@@ -0,0 +1,645 @@
+package data
+
+import (
+	"context"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+
+	"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/repositories"
+)
+
+func registerFwhApiTools(s *server.MCPServer) {
+	// 站点信息
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_site_base_info_list",
+			mcp.WithDescription("查询站点基本信息列表(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Description("站点SN,多个用逗号分隔")),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认20")),
+		),
+		handleFwhQuerySiteBaseInfoList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_site_list",
+			mcp.WithDescription("查询站点列表(FWH Pro API,支持SN搜索)"),
+			mcp.WithString("sn", mcp.Description("站点SN搜索")),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认20")),
+		),
+		handleFwhQuerySiteList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_site_mode_info",
+			mcp.WithDescription("查询站点工作模式信息(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+		),
+		handleFwhQuerySiteModeInfo,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_site_power_flow",
+			mcp.WithDescription("查询站点指定日期的功率流数据(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+			mcp.WithString("date", mcp.Required(), mcp.Description("日期,格式 yyyy-MM-dd")),
+		),
+		handleFwhQuerySitePowerFlow,
+	)
+
+	// 告警相关
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_site_alarm_list",
+			mcp.WithDescription("查询站点当前告警列表(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+		),
+		handleFwhQuerySiteAlarmList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_event_alarm_list",
+			mcp.WithDescription("查询站点时间范围内的事件/告警日志(FWH Pro API)。注意:大时间范围查询可能较慢"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+			mcp.WithString("start_time", mcp.Required(), mcp.Description("开始时间,格式 yyyy-MM-dd HH:mm:ss")),
+			mcp.WithString("end_time", mcp.Required(), mcp.Description("结束时间,格式 yyyy-MM-dd HH:mm:ss")),
+			mcp.WithString("type", mcp.Description("类型:all(默认)、event、alarm")),
+		),
+		handleFwhQueryEventAlarmList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_alarm_snapshot",
+			mcp.WithDescription("查询站点告警快照信息(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+			mcp.WithString("date", mcp.Required(), mcp.Description("日期,格式 yyyy-MM-dd")),
+		),
+		handleFwhQueryAlarmSnapshot,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_alarm_statistics",
+			mcp.WithDescription("查询实时告警统计数据(FWH Pro API)"),
+			mcp.WithNumber("country", mcp.Description("国家ID: 0-全部(默认), 2-美国, 3-澳大利亚")),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+		),
+		handleFwhQueryAlarmStatistics,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_alarm_statistics_from_code",
+			mcp.WithDescription("按告警码查询触发站点列表(FWH Pro API)"),
+			mcp.WithString("alarm_code", mcp.Required(), mcp.Description("告警码")),
+			mcp.WithNumber("country", mcp.Description("国家ID: 0-全部(默认), 2-美国, 3-澳大利亚")),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+		),
+		handleFwhQueryAlarmStatisticsFromCode,
+	)
+
+	// MQTT日志
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_mqtt_log_list",
+			mcp.WithDescription("查询站点时间范围内的MQTT日志(FWH Pro API)。注意:大时间范围查询可能较慢"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+			mcp.WithString("cmd_type", mcp.Required(), mcp.Description("MQTT命令类型(如 0, 101, 201 等)")),
+			mcp.WithString("start_time", mcp.Required(), mcp.Description("开始时间,格式 yyyy-MM-dd HH:mm:ss")),
+			mcp.WithString("end_time", mcp.Required(), mcp.Description("结束时间,格式 yyyy-MM-dd HH:mm:ss")),
+		),
+		handleFwhQueryMqttLogList,
+	)
+
+	// 停机列表
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_shutdown_list",
+			mcp.WithDescription("查询Shutdown站点列表(FWH Pro API)"),
+			mcp.WithString("date", mcp.Required(), mcp.Description("日期,格式 yyyy-MM-dd")),
+			mcp.WithNumber("country", mcp.Description("国家ID: 0-全部(默认), 2-美国, 3-澳大利亚")),
+		),
+		handleFwhQueryShutdownList,
+	)
+
+	// EMS日志
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_ems_log_record",
+			mcp.WithDescription("查询EMS日志召唤记录(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+		),
+		handleFwhQueryEmsLogRecord,
+	)
+
+	// 故障录波
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_fault_record_list",
+			mcp.WithDescription("查询故障录波日志列表(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+			mcp.WithString("start_time", mcp.Required(), mcp.Description("开始时间,格式 yyyy-MM-dd HH:mm:ss")),
+			mcp.WithString("end_time", mcp.Required(), mcp.Description("结束时间,格式 yyyy-MM-dd HH:mm:ss")),
+		),
+		handleFwhQueryFaultRecordList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_fault_record_callable_list",
+			mcp.WithDescription("查询设备可召唤的故障录波数据列表(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("设备SN(aGate/aPower/aHub)")),
+			mcp.WithNumber("dev_type", mcp.Description("设备类型: 0-aPower, 1-aGate, 2-MPPT, 3-aHub(默认1)")),
+		),
+		handleFwhQueryFaultRecordCallableList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_fault_record_call_record_list",
+			mcp.WithDescription("查询故障录波召唤记录(FWH Pro API)"),
+		),
+		handleFwhQueryFaultRecordCallRecordList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_fault_record_sub_list",
+			mcp.WithDescription("查询故障录波子记录(FWH Pro API)"),
+			mcp.WithString("fault_id", mcp.Required(), mcp.Description("故障录波记录ID")),
+		),
+		handleFwhQueryFaultRecordSubList,
+	)
+
+	// 系统升级
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_system_update_log",
+			mcp.WithDescription("查询系统升级记录(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+		),
+		handleFwhQuerySystemUpdateLog,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_system_update_log_detail",
+			mcp.WithDescription("查询系统升级记录详细信息(FWH Pro API)"),
+			mcp.WithString("order_id", mcp.Required(), mcp.Description("升级单ID")),
+		),
+		handleFwhQuerySystemUpdateLogDetail,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_ahub_update_log",
+			mcp.WithDescription("查询aHub升级记录(FWH Pro API)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+		),
+		handleFwhQueryAHubUpdateLog,
+	)
+
+	// TOU模板
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_tou_template_list",
+			mcp.WithDescription("查询TOU模板列表(FWH Pro API)"),
+			mcp.WithString("search", mcp.Description("搜索关键字")),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认20")),
+		),
+		handleFwhQueryTouTemplateList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_fwh_query_tou_template_detail",
+			mcp.WithDescription("查询TOU模板详情(FWH Pro API)"),
+			mcp.WithString("id", mcp.Required(), mcp.Description("模板ID")),
+		),
+		handleFwhQueryTouTemplateDetail,
+	)
+}
+
+// === Handlers ===
+
+func handleFwhQuerySiteBaseInfoList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn := req.GetString("sn", "")
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 20))
+
+	para := dto.SiteBaseInfoPara{
+		PageNum:  page,
+		PageSize: size,
+	}
+	if sn != "" {
+		snList := strings.Split(sn, ",")
+		para.SiteList = snList
+	}
+
+	data, err := repositories.QuerySiteBaseInfoList(para)
+	if err != nil {
+		return helper.ServiceError("查询站点基本信息列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQuerySiteList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn := req.GetString("sn", "")
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 20))
+
+	para := dto.SiteInfoPara{
+		PageNum:  page,
+		PageSize: size,
+	}
+	if sn != "" {
+		para.SN = &sn
+	}
+
+	data, err := repositories.QuerySiteInfoList(para)
+	if err != nil {
+		return helper.ServiceError("查询站点列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQuerySiteModeInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+
+	data, err := repositories.QuerySiteModeInfo(sn)
+	if err != nil {
+		return helper.ServiceError("查询站点工作模式", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQuerySitePowerFlow(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	dateStr, err := req.RequireString("date")
+	if err != nil {
+		return helper.ParamError("date"), nil
+	}
+
+	day, err := time.Parse("2006-01-02", dateStr)
+	if err != nil {
+		return helper.ParamInvalidError("date", fmt.Sprintf("需要 yyyy-MM-dd 格式: %v", err)), nil
+	}
+
+	data, err := repositories.QuerySitePowerFlow(sn, day)
+	if err != nil {
+		return helper.ServiceError("查询功率流", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQuerySiteAlarmList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+
+	data, err := repositories.QuerySiteAlarmList(sn)
+	if err != nil {
+		return helper.ServiceError("查询站点告警列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryEventAlarmList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	startTimeStr, err := req.RequireString("start_time")
+	if err != nil {
+		return helper.ParamError("start_time"), nil
+	}
+	endTimeStr, err := req.RequireString("end_time")
+	if err != nil {
+		return helper.ParamError("end_time"), nil
+	}
+
+	startTime, err := time.Parse(time.DateTime, startTimeStr)
+	if err != nil {
+		return helper.ParamInvalidError("start_time", fmt.Sprintf("需要 yyyy-MM-dd HH:mm:ss 格式: %v", err)), nil
+	}
+	endTime, err := time.Parse(time.DateTime, endTimeStr)
+	if err != nil {
+		return helper.ParamInvalidError("end_time", fmt.Sprintf("需要 yyyy-MM-dd HH:mm:ss 格式: %v", err)), nil
+	}
+
+	typeStr := req.GetString("type", "all")
+	var eventAlarmType repositories.EventAlarmType
+	switch typeStr {
+	case "event":
+		eventAlarmType = repositories.Event
+	case "alarm":
+		eventAlarmType = repositories.Alarm
+	default:
+		eventAlarmType = repositories.EventAndAlarm
+	}
+
+	data, err := repositories.QueryEventOrAlarmListOnTimeRange(sn, startTime, endTime, eventAlarmType)
+	if err != nil {
+		return helper.ServiceError("查询事件告警日志", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryAlarmSnapshot(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	dateStr, err := req.RequireString("date")
+	if err != nil {
+		return helper.ParamError("date"), nil
+	}
+
+	// 验证日期格式
+	if _, err := time.Parse("2006-01-02", dateStr); err != nil {
+		return helper.ParamInvalidError("date", fmt.Sprintf("需要 yyyy-MM-dd 格式: %v", err)), nil
+	}
+
+	data, err := repositories.QueryAlarmSnapshotInfo(sn, dateStr)
+	if err != nil {
+		return helper.ServiceError("查询告警快照", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryAlarmStatistics(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	country := domain.CountryType(int(req.GetFloat("country", 0)))
+	page := int(req.GetFloat("page", 1))
+
+	data, err := repositories.QueryAlarmStatistics(country, page)
+	if err != nil {
+		return helper.ServiceError("查询实时告警统计", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryAlarmStatisticsFromCode(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	alarmCode, err := req.RequireString("alarm_code")
+	if err != nil {
+		return helper.ParamError("alarm_code"), nil
+	}
+	country := domain.CountryType(int(req.GetFloat("country", 0)))
+	page := int(req.GetFloat("page", 1))
+
+	data, err := repositories.QueryAlarmStatisticsFromCode(country, alarmCode, page)
+	if err != nil {
+		return helper.ServiceError("查询告警码统计", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryMqttLogList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	cmdType, err := req.RequireString("cmd_type")
+	if err != nil {
+		return helper.ParamError("cmd_type"), nil
+	}
+	startTimeStr, err := req.RequireString("start_time")
+	if err != nil {
+		return helper.ParamError("start_time"), nil
+	}
+	endTimeStr, err := req.RequireString("end_time")
+	if err != nil {
+		return helper.ParamError("end_time"), nil
+	}
+
+	startTime, err := time.Parse(time.DateTime, startTimeStr)
+	if err != nil {
+		return helper.ParamInvalidError("start_time", fmt.Sprintf("需要 yyyy-MM-dd HH:mm:ss 格式: %v", err)), nil
+	}
+	endTime, err := time.Parse(time.DateTime, endTimeStr)
+	if err != nil {
+		return helper.ParamInvalidError("end_time", fmt.Sprintf("需要 yyyy-MM-dd HH:mm:ss 格式: %v", err)), nil
+	}
+
+	data, err := repositories.QueryMqttLogListOnTimeRange(sn, domain.MqttCmdType(cmdType), startTime, endTime)
+	if err != nil {
+		return helper.ServiceError("查询MQTT日志", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryShutdownList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	dateStr, err := req.RequireString("date")
+	if err != nil {
+		return helper.ParamError("date"), nil
+	}
+	country := int(req.GetFloat("country", 0))
+
+	// 验证日期格式
+	if _, err := time.Parse("2006-01-02", dateStr); err != nil {
+		return helper.ParamInvalidError("date", fmt.Sprintf("需要 yyyy-MM-dd 格式: %v", err)), nil
+	}
+
+	data, err := repositories.QuerySiteShutdownList(dateStr, country)
+	if err != nil {
+		return helper.ServiceError("查询停机列表", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryEmsLogRecord(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+
+	// 使用空日期和0类型查询所有记录
+	data, err := repositories.QueryEmsLogCallRecord(sn, "", 0)
+	if err != nil {
+		// 如果是"没有召唤日志"的错误,返回空结果
+		return helper.EmptyResult(fmt.Sprintf("未找到EMS日志记录: %v", err)), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryFaultRecordList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	startTimeStr, err := req.RequireString("start_time")
+	if err != nil {
+		return helper.ParamError("start_time"), nil
+	}
+	endTimeStr, err := req.RequireString("end_time")
+	if err != nil {
+		return helper.ParamError("end_time"), nil
+	}
+
+	data, err := repositories.QueryFaultRecordList(sn, startTimeStr, endTimeStr)
+	if err != nil {
+		return helper.ServiceError("查询故障录波列表", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryFaultRecordCallableList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	devType := int(req.GetFloat("dev_type", 1))
+
+	data, err := repositories.QueryFaultRecordCallableList(devType, sn)
+	if err != nil {
+		return helper.ServiceError("查询可召唤故障录波列表", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("无可召唤的故障录波数据"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryFaultRecordCallRecordList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.QueryFaultRecordCallRecordList()
+	if err != nil {
+		return helper.ServiceError("查询故障录波召唤记录", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryFaultRecordSubList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	faultIdStr, err := req.RequireString("fault_id")
+	if err != nil {
+		return helper.ParamError("fault_id"), nil
+	}
+
+	faultId, err := strconv.ParseInt(faultIdStr, 10, 64)
+	if err != nil {
+		return helper.ParamInvalidError("fault_id", "必须是数字"), nil
+	}
+
+	data, err := repositories.QueryFaultRecordSubList(faultId)
+	if err != nil {
+		return helper.ServiceError("查询故障录波子记录", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQuerySystemUpdateLog(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+
+	data, err := repositories.QuerySystemUpdateLog(sn)
+	if err != nil {
+		return helper.ServiceError("查询系统升级记录", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQuerySystemUpdateLogDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	orderIdStr, err := req.RequireString("order_id")
+	if err != nil {
+		return helper.ParamError("order_id"), nil
+	}
+
+	orderId, err := strconv.ParseInt(orderIdStr, 10, 64)
+	if err != nil {
+		return helper.ParamInvalidError("order_id", "必须是数字"), nil
+	}
+
+	data, err := repositories.QuerySystemUpdateLogDetail(orderId)
+	if err != nil {
+		return helper.ServiceError("查询升级详情", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryAHubUpdateLog(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+
+	data, err := repositories.QueryAHubUpdateLog(sn)
+	if err != nil {
+		return helper.ServiceError("查询aHub升级记录", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryTouTemplateList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	search := req.GetString("search", "")
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 20))
+
+	data, err := repositories.QueryTouTemplateInfoList(search, size, page, nil, nil)
+	if err != nil {
+		return helper.ServiceError("查询TOU模板列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleFwhQueryTouTemplateDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	idStr, err := req.RequireString("id")
+	if err != nil {
+		return helper.ParamError("id"), nil
+	}
+
+	id, err := strconv.ParseUint(idStr, 10, 64)
+	if err != nil {
+		return helper.ParamInvalidError("id", "必须是数字"), nil
+	}
+
+	data, err := repositories.QueryTouTemplateDetailInfo(uint(id))
+	if err != nil {
+		return helper.ServiceError("查询TOU模板详情", err), nil
+	}
+	if data == nil {
+		return helper.EmptyResult("模板不存在"), nil
+	}
+	return helper.JSONResult(data), nil
+}

+ 22 - 8
service/cmd/mcp/tools/data/mongo.go

@@ -16,14 +16,28 @@ import (
 
 // mongoAllowedCollections 允许查询的 collection 白名单
 var mongoAllowedCollections = []string{
-	"site_info",
-	"sim_card_manage",
-	"batch_download",
-	"site_analysis",
-	"shutdown_statistics",
-	"alarm_statistics",
-	"sites_monitor_config",
-	"user_config",
+	// 站点信息管理
+	"SiteInfoManageData",
+	"SiteInfoManageRecord",
+	// SIM卡管理
+	"SimCardManage",
+	"SimCardMonthlyData",
+	// 批量下载
+	"BatchDownloadConfig",
+	// 站点分析
+	"SiteAnalysisConfig",
+	// 统计
+	"StatisticsShutdown",
+	"StatisticsAlarm",
+	// 站点监控
+	"SiteMonitorConfig",
+	// 用户配置
+	"UserInfo",
+	// 自动升级
+	"AutoUpdateConfigOption",
+	"AutoUpdateConfig",
+	// AI会话
+	"AiChatSessions",
 }
 
 func registerMongoTools(s *server.MCPServer) {

+ 71 - 0
service/cmd/mcp/tools/data/pgsql.go

@@ -0,0 +1,71 @@
+package data
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/repositories"
+)
+
+func registerPgsqlTools(s *server.MCPServer) {
+	s.AddTool(
+		mcp.NewTool("data_pgsql_query_snapshot_file_record",
+			mcp.WithDescription("查询 PostgreSQL 中的告警快照文件解析记录"),
+			mcp.WithString("file_name", mcp.Required(), mcp.Description("快照文件名")),
+		),
+		handlePgsqlQuerySnapshotFileRecord,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_pgsql_query_snapshot_data",
+			mcp.WithDescription("查询 PostgreSQL 中的告警快照数据(按SN和日期)"),
+			mcp.WithString("sn", mcp.Required(), mcp.Description("站点SN")),
+			mcp.WithString("date", mcp.Required(), mcp.Description("日期,格式 yyyy-MM-dd")),
+		),
+		handlePgsqlQuerySnapshotData,
+	)
+}
+
+func handlePgsqlQuerySnapshotFileRecord(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 := repositories.QuerySnapshotFileRecord(ctx, fileName)
+	if err != nil {
+		return helper.EmptyResult(fmt.Sprintf("未找到文件记录: %v", err)), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handlePgsqlQuerySnapshotData(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	sn, err := req.RequireString("sn")
+	if err != nil {
+		return helper.ParamError("sn"), nil
+	}
+	dateStr, err := req.RequireString("date")
+	if err != nil {
+		return helper.ParamError("date"), nil
+	}
+
+	// 验证日期格式
+	if _, err := time.Parse("2006-01-02", dateStr); err != nil {
+		return helper.ParamInvalidError("date", fmt.Sprintf("需要 yyyy-MM-dd 格式: %v", err)), nil
+	}
+
+	data, err := repositories.QuerySnapshotData(ctx, sn, dateStr)
+	if err != nil {
+		return helper.ServiceError("查询快照数据", err), nil
+	}
+
+	if len(data) == 0 {
+		return helper.EmptyResult("查询结果为空"), nil
+	}
+	return helper.JSONResult(data), nil
+}

+ 229 - 0
service/cmd/mcp/tools/data/plane_api.go

@@ -0,0 +1,229 @@
+package data
+
+import (
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+)
+
+func registerPlaneApiTools(s *server.MCPServer) {
+	// === Project ===
+	s.AddTool(
+		mcp.NewTool("data_plane_list_projects",
+			mcp.WithDescription("列出 Plane 所有项目(V1 API)"),
+		),
+		handlePlaneListProjects,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_project",
+			mcp.WithDescription("获取 Plane 项目详情(V1 API)"),
+			mcp.WithString("project_id", mcp.Required(), mcp.Description("项目 UUID")),
+		),
+		handlePlaneGetProject,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_project_summary",
+			mcp.WithDescription("获取 Plane 项目摘要统计(V1 API)"),
+			mcp.WithString("project_id", mcp.Required(), mcp.Description("项目 UUID")),
+			mcp.WithString("fields", mcp.Description("要获取的字段,逗号分隔")),
+		),
+		handlePlaneGetProjectSummary,
+	)
+
+	// === Work Item ===
+	s.AddTool(
+		mcp.NewTool("data_plane_list_work_items",
+			mcp.WithDescription("列出 Plane 工作项列表(V1 API)"),
+			mcp.WithString("state", mcp.Description("状态ID过滤")),
+			mcp.WithString("priority", mcp.Description("优先级过滤")),
+			mcp.WithString("assignee", mcp.Description("指派人ID过滤")),
+			mcp.WithString("label", mcp.Description("标签ID过滤")),
+		),
+		handlePlaneListWorkItems,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_work_item",
+			mcp.WithDescription("获取 Plane 工作项详情(V1 API)"),
+			mcp.WithString("work_item_id", mcp.Required(), mcp.Description("工作项 UUID")),
+		),
+		handlePlaneGetWorkItem,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_work_item_by_identifier",
+			mcp.WithDescription("通过标识符获取工作项(如 PROJ-42)(V1 API)"),
+			mcp.WithString("identifier", mcp.Required(), mcp.Description("项目标识符(如 PROJ)")),
+			mcp.WithNumber("sequence_id", mcp.Required(), mcp.Description("序号(如 42)")),
+		),
+		handlePlaneGetWorkItemByIdentifier,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_search_work_items",
+			mcp.WithDescription("搜索 Plane 工作项(V1 API)"),
+			mcp.WithString("query", mcp.Required(), mcp.Description("搜索关键字")),
+			mcp.WithNumber("limit", mcp.Description("返回数量限制,默认20")),
+		),
+		handlePlaneSearchWorkItems,
+	)
+
+	// === Cycle ===
+	s.AddTool(
+		mcp.NewTool("data_plane_list_cycles",
+			mcp.WithDescription("列出 Plane 迭代列表(V1 API)"),
+		),
+		handlePlaneListCycles,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_cycle",
+			mcp.WithDescription("获取 Plane 迭代详情(V1 API)"),
+			mcp.WithString("cycle_id", mcp.Required(), mcp.Description("迭代 UUID")),
+		),
+		handlePlaneGetCycle,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_cycle_issues",
+			mcp.WithDescription("列出 Plane 迭代中的工作项(V1 API)"),
+			mcp.WithString("cycle_id", mcp.Required(), mcp.Description("迭代 UUID")),
+		),
+		handlePlaneListCycleIssues,
+	)
+
+	// === Module ===
+	s.AddTool(
+		mcp.NewTool("data_plane_list_modules",
+			mcp.WithDescription("列出 Plane 模块列表(V1 API)"),
+		),
+		handlePlaneListModules,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_module",
+			mcp.WithDescription("获取 Plane 模块详情(V1 API)"),
+			mcp.WithString("module_id", mcp.Required(), mcp.Description("模块 UUID")),
+		),
+		handlePlaneGetModule,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_module_issues",
+			mcp.WithDescription("列出 Plane 模块中的工作项(V1 API)"),
+			mcp.WithString("module_id", mcp.Required(), mcp.Description("模块 UUID")),
+		),
+		handlePlaneListModuleIssues,
+	)
+
+	// === State / Label / Member ===
+	s.AddTool(
+		mcp.NewTool("data_plane_list_states",
+			mcp.WithDescription("列出 Plane 项目状态列表(V1 API)"),
+		),
+		handlePlaneListStates,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_labels",
+			mcp.WithDescription("列出 Plane 项目标签列表(V1 API)"),
+		),
+		handlePlaneListLabels,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_workspace_members",
+			mcp.WithDescription("列出 Plane 工作空间成员(V1 API)"),
+		),
+		handlePlaneListWorkspaceMembers,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_project_members",
+			mcp.WithDescription("列出 Plane 项目成员(V1 API)"),
+		),
+		handlePlaneListProjectMembers,
+	)
+
+	// === Comment / Activity / Attachment / Relation ===
+	s.AddTool(
+		mcp.NewTool("data_plane_list_work_item_comments",
+			mcp.WithDescription("列出 Plane 工作项评论(V1 API)"),
+			mcp.WithString("work_item_id", mcp.Required(), mcp.Description("工作项 UUID")),
+		),
+		handlePlaneListWorkItemComments,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_work_item_activities",
+			mcp.WithDescription("列出 Plane 工作项活动历史(V1 API)"),
+			mcp.WithString("work_item_id", mcp.Required(), mcp.Description("工作项 UUID")),
+		),
+		handlePlaneListWorkItemActivities,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_work_item_links",
+			mcp.WithDescription("列出 Plane 工作项链接(V1 API)"),
+			mcp.WithString("work_item_id", mcp.Required(), mcp.Description("工作项 UUID")),
+		),
+		handlePlaneListWorkItemLinks,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_work_item_attachments",
+			mcp.WithDescription("列出 Plane 工作项附件(V1 API)"),
+			mcp.WithString("work_item_id", mcp.Required(), mcp.Description("工作项 UUID")),
+		),
+		handlePlaneListWorkItemAttachments,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_list_work_item_relations",
+			mcp.WithDescription("列出 Plane 工作项关系(V1 API)"),
+			mcp.WithString("work_item_id", mcp.Required(), mcp.Description("工作项 UUID")),
+		),
+		handlePlaneListWorkItemRelations,
+	)
+
+	// === Intake ===
+	s.AddTool(
+		mcp.NewTool("data_plane_get_intake_issue_list",
+			mcp.WithDescription("查询 Plane Intake Issue 列表(V1 API)"),
+		),
+		handlePlaneGetIntakeIssueList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_intake_issue_detail",
+			mcp.WithDescription("获取 Plane Intake Issue 详情(V1 API)"),
+			mcp.WithString("intake_id", mcp.Required(), mcp.Description("Intake Issue UUID")),
+		),
+		handlePlaneGetIntakeIssueDetail,
+	)
+
+	// === V2 Web API: Page ===
+	s.AddTool(
+		mcp.NewTool("data_plane_list_pages",
+			mcp.WithDescription("列出 Plane 页面列表(V2 Web API)"),
+		),
+		handlePlaneListPages,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_page",
+			mcp.WithDescription("获取 Plane 页面详情(V2 Web API)"),
+			mcp.WithString("page_id", mcp.Required(), mcp.Description("页面 UUID")),
+		),
+		handlePlaneGetPage,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_plane_get_page_description",
+			mcp.WithDescription("获取 Plane 页面描述内容(V2 Web API)"),
+			mcp.WithString("page_id", mcp.Required(), mcp.Description("页面 UUID")),
+		),
+		handlePlaneGetPageDescription,
+	)
+}

+ 272 - 0
service/cmd/mcp/tools/data/plane_api_handlers.go

@@ -0,0 +1,272 @@
+package data
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/mark3labs/mcp-go/mcp"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/repositories"
+)
+
+// planeRawResult 将 Plane API 返回的原始 JSON bytes 转为 MCP 结果
+func planeRawResult(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.TextResult(string(data)), nil
+	}
+	return helper.JSONResult(parsed), nil
+}
+
+// === Project Handlers ===
+
+func handlePlaneListProjects(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneListProjects()
+	return planeRawResult(data, err, "列出项目")
+}
+
+func handlePlaneGetProject(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	projectID, err := req.RequireString("project_id")
+	if err != nil {
+		return helper.ParamError("project_id"), nil
+	}
+	data, apiErr := repositories.PlaneGetProject(projectID)
+	return planeRawResult(data, apiErr, "获取项目详情")
+}
+
+func handlePlaneGetProjectSummary(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	projectID, err := req.RequireString("project_id")
+	if err != nil {
+		return helper.ParamError("project_id"), nil
+	}
+	fields := req.GetString("fields", "")
+	data, apiErr := repositories.PlaneGetProjectSummary(projectID, fields)
+	return planeRawResult(data, apiErr, "获取项目摘要")
+}
+
+// === Work Item Handlers ===
+
+func handlePlaneListWorkItems(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	params := make(map[string]string)
+	if v := req.GetString("state", ""); v != "" {
+		params["state"] = v
+	}
+	if v := req.GetString("priority", ""); v != "" {
+		params["priority"] = v
+	}
+	if v := req.GetString("assignee", ""); v != "" {
+		params["assignee"] = v
+	}
+	if v := req.GetString("label", ""); v != "" {
+		params["label"] = v
+	}
+	data, err := repositories.PlaneListWorkItems(params)
+	return planeRawResult(data, err, "列出工作项")
+}
+
+func handlePlaneGetWorkItem(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("work_item_id")
+	if err != nil {
+		return helper.ParamError("work_item_id"), nil
+	}
+	data, apiErr := repositories.PlaneGetWorkItem(id)
+	return planeRawResult(data, apiErr, "获取工作项详情")
+}
+
+func handlePlaneGetWorkItemByIdentifier(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	identifier, err := req.RequireString("identifier")
+	if err != nil {
+		return helper.ParamError("identifier"), nil
+	}
+	seqID := int(req.GetFloat("sequence_id", 0))
+	if seqID <= 0 {
+		return helper.ParamError("sequence_id"), nil
+	}
+	data, apiErr := repositories.PlaneGetWorkItemByIdentifier(identifier, seqID)
+	return planeRawResult(data, apiErr, "通过标识符获取工作项")
+}
+
+func handlePlaneSearchWorkItems(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	query, err := req.RequireString("query")
+	if err != nil {
+		return helper.ParamError("query"), nil
+	}
+	limit := int(req.GetFloat("limit", 20))
+	data, apiErr := repositories.PlaneSearchWorkItems(query, limit, "")
+	return planeRawResult(data, apiErr, "搜索工作项")
+}
+
+// === Cycle Handlers ===
+
+func handlePlaneListCycles(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneListCycles()
+	return planeRawResult(data, err, "列出迭代")
+}
+
+func handlePlaneGetCycle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("cycle_id")
+	if err != nil {
+		return helper.ParamError("cycle_id"), nil
+	}
+	data, apiErr := repositories.PlaneGetCycle(id)
+	return planeRawResult(data, apiErr, "获取迭代详情")
+}
+
+func handlePlaneListCycleIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("cycle_id")
+	if err != nil {
+		return helper.ParamError("cycle_id"), nil
+	}
+	data, apiErr := repositories.PlaneListCycleIssues(id)
+	return planeRawResult(data, apiErr, "列出迭代工作项")
+}
+
+// === Module Handlers ===
+
+func handlePlaneListModules(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneListModules()
+	return planeRawResult(data, err, "列出模块")
+}
+
+func handlePlaneGetModule(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("module_id")
+	if err != nil {
+		return helper.ParamError("module_id"), nil
+	}
+	data, apiErr := repositories.PlaneGetModule(id)
+	return planeRawResult(data, apiErr, "获取模块详情")
+}
+
+func handlePlaneListModuleIssues(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("module_id")
+	if err != nil {
+		return helper.ParamError("module_id"), nil
+	}
+	data, apiErr := repositories.PlaneListModuleIssues(id)
+	return planeRawResult(data, apiErr, "列出模块工作项")
+}
+
+// === State / Label / Member Handlers ===
+
+func handlePlaneListStates(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneListStates()
+	return planeRawResult(data, err, "列出状态")
+}
+
+func handlePlaneListLabels(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneListLabels()
+	return planeRawResult(data, err, "列出标签")
+}
+
+func handlePlaneListWorkspaceMembers(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneListWorkspaceMembers()
+	return planeRawResult(data, err, "列出工作空间成员")
+}
+
+func handlePlaneListProjectMembers(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneListProjectMembers()
+	return planeRawResult(data, err, "列出项目成员")
+}
+
+// === Comment / Activity / Link / Attachment / Relation Handlers ===
+
+func handlePlaneListWorkItemComments(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("work_item_id")
+	if err != nil {
+		return helper.ParamError("work_item_id"), nil
+	}
+	data, apiErr := repositories.PlaneListWorkItemComments(id)
+	return planeRawResult(data, apiErr, "列出评论")
+}
+
+func handlePlaneListWorkItemActivities(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("work_item_id")
+	if err != nil {
+		return helper.ParamError("work_item_id"), nil
+	}
+	data, apiErr := repositories.PlaneListWorkItemActivities(id)
+	return planeRawResult(data, apiErr, "列出活动历史")
+}
+
+func handlePlaneListWorkItemLinks(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("work_item_id")
+	if err != nil {
+		return helper.ParamError("work_item_id"), nil
+	}
+	data, apiErr := repositories.PlaneListWorkItemLinks(id)
+	return planeRawResult(data, apiErr, "列出链接")
+}
+
+func handlePlaneListWorkItemAttachments(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("work_item_id")
+	if err != nil {
+		return helper.ParamError("work_item_id"), nil
+	}
+	data, apiErr := repositories.PlaneListWorkItemAttachments(id)
+	return planeRawResult(data, apiErr, "列出附件")
+}
+
+func handlePlaneListWorkItemRelations(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("work_item_id")
+	if err != nil {
+		return helper.ParamError("work_item_id"), nil
+	}
+	data, apiErr := repositories.PlaneListWorkItemRelations(id)
+	return planeRawResult(data, apiErr, "列出关系")
+}
+
+// === Intake Handlers ===
+
+func handlePlaneGetIntakeIssueList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	data, err := repositories.PlaneGetIntakeIssueList()
+	if err != nil {
+		return helper.ServiceError("查询Intake列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handlePlaneGetIntakeIssueDetail(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("intake_id")
+	if err != nil {
+		return helper.ParamError("intake_id"), nil
+	}
+	data, apiErr := repositories.PlaneGetIntakeIssueDetail(id)
+	return planeRawResult(data, apiErr, "获取Intake详情")
+}
+
+// === V2 Web API: Page Handlers ===
+
+func handlePlaneListPages(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	slug := repositories.PlaneWebGetWorkspaceSlug()
+	projectID := repositories.PlaneWebGetProjectId()
+	data, err := repositories.PlaneWebListPages(slug, projectID)
+	return planeRawResult(data, err, "列出页面")
+}
+
+func handlePlaneGetPage(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("page_id")
+	if err != nil {
+		return helper.ParamError("page_id"), nil
+	}
+	slug := repositories.PlaneWebGetWorkspaceSlug()
+	projectID := repositories.PlaneWebGetProjectId()
+	data, apiErr := repositories.PlaneWebGetPage(slug, projectID, id)
+	return planeRawResult(data, apiErr, "获取页面详情")
+}
+
+func handlePlaneGetPageDescription(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	id, err := req.RequireString("page_id")
+	if err != nil {
+		return helper.ParamError("page_id"), nil
+	}
+	slug := repositories.PlaneWebGetWorkspaceSlug()
+	projectID := repositories.PlaneWebGetProjectId()
+	data, apiErr := repositories.PlaneWebGetPageDescription(slug, projectID, id)
+	return planeRawResult(data, apiErr, "获取页面描述")
+}

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

@@ -6,6 +6,9 @@ import "github.com/mark3labs/mcp-go/server"
 func RegisterDataTools(s *server.MCPServer) {
 	registerMongoTools(s)
 	registerRedisTools(s)
-	// registerPostgresTools(s)  // 后续按需启用
-	// registerMysqlTools(s)     // 后续按需启用
+	registerPgsqlTools(s)
+	registerEmsparaTools(s)
+	registerFwhApiTools(s)
+	registerPlaneApiTools(s)
+	registerSimApiTools(s)
 }

+ 189 - 0
service/cmd/mcp/tools/data/sim_api.go

@@ -0,0 +1,189 @@
+package data
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
+
+	"fwh-toolbox-server/cmd/mcp/tools/helper"
+	"fwh-toolbox-server/repositories"
+)
+
+func registerSimApiTools(s *server.MCPServer) {
+	// SmartHub
+	s.AddTool(
+		mcp.NewTool("data_sim_smarthub_batch_info",
+			mcp.WithDescription("批量查询 SmartHub 平台 SIM 卡信息"),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认20")),
+		),
+		handleSimSmartHubBatchInfo,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_sim_smarthub_card_info",
+			mcp.WithDescription("查询 SmartHub 平台单张 SIM 卡详细信息"),
+			mcp.WithString("giccid", mcp.Required(), mcp.Description("SIM 卡 GICCID")),
+		),
+		handleSimSmartHubCardInfo,
+	)
+
+	// Acceleronix
+	s.AddTool(
+		mcp.NewTool("data_sim_acceleronix_card_list",
+			mcp.WithDescription("查询 Acceleronix 平台 SIM 卡列表"),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认20")),
+		),
+		handleSimAcceleronixCardList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_sim_acceleronix_card_info",
+			mcp.WithDescription("查询 Acceleronix 平台单张 SIM 卡详情"),
+			mcp.WithString("iccid", mcp.Required(), mcp.Description("SIM 卡 ICCID")),
+		),
+		handleSimAcceleronixCardInfo,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_sim_acceleronix_batch_data_info",
+			mcp.WithDescription("批量获取 Acceleronix SIM 卡数据用量信息"),
+			mcp.WithString("iccid_list", mcp.Required(), mcp.Description("ICCID 列表,逗号分隔")),
+			mcp.WithString("month", mcp.Required(), mcp.Description("月份,格式 YYYYMM(如 202601)")),
+		),
+		handleSimAcceleronixBatchDataInfo,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_sim_acceleronix_data_pool_list",
+			mcp.WithDescription("查询 Acceleronix 流量池列表"),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认20")),
+		),
+		handleSimAcceleronixDataPoolList,
+	)
+
+	s.AddTool(
+		mcp.NewTool("data_sim_acceleronix_data_pool_sim_info",
+			mcp.WithDescription("查询 Acceleronix 流量池中的 SIM 卡详情"),
+			mcp.WithString("data_pool_id", mcp.Required(), mcp.Description("流量池ID")),
+			mcp.WithNumber("page", mcp.Description("页码,默认1")),
+			mcp.WithNumber("size", mcp.Description("每页数量,默认20")),
+		),
+		handleSimAcceleronixDataPoolSimInfo,
+	)
+}
+
+// === Handlers ===
+
+func handleSimSmartHubBatchInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 20))
+
+	data, err := repositories.QuerySmartHubBatchSimCardInfo(page, size)
+	if err != nil {
+		return helper.ServiceError("查询SmartHub SIM卡列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSimSmartHubCardInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	giccid, err := req.RequireString("giccid")
+	if err != nil {
+		return helper.ParamError("giccid"), nil
+	}
+
+	data, err := repositories.QuerySmartHubSimCardInfo(giccid)
+	if err != nil {
+		return helper.ServiceError("查询SmartHub SIM卡详情", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSimAcceleronixCardList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 20))
+
+	data, err := repositories.QueryAcceleronixSimCardList(page, size)
+	if err != nil {
+		return helper.ServiceError("查询Acceleronix SIM卡列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSimAcceleronixCardInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	iccid, err := req.RequireString("iccid")
+	if err != nil {
+		return helper.ParamError("iccid"), nil
+	}
+
+	data, err := repositories.QueryAcceleronixSimCardInfo(iccid)
+	if err != nil {
+		return helper.ServiceError("查询Acceleronix SIM卡详情", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSimAcceleronixBatchDataInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	iccidListStr, err := req.RequireString("iccid_list")
+	if err != nil {
+		return helper.ParamError("iccid_list"), nil
+	}
+	month, err := req.RequireString("month")
+	if err != nil {
+		return helper.ParamError("month"), nil
+	}
+
+	// 验证月份格式
+	if len(month) != 6 {
+		return helper.ParamInvalidError("month", "需要 YYYYMM 格式,如 202601"), nil
+	}
+
+	iccidList := strings.Split(iccidListStr, ",")
+	for i := range iccidList {
+		iccidList[i] = strings.TrimSpace(iccidList[i])
+	}
+
+	if len(iccidList) == 0 {
+		return helper.ParamInvalidError("iccid_list", "至少需要一个ICCID"), nil
+	}
+	if len(iccidList) > 50 {
+		return helper.ParamInvalidError("iccid_list", fmt.Sprintf("最多50个ICCID,当前%d个", len(iccidList))), nil
+	}
+
+	data, err := repositories.QueryAcceleronixBatchSimCardDataInfo(iccidList, month)
+	if err != nil {
+		return helper.ServiceError("批量查询Acceleronix数据用量", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSimAcceleronixDataPoolList(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 20))
+
+	data, err := repositories.QueryAcceleronixDataPoolList(page, size)
+	if err != nil {
+		return helper.ServiceError("查询Acceleronix流量池列表", err), nil
+	}
+	return helper.JSONResult(data), nil
+}
+
+func handleSimAcceleronixDataPoolSimInfo(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
+	poolID, err := req.RequireString("data_pool_id")
+	if err != nil {
+		return helper.ParamError("data_pool_id"), nil
+	}
+	page := uint(req.GetFloat("page", 1))
+	size := uint(req.GetFloat("size", 20))
+
+	data, err := repositories.QueryAcceleronixDataPoolSimInfo(poolID, page, size)
+	if err != nil {
+		return helper.ServiceError("查询流量池SIM详情", err), nil
+	}
+	return helper.JSONResult(data), nil
+}