1
1
import { z } from "zod" ;
2
- import { getCloudBaseManager } from '../cloudbase-manager.js'
2
+ import { getCloudBaseManager } from '../cloudbase-manager.js' ;
3
3
import { ExtendedMcpServer } from '../server.js' ;
4
4
5
+ // Input schema for queryStorage tool
6
+ const queryStorageInputSchema = {
7
+ action : z . enum ( [ 'list' , 'info' , 'url' ] ) . describe ( '查询操作类型:list=列出目录下的所有文件,info=获取指定文件的详细信息,url=获取文件的临时下载链接' ) ,
8
+ cloudPath : z . string ( ) . describe ( '云端文件路径,例如 files/data.txt 或 files/(目录)' ) ,
9
+ maxAge : z . number ( ) . min ( 1 ) . max ( 86400 ) . optional ( ) . default ( 3600 ) . describe ( '临时链接有效期,单位为秒,取值范围:1-86400,默认值:3600(1小时)' )
10
+ } ;
11
+
12
+ // Input schema for manageStorage tool
13
+ const manageStorageInputSchema = {
14
+ action : z . enum ( [ 'upload' , 'download' , 'delete' ] ) . describe ( '管理操作类型:upload=上传文件或目录,download=下载文件或目录,delete=删除文件或目录' ) ,
15
+ localPath : z . string ( ) . describe ( '本地文件路径,建议传入绝对路径,例如 /tmp/files/data.txt' ) ,
16
+ cloudPath : z . string ( ) . describe ( '云端文件路径,例如 files/data.txt' ) ,
17
+ force : z . boolean ( ) . optional ( ) . default ( false ) . describe ( '强制操作开关,删除操作时建议设置为true以确认删除,默认false' ) ,
18
+ isDirectory : z . boolean ( ) . optional ( ) . default ( false ) . describe ( '是否为目录操作,true=目录操作,false=文件操作,默认false' )
19
+ } ;
20
+
21
+ type QueryStorageInput = {
22
+ action : 'list' | 'info' | 'url' ;
23
+ cloudPath : string ;
24
+ maxAge ?: number ;
25
+ } ;
26
+
27
+ type ManageStorageInput = {
28
+ action : 'upload' | 'download' | 'delete' ;
29
+ localPath : string ;
30
+ cloudPath : string ;
31
+ force ?: boolean ;
32
+ isDirectory ?: boolean ;
33
+ } ;
5
34
6
35
export function registerStorageTools ( server : ExtendedMcpServer ) {
7
36
// 获取 cloudBaseOptions,如果没有则为 undefined
@@ -10,54 +39,291 @@ export function registerStorageTools(server: ExtendedMcpServer) {
10
39
// 创建闭包函数来获取 CloudBase Manager
11
40
const getManager = ( ) => getCloudBaseManager ( { cloudBaseOptions } ) ;
12
41
13
- // uploadFile - 上传文件到云存储 (cloud-incompatible)
42
+ // Tool 1: queryStorage - 查询存储信息(只读操作)
43
+ server . registerTool (
44
+ "queryStorage" ,
45
+ {
46
+ title : "查询存储信息" ,
47
+ description : "查询云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。" ,
48
+ inputSchema : queryStorageInputSchema ,
49
+ annotations : {
50
+ readOnlyHint : true ,
51
+ openWorldHint : true ,
52
+ category : "storage"
53
+ }
54
+ } ,
55
+ async ( args : QueryStorageInput ) => {
56
+ try {
57
+ const input = args ;
58
+ const manager = await getManager ( ) ;
59
+
60
+ if ( ! manager ) {
61
+ throw new Error ( "Failed to initialize CloudBase manager. Please check your credentials and environment configuration." ) ;
62
+ }
63
+
64
+ const storageService = manager . storage ;
65
+
66
+ switch ( input . action ) {
67
+ case 'list' : {
68
+ const result = await storageService . listDirectoryFiles ( input . cloudPath ) ;
69
+
70
+ return {
71
+ content : [
72
+ {
73
+ type : "text" ,
74
+ text : JSON . stringify ( {
75
+ success : true ,
76
+ data : {
77
+ action : 'list' ,
78
+ cloudPath : input . cloudPath ,
79
+ files : result || [ ] ,
80
+ totalCount : result ?. length || 0
81
+ } ,
82
+ message : `Successfully listed ${ result ?. length || 0 } files in directory '${ input . cloudPath } '`
83
+ } , null , 2 )
84
+ }
85
+ ]
86
+ } ;
87
+ }
88
+
89
+ case 'info' : {
90
+ const result = await storageService . getFileInfo ( input . cloudPath ) ;
91
+
92
+ return {
93
+ content : [
94
+ {
95
+ type : "text" ,
96
+ text : JSON . stringify ( {
97
+ success : true ,
98
+ data : {
99
+ action : 'info' ,
100
+ cloudPath : input . cloudPath ,
101
+ fileInfo : result
102
+ } ,
103
+ message : `Successfully retrieved file info for '${ input . cloudPath } '`
104
+ } , null , 2 )
105
+ }
106
+ ]
107
+ } ;
108
+ }
109
+
110
+ case 'url' : {
111
+ const result = await storageService . getTemporaryUrl ( [ {
112
+ cloudPath : input . cloudPath ,
113
+ maxAge : input . maxAge || 3600
114
+ } ] ) ;
115
+
116
+ return {
117
+ content : [
118
+ {
119
+ type : "text" ,
120
+ text : JSON . stringify ( {
121
+ success : true ,
122
+ data : {
123
+ action : 'url' ,
124
+ cloudPath : input . cloudPath ,
125
+ temporaryUrl : result [ 0 ] ?. url || "" ,
126
+ expireTime : `${ input . maxAge || 3600 } 秒` ,
127
+ fileId : result [ 0 ] ?. fileId || ""
128
+ } ,
129
+ message : `Successfully generated temporary URL for '${ input . cloudPath } '`
130
+ } , null , 2 )
131
+ }
132
+ ]
133
+ } ;
134
+ }
135
+
136
+ default :
137
+ throw new Error ( `Unsupported action: ${ input . action } ` ) ;
138
+ }
139
+
140
+ } catch ( error : any ) {
141
+ return {
142
+ content : [
143
+ {
144
+ type : "text" ,
145
+ text : JSON . stringify ( {
146
+ success : false ,
147
+ error : error . message || 'Unknown error occurred' ,
148
+ message : `Failed to query storage information. Please check your permissions and parameters.`
149
+ } , null , 2 )
150
+ }
151
+ ]
152
+ } ;
153
+ }
154
+ }
155
+ ) ;
156
+
157
+ // Tool 2: manageStorage - 管理存储文件(写操作)
14
158
server . registerTool (
15
- "uploadFile " ,
159
+ "manageStorage " ,
16
160
{
17
- title : "上传文件到云存储" ,
18
- description : "上传文件到云存储(区别于静态网站托管,云存储更适合存储业务数据文件)" ,
19
- inputSchema : {
20
- localPath : z . string ( ) . describe ( "本地文件路径,建议传入绝对路径,例如 /tmp/files/data.txt" ) ,
21
- cloudPath : z . string ( ) . describe ( "云端文件路径,例如 files/data.txt" )
22
- } ,
161
+ title : "管理存储文件" ,
162
+ description : "管理云存储文件,支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。" ,
163
+ inputSchema : manageStorageInputSchema ,
23
164
annotations : {
24
165
readOnlyHint : false ,
25
- destructiveHint : false ,
166
+ destructiveHint : true ,
26
167
idempotentHint : false ,
27
168
openWorldHint : true ,
28
169
category : "storage"
29
170
}
30
171
} ,
31
- async ( { localPath, cloudPath } : { localPath : string ; cloudPath : string } ) => {
32
- const cloudbase = await getManager ( )
33
- // 上传文件
34
- await cloudbase . storage . uploadFile ( {
35
- localPath,
36
- cloudPath,
37
- onProgress : ( progressData : any ) => {
38
- console . log ( "Upload progress:" , progressData ) ;
172
+ async ( args : ManageStorageInput ) => {
173
+ try {
174
+ const input = args ;
175
+ const manager = await getManager ( ) ;
176
+
177
+ if ( ! manager ) {
178
+ throw new Error ( "Failed to initialize CloudBase manager. Please check your credentials and environment configuration." ) ;
39
179
}
40
- } ) ;
41
-
42
- // 获取文件临时下载地址
43
- const fileUrls = await cloudbase . storage . getTemporaryUrl ( [ {
44
- cloudPath : cloudPath ,
45
- maxAge : 3600 // 临时链接有效期1小时
46
- } ] ) ;
47
-
48
- return {
49
- content : [
50
- {
51
- type : "text" ,
52
- text : JSON . stringify ( {
53
- message : "文件上传成功" ,
54
- cloudPath : cloudPath ,
55
- temporaryUrl : fileUrls [ 0 ] ?. url || "" ,
56
- expireTime : "1小时"
57
- } , null , 2 )
180
+
181
+ const storageService = manager . storage ;
182
+
183
+ switch ( input . action ) {
184
+ case 'upload' : {
185
+ if ( input . isDirectory ) {
186
+ // 上传目录
187
+ await storageService . uploadDirectory ( {
188
+ localPath : input . localPath ,
189
+ cloudPath : input . cloudPath ,
190
+ onProgress : ( progressData : any ) => {
191
+ console . log ( "Upload directory progress:" , progressData ) ;
192
+ }
193
+ } ) ;
194
+ } else {
195
+ // 上传文件
196
+ await storageService . uploadFile ( {
197
+ localPath : input . localPath ,
198
+ cloudPath : input . cloudPath ,
199
+ onProgress : ( progressData : any ) => {
200
+ console . log ( "Upload file progress:" , progressData ) ;
201
+ }
202
+ } ) ;
203
+ }
204
+
205
+ // 获取文件临时下载地址
206
+ const fileUrls = await storageService . getTemporaryUrl ( [ {
207
+ cloudPath : input . cloudPath ,
208
+ maxAge : 3600 // 临时链接有效期1小时
209
+ } ] ) ;
210
+
211
+ return {
212
+ content : [
213
+ {
214
+ type : "text" ,
215
+ text : JSON . stringify ( {
216
+ success : true ,
217
+ data : {
218
+ action : 'upload' ,
219
+ localPath : input . localPath ,
220
+ cloudPath : input . cloudPath ,
221
+ isDirectory : input . isDirectory ,
222
+ temporaryUrl : fileUrls [ 0 ] ?. url || "" ,
223
+ expireTime : "1小时"
224
+ } ,
225
+ message : `Successfully uploaded ${ input . isDirectory ? 'directory' : 'file' } from '${ input . localPath } ' to '${ input . cloudPath } '`
226
+ } , null , 2 )
227
+ }
228
+ ]
229
+ } ;
58
230
}
59
- ]
60
- } ;
231
+
232
+ case 'download' : {
233
+ if ( input . isDirectory ) {
234
+ // 下载目录
235
+ await storageService . downloadDirectory ( {
236
+ cloudPath : input . cloudPath ,
237
+ localPath : input . localPath
238
+ } ) ;
239
+ } else {
240
+ // 下载文件
241
+ await storageService . downloadFile ( {
242
+ cloudPath : input . cloudPath ,
243
+ localPath : input . localPath
244
+ } ) ;
245
+ }
246
+
247
+ return {
248
+ content : [
249
+ {
250
+ type : "text" ,
251
+ text : JSON . stringify ( {
252
+ success : true ,
253
+ data : {
254
+ action : 'download' ,
255
+ cloudPath : input . cloudPath ,
256
+ localPath : input . localPath ,
257
+ isDirectory : input . isDirectory
258
+ } ,
259
+ message : `Successfully downloaded ${ input . isDirectory ? 'directory' : 'file' } from '${ input . cloudPath } ' to '${ input . localPath } '`
260
+ } , null , 2 )
261
+ }
262
+ ]
263
+ } ;
264
+ }
265
+
266
+ case 'delete' : {
267
+ if ( ! input . force ) {
268
+ return {
269
+ content : [
270
+ {
271
+ type : "text" ,
272
+ text : JSON . stringify ( {
273
+ success : false ,
274
+ error : "Delete operation requires confirmation" ,
275
+ message : "Please set force: true to confirm deletion. This action cannot be undone."
276
+ } , null , 2 )
277
+ }
278
+ ]
279
+ } ;
280
+ }
281
+
282
+ if ( input . isDirectory ) {
283
+ // 删除目录
284
+ await storageService . deleteDirectory ( input . cloudPath ) ;
285
+ } else {
286
+ // 删除文件
287
+ await storageService . deleteFile ( [ input . cloudPath ] ) ;
288
+ }
289
+
290
+ return {
291
+ content : [
292
+ {
293
+ type : "text" ,
294
+ text : JSON . stringify ( {
295
+ success : true ,
296
+ data : {
297
+ action : 'delete' ,
298
+ cloudPath : input . cloudPath ,
299
+ isDirectory : input . isDirectory ,
300
+ deleted : true
301
+ } ,
302
+ message : `Successfully deleted ${ input . isDirectory ? 'directory' : 'file' } '${ input . cloudPath } '`
303
+ } , null , 2 )
304
+ }
305
+ ]
306
+ } ;
307
+ }
308
+
309
+ default :
310
+ throw new Error ( `Unsupported action: ${ input . action } ` ) ;
311
+ }
312
+
313
+ } catch ( error : any ) {
314
+ return {
315
+ content : [
316
+ {
317
+ type : "text" ,
318
+ text : JSON . stringify ( {
319
+ success : false ,
320
+ error : error . message || 'Unknown error occurred' ,
321
+ message : `Failed to manage storage. Please check your permissions and parameters.`
322
+ } , null , 2 )
323
+ }
324
+ ]
325
+ } ;
326
+ }
61
327
}
62
328
) ;
63
329
}
0 commit comments