@webapper/cloudsee-drive-mcp 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/dist/index.js +184 -104
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,15 +74,20 @@ or a local `.env` for development — see [`.env.example`](.env.example)).
|
|
|
74
74
|
| `CLOUDSEE_API_KEY_ID` | ✅ | — | Your CloudSee Drive API key id. |
|
|
75
75
|
| `CLOUDSEE_API_KEY_SECRET` | ✅ | — | The matching API key secret. **Never commit this.** |
|
|
76
76
|
| `CLOUDSEE_API_BASE_URL` | — | `https://drive-api.cloudsee.cloud` | API base URL. UAT: `https://drive-api-uat.cloudsee.cloud`. |
|
|
77
|
+
| `CLOUDSEE_DEFAULT_BUCKET` | — | — | Default drive (S3 bucket) for tool calls that omit `bucketName`. Find the name in the CloudSee dashboard. |
|
|
77
78
|
| `CLOUDSEE_LOG_LEVEL` | — | `info` | `error` \| `warn` \| `info` \| `debug` (logs go to stderr only). |
|
|
78
79
|
| `CLOUDSEE_TIMEOUT_MS` | — | `30000` | Per-request timeout in milliseconds. |
|
|
79
80
|
|
|
80
81
|
## Tools
|
|
81
82
|
|
|
83
|
+
Most tools operate on one **drive** (an S3 bucket): pass `bucketName`, or set
|
|
84
|
+
`CLOUDSEE_DEFAULT_BUCKET` once and omit it. `recent_files` and `list_buckets` don't need a drive.
|
|
85
|
+
|
|
82
86
|
| Tool | Description | Access |
|
|
83
87
|
| --- | --- | --- |
|
|
84
88
|
| `list_buckets` | List accessible buckets | read |
|
|
85
|
-
| `
|
|
89
|
+
| `list_files` | List all files in a drive (recursive) | read |
|
|
90
|
+
| `browse_folder` | List a folder's contents (indexed view) | read |
|
|
86
91
|
| `search_files` | Find files/folders by name keyword | read |
|
|
87
92
|
| `recent_files` | List recently used files | read |
|
|
88
93
|
| `get_file_metadata` | Get a file's metadata | read |
|
|
@@ -118,12 +123,18 @@ authorizes every operation server-side; confirmation is not the security boundar
|
|
|
118
123
|
|
|
119
124
|
This wraps CloudSee Drive's public API (the `drive-bridge` `/v1/*` gateway).
|
|
120
125
|
|
|
121
|
-
- **
|
|
126
|
+
- **Almost every tool needs a drive** (`bucketName`, or `CLOUDSEE_DEFAULT_BUCKET`). Without one,
|
|
127
|
+
drive-scoped tools return a clear "specify a drive" message.
|
|
128
|
+
- **Read & download tools are functional today** once a drive is given — `list_files`,
|
|
129
|
+
`get_file_metadata`, `get_file_tags`, `download_file`, `share_link` all return real data.
|
|
130
|
+
- **`list_buckets` currently returns an empty list** (drive enumeration isn't wired for public
|
|
131
|
+
API keys yet), so supply the drive name from the CloudSee dashboard for now.
|
|
132
|
+
- **`browse_folder`/`search_files` use the search-indexed view** and can return empty for
|
|
133
|
+
un-indexed content — use **`list_files`** for a complete, reliable listing of a drive.
|
|
122
134
|
- **Write/delete/share tools are present but their end-to-end authorization is sequenced
|
|
123
135
|
behind the gateway's RBAC/scope wiring (CSD-572).** Until that lands, the gateway may deny
|
|
124
136
|
these operations — the tools surface that cleanly; it is expected, not a bug here.
|
|
125
137
|
- `upload_file` supports single-file uploads up to 8 MiB; multipart (larger files) is planned.
|
|
126
|
-
- `search_files` matches a single folder level (not a whole-tree recursive search).
|
|
127
138
|
|
|
128
139
|
## Development
|
|
129
140
|
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ var envSchema = z.object({
|
|
|
33
33
|
CLOUDSEE_API_KEY_ID: z.string().trim().min(1, "is required (your CloudSee Drive API key id)"),
|
|
34
34
|
CLOUDSEE_API_KEY_SECRET: z.string().trim().min(1, "is required (the matching API key secret)"),
|
|
35
35
|
CLOUDSEE_API_BASE_URL: z.string().url("must be a valid URL").optional(),
|
|
36
|
+
CLOUDSEE_DEFAULT_BUCKET: z.string().trim().min(1).optional(),
|
|
36
37
|
CLOUDSEE_TIMEOUT_MS: z.coerce.number().int().positive().optional(),
|
|
37
38
|
CLOUDSEE_LOG_LEVEL: z.enum(["error", "warn", "info", "debug"]).optional()
|
|
38
39
|
});
|
|
@@ -53,6 +54,7 @@ Set the required environment variables (see .env.example or the README).`,
|
|
|
53
54
|
apiKeyId: e.CLOUDSEE_API_KEY_ID,
|
|
54
55
|
apiKeySecret: e.CLOUDSEE_API_KEY_SECRET,
|
|
55
56
|
baseUrl: (e.CLOUDSEE_API_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
|
|
57
|
+
defaultBucket: e.CLOUDSEE_DEFAULT_BUCKET,
|
|
56
58
|
timeoutMs: e.CLOUDSEE_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS,
|
|
57
59
|
logLevel: e.CLOUDSEE_LOG_LEVEL ?? "info"
|
|
58
60
|
};
|
|
@@ -273,7 +275,7 @@ function sleep(ms) {
|
|
|
273
275
|
}
|
|
274
276
|
|
|
275
277
|
// src/tools/read.ts
|
|
276
|
-
import { z as
|
|
278
|
+
import { z as z3 } from "zod";
|
|
277
279
|
|
|
278
280
|
// src/tools/format.ts
|
|
279
281
|
var DEFAULT_MAX_ITEMS = 100;
|
|
@@ -318,6 +320,20 @@ function withCursor(body, nextCursor) {
|
|
|
318
320
|
}
|
|
319
321
|
|
|
320
322
|
// src/tools/types.ts
|
|
323
|
+
import { z as z2 } from "zod";
|
|
324
|
+
var bucketField = z2.string().min(1).optional().describe(
|
|
325
|
+
"The CloudSee drive (S3 bucket) name to operate in, e.g. 'max-2778abc0' \u2014 find it in the CloudSee dashboard. Required unless CLOUDSEE_DEFAULT_BUCKET is configured on the server."
|
|
326
|
+
);
|
|
327
|
+
function resolveBucket(bucket, defaultBucket) {
|
|
328
|
+
const resolved = (bucket ?? defaultBucket ?? "").trim();
|
|
329
|
+
if (!resolved) {
|
|
330
|
+
throw new CloudSeeError(
|
|
331
|
+
"No CloudSee drive specified. Pass `bucketName` (your drive name, shown in the CloudSee dashboard), or set CLOUDSEE_DEFAULT_BUCKET in the server config.",
|
|
332
|
+
{ code: "no_bucket" }
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
return resolved;
|
|
336
|
+
}
|
|
321
337
|
function textResult(text) {
|
|
322
338
|
return { content: [{ type: "text", text }] };
|
|
323
339
|
}
|
|
@@ -335,57 +351,85 @@ var listBuckets = {
|
|
|
335
351
|
return textResult(summarize(data));
|
|
336
352
|
}
|
|
337
353
|
};
|
|
338
|
-
var browseSchema =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
354
|
+
var browseSchema = z3.object({
|
|
355
|
+
bucketName: bucketField,
|
|
356
|
+
path: z3.string().optional().describe("Folder path / prefix within the drive to list. Empty or omitted = the drive root."),
|
|
357
|
+
sortOption: z3.string().optional().describe("Sort key, e.g. 'name_asc', 'name_desc', 'date_desc'."),
|
|
358
|
+
pageSize: z3.number().int().min(1).max(200).optional().describe("Max items per page (1-200, default 50)."),
|
|
359
|
+
cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
|
|
343
360
|
});
|
|
344
361
|
var browseFolder = {
|
|
345
362
|
name: "browse_folder",
|
|
346
363
|
title: "Browse folder",
|
|
347
|
-
description: "List the files and sub-folders
|
|
364
|
+
description: "List the files and sub-folders inside a folder of a drive (the indexed view), with sorting and pagination. Requires the drive (bucketName). To filter by keyword use 'search_files'; for a complete, recursive file listing straight from storage use 'list_files'.",
|
|
348
365
|
endpoint: { method: "POST", path: "/storage/list", scopes: ["drive:read"] },
|
|
349
366
|
inputSchema: browseSchema.shape,
|
|
350
367
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
351
|
-
handler: async (args, { client }) => {
|
|
368
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
352
369
|
const a = browseSchema.parse(args);
|
|
370
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
353
371
|
const { data, nextCursor } = await client.postPaged(
|
|
354
372
|
"/storage/list",
|
|
355
|
-
{ dirPath: a.path ?? "", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },
|
|
373
|
+
{ bucketName, dirPath: a.path ?? "", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },
|
|
356
374
|
"nextPage",
|
|
357
375
|
a.cursor
|
|
358
376
|
);
|
|
359
377
|
return textResult(withCursor(summarize(data), nextCursor));
|
|
360
378
|
}
|
|
361
379
|
};
|
|
362
|
-
var searchSchema =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
380
|
+
var searchSchema = z3.object({
|
|
381
|
+
bucketName: bucketField,
|
|
382
|
+
query: z3.string().min(1).describe("Keyword to match against file and folder names."),
|
|
383
|
+
path: z3.string().optional().describe("Folder within the drive to search under. Empty = drive root."),
|
|
384
|
+
pageSize: z3.number().int().min(1).max(200).optional().describe("Max items per page (1-200, default 50)."),
|
|
385
|
+
cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
|
|
367
386
|
});
|
|
368
387
|
var searchFiles = {
|
|
369
388
|
name: "search_files",
|
|
370
389
|
title: "Search files",
|
|
371
|
-
description: "Search for files and folders by name keyword within a
|
|
390
|
+
description: "Search for files and folders by name keyword within a drive (the indexed view). Requires the drive (bucketName). Returns matches with pagination.",
|
|
372
391
|
endpoint: { method: "POST", path: "/storage/list", scopes: ["drive:read"] },
|
|
373
392
|
inputSchema: searchSchema.shape,
|
|
374
393
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
375
|
-
handler: async (args, { client }) => {
|
|
394
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
376
395
|
const a = searchSchema.parse(args);
|
|
396
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
377
397
|
const { data, nextCursor } = await client.postPaged(
|
|
378
398
|
"/storage/list",
|
|
379
|
-
{ dirPath: a.path ?? "", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },
|
|
399
|
+
{ bucketName, dirPath: a.path ?? "", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },
|
|
380
400
|
"nextPage",
|
|
381
401
|
a.cursor
|
|
382
402
|
);
|
|
383
403
|
return textResult(withCursor(summarize(data), nextCursor));
|
|
384
404
|
}
|
|
385
405
|
};
|
|
386
|
-
var
|
|
387
|
-
|
|
388
|
-
|
|
406
|
+
var listFilesSchema = z3.object({
|
|
407
|
+
bucketName: bucketField,
|
|
408
|
+
deep: z3.boolean().optional().describe("Recurse into sub-folders (default true)."),
|
|
409
|
+
cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
|
|
410
|
+
});
|
|
411
|
+
var listFiles = {
|
|
412
|
+
name: "list_files",
|
|
413
|
+
title: "List files in a drive",
|
|
414
|
+
description: "List the files in a drive straight from storage, recursively by default \u2014 the most reliable way to see what a drive actually contains. Requires the drive (bucketName). Returns names, sizes, storage classes and keys, with pagination.",
|
|
415
|
+
endpoint: { method: "POST", path: "/storage/bucket/files", scopes: ["drive:read"] },
|
|
416
|
+
inputSchema: listFilesSchema.shape,
|
|
417
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
418
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
419
|
+
const a = listFilesSchema.parse(args);
|
|
420
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
421
|
+
const { data, nextCursor } = await client.postPaged(
|
|
422
|
+
"/storage/bucket/files",
|
|
423
|
+
{ bucketName, deepQuery: a.deep ?? true },
|
|
424
|
+
"marker",
|
|
425
|
+
a.cursor
|
|
426
|
+
);
|
|
427
|
+
return textResult(withCursor(summarize(data), nextCursor));
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var recentSchema = z3.object({
|
|
431
|
+
limit: z3.number().int().min(1).max(200).optional().describe("Max items (1-200, default 50)."),
|
|
432
|
+
cursor: z3.string().optional().describe("Opaque pagination cursor returned by a previous call.")
|
|
389
433
|
});
|
|
390
434
|
var recentFiles = {
|
|
391
435
|
name: "recent_files",
|
|
@@ -402,34 +446,39 @@ var recentFiles = {
|
|
|
402
446
|
return textResult(withCursor(summarize(data), hasMore ? nextCursor : void 0));
|
|
403
447
|
}
|
|
404
448
|
};
|
|
405
|
-
var metaSchema =
|
|
449
|
+
var metaSchema = z3.object({
|
|
450
|
+
bucketName: bucketField,
|
|
451
|
+
objectKey: z3.string().min(1).describe("Full object key (path) of the file within the drive.")
|
|
452
|
+
});
|
|
406
453
|
var getFileMetadata = {
|
|
407
454
|
name: "get_file_metadata",
|
|
408
455
|
title: "Get file metadata",
|
|
409
|
-
description: "Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key.",
|
|
456
|
+
description: "Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key. Requires the drive (bucketName).",
|
|
410
457
|
endpoint: { method: "POST", path: "/storage/object/detail", scopes: ["drive:read"] },
|
|
411
458
|
inputSchema: metaSchema.shape,
|
|
412
459
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
413
|
-
handler: async (args, { client }) => {
|
|
460
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
414
461
|
const a = metaSchema.parse(args);
|
|
415
|
-
const
|
|
462
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
463
|
+
const data = await client.post("/storage/object/detail", { bucketName, objectKey: a.objectKey });
|
|
416
464
|
return textResult(summarize(data));
|
|
417
465
|
}
|
|
418
466
|
};
|
|
419
|
-
var tagsSchema =
|
|
420
|
-
|
|
421
|
-
|
|
467
|
+
var tagsSchema = z3.object({
|
|
468
|
+
bucketName: bucketField,
|
|
469
|
+
objectKey: z3.string().min(1).describe("Full object key (path) of the file within the drive.")
|
|
422
470
|
});
|
|
423
471
|
var getFileTags = {
|
|
424
472
|
name: "get_file_tags",
|
|
425
473
|
title: "Get file tags",
|
|
426
|
-
description: "Get the S3 object tags (key/value pairs) attached to a file, by object key.",
|
|
474
|
+
description: "Get the S3 object tags (key/value pairs) attached to a file, by object key. Requires the drive (bucketName).",
|
|
427
475
|
endpoint: { method: "POST", path: "/storage/object/tagging", scopes: ["drive:read"] },
|
|
428
476
|
inputSchema: tagsSchema.shape,
|
|
429
477
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
430
|
-
handler: async (args, { client }) => {
|
|
478
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
431
479
|
const a = tagsSchema.parse(args);
|
|
432
|
-
const
|
|
480
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
481
|
+
const data = await client.post("/storage/object/tagging", { bucketName, objectKey: a.objectKey });
|
|
433
482
|
return textResult(summarize(data));
|
|
434
483
|
}
|
|
435
484
|
};
|
|
@@ -437,28 +486,32 @@ var readTools = [
|
|
|
437
486
|
listBuckets,
|
|
438
487
|
browseFolder,
|
|
439
488
|
searchFiles,
|
|
489
|
+
listFiles,
|
|
440
490
|
recentFiles,
|
|
441
491
|
getFileMetadata,
|
|
442
492
|
getFileTags
|
|
443
493
|
];
|
|
444
494
|
|
|
445
495
|
// src/tools/download.ts
|
|
446
|
-
import { z as
|
|
447
|
-
var downloadSchema =
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
496
|
+
import { z as z4 } from "zod";
|
|
497
|
+
var downloadSchema = z4.object({
|
|
498
|
+
bucketName: bucketField,
|
|
499
|
+
filePath: z4.string().min(1).describe("Object key (path) of the file to download, within the drive."),
|
|
500
|
+
forceDownload: z4.boolean().optional().describe("If true, the link forces an attachment download instead of inline view."),
|
|
501
|
+
storageId: z4.string().optional().describe("Optional storage/index id.")
|
|
451
502
|
});
|
|
452
503
|
var downloadFile = {
|
|
453
504
|
name: "download_file",
|
|
454
505
|
title: "Download file",
|
|
455
|
-
description: "Get a short-lived pre-signed download URL for a file. The URL is time-limited and grants read access to that one object \u2014 share it with care. Returns the URL; it does not load file bytes into the conversation.",
|
|
506
|
+
description: "Get a short-lived pre-signed download URL for a file. Requires the drive (bucketName). The URL is time-limited and grants read access to that one object \u2014 share it with care. Returns the URL; it does not load file bytes into the conversation.",
|
|
456
507
|
endpoint: { method: "POST", path: "/storage/object/download-url", scopes: ["drive:read", "drive:download"] },
|
|
457
508
|
inputSchema: downloadSchema.shape,
|
|
458
509
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
459
|
-
handler: async (args, { client }) => {
|
|
510
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
460
511
|
const a = downloadSchema.parse(args);
|
|
512
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
461
513
|
const data = await client.post("/storage/object/download-url", {
|
|
514
|
+
bucketName,
|
|
462
515
|
filePath: a.filePath,
|
|
463
516
|
download: a.forceDownload ?? false,
|
|
464
517
|
storageId: a.storageId
|
|
@@ -466,20 +519,23 @@ var downloadFile = {
|
|
|
466
519
|
return textResult(summarize(data));
|
|
467
520
|
}
|
|
468
521
|
};
|
|
469
|
-
var shareSchema =
|
|
470
|
-
|
|
471
|
-
|
|
522
|
+
var shareSchema = z4.object({
|
|
523
|
+
bucketName: bucketField,
|
|
524
|
+
filePath: z4.string().min(1).describe("Object key (path) of the file to share, within the drive."),
|
|
525
|
+
storageId: z4.string().optional().describe("Optional storage/index id.")
|
|
472
526
|
});
|
|
473
527
|
var shareLink = {
|
|
474
528
|
name: "share_link",
|
|
475
529
|
title: "Create share link",
|
|
476
|
-
description: "Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.",
|
|
530
|
+
description: "Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). Requires the drive (bucketName). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.",
|
|
477
531
|
endpoint: { method: "POST", path: "/storage/object/download-url", scopes: ["drive:read", "drive:download"] },
|
|
478
532
|
inputSchema: shareSchema.shape,
|
|
479
533
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
480
|
-
handler: async (args, { client }) => {
|
|
534
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
481
535
|
const a = shareSchema.parse(args);
|
|
536
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
482
537
|
const data = await client.post("/storage/object/download-url", {
|
|
538
|
+
bucketName,
|
|
483
539
|
filePath: a.filePath,
|
|
484
540
|
shareableLink: true,
|
|
485
541
|
storageId: a.storageId
|
|
@@ -492,12 +548,12 @@ var downloadTools = [downloadFile, shareLink];
|
|
|
492
548
|
// src/tools/write.ts
|
|
493
549
|
import { readFile, stat } from "fs/promises";
|
|
494
550
|
import { basename } from "path";
|
|
495
|
-
import { z as
|
|
551
|
+
import { z as z6 } from "zod";
|
|
496
552
|
|
|
497
553
|
// src/confirm.ts
|
|
498
|
-
import { z as
|
|
554
|
+
import { z as z5 } from "zod";
|
|
499
555
|
var confirmShape = {
|
|
500
|
-
confirm:
|
|
556
|
+
confirm: z5.boolean().optional().describe(
|
|
501
557
|
"Must be true to actually perform this mutating/irreversible action. If omitted or false, the tool returns a preview and makes no changes."
|
|
502
558
|
)
|
|
503
559
|
};
|
|
@@ -543,22 +599,24 @@ function pickKey(data) {
|
|
|
543
599
|
}
|
|
544
600
|
return void 0;
|
|
545
601
|
}
|
|
546
|
-
var uploadSchema =
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
602
|
+
var uploadSchema = z6.object({
|
|
603
|
+
bucketName: bucketField,
|
|
604
|
+
localPath: z6.string().min(1).describe("Path to the local file to upload (absolute, or relative to the server's working directory)."),
|
|
605
|
+
destinationFolder: z6.string().describe("Destination folder/prefix in the drive. Empty = drive root."),
|
|
606
|
+
fileName: z6.string().optional().describe("Name to store the file as. Defaults to the local file's name."),
|
|
607
|
+
contentType: z6.string().optional().describe("MIME type. Defaults to application/octet-stream."),
|
|
608
|
+
storageClass: z6.string().optional().describe("Optional S3 storage class (e.g. STANDARD, INTELLIGENT_TIERING).")
|
|
552
609
|
});
|
|
553
610
|
var uploadFile = {
|
|
554
611
|
name: "upload_file",
|
|
555
612
|
title: "Upload file",
|
|
556
|
-
description: "Upload a local file to a
|
|
613
|
+
description: "Upload a local file to a folder in a drive. Requires the drive (bucketName). Reads the file from the local machine, requests a pre-signed upload URL, uploads the bytes to storage, then finalizes the object." + GATED,
|
|
557
614
|
endpoint: { method: "POST", path: "/storage/upload/url", scopes: ["drive:write"] },
|
|
558
615
|
inputSchema: uploadSchema.shape,
|
|
559
616
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
560
|
-
handler: async (args, { client }) => {
|
|
617
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
561
618
|
const a = uploadSchema.parse(args);
|
|
619
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
562
620
|
const fileName = a.fileName ?? basename(a.localPath);
|
|
563
621
|
const contentType = a.contentType ?? "application/octet-stream";
|
|
564
622
|
let info;
|
|
@@ -576,6 +634,7 @@ var uploadFile = {
|
|
|
576
634
|
}
|
|
577
635
|
const bytes = await readFile(a.localPath);
|
|
578
636
|
const presign = await client.post("/storage/upload/url", {
|
|
637
|
+
bucketName,
|
|
579
638
|
dirPath: a.destinationFolder,
|
|
580
639
|
fileName,
|
|
581
640
|
contentType,
|
|
@@ -595,6 +654,7 @@ var uploadFile = {
|
|
|
595
654
|
let complete;
|
|
596
655
|
try {
|
|
597
656
|
complete = await client.post("/storage/upload/complete", {
|
|
657
|
+
bucketName,
|
|
598
658
|
dirPath: a.destinationFolder,
|
|
599
659
|
objects: [{ fileName, key, contentType, size: info.size }]
|
|
600
660
|
});
|
|
@@ -610,45 +670,51 @@ var uploadFile = {
|
|
|
610
670
|
${summarize(complete)}`);
|
|
611
671
|
}
|
|
612
672
|
};
|
|
613
|
-
var createFolderSchema =
|
|
614
|
-
|
|
615
|
-
|
|
673
|
+
var createFolderSchema = z6.object({
|
|
674
|
+
bucketName: bucketField,
|
|
675
|
+
parentPath: z6.string().describe("Parent folder/prefix in the drive. Empty = drive root."),
|
|
676
|
+
name: z6.string().min(1).describe("New folder name.")
|
|
616
677
|
});
|
|
617
678
|
var createFolder = {
|
|
618
679
|
name: "create_folder",
|
|
619
680
|
title: "Create folder",
|
|
620
|
-
description: "Create a new folder at the given path." + GATED,
|
|
681
|
+
description: "Create a new folder at the given path in a drive. Requires the drive (bucketName)." + GATED,
|
|
621
682
|
endpoint: { method: "POST", path: "/storage/folder/create", scopes: ["drive:write"] },
|
|
622
683
|
inputSchema: createFolderSchema.shape,
|
|
623
684
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
624
|
-
handler: async (args, { client }) => {
|
|
685
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
625
686
|
const a = createFolderSchema.parse(args);
|
|
626
|
-
const
|
|
687
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
688
|
+
const data = await client.post("/storage/folder/create", { bucketName, dirPath: a.parentPath, object: a.name });
|
|
627
689
|
return textResult(`Created folder "${a.name}" in "${a.parentPath || "/"}".
|
|
628
690
|
|
|
629
691
|
${summarize(data)}`);
|
|
630
692
|
}
|
|
631
693
|
};
|
|
632
|
-
var renameSchema =
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
694
|
+
var renameSchema = z6.object({
|
|
695
|
+
bucketName: bucketField,
|
|
696
|
+
oldObjectId: z6.string().min(1).describe("Current object key/id of the file or folder."),
|
|
697
|
+
newName: z6.string().min(1).describe("New name."),
|
|
698
|
+
isFolder: z6.boolean().optional().describe("Set true when renaming a folder."),
|
|
699
|
+
storageId: z6.string().optional().describe("Optional storage/index id."),
|
|
637
700
|
...confirmShape
|
|
638
701
|
});
|
|
639
702
|
var renameFile = {
|
|
640
703
|
name: "rename_file",
|
|
641
704
|
title: "Rename file or folder",
|
|
642
|
-
description: "Rename a file or folder. Destructive (changes the object's key). Requires confirm=true." + GATED,
|
|
705
|
+
description: "Rename a file or folder in a drive. Requires the drive (bucketName). Destructive (changes the object's key). Requires confirm=true." + GATED,
|
|
643
706
|
endpoint: { method: "POST", path: "/storage/object/rename", scopes: ["drive:write"] },
|
|
644
707
|
inputSchema: renameSchema.shape,
|
|
645
708
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
646
|
-
handler: async (args, { client }) => {
|
|
709
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
647
710
|
const a = renameSchema.parse(args);
|
|
711
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
648
712
|
if (!a.confirm) {
|
|
649
|
-
return confirmationPreview(`rename ${a.isFolder ? "folder" : "file"} to "${a.newName}"`, `
|
|
713
|
+
return confirmationPreview(`rename ${a.isFolder ? "folder" : "file"} to "${a.newName}"`, `Drive: ${bucketName}
|
|
714
|
+
Target: ${a.oldObjectId}`);
|
|
650
715
|
}
|
|
651
716
|
const data = await client.post("/storage/object/rename", {
|
|
717
|
+
bucketName,
|
|
652
718
|
oldObjectId: a.oldObjectId,
|
|
653
719
|
newObjectName: a.newName,
|
|
654
720
|
isFolder: a.isFolder ?? false,
|
|
@@ -659,29 +725,32 @@ var renameFile = {
|
|
|
659
725
|
${summarize(data)}`);
|
|
660
726
|
}
|
|
661
727
|
};
|
|
662
|
-
var moveSchema =
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
728
|
+
var moveSchema = z6.object({
|
|
729
|
+
bucketName: bucketField,
|
|
730
|
+
sourcePath: z6.string().min(1).describe("Source object key/prefix."),
|
|
731
|
+
destinationPath: z6.string().min(1).describe("Destination prefix."),
|
|
732
|
+
asCopy: z6.boolean().optional().describe("Copy instead of move (copy is non-destructive; the source is kept)."),
|
|
733
|
+
isFolder: z6.boolean().optional().describe("Set true for a folder."),
|
|
734
|
+
destinationBucket: z6.string().optional().describe("Target drive, if different from the source drive."),
|
|
735
|
+
storageId: z6.string().optional().describe("Optional storage/index id."),
|
|
669
736
|
...confirmShape
|
|
670
737
|
});
|
|
671
738
|
var moveFile = {
|
|
672
739
|
name: "move_file",
|
|
673
740
|
title: "Move or copy file",
|
|
674
|
-
description: "Move (or copy, with asCopy=true) a file or folder to a new location. A move removes the source and is destructive, so it requires confirm=true; a copy does not." + GATED,
|
|
741
|
+
description: "Move (or copy, with asCopy=true) a file or folder to a new location. Requires the source drive (bucketName). A move removes the source and is destructive, so it requires confirm=true; a copy does not." + GATED,
|
|
675
742
|
endpoint: { method: "POST", path: "/storage/object/move", scopes: ["drive:write"] },
|
|
676
743
|
inputSchema: moveSchema.shape,
|
|
677
744
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
678
|
-
handler: async (args, { client }) => {
|
|
745
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
679
746
|
const a = moveSchema.parse(args);
|
|
747
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
680
748
|
const isMove = !a.asCopy;
|
|
681
749
|
if (isMove && !a.confirm) {
|
|
682
750
|
return confirmationPreview(`move "${a.sourcePath}" \u2192 "${a.destinationPath}"`, "This removes the source. Pass asCopy=true to copy instead.");
|
|
683
751
|
}
|
|
684
752
|
const data = await client.post("/storage/object/move", {
|
|
753
|
+
bucketName,
|
|
685
754
|
asCopy: a.asCopy ?? false,
|
|
686
755
|
isFolder: a.isFolder ?? false,
|
|
687
756
|
sourcePath: a.sourcePath,
|
|
@@ -694,93 +763,104 @@ var moveFile = {
|
|
|
694
763
|
${summarize(data)}`);
|
|
695
764
|
}
|
|
696
765
|
};
|
|
697
|
-
var duplicateSchema =
|
|
698
|
-
|
|
699
|
-
|
|
766
|
+
var duplicateSchema = z6.object({
|
|
767
|
+
bucketName: bucketField,
|
|
768
|
+
objectKey: z6.string().min(1).describe("Source object key to duplicate."),
|
|
769
|
+
storageId: z6.string().optional().describe("Optional storage/index id.")
|
|
700
770
|
});
|
|
701
771
|
var duplicateFile = {
|
|
702
772
|
name: "duplicate_file",
|
|
703
773
|
title: "Duplicate file",
|
|
704
|
-
description: "Create a copy of a file in the same location." + GATED,
|
|
774
|
+
description: "Create a copy of a file in the same location. Requires the drive (bucketName)." + GATED,
|
|
705
775
|
endpoint: { method: "POST", path: "/storage/object/duplicate", scopes: ["drive:write"] },
|
|
706
776
|
inputSchema: duplicateSchema.shape,
|
|
707
777
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
708
|
-
handler: async (args, { client }) => {
|
|
778
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
709
779
|
const a = duplicateSchema.parse(args);
|
|
710
|
-
const
|
|
780
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
781
|
+
const data = await client.post("/storage/object/duplicate", { bucketName, objectKey: a.objectKey, storageId: a.storageId });
|
|
711
782
|
return textResult(`Duplicated "${a.objectKey}".
|
|
712
783
|
|
|
713
784
|
${summarize(data)}`);
|
|
714
785
|
}
|
|
715
786
|
};
|
|
716
|
-
var deleteSchema =
|
|
717
|
-
|
|
787
|
+
var deleteSchema = z6.object({
|
|
788
|
+
bucketName: bucketField,
|
|
789
|
+
objectKeys: z6.array(z6.string().min(1)).min(1).describe("Object keys (paths) to permanently delete."),
|
|
718
790
|
...confirmShape
|
|
719
791
|
});
|
|
720
792
|
var deleteFiles = {
|
|
721
793
|
name: "delete_files",
|
|
722
794
|
title: "Delete files",
|
|
723
|
-
description: "Permanently delete one or more files/objects by key. Destructive and irreversible. Requires confirm=true." + GATED,
|
|
795
|
+
description: "Permanently delete one or more files/objects by key from a drive. Requires the drive (bucketName). Destructive and irreversible. Requires confirm=true." + GATED,
|
|
724
796
|
endpoint: { method: "POST", path: "/storage/objects/delete", scopes: ["drive:delete"] },
|
|
725
797
|
inputSchema: deleteSchema.shape,
|
|
726
798
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
727
|
-
handler: async (args, { client }) => {
|
|
799
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
728
800
|
const a = deleteSchema.parse(args);
|
|
801
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
729
802
|
if (!a.confirm) {
|
|
730
|
-
return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s)`, a.objectKeys.map((k) => ` - ${k}`).join("\n"));
|
|
803
|
+
return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s) from "${bucketName}"`, a.objectKeys.map((k) => ` - ${k}`).join("\n"));
|
|
731
804
|
}
|
|
732
|
-
const data = await client.post("/storage/objects/delete", { deletedObjects: a.objectKeys });
|
|
805
|
+
const data = await client.post("/storage/objects/delete", { bucketName, deletedObjects: a.objectKeys });
|
|
733
806
|
return textResult(`Deleted ${a.objectKeys.length} object(s).
|
|
734
807
|
|
|
735
808
|
${summarize(data)}`);
|
|
736
809
|
}
|
|
737
810
|
};
|
|
738
|
-
var updateMetaSchema =
|
|
739
|
-
|
|
740
|
-
|
|
811
|
+
var updateMetaSchema = z6.object({
|
|
812
|
+
bucketName: bucketField,
|
|
813
|
+
objectKey: z6.string().min(1).describe("Object key whose metadata to update."),
|
|
814
|
+
metadata: z6.record(z6.string(), z6.unknown()).describe("Metadata fields to set (key/value map). Overwrites the listed fields."),
|
|
741
815
|
...confirmShape
|
|
742
816
|
});
|
|
743
817
|
var updateMetadata = {
|
|
744
818
|
name: "update_metadata",
|
|
745
819
|
title: "Update file metadata",
|
|
746
|
-
description: "Update a file's metadata. Destructive (overwrites the listed metadata fields). Requires confirm=true." + GATED,
|
|
820
|
+
description: "Update a file's metadata in a drive. Requires the drive (bucketName). Destructive (overwrites the listed metadata fields). Requires confirm=true." + GATED,
|
|
747
821
|
endpoint: { method: "POST", path: "/storage/object/metadata", scopes: ["drive:write"] },
|
|
748
822
|
inputSchema: updateMetaSchema.shape,
|
|
749
823
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
750
|
-
handler: async (args, { client }) => {
|
|
824
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
751
825
|
const a = updateMetaSchema.parse(args);
|
|
826
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
752
827
|
if (!a.confirm) {
|
|
753
|
-
return confirmationPreview(`update metadata on "${a.objectKey}"`, `
|
|
828
|
+
return confirmationPreview(`update metadata on "${a.objectKey}"`, `Drive: ${bucketName}
|
|
829
|
+
Fields: ${Object.keys(a.metadata).join(", ") || "(none)"}`);
|
|
754
830
|
}
|
|
755
|
-
const data = await client.post("/storage/object/metadata", { objectKey: a.objectKey, metadata: a.metadata });
|
|
831
|
+
const data = await client.post("/storage/object/metadata", { bucketName, objectKey: a.objectKey, metadata: a.metadata });
|
|
756
832
|
return textResult(`Updated metadata on "${a.objectKey}".
|
|
757
833
|
|
|
758
834
|
${summarize(data)}`);
|
|
759
835
|
}
|
|
760
836
|
};
|
|
761
|
-
var restoreSchema =
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
837
|
+
var restoreSchema = z6.object({
|
|
838
|
+
bucketName: bucketField,
|
|
839
|
+
objectKey: z6.string().min(1).describe("Object key of the archived (Glacier) object to restore."),
|
|
840
|
+
days: z6.number().int().min(1).optional().describe("How many days to keep the restored copy available."),
|
|
841
|
+
retrievalTier: z6.enum(["Expedited", "Standard", "Bulk"]).optional().describe("Glacier retrieval tier (default Standard)."),
|
|
842
|
+
storageId: z6.string().optional().describe("Optional storage/index id."),
|
|
766
843
|
...confirmShape
|
|
767
844
|
});
|
|
768
845
|
var restoreArchivedFile = {
|
|
769
846
|
name: "restore_archived_file",
|
|
770
847
|
title: "Restore archived file",
|
|
771
|
-
description: "Begin restoring an archived (S3 Glacier) object so it can be downloaded. This is Glacier un-archiving \u2014 NOT recovery of a deleted file \u2014 and may incur retrieval cost and take minutes to hours. Requires confirm=true." + GATED,
|
|
848
|
+
description: "Begin restoring an archived (S3 Glacier) object so it can be downloaded. Requires the drive (bucketName). This is Glacier un-archiving \u2014 NOT recovery of a deleted file \u2014 and may incur retrieval cost and take minutes to hours. Requires confirm=true." + GATED,
|
|
772
849
|
endpoint: { method: "POST", path: "/storage/object/restore", scopes: ["drive:write"] },
|
|
773
850
|
inputSchema: restoreSchema.shape,
|
|
774
851
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
775
|
-
handler: async (args, { client }) => {
|
|
852
|
+
handler: async (args, { client, defaultBucket }) => {
|
|
776
853
|
const a = restoreSchema.parse(args);
|
|
854
|
+
const bucketName = resolveBucket(a.bucketName, defaultBucket);
|
|
777
855
|
if (!a.confirm) {
|
|
778
856
|
return confirmationPreview(
|
|
779
857
|
`restore archived object "${a.objectKey}"`,
|
|
780
|
-
`
|
|
858
|
+
`Drive: ${bucketName}
|
|
859
|
+
Tier: ${a.retrievalTier ?? "Standard"}${a.days ? `, ${a.days} day(s)` : ""}. May incur retrieval cost.`
|
|
781
860
|
);
|
|
782
861
|
}
|
|
783
862
|
const data = await client.post("/storage/object/restore", {
|
|
863
|
+
bucketName,
|
|
784
864
|
objectKey: a.objectKey,
|
|
785
865
|
days: a.days,
|
|
786
866
|
retrievalTier: a.retrievalTier,
|
|
@@ -820,7 +900,7 @@ function createServer(config) {
|
|
|
820
900
|
},
|
|
821
901
|
async (args) => {
|
|
822
902
|
try {
|
|
823
|
-
return await tool.handler(args ?? {}, { client });
|
|
903
|
+
return await tool.handler(args ?? {}, { client, defaultBucket: config.defaultBucket });
|
|
824
904
|
} catch (err) {
|
|
825
905
|
const message = err instanceof Error ? err.message : String(err);
|
|
826
906
|
logger.error(`tool "${tool.name}" failed`, message);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/logger.ts","../src/server.ts","../src/version.ts","../src/client/pagination.ts","../src/client/CloudSeeClient.ts","../src/tools/read.ts","../src/tools/format.ts","../src/tools/types.ts","../src/tools/download.ts","../src/tools/write.ts","../src/confirm.ts","../src/tools/index.ts"],"sourcesContent":["import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { loadConfig, type Config } from \"./config\";\r\nimport { configureLogger, logger } from \"./logger\";\r\nimport { createServer } from \"./server\";\r\nimport { VERSION } from \"./version\";\r\n\r\nfunction loadConfigOrExit(): Config {\r\n try {\r\n return loadConfig();\r\n } catch (err) {\r\n // Config errors are fatal and printed to stderr (not stdout — the transport).\r\n process.stderr.write(`\\n${err instanceof Error ? err.message : String(err)}\\n\\n`);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const config = loadConfigOrExit();\r\n // Register the secret for redaction before anything else runs.\r\n configureLogger({ level: config.logLevel, redact: [config.apiKeySecret] });\r\n\r\n const server = createServer(config);\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n\r\n logger.info(`cloudsee-drive-mcp v${VERSION} ready on stdio (base: ${config.baseUrl})`);\r\n}\r\n\r\nmain().catch((err) => {\r\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\r\n process.exit(1);\r\n});\r\n","import { z } from \"zod\";\r\nimport { CloudSeeError } from \"./errors\";\r\nimport type { LogLevel } from \"./logger\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://drive-api.cloudsee.cloud\";\r\nexport const DEFAULT_TIMEOUT_MS = 30_000;\r\n\r\nexport interface Config {\r\n apiKeyId: string;\r\n apiKeySecret: string;\r\n baseUrl: string;\r\n timeoutMs: number;\r\n logLevel: LogLevel;\r\n}\r\n\r\nconst envSchema = z.object({\r\n CLOUDSEE_API_KEY_ID: z.string().trim().min(1, \"is required (your CloudSee Drive API key id)\"),\r\n CLOUDSEE_API_KEY_SECRET: z.string().trim().min(1, \"is required (the matching API key secret)\"),\r\n CLOUDSEE_API_BASE_URL: z.string().url(\"must be a valid URL\").optional(),\r\n CLOUDSEE_TIMEOUT_MS: z.coerce.number().int().positive().optional(),\r\n CLOUDSEE_LOG_LEVEL: z.enum([\"error\", \"warn\", \"info\", \"debug\"]).optional(),\r\n});\r\n\r\n/**\r\n * Load and validate configuration from the environment. Throws a CloudSeeError\r\n * with an actionable, secret-free message when required vars are missing.\r\n */\r\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\r\n const parsed = envSchema.safeParse(env);\r\n if (!parsed.success) {\r\n const issues = parsed.error.issues.map((i) => ` • ${i.path.join(\".\")} ${i.message}`).join(\"\\n\");\r\n throw new CloudSeeError(\r\n `CloudSee Drive MCP server is not configured:\\n${issues}\\n\\n` +\r\n `Set the required environment variables (see .env.example or the README).`,\r\n { code: \"config\" },\r\n );\r\n }\r\n const e = parsed.data;\r\n return {\r\n apiKeyId: e.CLOUDSEE_API_KEY_ID,\r\n apiKeySecret: e.CLOUDSEE_API_KEY_SECRET,\r\n baseUrl: (e.CLOUDSEE_API_BASE_URL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\"),\r\n timeoutMs: e.CLOUDSEE_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS,\r\n logLevel: e.CLOUDSEE_LOG_LEVEL ?? \"info\",\r\n };\r\n}\r\n","export interface CloudSeeErrorOptions {\r\n code?: string;\r\n status?: number;\r\n retryable?: boolean;\r\n}\r\n\r\n/** Error raised for any failed CloudSee API interaction. Messages are redacted\r\n * by the client before construction and must never contain the API secret. */\r\nexport class CloudSeeError extends Error {\r\n readonly code: string;\r\n readonly status?: number;\r\n readonly retryable: boolean;\r\n\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message);\r\n this.name = \"CloudSeeError\";\r\n this.code = options.code ?? \"error\";\r\n this.status = options.status;\r\n this.retryable = options.retryable ?? false;\r\n }\r\n}\r\n\r\n/** Authentication/authorization failure (HTTP 401/403). Terminal — never retried. */\r\nexport class AuthError extends CloudSeeError {\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message, { ...options, code: options.code ?? \"auth\" });\r\n this.name = \"AuthError\";\r\n }\r\n}\r\n","// Structured logging that writes to **stderr only**. stdout is reserved for the\r\n// MCP stdio transport — anything written there corrupts the protocol stream.\r\n// All output is passed through secret redaction as a defense-in-depth backstop;\r\n// the real rule is to never hand the API secret to the logger in the first place.\r\n\r\nexport type LogLevel = \"error\" | \"warn\" | \"info\" | \"debug\";\r\n\r\nconst LEVEL_WEIGHT: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 };\r\n\r\nlet currentLevel: LogLevel = \"info\";\r\nlet secrets: string[] = [];\r\n\r\nexport function configureLogger(options: { level?: LogLevel; redact?: string[] }): void {\r\n if (options.level) currentLevel = options.level;\r\n if (options.redact) {\r\n secrets = options.redact.filter((s): s is string => typeof s === \"string\" && s.length > 0);\r\n }\r\n}\r\n\r\n/** Replace any registered secret with `***` anywhere it appears in `text`. */\r\nexport function redactSecrets(text: string): string {\r\n let out = text;\r\n for (const secret of secrets) {\r\n if (out.includes(secret)) out = out.split(secret).join(\"***\");\r\n }\r\n return out;\r\n}\r\n\r\nfunction stringify(meta: unknown): string {\r\n if (meta === undefined) return \"\";\r\n if (typeof meta === \"string\") return meta;\r\n try {\r\n return JSON.stringify(meta);\r\n } catch {\r\n return String(meta);\r\n }\r\n}\r\n\r\nfunction emit(level: LogLevel, message: string, meta?: unknown): void {\r\n if (LEVEL_WEIGHT[level] > LEVEL_WEIGHT[currentLevel]) return;\r\n const suffix = meta === undefined ? \"\" : ` ${stringify(meta)}`;\r\n process.stderr.write(`[cloudsee-drive-mcp] ${level.toUpperCase()}: ${redactSecrets(message + suffix)}\\n`);\r\n}\r\n\r\nexport const logger = {\r\n error: (message: string, meta?: unknown): void => emit(\"error\", message, meta),\r\n warn: (message: string, meta?: unknown): void => emit(\"warn\", message, meta),\r\n info: (message: string, meta?: unknown): void => emit(\"info\", message, meta),\r\n debug: (message: string, meta?: unknown): void => emit(\"debug\", message, meta),\r\n};\r\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { CloudSeeClient } from \"./client/CloudSeeClient\";\r\nimport { allTools } from \"./tools/index\";\r\nimport { logger } from \"./logger\";\r\nimport { VERSION } from \"./version\";\r\nimport type { Config } from \"./config\";\r\n\r\n/**\r\n * Build the MCP server: one CloudSeeClient shared across all tools, each tool\r\n * registered with its Zod input schema and MCP annotations. Tool handler errors\r\n * are caught and returned as `isError` results (never thrown across the\r\n * transport), and are logged to stderr with the secret redacted.\r\n */\r\nexport function createServer(config: Config): McpServer {\r\n const client = new CloudSeeClient(config);\r\n const server = new McpServer({ name: \"cloudsee-drive-mcp\", version: VERSION });\r\n\r\n for (const tool of allTools) {\r\n server.registerTool(\r\n tool.name,\r\n {\r\n title: tool.title,\r\n description: tool.description,\r\n inputSchema: tool.inputSchema,\r\n annotations: { title: tool.title, ...tool.annotations },\r\n },\r\n async (args: Record<string, unknown>) => {\r\n try {\r\n return await tool.handler(args ?? {}, { client });\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n logger.error(`tool \"${tool.name}\" failed`, message);\r\n return { content: [{ type: \"text\" as const, text: `Error: ${message}` }], isError: true };\r\n }\r\n },\r\n );\r\n }\r\n\r\n logger.info(`registered ${allTools.length} tools`);\r\n return server;\r\n}\r\n","// Single source of truth for the package version, surfaced to the MCP client\r\n// handshake and the User-Agent. Kept in sync with package.json by release tooling.\r\nexport const VERSION = \"0.1.0\";\r\n","import { CloudSeeError } from \"../errors\";\r\n\r\n// The data plane uses five different pagination param names across endpoint\r\n// families (C10): `nextPage`, `marker`, `lastEvaluatedKey`, `pageToken`,\r\n// `nextToken`. Tools expose a single opaque `cursor`; this module encodes the\r\n// dialect + raw token into that cursor and decodes it back, so a cursor minted\r\n// for one operation can't be misapplied to another.\r\n\r\nexport type PaginationDialect = \"nextPage\" | \"marker\" | \"lastEvaluatedKey\" | \"pageToken\" | \"nextToken\";\r\n\r\ninterface CursorPayload {\r\n d: PaginationDialect;\r\n v: string;\r\n}\r\n\r\nexport function encodeCursor(dialect: PaginationDialect, value: string | null | undefined): string | undefined {\r\n if (value === null || value === undefined || value === \"\") return undefined;\r\n const payload: CursorPayload = { d: dialect, v: String(value) };\r\n return Buffer.from(JSON.stringify(payload), \"utf8\").toString(\"base64url\");\r\n}\r\n\r\nexport function decodeCursor(cursor: string, expected: PaginationDialect): string {\r\n let payload: CursorPayload;\r\n try {\r\n payload = JSON.parse(Buffer.from(cursor, \"base64url\").toString(\"utf8\")) as CursorPayload;\r\n } catch {\r\n throw new CloudSeeError(\"Invalid pagination cursor.\", { code: \"bad_cursor\" });\r\n }\r\n if (!payload || typeof payload.v !== \"string\" || payload.d !== expected) {\r\n throw new CloudSeeError(\"Pagination cursor is not valid for this operation.\", { code: \"bad_cursor\" });\r\n }\r\n return payload.v;\r\n}\r\n\r\nconst RESPONSE_FIELDS = [\"nextPage\", \"nextPageToken\", \"marker\", \"lastEvaluatedKey\", \"pageToken\", \"nextToken\"];\r\n\r\n/**\r\n * Pull the raw continuation token out of a response envelope's `data`, tolerant\r\n * of the exact field name (which varies by endpoint and isn't guaranteed by the\r\n * RPC contract). Prefers the operation's own dialect field, then known aliases.\r\n */\r\nexport function extractNextToken(data: unknown, dialect: PaginationDialect): string | undefined {\r\n if (!data || typeof data !== \"object\") return undefined;\r\n const record = data as Record<string, unknown>;\r\n for (const field of [dialect, ...RESPONSE_FIELDS]) {\r\n const value = record[field];\r\n if (typeof value === \"string\" && value !== \"\") return value;\r\n if (value && typeof value === \"object\") {\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n /* ignore non-serializable */\r\n }\r\n }\r\n }\r\n return undefined;\r\n}\r\n","import { AuthError, CloudSeeError } from \"../errors\";\r\nimport { logger } from \"../logger\";\r\nimport { VERSION } from \"../version\";\r\nimport type { Config } from \"../config\";\r\nimport { decodeCursor, encodeCursor, extractNextToken, type PaginationDialect } from \"./pagination\";\r\n\r\ninterface Envelope<T> {\r\n success?: boolean;\r\n data?: T;\r\n errorMessage?: string;\r\n code?: string;\r\n}\r\n\r\nexport interface RequestOptions {\r\n retries?: number;\r\n signal?: AbortSignal;\r\n}\r\n\r\nexport interface PagedResult<T> {\r\n data: T;\r\n nextCursor?: string;\r\n}\r\n\r\ninterface ParsedResponse<T> {\r\n data: T;\r\n envelope: Record<string, unknown> | undefined;\r\n}\r\n\r\nconst DEFAULT_RETRIES = 3;\r\nconst BASE_BACKOFF_MS = 300;\r\nconst MAX_BACKOFF_MS = 8_000;\r\n\r\n/**\r\n * Typed wrapper over the drive-bridge `/v1/*` data plane. Every endpoint is an\r\n * RPC `POST /noun/verb` returning the platform `{ success, data, errorMessage,\r\n * code }` envelope. The client:\r\n * - sets the `X-Api-Key-Id` / `X-Api-Key-Secret` auth headers from config,\r\n * - retries 429/5xx with exponential backoff honoring `Retry-After` (the\r\n * `csd-mcp` usage plan is dormant, so the server must self-throttle — C6),\r\n * - unwraps the envelope and surfaces `success:false` as a CloudSeeError,\r\n * - never logs the secret and redacts it from any error message.\r\n */\r\nexport class CloudSeeClient {\r\n private readonly baseUrl: string;\r\n private readonly timeoutMs: number;\r\n private readonly secret: string;\r\n private readonly headers: Record<string, string>;\r\n\r\n constructor(config: Config) {\r\n this.baseUrl = config.baseUrl;\r\n this.timeoutMs = config.timeoutMs;\r\n this.secret = config.apiKeySecret;\r\n this.headers = {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n \"X-Api-Key-Id\": config.apiKeyId,\r\n \"X-Api-Key-Secret\": config.apiKeySecret,\r\n \"User-Agent\": `cloudsee-drive-mcp/${VERSION}`,\r\n };\r\n }\r\n\r\n /** POST a data-plane RPC endpoint (path WITHOUT `/v1`). Returns unwrapped `data`. */\r\n async post<T = unknown>(path: string, body: Record<string, unknown> = {}, options: RequestOptions = {}): Promise<T> {\r\n return (await this.request<T>(path, body, options)).data;\r\n }\r\n\r\n /** POST a paginated endpoint. Translates the opaque `cursor` to/from the\r\n * endpoint's pagination dialect and returns a normalized `nextCursor`. The\r\n * continuation token can sit at the envelope level (e.g. `/storage/recent`\r\n * returns `nextToken` as a sibling of `data`) or inside `data` — check the\r\n * envelope first, then `data`, so a token is never silently dropped. */\r\n async postPaged<T = unknown>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n dialect: PaginationDialect,\r\n cursor?: string,\r\n options: RequestOptions = {},\r\n ): Promise<PagedResult<T>> {\r\n const requestBody = { ...body };\r\n if (cursor) requestBody[dialect] = decodeCursor(cursor, dialect);\r\n const { data, envelope } = await this.request<T>(path, requestBody, options);\r\n const token = extractNextToken(envelope, dialect) ?? extractNextToken(data, dialect);\r\n return { data, nextCursor: encodeCursor(dialect, token) };\r\n }\r\n\r\n /** Run the request with retry/backoff, returning the unwrapped `data` together\r\n * with the raw response envelope (needed for envelope-level pagination tokens). */\r\n private async request<T>(path: string, body: Record<string, unknown>, options: RequestOptions): Promise<ParsedResponse<T>> {\r\n const url = `${this.baseUrl}/v1${path}`;\r\n const retries = options.retries ?? DEFAULT_RETRIES;\r\n let attempt = 0;\r\n for (;;) {\r\n attempt += 1;\r\n const response = await this.fetchOnce(url, path, body, options.signal);\r\n\r\n if (response.status === 401 || response.status === 403) {\r\n throw new AuthError(\r\n \"Authentication failed (HTTP \" +\r\n response.status +\r\n \"). Check CLOUDSEE_API_KEY_ID and CLOUDSEE_API_KEY_SECRET, and that the key is active and not expired.\",\r\n { status: response.status },\r\n );\r\n }\r\n\r\n if ((response.status === 429 || response.status >= 500) && attempt <= retries) {\r\n const waitMs = retryAfterMs(response.headers.get(\"retry-after\")) ?? backoffMs(attempt);\r\n logger.warn(`HTTP ${response.status} from ${path}; retrying in ${waitMs}ms (attempt ${attempt}/${retries}).`);\r\n await sleep(waitMs);\r\n continue;\r\n }\r\n\r\n return await this.parse<T>(response, path);\r\n }\r\n }\r\n\r\n private async fetchOnce(\r\n url: string,\r\n path: string,\r\n body: Record<string, unknown>,\r\n external?: AbortSignal,\r\n ): Promise<Response> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n const onAbort = (): void => controller.abort();\r\n if (external) external.addEventListener(\"abort\", onAbort, { once: true });\r\n try {\r\n return await fetch(url, {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify(body),\r\n signal: controller.signal,\r\n });\r\n } catch (err) {\r\n const reason = controller.signal.aborted ? `timed out after ${this.timeoutMs}ms` : this.redact(errMessage(err));\r\n throw new CloudSeeError(`Network error calling ${path}: ${reason}`, { code: \"network\", retryable: true });\r\n } finally {\r\n clearTimeout(timer);\r\n if (external) external.removeEventListener(\"abort\", onAbort);\r\n }\r\n }\r\n\r\n private async parse<T>(response: Response, path: string): Promise<ParsedResponse<T>> {\r\n const text = await response.text();\r\n let json: Envelope<T> | undefined;\r\n if (text) {\r\n try {\r\n json = JSON.parse(text) as Envelope<T>;\r\n } catch {\r\n throw new CloudSeeError(\r\n response.ok\r\n ? `CloudSee API returned a non-JSON response for ${path}.`\r\n : `CloudSee API returned HTTP ${response.status} for ${path}.`,\r\n { status: response.status, code: response.ok ? \"bad_response\" : \"http_error\" },\r\n );\r\n }\r\n }\r\n if (!response.ok) {\r\n const message = json?.errorMessage ?? `HTTP ${response.status}`;\r\n throw new CloudSeeError(`CloudSee API error for ${path}: ${this.redact(message)}`, {\r\n status: response.status,\r\n code: json?.code ?? \"http_error\",\r\n });\r\n }\r\n if (json && typeof json === \"object\" && json.success === false) {\r\n throw new CloudSeeError(this.redact(json.errorMessage || `Operation failed (${json.code ?? \"unknown\"}).`), {\r\n code: json.code ?? \"operation_failed\",\r\n status: response.status,\r\n });\r\n }\r\n const envelope = json && typeof json === \"object\" ? (json as Record<string, unknown>) : undefined;\r\n if (json && typeof json === \"object\" && \"data\" in json) return { data: json.data as T, envelope };\r\n return { data: (json ?? ({} as T)) as T, envelope };\r\n }\r\n\r\n private redact(text: string): string {\r\n return this.secret && text.includes(this.secret) ? text.split(this.secret).join(\"***\") : text;\r\n }\r\n}\r\n\r\nfunction errMessage(err: unknown): string {\r\n return err instanceof Error ? err.message : String(err);\r\n}\r\n\r\nfunction backoffMs(attempt: number): number {\r\n const ceiling = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** (attempt - 1));\r\n // Full jitter over [ceiling/2, ceiling] to avoid thundering-herd retries.\r\n return Math.round(ceiling / 2 + Math.random() * (ceiling / 2));\r\n}\r\n\r\nfunction retryAfterMs(header: string | null): number | undefined {\r\n if (!header) return undefined;\r\n const seconds = Number(header);\r\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\r\n const date = Date.parse(header);\r\n if (Number.isFinite(date)) return Math.max(0, date - Date.now());\r\n return undefined;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize, withCursor } from \"./format\";\r\nimport { textResult, type ToolDef } from \"./types\";\r\n\r\n// ---- list_buckets → POST /storage/buckets (storageBucketList, drive:read) ----\r\nconst listBuckets: ToolDef = {\r\n name: \"list_buckets\",\r\n title: \"List buckets\",\r\n description:\r\n \"List the storage buckets (Amazon S3 buckets) the authenticated CloudSee Drive account can access. Use this first to discover available buckets before browsing or searching.\",\r\n endpoint: { method: \"POST\", path: \"/storage/buckets\", scopes: [\"drive:read\"] },\r\n inputSchema: {},\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (_args, { client }) => {\r\n const data = await client.post(\"/storage/buckets\", {});\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- browse_folder → POST /storage/list (storageList, drive:read) ----\r\nconst browseSchema = z.object({\r\n path: z.string().optional().describe(\"Folder path / prefix to list. Empty or omitted = the root.\"),\r\n sortOption: z.string().optional().describe(\"Sort key, e.g. 'name_asc', 'name_desc', 'date_desc'.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst browseFolder: ToolDef = {\r\n name: \"browse_folder\",\r\n title: \"Browse folder\",\r\n description:\r\n \"List the files and sub-folders directly inside a folder (one level, non-recursive), with sorting and pagination. To filter by keyword use 'search_files'.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: browseSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = browseSchema.parse(args);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { dirPath: a.path ?? \"\", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- search_files → POST /storage/list with searchingKeyword (drive:read) ----\r\nconst searchSchema = z.object({\r\n query: z.string().min(1).describe(\"Keyword to match against file and folder names.\"),\r\n path: z.string().optional().describe(\"Folder to search within (this folder level, non-recursive). Empty = root.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst searchFiles: ToolDef = {\r\n name: \"search_files\",\r\n title: \"Search files\",\r\n description:\r\n \"Search for files and folders by name keyword within a folder. Note: this matches the given folder level and is NOT a whole-tree recursive search — browse into sub-folders to search deeper. Returns matches with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: searchSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = searchSchema.parse(args);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { dirPath: a.path ?? \"\", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- recent_files → POST /storage/recent (storageRecentFiles, drive:read) ----\r\nconst recentSchema = z.object({\r\n limit: z.number().int().min(1).max(200).optional().describe(\"Max items (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst recentFiles: ToolDef = {\r\n name: \"recent_files\",\r\n title: \"Recent files\",\r\n description: \"List the account's most recently accessed or modified files, newest first, with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/recent\", scopes: [\"drive:read\"] },\r\n inputSchema: recentSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = recentSchema.parse(args);\r\n const limit = a.limit ?? 50;\r\n // /storage/recent paginates with `nextToken` (a composite key returned at the\r\n // envelope level), not `nextPage`. It echoes that token even when the list is\r\n // exhausted, so only advertise a next page when this page came back full.\r\n const { data, nextCursor } = await client.postPaged(\"/storage/recent\", { limit }, \"nextToken\", a.cursor);\r\n const hasMore = Array.isArray(data) && data.length >= limit;\r\n return textResult(withCursor(summarize(data), hasMore ? nextCursor : undefined));\r\n },\r\n};\r\n\r\n// ---- get_file_metadata → POST /storage/object/detail (storageDetail, drive:read) ----\r\nconst metaSchema = z.object({ objectKey: z.string().min(1).describe(\"Full object key (path) of the file.\") });\r\nconst getFileMetadata: ToolDef = {\r\n name: \"get_file_metadata\",\r\n title: \"Get file metadata\",\r\n description:\r\n \"Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/detail\", scopes: [\"drive:read\"] },\r\n inputSchema: metaSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = metaSchema.parse(args);\r\n const data = await client.post(\"/storage/object/detail\", { objectKey: a.objectKey });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- get_file_tags → POST /storage/object/tagging (getObjectTagging, drive:read) ----\r\nconst tagsSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Full object key (path) of the file.\"),\r\n bucketName: z.string().optional().describe(\"Bucket name, if not implied by the key.\"),\r\n});\r\nconst getFileTags: ToolDef = {\r\n name: \"get_file_tags\",\r\n title: \"Get file tags\",\r\n description: \"Get the S3 object tags (key/value pairs) attached to a file, by object key.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/tagging\", scopes: [\"drive:read\"] },\r\n inputSchema: tagsSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = tagsSchema.parse(args);\r\n const data = await client.post(\"/storage/object/tagging\", { objectKey: a.objectKey, bucketName: a.bucketName });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const readTools: ToolDef[] = [\r\n listBuckets,\r\n browseFolder,\r\n searchFiles,\r\n recentFiles,\r\n getFileMetadata,\r\n getFileTags,\r\n];\r\n","// Output bounding so large listings never flood the model context (R8 / AC).\r\n// Arrays are capped, deeply, and the whole rendering is hard-capped by length.\r\n\r\nconst DEFAULT_MAX_ITEMS = 100;\r\nconst DEFAULT_MAX_CHARS = 8_000;\r\n\r\nexport function summarize(data: unknown, opts: { maxItems?: number; maxChars?: number } = {}): string {\r\n const maxItems = opts.maxItems ?? DEFAULT_MAX_ITEMS;\r\n const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;\r\n let json: string;\r\n try {\r\n json = JSON.stringify(boundArrays(data, maxItems), null, 2);\r\n } catch {\r\n json = String(data);\r\n }\r\n if (json.length > maxChars) {\r\n json = `${json.slice(0, maxChars)}\\n… (output truncated at ${maxChars} characters — narrow your query or paginate).`;\r\n }\r\n return json;\r\n}\r\n\r\nfunction boundArrays(value: unknown, maxItems: number): unknown {\r\n if (Array.isArray(value)) {\r\n const capped: unknown[] = value.slice(0, maxItems).map((v) => boundArrays(v, maxItems));\r\n if (value.length > maxItems) {\r\n capped.push(`… ${value.length - maxItems} more item(s) omitted — paginate for the rest.`);\r\n }\r\n return capped;\r\n }\r\n if (value && typeof value === \"object\") {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\r\n out[k] = boundArrays(v, maxItems);\r\n }\r\n return out;\r\n }\r\n return value;\r\n}\r\n\r\n/** Append a continuation hint when the API returned another page. */\r\nexport function withCursor(body: string, nextCursor?: string): string {\r\n if (!nextCursor) return body;\r\n return `${body}\\n\\n↪ More results available. Call this tool again with cursor=\"${nextCursor}\" for the next page.`;\r\n}\r\n","import type { ZodRawShape } from \"zod\";\r\nimport type { CloudSeeClient } from \"../client/CloudSeeClient\";\r\n\r\nexport interface ToolContext {\r\n client: CloudSeeClient;\r\n}\r\n\r\nexport interface ToolResult {\r\n // Index signature mirrors the MCP SDK's CallToolResult (which permits extra\r\n // fields like _meta); lets ToolResult satisfy the registerTool callback type.\r\n [x: string]: unknown;\r\n content: Array<{ type: \"text\"; text: string }>;\r\n isError?: boolean;\r\n}\r\n\r\nexport interface ToolAnnotations {\r\n readOnlyHint?: boolean;\r\n destructiveHint?: boolean;\r\n idempotentHint?: boolean;\r\n openWorldHint?: boolean;\r\n}\r\n\r\n/** The real data-plane endpoint a tool wraps — asserted against the committed\r\n * contract snapshot by the drift test. `path` excludes the `/v1` prefix. */\r\nexport interface ToolEndpoint {\r\n method: \"POST\";\r\n path: string;\r\n scopes: string[];\r\n}\r\n\r\nexport interface ToolDef {\r\n name: string;\r\n title: string;\r\n description: string;\r\n endpoint: ToolEndpoint;\r\n inputSchema: ZodRawShape;\r\n annotations: ToolAnnotations;\r\n handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult>;\r\n}\r\n\r\nexport function textResult(text: string): ToolResult {\r\n return { content: [{ type: \"text\", text }] };\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize } from \"./format\";\r\nimport { textResult, type ToolDef } from \"./types\";\r\n\r\n// Both tools wrap POST /storage/object/download-url (getObjectUrl, drive:read +\r\n// drive:download). The API returns a short-lived pre-signed URL, never AWS\r\n// credentials. We surface the URL (a capability link, not a secret); the model\r\n// can hand it to the user. We never download bytes into the conversation.\r\n\r\n// ---- download_file ----\r\nconst downloadSchema = z.object({\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to download.\"),\r\n forceDownload: z.boolean().optional().describe(\"If true, the link forces an attachment download instead of inline view.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst downloadFile: ToolDef = {\r\n name: \"download_file\",\r\n title: \"Download file\",\r\n description:\r\n \"Get a short-lived pre-signed download URL for a file. The URL is time-limited and grants read access to that one object — share it with care. Returns the URL; it does not load file bytes into the conversation.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: downloadSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = downloadSchema.parse(args);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n filePath: a.filePath,\r\n download: a.forceDownload ?? false,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- share_link ----\r\nconst shareSchema = z.object({\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to share.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst shareLink: ToolDef = {\r\n name: \"share_link\",\r\n title: \"Create share link\",\r\n description:\r\n \"Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: shareSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = shareSchema.parse(args);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n filePath: a.filePath,\r\n shareableLink: true,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const downloadTools: ToolDef[] = [downloadFile, shareLink];\r\n","import { readFile, stat } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { z } from \"zod\";\r\nimport { CloudSeeError } from \"../errors\";\r\nimport { confirmShape, confirmationPreview } from \"../confirm\";\r\nimport { summarize } from \"./format\";\r\nimport { textResult, type ToolDef } from \"./types\";\r\n\r\n// Write/delete tools are present but their end-to-end success is sequenced\r\n// behind CSD-572 (the gateway enforces no scope/RBAC yet and the write path is\r\n// a Phase-1 stub). This note is appended to each tool's description so the model\r\n// and the user understand a gateway denial here is expected, not a tool bug.\r\nconst GATED =\r\n \" (Write access is authorized server-side; until CloudSee's public-API RBAC wiring (CSD-572) is live, the gateway may deny this — that is expected, not a tool failure.)\";\r\n\r\nconst MULTIPART_THRESHOLD = 8 * 1024 * 1024; // 8 MiB\r\n\r\nfunction pickUrl(data: unknown): string | undefined {\r\n if (typeof data === \"string\") return data;\r\n if (data && typeof data === \"object\") {\r\n const record = data as Record<string, unknown>;\r\n for (const key of [\"url\", \"uploadUrl\", \"signedUrl\", \"preSignedUrl\", \"presignedUrl\", \"putUrl\"]) {\r\n const value = record[key];\r\n if (typeof value === \"string\" && value) return value;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n// The object key that `complete` finalizes MUST come from the server's presign\r\n// response — never guessed — or we could register a record that doesn't match\r\n// the bytes actually written to the (server-chosen) S3 key.\r\nfunction pickKey(data: unknown): string | undefined {\r\n if (data && typeof data === \"object\") {\r\n const record = data as Record<string, unknown>;\r\n for (const key of [\"key\", \"objectKey\", \"Key\", \"objectPath\", \"filePath\"]) {\r\n const value = record[key];\r\n if (typeof value === \"string\" && value) return value;\r\n }\r\n const fields = record[\"fields\"];\r\n if (fields && typeof fields === \"object\") {\r\n const nested = (fields as Record<string, unknown>)[\"key\"];\r\n if (typeof nested === \"string\" && nested) return nested;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n// ---- upload_file → POST /storage/upload/url then /storage/upload/complete (drive:write) ----\r\nconst uploadSchema = z.object({\r\n localPath: z.string().min(1).describe(\"Path to the local file to upload (absolute, or relative to the server's working directory).\"),\r\n destinationFolder: z.string().describe(\"Destination folder/prefix in CloudSee Drive. Empty = root.\"),\r\n fileName: z.string().optional().describe(\"Name to store the file as. Defaults to the local file's name.\"),\r\n contentType: z.string().optional().describe(\"MIME type. Defaults to application/octet-stream.\"),\r\n storageClass: z.string().optional().describe(\"Optional S3 storage class (e.g. STANDARD, INTELLIGENT_TIERING).\"),\r\n});\r\nconst uploadFile: ToolDef = {\r\n name: \"upload_file\",\r\n title: \"Upload file\",\r\n description:\r\n \"Upload a local file to a CloudSee Drive folder. Reads the file from the local machine, requests a pre-signed upload URL, uploads the bytes to storage, then finalizes the object.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/upload/url\", scopes: [\"drive:write\"] },\r\n inputSchema: uploadSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = uploadSchema.parse(args);\r\n const fileName = a.fileName ?? basename(a.localPath);\r\n const contentType = a.contentType ?? \"application/octet-stream\";\r\n\r\n let info;\r\n try {\r\n info = await stat(a.localPath);\r\n } catch {\r\n throw new CloudSeeError(`Local file not found: ${a.localPath}`, { code: \"file_not_found\" });\r\n }\r\n if (!info.isFile()) throw new CloudSeeError(`Not a regular file: ${a.localPath}`, { code: \"not_a_file\" });\r\n if (info.size > MULTIPART_THRESHOLD) {\r\n throw new CloudSeeError(\r\n `File is ${(info.size / 1048576).toFixed(1)} MiB. Multipart upload (>8 MiB) is not enabled in this release — upload a smaller file or use the CloudSee web app for large files.`,\r\n { code: \"too_large\" },\r\n );\r\n }\r\n\r\n const bytes = await readFile(a.localPath);\r\n const presign = await client.post<Record<string, unknown>>(\"/storage/upload/url\", {\r\n dirPath: a.destinationFolder,\r\n fileName,\r\n contentType,\r\n storageClass: a.storageClass,\r\n });\r\n const uploadUrl = pickUrl(presign);\r\n if (!uploadUrl) throw new CloudSeeError(\"The API did not return a usable upload URL.\", { code: \"no_upload_url\" });\r\n\r\n // Resolve the server-assigned key BEFORE uploading any bytes — if it's\r\n // absent we cannot finalize safely, so we fail without writing anything.\r\n const key = pickKey(presign);\r\n if (!key) {\r\n throw new CloudSeeError(\r\n \"The upload-URL response did not include the object key, so the upload cannot be finalized safely. (No bytes were uploaded.)\",\r\n { code: \"no_object_key\" },\r\n );\r\n }\r\n\r\n const put = await fetch(uploadUrl, { method: \"PUT\", headers: { \"Content-Type\": contentType }, body: new Uint8Array(bytes) });\r\n if (!put.ok) throw new CloudSeeError(`Upload to storage failed (HTTP ${put.status}).`, { status: put.status, code: \"upload_failed\" });\r\n\r\n let complete: unknown;\r\n try {\r\n complete = await client.post(\"/storage/upload/complete\", {\r\n dirPath: a.destinationFolder,\r\n objects: [{ fileName, key, contentType, size: info.size }],\r\n });\r\n } catch (err) {\r\n const reason = err instanceof Error ? err.message : String(err);\r\n throw new CloudSeeError(\r\n `Bytes were uploaded to storage but finalization failed: ${reason}. The object may exist without an index entry — retry the upload, or remove it via the CloudSee app.`,\r\n { code: \"finalize_failed\" },\r\n );\r\n }\r\n return textResult(`Uploaded \"${fileName}\" (${info.size} bytes) to \"${a.destinationFolder || \"/\"}\".\\n\\n${summarize(complete)}`);\r\n },\r\n};\r\n\r\n// ---- create_folder → POST /storage/folder/create (createFolder, drive:write) ----\r\nconst createFolderSchema = z.object({\r\n parentPath: z.string().describe(\"Parent folder/prefix. Empty = root.\"),\r\n name: z.string().min(1).describe(\"New folder name.\"),\r\n});\r\nconst createFolder: ToolDef = {\r\n name: \"create_folder\",\r\n title: \"Create folder\",\r\n description: \"Create a new folder at the given path.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/folder/create\", scopes: [\"drive:write\"] },\r\n inputSchema: createFolderSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = createFolderSchema.parse(args);\r\n const data = await client.post(\"/storage/folder/create\", { dirPath: a.parentPath, object: a.name });\r\n return textResult(`Created folder \"${a.name}\" in \"${a.parentPath || \"/\"}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- rename_file → POST /storage/object/rename (storageRename, drive:write) — destructive ----\r\nconst renameSchema = z.object({\r\n oldObjectId: z.string().min(1).describe(\"Current object key/id of the file or folder.\"),\r\n newName: z.string().min(1).describe(\"New name.\"),\r\n isFolder: z.boolean().optional().describe(\"Set true when renaming a folder.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n ...confirmShape,\r\n});\r\nconst renameFile: ToolDef = {\r\n name: \"rename_file\",\r\n title: \"Rename file or folder\",\r\n description: \"Rename a file or folder. Destructive (changes the object's key). Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/rename\", scopes: [\"drive:write\"] },\r\n inputSchema: renameSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = renameSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(`rename ${a.isFolder ? \"folder\" : \"file\"} to \"${a.newName}\"`, `Target: ${a.oldObjectId}`);\r\n }\r\n const data = await client.post(\"/storage/object/rename\", {\r\n oldObjectId: a.oldObjectId,\r\n newObjectName: a.newName,\r\n isFolder: a.isFolder ?? false,\r\n storageId: a.storageId,\r\n });\r\n return textResult(`Renamed to \"${a.newName}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- move_file → POST /storage/object/move (storageMoveObject, drive:write) — destructive unless asCopy ----\r\nconst moveSchema = z.object({\r\n sourcePath: z.string().min(1).describe(\"Source object key/prefix.\"),\r\n destinationPath: z.string().min(1).describe(\"Destination prefix.\"),\r\n asCopy: z.boolean().optional().describe(\"Copy instead of move (copy is non-destructive; the source is kept).\"),\r\n isFolder: z.boolean().optional().describe(\"Set true for a folder.\"),\r\n destinationBucket: z.string().optional().describe(\"Target bucket, if different.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n ...confirmShape,\r\n});\r\nconst moveFile: ToolDef = {\r\n name: \"move_file\",\r\n title: \"Move or copy file\",\r\n description:\r\n \"Move (or copy, with asCopy=true) a file or folder to a new location. A move removes the source and is destructive, so it requires confirm=true; a copy does not.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/move\", scopes: [\"drive:write\"] },\r\n inputSchema: moveSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = moveSchema.parse(args);\r\n const isMove = !a.asCopy;\r\n if (isMove && !a.confirm) {\r\n return confirmationPreview(`move \"${a.sourcePath}\" → \"${a.destinationPath}\"`, \"This removes the source. Pass asCopy=true to copy instead.\");\r\n }\r\n const data = await client.post(\"/storage/object/move\", {\r\n asCopy: a.asCopy ?? false,\r\n isFolder: a.isFolder ?? false,\r\n sourcePath: a.sourcePath,\r\n destinationPath: a.destinationPath,\r\n destinationBucket: a.destinationBucket,\r\n storageId: a.storageId,\r\n });\r\n return textResult(`${isMove ? \"Moved\" : \"Copied\"} \"${a.sourcePath}\" → \"${a.destinationPath}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- duplicate_file → POST /storage/object/duplicate (duplicateFile, drive:write) ----\r\nconst duplicateSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Source object key to duplicate.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst duplicateFile: ToolDef = {\r\n name: \"duplicate_file\",\r\n title: \"Duplicate file\",\r\n description: \"Create a copy of a file in the same location.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/duplicate\", scopes: [\"drive:write\"] },\r\n inputSchema: duplicateSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = duplicateSchema.parse(args);\r\n const data = await client.post(\"/storage/object/duplicate\", { objectKey: a.objectKey, storageId: a.storageId });\r\n return textResult(`Duplicated \"${a.objectKey}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- delete_files → POST /storage/objects/delete (deleteObjects, drive:delete) — destructive ----\r\nconst deleteSchema = z.object({\r\n objectKeys: z.array(z.string().min(1)).min(1).describe(\"Object keys (paths) to permanently delete.\"),\r\n ...confirmShape,\r\n});\r\nconst deleteFiles: ToolDef = {\r\n name: \"delete_files\",\r\n title: \"Delete files\",\r\n description: \"Permanently delete one or more files/objects by key. Destructive and irreversible. Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/objects/delete\", scopes: [\"drive:delete\"] },\r\n inputSchema: deleteSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = deleteSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s)`, a.objectKeys.map((k) => ` - ${k}`).join(\"\\n\"));\r\n }\r\n const data = await client.post(\"/storage/objects/delete\", { deletedObjects: a.objectKeys });\r\n return textResult(`Deleted ${a.objectKeys.length} object(s).\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- update_metadata → POST /storage/object/metadata (storageUpdateObjectMetadata, drive:write) — destructive ----\r\nconst updateMetaSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Object key whose metadata to update.\"),\r\n metadata: z.record(z.string(), z.unknown()).describe(\"Metadata fields to set (key/value map). Overwrites the listed fields.\"),\r\n ...confirmShape,\r\n});\r\nconst updateMetadata: ToolDef = {\r\n name: \"update_metadata\",\r\n title: \"Update file metadata\",\r\n description: \"Update a file's metadata. Destructive (overwrites the listed metadata fields). Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/metadata\", scopes: [\"drive:write\"] },\r\n inputSchema: updateMetaSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = updateMetaSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(`update metadata on \"${a.objectKey}\"`, `Fields: ${Object.keys(a.metadata).join(\", \") || \"(none)\"}`);\r\n }\r\n const data = await client.post(\"/storage/object/metadata\", { objectKey: a.objectKey, metadata: a.metadata });\r\n return textResult(`Updated metadata on \"${a.objectKey}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\n// ---- restore_archived_file → POST /storage/object/restore (restoreObject, drive:write) — Glacier un-archive ----\r\nconst restoreSchema = z.object({\r\n objectKey: z.string().min(1).describe(\"Object key of the archived (Glacier) object to restore.\"),\r\n days: z.number().int().min(1).optional().describe(\"How many days to keep the restored copy available.\"),\r\n retrievalTier: z.enum([\"Expedited\", \"Standard\", \"Bulk\"]).optional().describe(\"Glacier retrieval tier (default Standard).\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n ...confirmShape,\r\n});\r\nconst restoreArchivedFile: ToolDef = {\r\n name: \"restore_archived_file\",\r\n title: \"Restore archived file\",\r\n description:\r\n \"Begin restoring an archived (S3 Glacier) object so it can be downloaded. This is Glacier un-archiving — NOT recovery of a deleted file — and may incur retrieval cost and take minutes to hours. Requires confirm=true.\" + GATED,\r\n endpoint: { method: \"POST\", path: \"/storage/object/restore\", scopes: [\"drive:write\"] },\r\n inputSchema: restoreSchema.shape,\r\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = restoreSchema.parse(args);\r\n if (!a.confirm) {\r\n return confirmationPreview(\r\n `restore archived object \"${a.objectKey}\"`,\r\n `Tier: ${a.retrievalTier ?? \"Standard\"}${a.days ? `, ${a.days} day(s)` : \"\"}. May incur retrieval cost.`,\r\n );\r\n }\r\n const data = await client.post(\"/storage/object/restore\", {\r\n objectKey: a.objectKey,\r\n days: a.days,\r\n retrievalTier: a.retrievalTier,\r\n storageId: a.storageId,\r\n });\r\n return textResult(`Restore initiated for \"${a.objectKey}\".\\n\\n${summarize(data)}`);\r\n },\r\n};\r\n\r\nexport const writeTools: ToolDef[] = [\r\n uploadFile,\r\n createFolder,\r\n renameFile,\r\n moveFile,\r\n duplicateFile,\r\n deleteFiles,\r\n updateMetadata,\r\n restoreArchivedFile,\r\n];\r\n","import { z } from \"zod\";\r\nimport { textResult, type ToolResult } from \"./tools/types\";\r\n\r\n// Two-step confirmation for destructive tools. A first call WITHOUT confirm=true\r\n// returns a human-readable preview and performs no mutation; the model (or user)\r\n// must re-call with confirm=true to execute. This is UX, NOT the security\r\n// boundary — the CloudSee API authorizes every operation server-side (CSD-572).\r\n\r\nexport const confirmShape = {\r\n confirm: z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n \"Must be true to actually perform this mutating/irreversible action. If omitted or false, the tool returns a preview and makes no changes.\",\r\n ),\r\n};\r\n\r\nconst SECURITY_NOTE =\r\n \"Note: this confirmation is a client-side safety prompt, not the security boundary — \" +\r\n \"the CloudSee API authorizes every operation server-side.\";\r\n\r\n/** The no-op preview returned when a destructive tool is called without confirm=true. */\r\nexport function confirmationPreview(action: string, details: string): ToolResult {\r\n return textResult(\r\n `⚠️ Confirmation required — no changes have been made.\\n\\n` +\r\n `About to: ${action}\\n${details}\\n\\n` +\r\n `Re-run this tool with confirm=true to proceed.\\n${SECURITY_NOTE}`,\r\n );\r\n}\r\n","import type { ToolDef } from \"./types\";\r\nimport { readTools } from \"./read\";\r\nimport { downloadTools } from \"./download\";\r\nimport { writeTools } from \"./write\";\r\n\r\n/** The complete, grounded tool set (CSD-586 §2.4). Read + download tools are\r\n * callable today; write/delete tools are present but gated on CSD-572 RBAC. */\r\nexport const allTools: ToolDef[] = [...readTools, ...downloadTools, ...writeTools];\r\n\r\nexport type { ToolDef } from \"./types\";\r\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/errors.ts","../src/logger.ts","../src/server.ts","../src/version.ts","../src/client/pagination.ts","../src/client/CloudSeeClient.ts","../src/tools/read.ts","../src/tools/format.ts","../src/tools/types.ts","../src/tools/download.ts","../src/tools/write.ts","../src/confirm.ts","../src/tools/index.ts"],"sourcesContent":["import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\r\nimport { loadConfig, type Config } from \"./config\";\r\nimport { configureLogger, logger } from \"./logger\";\r\nimport { createServer } from \"./server\";\r\nimport { VERSION } from \"./version\";\r\n\r\nfunction loadConfigOrExit(): Config {\r\n try {\r\n return loadConfig();\r\n } catch (err) {\r\n // Config errors are fatal and printed to stderr (not stdout — the transport).\r\n process.stderr.write(`\\n${err instanceof Error ? err.message : String(err)}\\n\\n`);\r\n process.exit(1);\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const config = loadConfigOrExit();\r\n // Register the secret for redaction before anything else runs.\r\n configureLogger({ level: config.logLevel, redact: [config.apiKeySecret] });\r\n\r\n const server = createServer(config);\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n\r\n logger.info(`cloudsee-drive-mcp v${VERSION} ready on stdio (base: ${config.baseUrl})`);\r\n}\r\n\r\nmain().catch((err) => {\r\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`);\r\n process.exit(1);\r\n});\r\n","import { z } from \"zod\";\r\nimport { CloudSeeError } from \"./errors\";\r\nimport type { LogLevel } from \"./logger\";\r\n\r\nexport const DEFAULT_BASE_URL = \"https://drive-api.cloudsee.cloud\";\r\nexport const DEFAULT_TIMEOUT_MS = 30_000;\r\n\r\nexport interface Config {\r\n apiKeyId: string;\r\n apiKeySecret: string;\r\n baseUrl: string;\r\n defaultBucket?: string;\r\n timeoutMs: number;\r\n logLevel: LogLevel;\r\n}\r\n\r\nconst envSchema = z.object({\r\n CLOUDSEE_API_KEY_ID: z.string().trim().min(1, \"is required (your CloudSee Drive API key id)\"),\r\n CLOUDSEE_API_KEY_SECRET: z.string().trim().min(1, \"is required (the matching API key secret)\"),\r\n CLOUDSEE_API_BASE_URL: z.string().url(\"must be a valid URL\").optional(),\r\n CLOUDSEE_DEFAULT_BUCKET: z.string().trim().min(1).optional(),\r\n CLOUDSEE_TIMEOUT_MS: z.coerce.number().int().positive().optional(),\r\n CLOUDSEE_LOG_LEVEL: z.enum([\"error\", \"warn\", \"info\", \"debug\"]).optional(),\r\n});\r\n\r\n/**\r\n * Load and validate configuration from the environment. Throws a CloudSeeError\r\n * with an actionable, secret-free message when required vars are missing.\r\n */\r\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\r\n const parsed = envSchema.safeParse(env);\r\n if (!parsed.success) {\r\n const issues = parsed.error.issues.map((i) => ` • ${i.path.join(\".\")} ${i.message}`).join(\"\\n\");\r\n throw new CloudSeeError(\r\n `CloudSee Drive MCP server is not configured:\\n${issues}\\n\\n` +\r\n `Set the required environment variables (see .env.example or the README).`,\r\n { code: \"config\" },\r\n );\r\n }\r\n const e = parsed.data;\r\n return {\r\n apiKeyId: e.CLOUDSEE_API_KEY_ID,\r\n apiKeySecret: e.CLOUDSEE_API_KEY_SECRET,\r\n baseUrl: (e.CLOUDSEE_API_BASE_URL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\"),\r\n defaultBucket: e.CLOUDSEE_DEFAULT_BUCKET,\r\n timeoutMs: e.CLOUDSEE_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS,\r\n logLevel: e.CLOUDSEE_LOG_LEVEL ?? \"info\",\r\n };\r\n}\r\n","export interface CloudSeeErrorOptions {\r\n code?: string;\r\n status?: number;\r\n retryable?: boolean;\r\n}\r\n\r\n/** Error raised for any failed CloudSee API interaction. Messages are redacted\r\n * by the client before construction and must never contain the API secret. */\r\nexport class CloudSeeError extends Error {\r\n readonly code: string;\r\n readonly status?: number;\r\n readonly retryable: boolean;\r\n\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message);\r\n this.name = \"CloudSeeError\";\r\n this.code = options.code ?? \"error\";\r\n this.status = options.status;\r\n this.retryable = options.retryable ?? false;\r\n }\r\n}\r\n\r\n/** Authentication/authorization failure (HTTP 401/403). Terminal — never retried. */\r\nexport class AuthError extends CloudSeeError {\r\n constructor(message: string, options: CloudSeeErrorOptions = {}) {\r\n super(message, { ...options, code: options.code ?? \"auth\" });\r\n this.name = \"AuthError\";\r\n }\r\n}\r\n","// Structured logging that writes to **stderr only**. stdout is reserved for the\r\n// MCP stdio transport — anything written there corrupts the protocol stream.\r\n// All output is passed through secret redaction as a defense-in-depth backstop;\r\n// the real rule is to never hand the API secret to the logger in the first place.\r\n\r\nexport type LogLevel = \"error\" | \"warn\" | \"info\" | \"debug\";\r\n\r\nconst LEVEL_WEIGHT: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 };\r\n\r\nlet currentLevel: LogLevel = \"info\";\r\nlet secrets: string[] = [];\r\n\r\nexport function configureLogger(options: { level?: LogLevel; redact?: string[] }): void {\r\n if (options.level) currentLevel = options.level;\r\n if (options.redact) {\r\n secrets = options.redact.filter((s): s is string => typeof s === \"string\" && s.length > 0);\r\n }\r\n}\r\n\r\n/** Replace any registered secret with `***` anywhere it appears in `text`. */\r\nexport function redactSecrets(text: string): string {\r\n let out = text;\r\n for (const secret of secrets) {\r\n if (out.includes(secret)) out = out.split(secret).join(\"***\");\r\n }\r\n return out;\r\n}\r\n\r\nfunction stringify(meta: unknown): string {\r\n if (meta === undefined) return \"\";\r\n if (typeof meta === \"string\") return meta;\r\n try {\r\n return JSON.stringify(meta);\r\n } catch {\r\n return String(meta);\r\n }\r\n}\r\n\r\nfunction emit(level: LogLevel, message: string, meta?: unknown): void {\r\n if (LEVEL_WEIGHT[level] > LEVEL_WEIGHT[currentLevel]) return;\r\n const suffix = meta === undefined ? \"\" : ` ${stringify(meta)}`;\r\n process.stderr.write(`[cloudsee-drive-mcp] ${level.toUpperCase()}: ${redactSecrets(message + suffix)}\\n`);\r\n}\r\n\r\nexport const logger = {\r\n error: (message: string, meta?: unknown): void => emit(\"error\", message, meta),\r\n warn: (message: string, meta?: unknown): void => emit(\"warn\", message, meta),\r\n info: (message: string, meta?: unknown): void => emit(\"info\", message, meta),\r\n debug: (message: string, meta?: unknown): void => emit(\"debug\", message, meta),\r\n};\r\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\r\nimport { CloudSeeClient } from \"./client/CloudSeeClient\";\r\nimport { allTools } from \"./tools/index\";\r\nimport { logger } from \"./logger\";\r\nimport { VERSION } from \"./version\";\r\nimport type { Config } from \"./config\";\r\n\r\n/**\r\n * Build the MCP server: one CloudSeeClient shared across all tools, each tool\r\n * registered with its Zod input schema and MCP annotations. Tool handler errors\r\n * are caught and returned as `isError` results (never thrown across the\r\n * transport), and are logged to stderr with the secret redacted.\r\n */\r\nexport function createServer(config: Config): McpServer {\r\n const client = new CloudSeeClient(config);\r\n const server = new McpServer({ name: \"cloudsee-drive-mcp\", version: VERSION });\r\n\r\n for (const tool of allTools) {\r\n server.registerTool(\r\n tool.name,\r\n {\r\n title: tool.title,\r\n description: tool.description,\r\n inputSchema: tool.inputSchema,\r\n annotations: { title: tool.title, ...tool.annotations },\r\n },\r\n async (args: Record<string, unknown>) => {\r\n try {\r\n return await tool.handler(args ?? {}, { client, defaultBucket: config.defaultBucket });\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n logger.error(`tool \"${tool.name}\" failed`, message);\r\n return { content: [{ type: \"text\" as const, text: `Error: ${message}` }], isError: true };\r\n }\r\n },\r\n );\r\n }\r\n\r\n logger.info(`registered ${allTools.length} tools`);\r\n return server;\r\n}\r\n","// Single source of truth for the package version, surfaced to the MCP client\r\n// handshake and the User-Agent. Kept in sync with package.json by release tooling.\r\nexport const VERSION = \"0.1.0\";\r\n","import { CloudSeeError } from \"../errors\";\r\n\r\n// The data plane uses five different pagination param names across endpoint\r\n// families (C10): `nextPage`, `marker`, `lastEvaluatedKey`, `pageToken`,\r\n// `nextToken`. Tools expose a single opaque `cursor`; this module encodes the\r\n// dialect + raw token into that cursor and decodes it back, so a cursor minted\r\n// for one operation can't be misapplied to another.\r\n\r\nexport type PaginationDialect = \"nextPage\" | \"marker\" | \"lastEvaluatedKey\" | \"pageToken\" | \"nextToken\";\r\n\r\ninterface CursorPayload {\r\n d: PaginationDialect;\r\n v: string;\r\n}\r\n\r\nexport function encodeCursor(dialect: PaginationDialect, value: string | null | undefined): string | undefined {\r\n if (value === null || value === undefined || value === \"\") return undefined;\r\n const payload: CursorPayload = { d: dialect, v: String(value) };\r\n return Buffer.from(JSON.stringify(payload), \"utf8\").toString(\"base64url\");\r\n}\r\n\r\nexport function decodeCursor(cursor: string, expected: PaginationDialect): string {\r\n let payload: CursorPayload;\r\n try {\r\n payload = JSON.parse(Buffer.from(cursor, \"base64url\").toString(\"utf8\")) as CursorPayload;\r\n } catch {\r\n throw new CloudSeeError(\"Invalid pagination cursor.\", { code: \"bad_cursor\" });\r\n }\r\n if (!payload || typeof payload.v !== \"string\" || payload.d !== expected) {\r\n throw new CloudSeeError(\"Pagination cursor is not valid for this operation.\", { code: \"bad_cursor\" });\r\n }\r\n return payload.v;\r\n}\r\n\r\nconst RESPONSE_FIELDS = [\"nextPage\", \"nextPageToken\", \"marker\", \"lastEvaluatedKey\", \"pageToken\", \"nextToken\"];\r\n\r\n/**\r\n * Pull the raw continuation token out of a response envelope's `data`, tolerant\r\n * of the exact field name (which varies by endpoint and isn't guaranteed by the\r\n * RPC contract). Prefers the operation's own dialect field, then known aliases.\r\n */\r\nexport function extractNextToken(data: unknown, dialect: PaginationDialect): string | undefined {\r\n if (!data || typeof data !== \"object\") return undefined;\r\n const record = data as Record<string, unknown>;\r\n for (const field of [dialect, ...RESPONSE_FIELDS]) {\r\n const value = record[field];\r\n if (typeof value === \"string\" && value !== \"\") return value;\r\n if (value && typeof value === \"object\") {\r\n try {\r\n return JSON.stringify(value);\r\n } catch {\r\n /* ignore non-serializable */\r\n }\r\n }\r\n }\r\n return undefined;\r\n}\r\n","import { AuthError, CloudSeeError } from \"../errors\";\r\nimport { logger } from \"../logger\";\r\nimport { VERSION } from \"../version\";\r\nimport type { Config } from \"../config\";\r\nimport { decodeCursor, encodeCursor, extractNextToken, type PaginationDialect } from \"./pagination\";\r\n\r\ninterface Envelope<T> {\r\n success?: boolean;\r\n data?: T;\r\n errorMessage?: string;\r\n code?: string;\r\n}\r\n\r\nexport interface RequestOptions {\r\n retries?: number;\r\n signal?: AbortSignal;\r\n}\r\n\r\nexport interface PagedResult<T> {\r\n data: T;\r\n nextCursor?: string;\r\n}\r\n\r\ninterface ParsedResponse<T> {\r\n data: T;\r\n envelope: Record<string, unknown> | undefined;\r\n}\r\n\r\nconst DEFAULT_RETRIES = 3;\r\nconst BASE_BACKOFF_MS = 300;\r\nconst MAX_BACKOFF_MS = 8_000;\r\n\r\n/**\r\n * Typed wrapper over the drive-bridge `/v1/*` data plane. Every endpoint is an\r\n * RPC `POST /noun/verb` returning the platform `{ success, data, errorMessage,\r\n * code }` envelope. The client:\r\n * - sets the `X-Api-Key-Id` / `X-Api-Key-Secret` auth headers from config,\r\n * - retries 429/5xx with exponential backoff honoring `Retry-After` (the\r\n * `csd-mcp` usage plan is dormant, so the server must self-throttle — C6),\r\n * - unwraps the envelope and surfaces `success:false` as a CloudSeeError,\r\n * - never logs the secret and redacts it from any error message.\r\n */\r\nexport class CloudSeeClient {\r\n private readonly baseUrl: string;\r\n private readonly timeoutMs: number;\r\n private readonly secret: string;\r\n private readonly headers: Record<string, string>;\r\n\r\n constructor(config: Config) {\r\n this.baseUrl = config.baseUrl;\r\n this.timeoutMs = config.timeoutMs;\r\n this.secret = config.apiKeySecret;\r\n this.headers = {\r\n \"Content-Type\": \"application/json\",\r\n Accept: \"application/json\",\r\n \"X-Api-Key-Id\": config.apiKeyId,\r\n \"X-Api-Key-Secret\": config.apiKeySecret,\r\n \"User-Agent\": `cloudsee-drive-mcp/${VERSION}`,\r\n };\r\n }\r\n\r\n /** POST a data-plane RPC endpoint (path WITHOUT `/v1`). Returns unwrapped `data`. */\r\n async post<T = unknown>(path: string, body: Record<string, unknown> = {}, options: RequestOptions = {}): Promise<T> {\r\n return (await this.request<T>(path, body, options)).data;\r\n }\r\n\r\n /** POST a paginated endpoint. Translates the opaque `cursor` to/from the\r\n * endpoint's pagination dialect and returns a normalized `nextCursor`. The\r\n * continuation token can sit at the envelope level (e.g. `/storage/recent`\r\n * returns `nextToken` as a sibling of `data`) or inside `data` — check the\r\n * envelope first, then `data`, so a token is never silently dropped. */\r\n async postPaged<T = unknown>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n dialect: PaginationDialect,\r\n cursor?: string,\r\n options: RequestOptions = {},\r\n ): Promise<PagedResult<T>> {\r\n const requestBody = { ...body };\r\n if (cursor) requestBody[dialect] = decodeCursor(cursor, dialect);\r\n const { data, envelope } = await this.request<T>(path, requestBody, options);\r\n const token = extractNextToken(envelope, dialect) ?? extractNextToken(data, dialect);\r\n return { data, nextCursor: encodeCursor(dialect, token) };\r\n }\r\n\r\n /** Run the request with retry/backoff, returning the unwrapped `data` together\r\n * with the raw response envelope (needed for envelope-level pagination tokens). */\r\n private async request<T>(path: string, body: Record<string, unknown>, options: RequestOptions): Promise<ParsedResponse<T>> {\r\n const url = `${this.baseUrl}/v1${path}`;\r\n const retries = options.retries ?? DEFAULT_RETRIES;\r\n let attempt = 0;\r\n for (;;) {\r\n attempt += 1;\r\n const response = await this.fetchOnce(url, path, body, options.signal);\r\n\r\n if (response.status === 401 || response.status === 403) {\r\n throw new AuthError(\r\n \"Authentication failed (HTTP \" +\r\n response.status +\r\n \"). Check CLOUDSEE_API_KEY_ID and CLOUDSEE_API_KEY_SECRET, and that the key is active and not expired.\",\r\n { status: response.status },\r\n );\r\n }\r\n\r\n if ((response.status === 429 || response.status >= 500) && attempt <= retries) {\r\n const waitMs = retryAfterMs(response.headers.get(\"retry-after\")) ?? backoffMs(attempt);\r\n logger.warn(`HTTP ${response.status} from ${path}; retrying in ${waitMs}ms (attempt ${attempt}/${retries}).`);\r\n await sleep(waitMs);\r\n continue;\r\n }\r\n\r\n return await this.parse<T>(response, path);\r\n }\r\n }\r\n\r\n private async fetchOnce(\r\n url: string,\r\n path: string,\r\n body: Record<string, unknown>,\r\n external?: AbortSignal,\r\n ): Promise<Response> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\r\n const onAbort = (): void => controller.abort();\r\n if (external) external.addEventListener(\"abort\", onAbort, { once: true });\r\n try {\r\n return await fetch(url, {\r\n method: \"POST\",\r\n headers: this.headers,\r\n body: JSON.stringify(body),\r\n signal: controller.signal,\r\n });\r\n } catch (err) {\r\n const reason = controller.signal.aborted ? `timed out after ${this.timeoutMs}ms` : this.redact(errMessage(err));\r\n throw new CloudSeeError(`Network error calling ${path}: ${reason}`, { code: \"network\", retryable: true });\r\n } finally {\r\n clearTimeout(timer);\r\n if (external) external.removeEventListener(\"abort\", onAbort);\r\n }\r\n }\r\n\r\n private async parse<T>(response: Response, path: string): Promise<ParsedResponse<T>> {\r\n const text = await response.text();\r\n let json: Envelope<T> | undefined;\r\n if (text) {\r\n try {\r\n json = JSON.parse(text) as Envelope<T>;\r\n } catch {\r\n throw new CloudSeeError(\r\n response.ok\r\n ? `CloudSee API returned a non-JSON response for ${path}.`\r\n : `CloudSee API returned HTTP ${response.status} for ${path}.`,\r\n { status: response.status, code: response.ok ? \"bad_response\" : \"http_error\" },\r\n );\r\n }\r\n }\r\n if (!response.ok) {\r\n const message = json?.errorMessage ?? `HTTP ${response.status}`;\r\n throw new CloudSeeError(`CloudSee API error for ${path}: ${this.redact(message)}`, {\r\n status: response.status,\r\n code: json?.code ?? \"http_error\",\r\n });\r\n }\r\n if (json && typeof json === \"object\" && json.success === false) {\r\n throw new CloudSeeError(this.redact(json.errorMessage || `Operation failed (${json.code ?? \"unknown\"}).`), {\r\n code: json.code ?? \"operation_failed\",\r\n status: response.status,\r\n });\r\n }\r\n const envelope = json && typeof json === \"object\" ? (json as Record<string, unknown>) : undefined;\r\n if (json && typeof json === \"object\" && \"data\" in json) return { data: json.data as T, envelope };\r\n return { data: (json ?? ({} as T)) as T, envelope };\r\n }\r\n\r\n private redact(text: string): string {\r\n return this.secret && text.includes(this.secret) ? text.split(this.secret).join(\"***\") : text;\r\n }\r\n}\r\n\r\nfunction errMessage(err: unknown): string {\r\n return err instanceof Error ? err.message : String(err);\r\n}\r\n\r\nfunction backoffMs(attempt: number): number {\r\n const ceiling = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** (attempt - 1));\r\n // Full jitter over [ceiling/2, ceiling] to avoid thundering-herd retries.\r\n return Math.round(ceiling / 2 + Math.random() * (ceiling / 2));\r\n}\r\n\r\nfunction retryAfterMs(header: string | null): number | undefined {\r\n if (!header) return undefined;\r\n const seconds = Number(header);\r\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\r\n const date = Date.parse(header);\r\n if (Number.isFinite(date)) return Math.max(0, date - Date.now());\r\n return undefined;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize, withCursor } from \"./format\";\r\nimport { bucketField, resolveBucket, textResult, type ToolDef } from \"./types\";\r\n\r\n// ---- list_buckets → POST /storage/buckets (storageBucketList, drive:read) ----\r\nconst listBuckets: ToolDef = {\r\n name: \"list_buckets\",\r\n title: \"List buckets\",\r\n description:\r\n \"List the storage buckets (Amazon S3 buckets) the authenticated CloudSee Drive account can access. Use this first to discover available buckets before browsing or searching.\",\r\n endpoint: { method: \"POST\", path: \"/storage/buckets\", scopes: [\"drive:read\"] },\r\n inputSchema: {},\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (_args, { client }) => {\r\n const data = await client.post(\"/storage/buckets\", {});\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- browse_folder → POST /storage/list (storageList, drive:read) ----\r\nconst browseSchema = z.object({\r\n bucketName: bucketField,\r\n path: z.string().optional().describe(\"Folder path / prefix within the drive to list. Empty or omitted = the drive root.\"),\r\n sortOption: z.string().optional().describe(\"Sort key, e.g. 'name_asc', 'name_desc', 'date_desc'.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst browseFolder: ToolDef = {\r\n name: \"browse_folder\",\r\n title: \"Browse folder\",\r\n description:\r\n \"List the files and sub-folders inside a folder of a drive (the indexed view), with sorting and pagination. Requires the drive (bucketName). To filter by keyword use 'search_files'; for a complete, recursive file listing straight from storage use 'list_files'.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: browseSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = browseSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { bucketName, dirPath: a.path ?? \"\", sortOption: a.sortOption, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- search_files → POST /storage/list with searchingKeyword (drive:read) ----\r\nconst searchSchema = z.object({\r\n bucketName: bucketField,\r\n query: z.string().min(1).describe(\"Keyword to match against file and folder names.\"),\r\n path: z.string().optional().describe(\"Folder within the drive to search under. Empty = drive root.\"),\r\n pageSize: z.number().int().min(1).max(200).optional().describe(\"Max items per page (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst searchFiles: ToolDef = {\r\n name: \"search_files\",\r\n title: \"Search files\",\r\n description:\r\n \"Search for files and folders by name keyword within a drive (the indexed view). Requires the drive (bucketName). Returns matches with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/list\", scopes: [\"drive:read\"] },\r\n inputSchema: searchSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = searchSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/list\",\r\n { bucketName, dirPath: a.path ?? \"\", searchingKeyword: a.query, pageSize: a.pageSize ?? 50 },\r\n \"nextPage\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- list_files → POST /storage/bucket/files (getAllFilesInTheBucket, drive:read) ----\r\nconst listFilesSchema = z.object({\r\n bucketName: bucketField,\r\n deep: z.boolean().optional().describe(\"Recurse into sub-folders (default true).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst listFiles: ToolDef = {\r\n name: \"list_files\",\r\n title: \"List files in a drive\",\r\n description:\r\n \"List the files in a drive straight from storage, recursively by default — the most reliable way to see what a drive actually contains. Requires the drive (bucketName). Returns names, sizes, storage classes and keys, with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/bucket/files\", scopes: [\"drive:read\"] },\r\n inputSchema: listFilesSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = listFilesSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const { data, nextCursor } = await client.postPaged(\r\n \"/storage/bucket/files\",\r\n { bucketName, deepQuery: a.deep ?? true },\r\n \"marker\",\r\n a.cursor,\r\n );\r\n return textResult(withCursor(summarize(data), nextCursor));\r\n },\r\n};\r\n\r\n// ---- recent_files → POST /storage/recent (storageRecentFiles, drive:read) ----\r\nconst recentSchema = z.object({\r\n limit: z.number().int().min(1).max(200).optional().describe(\"Max items (1-200, default 50).\"),\r\n cursor: z.string().optional().describe(\"Opaque pagination cursor returned by a previous call.\"),\r\n});\r\nconst recentFiles: ToolDef = {\r\n name: \"recent_files\",\r\n title: \"Recent files\",\r\n description: \"List the account's most recently accessed or modified files, newest first, with pagination.\",\r\n endpoint: { method: \"POST\", path: \"/storage/recent\", scopes: [\"drive:read\"] },\r\n inputSchema: recentSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client }) => {\r\n const a = recentSchema.parse(args);\r\n const limit = a.limit ?? 50;\r\n // /storage/recent paginates with `nextToken` (a composite key returned at the\r\n // envelope level), not `nextPage`. It echoes that token even when the list is\r\n // exhausted, so only advertise a next page when this page came back full.\r\n const { data, nextCursor } = await client.postPaged(\"/storage/recent\", { limit }, \"nextToken\", a.cursor);\r\n const hasMore = Array.isArray(data) && data.length >= limit;\r\n return textResult(withCursor(summarize(data), hasMore ? nextCursor : undefined));\r\n },\r\n};\r\n\r\n// ---- get_file_metadata → POST /storage/object/detail (storageDetail, drive:read) ----\r\nconst metaSchema = z.object({\r\n bucketName: bucketField,\r\n objectKey: z.string().min(1).describe(\"Full object key (path) of the file within the drive.\"),\r\n});\r\nconst getFileMetadata: ToolDef = {\r\n name: \"get_file_metadata\",\r\n title: \"Get file metadata\",\r\n description:\r\n \"Get detailed metadata for a single file or object (size, type, timestamps, storage class, and other attributes) by its object key. Requires the drive (bucketName).\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/detail\", scopes: [\"drive:read\"] },\r\n inputSchema: metaSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = metaSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/detail\", { bucketName, objectKey: a.objectKey });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- get_file_tags → POST /storage/object/tagging (getObjectTagging, drive:read) ----\r\nconst tagsSchema = z.object({\r\n bucketName: bucketField,\r\n objectKey: z.string().min(1).describe(\"Full object key (path) of the file within the drive.\"),\r\n});\r\nconst getFileTags: ToolDef = {\r\n name: \"get_file_tags\",\r\n title: \"Get file tags\",\r\n description: \"Get the S3 object tags (key/value pairs) attached to a file, by object key. Requires the drive (bucketName).\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/tagging\", scopes: [\"drive:read\"] },\r\n inputSchema: tagsSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = tagsSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/tagging\", { bucketName, objectKey: a.objectKey });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const readTools: ToolDef[] = [\r\n listBuckets,\r\n browseFolder,\r\n searchFiles,\r\n listFiles,\r\n recentFiles,\r\n getFileMetadata,\r\n getFileTags,\r\n];\r\n","// Output bounding so large listings never flood the model context (R8 / AC).\r\n// Arrays are capped, deeply, and the whole rendering is hard-capped by length.\r\n\r\nconst DEFAULT_MAX_ITEMS = 100;\r\nconst DEFAULT_MAX_CHARS = 8_000;\r\n\r\nexport function summarize(data: unknown, opts: { maxItems?: number; maxChars?: number } = {}): string {\r\n const maxItems = opts.maxItems ?? DEFAULT_MAX_ITEMS;\r\n const maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;\r\n let json: string;\r\n try {\r\n json = JSON.stringify(boundArrays(data, maxItems), null, 2);\r\n } catch {\r\n json = String(data);\r\n }\r\n if (json.length > maxChars) {\r\n json = `${json.slice(0, maxChars)}\\n… (output truncated at ${maxChars} characters — narrow your query or paginate).`;\r\n }\r\n return json;\r\n}\r\n\r\nfunction boundArrays(value: unknown, maxItems: number): unknown {\r\n if (Array.isArray(value)) {\r\n const capped: unknown[] = value.slice(0, maxItems).map((v) => boundArrays(v, maxItems));\r\n if (value.length > maxItems) {\r\n capped.push(`… ${value.length - maxItems} more item(s) omitted — paginate for the rest.`);\r\n }\r\n return capped;\r\n }\r\n if (value && typeof value === \"object\") {\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\r\n out[k] = boundArrays(v, maxItems);\r\n }\r\n return out;\r\n }\r\n return value;\r\n}\r\n\r\n/** Append a continuation hint when the API returned another page. */\r\nexport function withCursor(body: string, nextCursor?: string): string {\r\n if (!nextCursor) return body;\r\n return `${body}\\n\\n↪ More results available. Call this tool again with cursor=\"${nextCursor}\" for the next page.`;\r\n}\r\n","import { z, type ZodRawShape } from \"zod\";\r\nimport type { CloudSeeClient } from \"../client/CloudSeeClient\";\r\nimport { CloudSeeError } from \"../errors\";\r\n\r\nexport interface ToolContext {\r\n client: CloudSeeClient;\r\n /** Optional configured default drive (CLOUDSEE_DEFAULT_BUCKET); never hardcoded. */\r\n defaultBucket?: string;\r\n}\r\n\r\n/** Reusable Zod field for the drive/bucket selector. Optional in the schema so a\r\n * configured CLOUDSEE_DEFAULT_BUCKET can supply it; `resolveBucket` enforces presence. */\r\nexport const bucketField = z\r\n .string()\r\n .min(1)\r\n .optional()\r\n .describe(\r\n \"The CloudSee drive (S3 bucket) name to operate in, e.g. 'max-2778abc0' — find it in the CloudSee dashboard. Required unless CLOUDSEE_DEFAULT_BUCKET is configured on the server.\",\r\n );\r\n\r\n/** Resolve the drive/bucket from the parsed tool input, falling back to the\r\n * configured default. The value is always taken from input or config — never\r\n * hardcoded — and an actionable error is thrown when neither is present. */\r\nexport function resolveBucket(bucket: string | undefined, defaultBucket: string | undefined): string {\r\n const resolved = (bucket ?? defaultBucket ?? \"\").trim();\r\n if (!resolved) {\r\n throw new CloudSeeError(\r\n \"No CloudSee drive specified. Pass `bucketName` (your drive name, shown in the CloudSee dashboard), \" +\r\n \"or set CLOUDSEE_DEFAULT_BUCKET in the server config.\",\r\n { code: \"no_bucket\" },\r\n );\r\n }\r\n return resolved;\r\n}\r\n\r\nexport interface ToolResult {\r\n // Index signature mirrors the MCP SDK's CallToolResult (which permits extra\r\n // fields like _meta); lets ToolResult satisfy the registerTool callback type.\r\n [x: string]: unknown;\r\n content: Array<{ type: \"text\"; text: string }>;\r\n isError?: boolean;\r\n}\r\n\r\nexport interface ToolAnnotations {\r\n readOnlyHint?: boolean;\r\n destructiveHint?: boolean;\r\n idempotentHint?: boolean;\r\n openWorldHint?: boolean;\r\n}\r\n\r\n/** The real data-plane endpoint a tool wraps — asserted against the committed\r\n * contract snapshot by the drift test. `path` excludes the `/v1` prefix. */\r\nexport interface ToolEndpoint {\r\n method: \"POST\";\r\n path: string;\r\n scopes: string[];\r\n}\r\n\r\nexport interface ToolDef {\r\n name: string;\r\n title: string;\r\n description: string;\r\n endpoint: ToolEndpoint;\r\n inputSchema: ZodRawShape;\r\n annotations: ToolAnnotations;\r\n handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<ToolResult>;\r\n}\r\n\r\nexport function textResult(text: string): ToolResult {\r\n return { content: [{ type: \"text\", text }] };\r\n}\r\n","import { z } from \"zod\";\r\nimport { summarize } from \"./format\";\r\nimport { bucketField, resolveBucket, textResult, type ToolDef } from \"./types\";\r\n\r\n// Both tools wrap POST /storage/object/download-url (getObjectUrl, drive:read +\r\n// drive:download). The API returns a short-lived pre-signed URL, never AWS\r\n// credentials. We surface the URL (a capability link, not a secret); the model\r\n// can hand it to the user. We never download bytes into the conversation.\r\n\r\n// ---- download_file ----\r\nconst downloadSchema = z.object({\r\n bucketName: bucketField,\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to download, within the drive.\"),\r\n forceDownload: z.boolean().optional().describe(\"If true, the link forces an attachment download instead of inline view.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst downloadFile: ToolDef = {\r\n name: \"download_file\",\r\n title: \"Download file\",\r\n description:\r\n \"Get a short-lived pre-signed download URL for a file. Requires the drive (bucketName). The URL is time-limited and grants read access to that one object — share it with care. Returns the URL; it does not load file bytes into the conversation.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: downloadSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = downloadSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n bucketName,\r\n filePath: a.filePath,\r\n download: a.forceDownload ?? false,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\n// ---- share_link ----\r\nconst shareSchema = z.object({\r\n bucketName: bucketField,\r\n filePath: z.string().min(1).describe(\"Object key (path) of the file to share, within the drive.\"),\r\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\r\n});\r\nconst shareLink: ToolDef = {\r\n name: \"share_link\",\r\n title: \"Create share link\",\r\n description:\r\n \"Create a shareable, time-limited link to a file (a pre-signed URL suitable for sharing). Requires the drive (bucketName). For richer share management (revocation, folder/prefix shares, expiry control) use the CloudSee dashboard.\",\r\n endpoint: { method: \"POST\", path: \"/storage/object/download-url\", scopes: [\"drive:read\", \"drive:download\"] },\r\n inputSchema: shareSchema.shape,\r\n annotations: { readOnlyHint: true, openWorldHint: true },\r\n handler: async (args, { client, defaultBucket }) => {\r\n const a = shareSchema.parse(args);\r\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\r\n const data = await client.post(\"/storage/object/download-url\", {\r\n bucketName,\r\n filePath: a.filePath,\r\n shareableLink: true,\r\n storageId: a.storageId,\r\n });\r\n return textResult(summarize(data));\r\n },\r\n};\r\n\r\nexport const downloadTools: ToolDef[] = [downloadFile, shareLink];\r\n","import { readFile, stat } from \"node:fs/promises\";\nimport { basename } from \"node:path\";\nimport { z } from \"zod\";\nimport { CloudSeeError } from \"../errors\";\nimport { confirmShape, confirmationPreview } from \"../confirm\";\nimport { summarize } from \"./format\";\nimport { bucketField, resolveBucket, textResult, type ToolDef } from \"./types\";\n\n// Write/delete tools are present but their end-to-end success is sequenced\n// behind CSD-572 (the gateway enforces no scope/RBAC yet and the write path is\n// a Phase-1 stub). This note is appended to each tool's description so the model\n// and the user understand a gateway denial here is expected, not a tool bug.\nconst GATED =\n \" (Write access is authorized server-side; until CloudSee's public-API RBAC wiring (CSD-572) is live, the gateway may deny this — that is expected, not a tool failure.)\";\n\nconst MULTIPART_THRESHOLD = 8 * 1024 * 1024; // 8 MiB\n\nfunction pickUrl(data: unknown): string | undefined {\n if (typeof data === \"string\") return data;\n if (data && typeof data === \"object\") {\n const record = data as Record<string, unknown>;\n for (const key of [\"url\", \"uploadUrl\", \"signedUrl\", \"preSignedUrl\", \"presignedUrl\", \"putUrl\"]) {\n const value = record[key];\n if (typeof value === \"string\" && value) return value;\n }\n }\n return undefined;\n}\n\n// The object key that `complete` finalizes MUST come from the server's presign\n// response — never guessed — or we could register a record that doesn't match\n// the bytes actually written to the (server-chosen) S3 key.\nfunction pickKey(data: unknown): string | undefined {\n if (data && typeof data === \"object\") {\n const record = data as Record<string, unknown>;\n for (const key of [\"key\", \"objectKey\", \"Key\", \"objectPath\", \"filePath\"]) {\n const value = record[key];\n if (typeof value === \"string\" && value) return value;\n }\n const fields = record[\"fields\"];\n if (fields && typeof fields === \"object\") {\n const nested = (fields as Record<string, unknown>)[\"key\"];\n if (typeof nested === \"string\" && nested) return nested;\n }\n }\n return undefined;\n}\n\n// ---- upload_file → POST /storage/upload/url then /storage/upload/complete (drive:write) ----\nconst uploadSchema = z.object({\n bucketName: bucketField,\n localPath: z.string().min(1).describe(\"Path to the local file to upload (absolute, or relative to the server's working directory).\"),\n destinationFolder: z.string().describe(\"Destination folder/prefix in the drive. Empty = drive root.\"),\n fileName: z.string().optional().describe(\"Name to store the file as. Defaults to the local file's name.\"),\n contentType: z.string().optional().describe(\"MIME type. Defaults to application/octet-stream.\"),\n storageClass: z.string().optional().describe(\"Optional S3 storage class (e.g. STANDARD, INTELLIGENT_TIERING).\"),\n});\nconst uploadFile: ToolDef = {\n name: \"upload_file\",\n title: \"Upload file\",\n description:\n \"Upload a local file to a folder in a drive. Requires the drive (bucketName). Reads the file from the local machine, requests a pre-signed upload URL, uploads the bytes to storage, then finalizes the object.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/upload/url\", scopes: [\"drive:write\"] },\n inputSchema: uploadSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = uploadSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const fileName = a.fileName ?? basename(a.localPath);\n const contentType = a.contentType ?? \"application/octet-stream\";\n\n let info;\n try {\n info = await stat(a.localPath);\n } catch {\n throw new CloudSeeError(`Local file not found: ${a.localPath}`, { code: \"file_not_found\" });\n }\n if (!info.isFile()) throw new CloudSeeError(`Not a regular file: ${a.localPath}`, { code: \"not_a_file\" });\n if (info.size > MULTIPART_THRESHOLD) {\n throw new CloudSeeError(\n `File is ${(info.size / 1048576).toFixed(1)} MiB. Multipart upload (>8 MiB) is not enabled in this release — upload a smaller file or use the CloudSee web app for large files.`,\n { code: \"too_large\" },\n );\n }\n\n const bytes = await readFile(a.localPath);\n const presign = await client.post<Record<string, unknown>>(\"/storage/upload/url\", {\n bucketName,\n dirPath: a.destinationFolder,\n fileName,\n contentType,\n storageClass: a.storageClass,\n });\n const uploadUrl = pickUrl(presign);\n if (!uploadUrl) throw new CloudSeeError(\"The API did not return a usable upload URL.\", { code: \"no_upload_url\" });\n\n // Resolve the server-assigned key BEFORE uploading any bytes — if it's\n // absent we cannot finalize safely, so we fail without writing anything.\n const key = pickKey(presign);\n if (!key) {\n throw new CloudSeeError(\n \"The upload-URL response did not include the object key, so the upload cannot be finalized safely. (No bytes were uploaded.)\",\n { code: \"no_object_key\" },\n );\n }\n\n const put = await fetch(uploadUrl, { method: \"PUT\", headers: { \"Content-Type\": contentType }, body: new Uint8Array(bytes) });\n if (!put.ok) throw new CloudSeeError(`Upload to storage failed (HTTP ${put.status}).`, { status: put.status, code: \"upload_failed\" });\n\n let complete: unknown;\n try {\n complete = await client.post(\"/storage/upload/complete\", {\n bucketName,\n dirPath: a.destinationFolder,\n objects: [{ fileName, key, contentType, size: info.size }],\n });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new CloudSeeError(\n `Bytes were uploaded to storage but finalization failed: ${reason}. The object may exist without an index entry — retry the upload, or remove it via the CloudSee app.`,\n { code: \"finalize_failed\" },\n );\n }\n return textResult(`Uploaded \"${fileName}\" (${info.size} bytes) to \"${a.destinationFolder || \"/\"}\".\\n\\n${summarize(complete)}`);\n },\n};\n\n// ---- create_folder → POST /storage/folder/create (createFolder, drive:write) ----\nconst createFolderSchema = z.object({\n bucketName: bucketField,\n parentPath: z.string().describe(\"Parent folder/prefix in the drive. Empty = drive root.\"),\n name: z.string().min(1).describe(\"New folder name.\"),\n});\nconst createFolder: ToolDef = {\n name: \"create_folder\",\n title: \"Create folder\",\n description: \"Create a new folder at the given path in a drive. Requires the drive (bucketName).\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/folder/create\", scopes: [\"drive:write\"] },\n inputSchema: createFolderSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = createFolderSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const data = await client.post(\"/storage/folder/create\", { bucketName, dirPath: a.parentPath, object: a.name });\n return textResult(`Created folder \"${a.name}\" in \"${a.parentPath || \"/\"}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- rename_file → POST /storage/object/rename (storageRename, drive:write) — destructive ----\nconst renameSchema = z.object({\n bucketName: bucketField,\n oldObjectId: z.string().min(1).describe(\"Current object key/id of the file or folder.\"),\n newName: z.string().min(1).describe(\"New name.\"),\n isFolder: z.boolean().optional().describe(\"Set true when renaming a folder.\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n ...confirmShape,\n});\nconst renameFile: ToolDef = {\n name: \"rename_file\",\n title: \"Rename file or folder\",\n description: \"Rename a file or folder in a drive. Requires the drive (bucketName). Destructive (changes the object's key). Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/rename\", scopes: [\"drive:write\"] },\n inputSchema: renameSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = renameSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(`rename ${a.isFolder ? \"folder\" : \"file\"} to \"${a.newName}\"`, `Drive: ${bucketName}\\nTarget: ${a.oldObjectId}`);\n }\n const data = await client.post(\"/storage/object/rename\", {\n bucketName,\n oldObjectId: a.oldObjectId,\n newObjectName: a.newName,\n isFolder: a.isFolder ?? false,\n storageId: a.storageId,\n });\n return textResult(`Renamed to \"${a.newName}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- move_file → POST /storage/object/move (storageMoveObject, drive:write) — destructive unless asCopy ----\nconst moveSchema = z.object({\n bucketName: bucketField,\n sourcePath: z.string().min(1).describe(\"Source object key/prefix.\"),\n destinationPath: z.string().min(1).describe(\"Destination prefix.\"),\n asCopy: z.boolean().optional().describe(\"Copy instead of move (copy is non-destructive; the source is kept).\"),\n isFolder: z.boolean().optional().describe(\"Set true for a folder.\"),\n destinationBucket: z.string().optional().describe(\"Target drive, if different from the source drive.\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n ...confirmShape,\n});\nconst moveFile: ToolDef = {\n name: \"move_file\",\n title: \"Move or copy file\",\n description:\n \"Move (or copy, with asCopy=true) a file or folder to a new location. Requires the source drive (bucketName). A move removes the source and is destructive, so it requires confirm=true; a copy does not.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/move\", scopes: [\"drive:write\"] },\n inputSchema: moveSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = moveSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const isMove = !a.asCopy;\n if (isMove && !a.confirm) {\n return confirmationPreview(`move \"${a.sourcePath}\" → \"${a.destinationPath}\"`, \"This removes the source. Pass asCopy=true to copy instead.\");\n }\n const data = await client.post(\"/storage/object/move\", {\n bucketName,\n asCopy: a.asCopy ?? false,\n isFolder: a.isFolder ?? false,\n sourcePath: a.sourcePath,\n destinationPath: a.destinationPath,\n destinationBucket: a.destinationBucket,\n storageId: a.storageId,\n });\n return textResult(`${isMove ? \"Moved\" : \"Copied\"} \"${a.sourcePath}\" → \"${a.destinationPath}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- duplicate_file → POST /storage/object/duplicate (duplicateFile, drive:write) ----\nconst duplicateSchema = z.object({\n bucketName: bucketField,\n objectKey: z.string().min(1).describe(\"Source object key to duplicate.\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n});\nconst duplicateFile: ToolDef = {\n name: \"duplicate_file\",\n title: \"Duplicate file\",\n description: \"Create a copy of a file in the same location. Requires the drive (bucketName).\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/duplicate\", scopes: [\"drive:write\"] },\n inputSchema: duplicateSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = duplicateSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n const data = await client.post(\"/storage/object/duplicate\", { bucketName, objectKey: a.objectKey, storageId: a.storageId });\n return textResult(`Duplicated \"${a.objectKey}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- delete_files → POST /storage/objects/delete (deleteObjects, drive:delete) — destructive ----\nconst deleteSchema = z.object({\n bucketName: bucketField,\n objectKeys: z.array(z.string().min(1)).min(1).describe(\"Object keys (paths) to permanently delete.\"),\n ...confirmShape,\n});\nconst deleteFiles: ToolDef = {\n name: \"delete_files\",\n title: \"Delete files\",\n description: \"Permanently delete one or more files/objects by key from a drive. Requires the drive (bucketName). Destructive and irreversible. Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/objects/delete\", scopes: [\"drive:delete\"] },\n inputSchema: deleteSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = deleteSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(`permanently delete ${a.objectKeys.length} object(s) from \"${bucketName}\"`, a.objectKeys.map((k) => ` - ${k}`).join(\"\\n\"));\n }\n const data = await client.post(\"/storage/objects/delete\", { bucketName, deletedObjects: a.objectKeys });\n return textResult(`Deleted ${a.objectKeys.length} object(s).\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- update_metadata → POST /storage/object/metadata (storageUpdateObjectMetadata, drive:write) — destructive ----\nconst updateMetaSchema = z.object({\n bucketName: bucketField,\n objectKey: z.string().min(1).describe(\"Object key whose metadata to update.\"),\n metadata: z.record(z.string(), z.unknown()).describe(\"Metadata fields to set (key/value map). Overwrites the listed fields.\"),\n ...confirmShape,\n});\nconst updateMetadata: ToolDef = {\n name: \"update_metadata\",\n title: \"Update file metadata\",\n description: \"Update a file's metadata in a drive. Requires the drive (bucketName). Destructive (overwrites the listed metadata fields). Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/metadata\", scopes: [\"drive:write\"] },\n inputSchema: updateMetaSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = updateMetaSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(`update metadata on \"${a.objectKey}\"`, `Drive: ${bucketName}\\nFields: ${Object.keys(a.metadata).join(\", \") || \"(none)\"}`);\n }\n const data = await client.post(\"/storage/object/metadata\", { bucketName, objectKey: a.objectKey, metadata: a.metadata });\n return textResult(`Updated metadata on \"${a.objectKey}\".\\n\\n${summarize(data)}`);\n },\n};\n\n// ---- restore_archived_file → POST /storage/object/restore (restoreObject, drive:write) — Glacier un-archive ----\nconst restoreSchema = z.object({\n bucketName: bucketField,\n objectKey: z.string().min(1).describe(\"Object key of the archived (Glacier) object to restore.\"),\n days: z.number().int().min(1).optional().describe(\"How many days to keep the restored copy available.\"),\n retrievalTier: z.enum([\"Expedited\", \"Standard\", \"Bulk\"]).optional().describe(\"Glacier retrieval tier (default Standard).\"),\n storageId: z.string().optional().describe(\"Optional storage/index id.\"),\n ...confirmShape,\n});\nconst restoreArchivedFile: ToolDef = {\n name: \"restore_archived_file\",\n title: \"Restore archived file\",\n description:\n \"Begin restoring an archived (S3 Glacier) object so it can be downloaded. Requires the drive (bucketName). This is Glacier un-archiving — NOT recovery of a deleted file — and may incur retrieval cost and take minutes to hours. Requires confirm=true.\" + GATED,\n endpoint: { method: \"POST\", path: \"/storage/object/restore\", scopes: [\"drive:write\"] },\n inputSchema: restoreSchema.shape,\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },\n handler: async (args, { client, defaultBucket }) => {\n const a = restoreSchema.parse(args);\n const bucketName = resolveBucket(a.bucketName, defaultBucket);\n if (!a.confirm) {\n return confirmationPreview(\n `restore archived object \"${a.objectKey}\"`,\n `Drive: ${bucketName}\\nTier: ${a.retrievalTier ?? \"Standard\"}${a.days ? `, ${a.days} day(s)` : \"\"}. May incur retrieval cost.`,\n );\n }\n const data = await client.post(\"/storage/object/restore\", {\n bucketName,\n objectKey: a.objectKey,\n days: a.days,\n retrievalTier: a.retrievalTier,\n storageId: a.storageId,\n });\n return textResult(`Restore initiated for \"${a.objectKey}\".\\n\\n${summarize(data)}`);\n },\n};\n\nexport const writeTools: ToolDef[] = [\n uploadFile,\n createFolder,\n renameFile,\n moveFile,\n duplicateFile,\n deleteFiles,\n updateMetadata,\n restoreArchivedFile,\n];\n","import { z } from \"zod\";\r\nimport { textResult, type ToolResult } from \"./tools/types\";\r\n\r\n// Two-step confirmation for destructive tools. A first call WITHOUT confirm=true\r\n// returns a human-readable preview and performs no mutation; the model (or user)\r\n// must re-call with confirm=true to execute. This is UX, NOT the security\r\n// boundary — the CloudSee API authorizes every operation server-side (CSD-572).\r\n\r\nexport const confirmShape = {\r\n confirm: z\r\n .boolean()\r\n .optional()\r\n .describe(\r\n \"Must be true to actually perform this mutating/irreversible action. If omitted or false, the tool returns a preview and makes no changes.\",\r\n ),\r\n};\r\n\r\nconst SECURITY_NOTE =\r\n \"Note: this confirmation is a client-side safety prompt, not the security boundary — \" +\r\n \"the CloudSee API authorizes every operation server-side.\";\r\n\r\n/** The no-op preview returned when a destructive tool is called without confirm=true. */\r\nexport function confirmationPreview(action: string, details: string): ToolResult {\r\n return textResult(\r\n `⚠️ Confirmation required — no changes have been made.\\n\\n` +\r\n `About to: ${action}\\n${details}\\n\\n` +\r\n `Re-run this tool with confirm=true to proceed.\\n${SECURITY_NOTE}`,\r\n );\r\n}\r\n","import type { ToolDef } from \"./types\";\r\nimport { readTools } from \"./read\";\r\nimport { downloadTools } from \"./download\";\r\nimport { writeTools } from \"./write\";\r\n\r\n/** The complete, grounded tool set (CSD-586 §2.4). Read + download tools are\r\n * callable today; write/delete tools are present but gated on CSD-572 RBAC. */\r\nexport const allTools: ToolDef[] = [...readTools, ...downloadTools, ...writeTools];\r\n\r\nexport type { ToolDef } from \"./types\";\r\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webapper/cloudsee-drive-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Open-source MCP server for CloudSee Drive — connect your CloudSee account (Amazon S3) to Claude Desktop and any MCP client with an API key.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|