1+ import * as fs from "fs/promises" ;
2+ import * as os from "os" ;
3+ import * as path from "path" ;
14import { z } from "zod" ;
25import { getCloudBaseManager } from '../cloudbase-manager.js' ;
36import { ExtendedMcpServer } from '../server.js' ;
47
8+ const MAX_INLINE_TEXT_BYTES = 256 * 1024 ;
9+ const STORAGE_READ_TEMP_PREFIX = 'cloudbase-mcp-storage-read-' ;
10+
511// Input schema for queryStorage tool
612const queryStorageInputSchema = {
7- action : z . enum ( [ 'list' , 'info' , 'url' ] ) . describe ( '查询操作类型:list=列出目录下的所有文件,info=获取指定文件的详细信息,url=获取文件的临时下载链接' ) ,
13+ action : z . enum ( [ 'list' , 'info' , 'url' , 'read' ] ) . describe ( '查询操作类型:list=列出目录下的所有文件,info=获取指定文件的详细信息,url=获取文件的临时下载链接,read=读取文本文件内容 ' ) ,
814 cloudPath : z . string ( ) . describe ( '云端文件路径,例如 files/data.txt 或 files/(目录)' ) ,
915 maxAge : z . number ( ) . min ( 1 ) . max ( 86400 ) . optional ( ) . default ( 3600 ) . describe ( '临时链接有效期,单位为秒,取值范围:1-86400,默认值:3600(1小时)' )
1016} ;
@@ -19,11 +25,30 @@ const manageStorageInputSchema = {
1925} ;
2026
2127type QueryStorageInput = {
22- action : 'list' | 'info' | 'url' ;
28+ action : 'list' | 'info' | 'url' | 'read' ;
2329 cloudPath : string ;
2430 maxAge ?: number ;
2531} ;
2632
33+ function getStorageTempFileName ( cloudPath : string ) {
34+ const baseName = path . posix . basename ( cloudPath ) ;
35+ return baseName || 'storage-file' ;
36+ }
37+
38+ function decodeInlineTextContent ( buffer : Buffer ) {
39+ const inlineBuffer = buffer . subarray ( 0 , MAX_INLINE_TEXT_BYTES ) ;
40+ if ( inlineBuffer . includes ( 0 ) ) {
41+ throw new Error ( 'queryStorage action=read 仅支持读取文本文件内容;二进制文件请改用 action=url 获取下载链接,或使用 manageStorage(action="download") 下载到本地。' ) ;
42+ }
43+
44+ return {
45+ content : inlineBuffer . toString ( 'utf8' ) ,
46+ truncated : buffer . length > MAX_INLINE_TEXT_BYTES ,
47+ sizeBytes : buffer . length ,
48+ encoding : 'utf8' as const ,
49+ } ;
50+ }
51+
2752type ManageStorageInput = {
2853 action : 'upload' | 'download' | 'delete' ;
2954 localPath : string ;
@@ -132,6 +157,45 @@ export function registerStorageTools(server: ExtendedMcpServer) {
132157 } ;
133158 }
134159
160+ case 'read' : {
161+ const tempDir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , STORAGE_READ_TEMP_PREFIX ) ) ;
162+ const localPath = path . join ( tempDir , getStorageTempFileName ( input . cloudPath ) ) ;
163+
164+ try {
165+ await storageService . downloadFile ( {
166+ cloudPath : input . cloudPath ,
167+ localPath
168+ } ) ;
169+
170+ const buffer = await fs . readFile ( localPath ) ;
171+ const decoded = decodeInlineTextContent ( buffer ) ;
172+
173+ return {
174+ content : [
175+ {
176+ type : "text" ,
177+ text : JSON . stringify ( {
178+ success : true ,
179+ data : {
180+ action : 'read' ,
181+ cloudPath : input . cloudPath ,
182+ content : decoded . content ,
183+ encoding : decoded . encoding ,
184+ sizeBytes : decoded . sizeBytes ,
185+ truncated : decoded . truncated
186+ } ,
187+ message : decoded . truncated
188+ ? `Successfully read text content for '${ input . cloudPath } ' (truncated to ${ MAX_INLINE_TEXT_BYTES } bytes)`
189+ : `Successfully read text content for '${ input . cloudPath } '`
190+ } , null , 2 )
191+ }
192+ ]
193+ } ;
194+ } finally {
195+ await fs . rm ( tempDir , { recursive : true , force : true } ) ;
196+ }
197+ }
198+
135199 default :
136200 throw new Error ( `Unsupported action: ${ input . action } ` ) ;
137201 }
0 commit comments