Skip to main content
Glama
Qihoo360

360 AI Cloud Drive MCP Server

by Qihoo360

file-save

Save files to cloud storage by providing a URL or text content, specifying the upload path for organized storage.

Instructions

通过URL或文本内容保存文件到云盘

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlNo文件下载地址,url或content必传1个
contentNo文件内容(md格式),url或content必传1个,需要传用户指定的完整内容,不能省略任何部分
upload_pathNo云盘存储路径,必须以/开头和结尾。如不指定,默认为带有当前日期的'/AI为我下载/YYYYMMDD/'/AI为我下载/20260103/

Implementation Reference

  • Main execution logic for 'file-save' tool: validates input, authenticates, saves file via API, polls asynchronous task status, formats response with file details and cloud disk link.
    async ({ url, content }, mcpReq: any) => { // 参数验证 if (!url && !content) { return { content: [{ type: "text", text: "❌ 参数错误: 必须提供url或content参数" }] }; } const httpContext = gethttpContext(mcpReq, server); const transportAuthInfo = httpContext.authInfo; try { let authInfo: AuthInfo; try { // 获取鉴权信息 authInfo = await getAuthInfo({ method: 'MCP.saveFile' }, transportAuthInfo); authInfo.request_url = getConfig(transportAuthInfo?.ecsEnv).request_url; } catch (authError) { console.error("获取鉴权信息失败:", authError); throw new Error("获取鉴权信息失败"); } // 调用保存文件API const saveResult = await saveFile(authInfo, { url, content }); if (saveResult && saveResult.errno === 0) { const taskId = saveResult.data?.task_id; if (!taskId) { return { content: [{ type: "text", text: "❌ 保存文件失败: 未获取到任务ID" }] }; } // 轮询任务状态 try { const finalResult = await pollTaskStatus(authInfo, taskId); const resultData = finalResult.data; let resultText = `✅ 文件保存成功!\n\n`; resultText += `🆔 任务ID: ${taskId}\n`; const fileInfo: any = { taskId }; if (resultData) { const qid = resultData.qid || authInfo.qid; const path = resultData.file_path; const nid = resultData.nid; const size = resultData.file_size; fileInfo.path = path; fileInfo.nid = nid; fileInfo.size = size; fileInfo.qid = qid; if (path) { resultText += `📂 云盘路径: ${path}\n`; } if (size) { resultText += `📦 文件大小: ${formatBytes(size)}\n`; } if (path && nid && qid) { const dirPath = path.substring(0, path.lastIndexOf('/') + 1); const href = `https://www.yunpan.com/file/index#/fileManage/my/file/${encodeURIComponent(dirPath)}?focus_nid=${nid}&owner_qid=${qid}`; resultText += `🔗 [点击查看云盘文件](${href})\n`; fileInfo.href = href; } } return { content: [ { type: "text", name: "x-save-result-json", text: JSON.stringify({ status: 'success', file: fileInfo }) }, { type: "text", name: "x-save-result-display", text: resultText } ] }; } catch (pollError: any) { const isTimeout = pollError.message.includes('轮询超时'); const status = isTimeout ? 'timeout' : 'failed'; const resultText = isTimeout ? `⏳ 文件保存轮询超时\n\n- 任务ID: ${taskId}\n- 请稍后检查云盘或使用任务ID查询状态。` : `❌ 文件保存失败\n\n- 任务ID: ${taskId}\n- 错误信息: ${pollError.message}`; return { content: [ { type: "text", name: "x-save-result-json", text: JSON.stringify({ status: status, taskId: taskId, error: pollError.message }) }, { type: "text", name: "x-save-result-display", text: resultText } ] }; } } else { throw new Error(saveResult?.errmsg || "API请求失败"); } } catch (error: any) { return { content: [ { type: "text", text: `保存文件时发生错误: ${error.message}`, } ], }; } },
  • Zod input schema defining optional 'url' (file download URL) or 'content' (file text content), one required. upload_path commented out.
    { url: z.string().optional().describe("文件下载地址,url或content必传1个"), content: z.string().optional().describe("文件内容(md格式),url或content必传1个,需要传用户指定的完整内容,不能省略任何部分"), // upload_path: z.string() // .default('/来自mcp_server/') // .describe("云盘存储路径,必须以/开头和结尾。如不指定,默认为'/来自mcp_server/'。\n- 支持自动创建不存在的一级目录\n- 不支持不存在的多级目录") // .refine((path) => path.endsWith('/'), { // message: "路径必须以/结尾" // }) // .refine((path) => path.startsWith('/'), { // message: "路径必须以/开头" // }) },
  • Registers the 'file-save' tool on the MCP server instance, specifying name, description, input schema, and handler function.
    export function registerFileSaveTool(server: McpServer) { server.tool( "file-save", "通过URL或文本内容保存文件到云盘", { url: z.string().optional().describe("文件下载地址,url或content必传1个"), content: z.string().optional().describe("文件内容(md格式),url或content必传1个,需要传用户指定的完整内容,不能省略任何部分"), // upload_path: z.string() // .default('/来自mcp_server/') // .describe("云盘存储路径,必须以/开头和结尾。如不指定,默认为'/来自mcp_server/'。\n- 支持自动创建不存在的一级目录\n- 不支持不存在的多级目录") // .refine((path) => path.endsWith('/'), { // message: "路径必须以/结尾" // }) // .refine((path) => path.startsWith('/'), { // message: "路径必须以/开头" // }) }, async ({ url, content }, mcpReq: any) => { // 参数验证 if (!url && !content) { return { content: [{ type: "text", text: "❌ 参数错误: 必须提供url或content参数" }] }; } const httpContext = gethttpContext(mcpReq, server); const transportAuthInfo = httpContext.authInfo; try { let authInfo: AuthInfo; try { // 获取鉴权信息 authInfo = await getAuthInfo({ method: 'MCP.saveFile' }, transportAuthInfo); authInfo.request_url = getConfig(transportAuthInfo?.ecsEnv).request_url; } catch (authError) { console.error("获取鉴权信息失败:", authError); throw new Error("获取鉴权信息失败"); } // 调用保存文件API const saveResult = await saveFile(authInfo, { url, content }); if (saveResult && saveResult.errno === 0) { const taskId = saveResult.data?.task_id; if (!taskId) { return { content: [{ type: "text", text: "❌ 保存文件失败: 未获取到任务ID" }] }; } // 轮询任务状态 try { const finalResult = await pollTaskStatus(authInfo, taskId); const resultData = finalResult.data; let resultText = `✅ 文件保存成功!\n\n`; resultText += `🆔 任务ID: ${taskId}\n`; const fileInfo: any = { taskId }; if (resultData) { const qid = resultData.qid || authInfo.qid; const path = resultData.file_path; const nid = resultData.nid; const size = resultData.file_size; fileInfo.path = path; fileInfo.nid = nid; fileInfo.size = size; fileInfo.qid = qid; if (path) { resultText += `📂 云盘路径: ${path}\n`; } if (size) { resultText += `📦 文件大小: ${formatBytes(size)}\n`; } if (path && nid && qid) { const dirPath = path.substring(0, path.lastIndexOf('/') + 1); const href = `https://www.yunpan.com/file/index#/fileManage/my/file/${encodeURIComponent(dirPath)}?focus_nid=${nid}&owner_qid=${qid}`; resultText += `🔗 [点击查看云盘文件](${href})\n`; fileInfo.href = href; } } return { content: [ { type: "text", name: "x-save-result-json", text: JSON.stringify({ status: 'success', file: fileInfo }) }, { type: "text", name: "x-save-result-display", text: resultText } ] }; } catch (pollError: any) { const isTimeout = pollError.message.includes('轮询超时'); const status = isTimeout ? 'timeout' : 'failed'; const resultText = isTimeout ? `⏳ 文件保存轮询超时\n\n- 任务ID: ${taskId}\n- 请稍后检查云盘或使用任务ID查询状态。` : `❌ 文件保存失败\n\n- 任务ID: ${taskId}\n- 错误信息: ${pollError.message}`; return { content: [ { type: "text", name: "x-save-result-json", text: JSON.stringify({ status: status, taskId: taskId, error: pollError.message }) }, { type: "text", name: "x-save-result-display", text: resultText } ] }; } } else { throw new Error(saveResult?.errmsg || "API请求失败"); } } catch (error: any) { return { content: [ { type: "text", text: `保存文件时发生错误: ${error.message}`, } ], }; } }, ); }
  • Includes 'file-save' tool registration by calling registerFileSaveTool within the registerAllTools function.
    registerFileSaveTool(server);
  • Top-level call to registerAllTools on the McpServer instance in ECSMcpServer constructor, which chains to file-save registration.
    registerAllTools(this.server);

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