cloudpods_images_tool.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package tools
  15. import (
  16. "context"
  17. "encoding/json"
  18. "fmt"
  19. "strconv"
  20. "strings"
  21. "github.com/mark3labs/mcp-go/mcp"
  22. "yunion.io/x/log"
  23. "yunion.io/x/onecloud/pkg/mcp-server/adapters"
  24. "yunion.io/x/onecloud/pkg/mcp-server/models"
  25. )
  26. // CloudpodsImagesTool 是一个用于查询 Cloudpods 镜像列表的工具
  27. // 它封装了 Cloudpods 适配器和日志记录器
  28. type CloudpodsImagesTool struct {
  29. // adapter 用于与 Cloudpods API 进行交互
  30. adapter *adapters.CloudpodsAdapter
  31. }
  32. // NewCloudpodsImagesTool 创建一个新的 CloudpodsImagesTool 实例
  33. // 参数:
  34. // - adapter: Cloudpods 适配器实例,用于与 Cloudpods API 交互
  35. //
  36. // 返回值:
  37. // - *CloudpodsImagesTool: 新创建的 CloudpodsImagesTool 实例
  38. func NewCloudpodsImagesTool(adapter *adapters.CloudpodsAdapter) *CloudpodsImagesTool {
  39. return &CloudpodsImagesTool{
  40. adapter: adapter,
  41. }
  42. }
  43. // GetTool 定义并返回 Cloudpods 镜像列表查询工具的元数据
  44. // 该工具允许用户查询 Cloudpods 中的磁盘镜像列表,并支持多种查询参数
  45. // 返回值:
  46. // - mcp.Tool: 定义了工具名称、描述和参数的工具对象
  47. func (c *CloudpodsImagesTool) GetTool() mcp.Tool {
  48. return mcp.NewTool(
  49. "cloudpods_list_images",
  50. mcp.WithDescription("查询Cloudpods磁盘镜像列表,获取系统镜像信息"),
  51. mcp.WithString("limit", mcp.Description("返回结果数量限制,默认为20")),
  52. mcp.WithString("offset", mcp.Description("返回结果偏移量,默认为0")),
  53. mcp.WithString("search", mcp.Description("搜索关键词,可以按镜像名称搜索")),
  54. mcp.WithString("os_types", mcp.Description("操作系统类型,多个用逗号分隔,如:Linux,Windows,FreeBSD")),
  55. mcp.WithString("ak", mcp.Description("用户登录cloudpods后获取的access key")),
  56. mcp.WithString("sk", mcp.Description("用户登录cloudpods后获取的secret key")),
  57. )
  58. }
  59. // Handle 处理 Cloudpods 镜像列表查询请求
  60. // 该方法解析请求参数,调用适配器查询镜像列表,并格式化返回结果
  61. // 参数:
  62. // - ctx: 上下文对象,用于控制请求生命周期
  63. // - req: 工具调用请求对象,包含查询参数
  64. //
  65. // 返回值:
  66. // - *mcp.CallToolResult: 格式化后的镜像列表查询结果
  67. // - error: 如果查询过程中发生错误,则返回相应的错误信息
  68. func (c *CloudpodsImagesTool) Handle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  69. // 设置默认的查询结果数量限制为20
  70. limit := 20
  71. // 如果请求中包含limit参数且为有效正整数,则使用该值
  72. if limitStr := req.GetString("limit", ""); limitStr != "" {
  73. if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 {
  74. limit = parsedLimit
  75. }
  76. }
  77. // 设置默认的查询偏移量为0
  78. offset := 0
  79. // 如果请求中包含offset参数且为有效非负整数,则使用该值
  80. if offsetStr := req.GetString("offset", ""); offsetStr != "" {
  81. if parsedOffset, err := strconv.Atoi(offsetStr); err == nil && parsedOffset >= 0 {
  82. offset = parsedOffset
  83. }
  84. }
  85. // 获取搜索关键词参数
  86. search := req.GetString("search", "")
  87. // 解析操作系统类型参数,支持多个类型用逗号分隔
  88. var osTypes []string
  89. if osTypesStr := req.GetString("os_types", ""); osTypesStr != "" {
  90. osTypes = strings.Split(osTypesStr, ",")
  91. for i, osType := range osTypes {
  92. osTypes[i] = strings.TrimSpace(osType)
  93. }
  94. }
  95. // 获取访问凭证
  96. ak := req.GetString("ak", "")
  97. sk := req.GetString("sk", "")
  98. // 调用适配器查询镜像列表
  99. imagesResponse, err := c.adapter.ListImages(ctx, limit, offset, search, osTypes, ak, sk)
  100. if err != nil {
  101. log.Errorf("Fail to query image: %s", err)
  102. return nil, fmt.Errorf("fail to query image: %w", err)
  103. }
  104. // 格式化查询结果
  105. formattedResult := c.formatImagesResult(imagesResponse, limit, offset, search, osTypes)
  106. // 将结果序列化为JSON格式
  107. resultJSON, err := json.MarshalIndent(formattedResult, "", " ")
  108. if err != nil {
  109. log.Errorf("Fail to serialize result: %s", err)
  110. return nil, fmt.Errorf("fail to serialize result: %w", err)
  111. }
  112. // 返回格式化后的结果
  113. return mcp.NewToolResultText(string(resultJSON)), nil
  114. }
  115. // GetName 返回工具的名称标识符
  116. // 返回值:
  117. // - string: 工具名称,用于唯一标识该工具
  118. func (c *CloudpodsImagesTool) GetName() string {
  119. return "cloudpods_list_images"
  120. }
  121. // formatImagesResult 格式化镜像列表查询结果
  122. // 该方法将从适配器获取的原始镜像数据转换为结构化的响应格式,包含查询信息、镜像详情和摘要信息
  123. // 参数:
  124. // - response: 从适配器获取的原始镜像列表响应数据
  125. // - limit: 查询结果数量限制
  126. // - offset: 查询偏移量
  127. // - search: 搜索关键词
  128. // - osTypes: 操作系统类型过滤条件
  129. //
  130. // 返回值:
  131. // - map[string]interface{}: 格式化后的镜像列表数据,包含查询信息、镜像详情和摘要
  132. func (c *CloudpodsImagesTool) formatImagesResult(response *models.ImageListResponse, limit, offset int, search string, osTypes []string) map[string]interface{} {
  133. // 初始化格式化结果结构
  134. formatted := map[string]interface{}{
  135. // 查询信息部分,包含查询参数和结果统计
  136. "query_info": map[string]interface{}{
  137. "limit": limit,
  138. "offset": offset,
  139. "search": search,
  140. "os_types": osTypes,
  141. "total": response.Total,
  142. "count": len(response.Images),
  143. },
  144. // 镜像列表部分,初始化为空数组
  145. "images": make([]map[string]interface{}, 0, len(response.Images)),
  146. }
  147. // 遍历原始镜像数据,提取每个镜像的详细信息
  148. for _, image := range response.Images {
  149. // 构造单个镜像的详细信息
  150. imageInfo := map[string]interface{}{
  151. "id": image.Id,
  152. "name": image.Name,
  153. "description": image.Description,
  154. "status": image.Status,
  155. "disk_format": image.DiskFormat,
  156. "size": image.Size,
  157. "checksum": image.Checksum,
  158. "oss_checksum": image.OssChecksum,
  159. "fast_hash": image.FastHash,
  160. "location": image.Location,
  161. "os_arch": image.OsArch,
  162. "min_disk": image.MinDisk,
  163. "min_ram": image.MinRam,
  164. "is_data": image.IsData,
  165. "is_guest_image": image.IsGuestImage,
  166. "is_public": image.IsPublic,
  167. "is_standard": image.IsStandard,
  168. "is_system": image.IsSystem,
  169. "is_emulated": image.IsEmulated,
  170. "protected": image.Protected,
  171. "disable_delete": image.DisableDelete,
  172. "freezed": image.Freezed,
  173. "pending_deleted": image.PendingDeleted,
  174. "pending_deleted_at": image.PendingDeletedAt,
  175. "auto_delete_at": image.AutoDeleteAt,
  176. "encrypt_alg": image.EncryptAlg,
  177. "encrypt_key": image.EncryptKey,
  178. "encrypt_key_id": image.EncryptKeyId,
  179. "encrypt_key_user": image.EncryptKeyUser,
  180. "encrypt_key_user_domain": image.EncryptKeyUserDomain,
  181. "encrypt_key_user_domain_id": image.EncryptKeyUserDomainId,
  182. "encrypt_key_user_id": image.EncryptKeyUserId,
  183. "encrypt_status": image.EncryptStatus,
  184. "owner": image.Owner,
  185. "project": image.Project,
  186. "project_id": image.ProjectId,
  187. "project_domain": image.ProjectDomain,
  188. "project_metadata": image.ProjectMetadata,
  189. "project_src": image.ProjectSrc,
  190. "tenant": image.Tenant,
  191. "tenant_id": image.TenantId,
  192. "domain_id": image.DomainId,
  193. "public_scope": image.PublicScope,
  194. "public_src": image.PublicSrc,
  195. "shared_domains": image.SharedDomains,
  196. "shared_projects": image.SharedProjects,
  197. "properties": image.Properties,
  198. "metadata": image.Metadata,
  199. "progress": image.Progress,
  200. "can_delete": image.CanDelete,
  201. "can_update": image.CanUpdate,
  202. "update_version": image.UpdateVersion,
  203. "created_at": image.CreatedAt,
  204. "updated_at": image.UpdatedAt,
  205. }
  206. // 将镜像信息添加到结果数组中
  207. formatted["images"] = append(formatted["images"].([]map[string]interface{}), imageInfo)
  208. }
  209. // 构造结果摘要信息
  210. formatted["summary"] = map[string]interface{}{
  211. "total_images": response.Total,
  212. "returned_count": len(response.Images),
  213. "has_more": response.Total > int64(offset+len(response.Images)),
  214. "next_offset": offset + len(response.Images),
  215. }
  216. // 返回格式化后的完整结果
  217. return formatted
  218. }