Skip to main content
Glama
Qihoo360

360 AI Cloud Drive MCP Server

by Qihoo360

file-search

Search for files and folders in your cloud drive using keywords, filter by file type, and browse results with pagination.

Instructions

在云盘中根据关键词搜索文件和文件夹,支持按文件类型筛选和分页查询。返回符合条件的文件详细信息。

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_categoryNo文件类型筛选:-1(全部)、0(其他)、1(图片)、2(文档)、3(音乐)、4(视频)
keyNo搜索关键词,当file_category 不为 -1 时,可以为空,否则必填
pageNo页码,从1开始
page_sizeNo每页显示的条目数,默认20条,最大100条

Implementation Reference

  • The main execution handler for the 'file-search' tool. It handles input parameters, retrieves authentication info, calls the searchFiles helper to query the cloud storage API, processes the response, formats file information, and returns structured content or error messages.
      const httpContext = gethttpContext(mcpReq, server);
      
      // 使用transport中的authInfo
      const transportAuthInfo = httpContext.authInfo;
      try {
        let authInfo: AuthInfo;
        
        // 参数验证
        if (!key && file_category === -1) {
          throw new Error("必须提供搜索关键词(key)或指定文件类型(file_category)");
        }
        
        const extraParams: Record<string|number, string|number> = {
          file_category,
          key,
          page: page || 1,
          page_size: page_size || 20,
        }
        try {
          // 传入方法名和路径等参数
          authInfo = await getAuthInfo({
            method: 'File.searchList',
            extraParams: extraParams
          }, transportAuthInfo);
          
          authInfo.request_url = getConfig(transportAuthInfo?.ecsEnv).request_url
        } catch (authError) {
          throw new Error("获取鉴权信息失败,请提供有效的API_KEY");
        }
        
        // 调用API搜索文件
        const apiResponse = await searchFiles(authInfo, extraParams);
        
        // 检查API响应是否成功
        if (apiResponse && apiResponse.errno === 0) {
          const files = (apiResponse.data && apiResponse.data.node_list) || [];
          
          if (files.length === 0) {
            return {
              content: [
                {
                  type: "text",
                  text: "没有找到符合条件的文件",
                },
              ],
            };
          }
          
          // 统计文件夹和文件数量
          const dirCount = files.filter((file: YunPanFile) => file.type === "1").length;
          const fileCount = files.length - dirCount;
          
          // 格式化结果
          const formattedFiles = files.map(formatFileInfo);
          const filesText = `云盘文件搜索结果 (关键词: ${key})\n共 ${files.length} 项 (${dirCount} 个文件夹, ${fileCount} 个文件)\n\n${formattedFiles.join("\n")}`;
          
          return {
            content: [
              {
                type: "text",
                text: filesText,
              },
              {
                type: "text",
                text: TOOL_LIMIT_NOTE,
              },
            ],
          };
        } else {
          throw new Error(apiResponse?.errmsg || "API请求失败");
        }
      } catch (error: any) {
        return {
          content: [
            {
              type: "text",
              text: `搜索文件时发生错误: ${error.message}`,
            },
            {
              type: "text",
              text: TOOL_LIMIT_NOTE,
            },
          ],
        };
      }
    },
  • Zod input schema defining parameters for the file-search tool: file_category (file type filter), key (search keyword), page (pagination), page_size (items per page).
      file_category: z.number().optional().default(-1).describe("文件类型筛选:-1(全部)、0(其他)、1(图片)、2(文档)、3(音乐)、4(视频)"),
      key: z.string().optional().default("").describe("搜索关键词,当file_category 不为 -1 时,可以为空,否则必填"),
      page: z.number().optional().default(1).describe("页码,从1开始"),
      page_size: z.number().optional().default(20).describe("每页显示的条目数,默认20条,最大100条"),
    },
  • registerFileSearchTool function that registers the 'file-search' tool on the MCP server using server.tool(), including name, description, schema, and handler.
    export function registerFileSearchTool(server: McpServer) {
      server.tool(
        "file-search",
        "在云盘中根据关键词搜索文件和文件夹,支持按文件类型筛选和分页查询。返回符合条件的文件详细信息。",
        {
          file_category: z.number().optional().default(-1).describe("文件类型筛选:-1(全部)、0(其他)、1(图片)、2(文档)、3(音乐)、4(视频)"),
          key: z.string().optional().default("").describe("搜索关键词,当file_category 不为 -1 时,可以为空,否则必填"),
          page: z.number().optional().default(1).describe("页码,从1开始"),
          page_size: z.number().optional().default(20).describe("每页显示的条目数,默认20条,最大100条"),
        },
        async ({ file_category, key, page, page_size }, mcpReq: any) => {
          const httpContext = gethttpContext(mcpReq, server);
          
          // 使用transport中的authInfo
          const transportAuthInfo = httpContext.authInfo;
          try {
            let authInfo: AuthInfo;
            
            // 参数验证
            if (!key && file_category === -1) {
              throw new Error("必须提供搜索关键词(key)或指定文件类型(file_category)");
            }
            
            const extraParams: Record<string|number, string|number> = {
              file_category,
              key,
              page: page || 1,
              page_size: page_size || 20,
            }
            try {
              // 传入方法名和路径等参数
              authInfo = await getAuthInfo({
                method: 'File.searchList',
                extraParams: extraParams
              }, transportAuthInfo);
              
              authInfo.request_url = getConfig(transportAuthInfo?.ecsEnv).request_url
            } catch (authError) {
              throw new Error("获取鉴权信息失败,请提供有效的API_KEY");
            }
            
            // 调用API搜索文件
            const apiResponse = await searchFiles(authInfo, extraParams);
            
            // 检查API响应是否成功
            if (apiResponse && apiResponse.errno === 0) {
              const files = (apiResponse.data && apiResponse.data.node_list) || [];
              
              if (files.length === 0) {
                return {
                  content: [
                    {
                      type: "text",
                      text: "没有找到符合条件的文件",
                    },
                  ],
                };
              }
              
              // 统计文件夹和文件数量
              const dirCount = files.filter((file: YunPanFile) => file.type === "1").length;
              const fileCount = files.length - dirCount;
              
              // 格式化结果
              const formattedFiles = files.map(formatFileInfo);
              const filesText = `云盘文件搜索结果 (关键词: ${key})\n共 ${files.length} 项 (${dirCount} 个文件夹, ${fileCount} 个文件)\n\n${formattedFiles.join("\n")}`;
              
              return {
                content: [
                  {
                    type: "text",
                    text: filesText,
                  },
                  {
                    type: "text",
                    text: TOOL_LIMIT_NOTE,
                  },
                ],
              };
            } else {
              throw new Error(apiResponse?.errmsg || "API请求失败");
            }
          } catch (error: any) {
            return {
              content: [
                {
                  type: "text",
                  text: `搜索文件时发生错误: ${error.message}`,
                },
                {
                  type: "text",
                  text: TOOL_LIMIT_NOTE,
                },
              ],
            };
          }
        },
      );
    }
  • searchFiles helper function that constructs and sends HTTP POST request to the cloud disk API's File.searchList endpoint with authentication and search parameters.
    async function searchFiles(authInfo: AuthInfo, extraParams: Record<string|number, string|number>): Promise<any> {
      try {
        const url = new URL(authInfo.request_url || '');
        
        // 构建请求头
        const headers = {
          'Access-Token': authInfo.access_token || '',
          'Content-Type': 'application/x-www-form-urlencoded'
        };
    
        // 构建基本请求参数
        const baseParams: Record<string, string> = {
          'method': 'File.searchList',
          'access_token': authInfo.access_token || '',
          'qid': authInfo.qid || '',
          'sign': authInfo.sign || ''
        };
    
        // 添加所有基本参数到URL
        Object.entries(baseParams).forEach(([key, value]) => {
          url.searchParams.append(key, String(value));
        });
    
        // 构建表单数据
        const body = new URLSearchParams();
        
        // 添加额外参数到表单数据
        for (const [key, value] of Object.entries(extraParams)) {
          // 去除 access_token,因为已经在URL中添加
          if (key !== 'access_token') {
            body.append(String(key), String(value));
          }
        }
        
        const response = await fetch(url.toString(), {
          method: 'POST',
          headers: headers,
          body
        });
        
        if (!response.ok) {
          throw new Error(`API 请求失败,状态码: ${response.status}`);
        }
        
        // 先获取原始响应文本
        const responseText = await response.text();
        
        try {
          // 尝试解析为JSON
          const data = JSON.parse(responseText);
          return data;
        } catch (jsonError) {
          throw new Error(`无法解析API响应: ${responseText.substring(0, 100)}...`);
        }
      } catch (error) {
        console.error('搜索文件失败:', error);
        throw error;
      }
    }
  • Call to registerFileSearchTool(server) within registerAllTools to include the file-search tool in the server's toolset.
    registerFileSearchTool(server);
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions that the tool returns detailed file information for matching results, which is useful, but it lacks critical details such as whether this is a read-only operation (implied by '搜索' but not stated), any rate limits, authentication requirements, error handling, or the format of the returned information. For a search tool with zero annotation coverage, this leaves significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded, stating the core functionality in a single sentence. It efficiently covers the key aspects (searching, filtering, pagination, and return details) without unnecessary words. However, it could be slightly improved by structuring it into clearer clauses, but overall it earns its place with minimal waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a search tool with 4 parameters, no annotations, and no output schema, the description is incomplete. It doesn't explain the return format (e.g., what '详细信息' includes), error conditions, or how results are ordered. Without annotations to cover behavioral traits, the description should provide more context to help the agent use the tool effectively, but it falls short.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds minimal semantic value beyond the input schema, which has 100% coverage. It mentions '根据关键词搜索' (search by keyword) and '按文件类型筛选和分页查询' (filter by file type and pagination query), but these are already covered in the schema descriptions for 'key', 'file_category', 'page', and 'page_size'. With high schema coverage, the baseline is 3, as the description doesn't provide additional context like examples or edge cases.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches for files and folders in cloud storage using keywords, with filtering by file type and pagination. It specifies the verb '搜索' (search) and resource '文件和文件夹' (files and folders), making the purpose evident. However, it doesn't explicitly differentiate from sibling tools like 'file-list' or 'get-download-url', which is why it doesn't reach a score of 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives such as 'file-list' (which might list files without searching) or 'get-download-url' (which retrieves download links). It mentions support for filtering and pagination, but this is more about functionality than usage context, leaving the agent with no explicit when-to-use or when-not-to-use instructions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Qihoo360/ecs_mcp_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server