@webapper/cloudsee-drive-mcp 0.1.0

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.
@@ -0,0 +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"]}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@webapper/cloudsee-drive-mcp",
3
+ "version": "0.1.0",
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
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Webapper",
8
+ "homepage": "https://github.com/webapper/cloudsee-drive-mcp#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/webapper/cloudsee-drive-mcp.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/webapper/cloudsee-drive-mcp/issues"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "cloudsee",
20
+ "cloudsee-drive",
21
+ "s3",
22
+ "claude",
23
+ "ai",
24
+ "file-management"
25
+ ],
26
+ "bin": {
27
+ "cloudsee-drive-mcp": "dist/index.js"
28
+ },
29
+ "main": "dist/index.js",
30
+ "module": "dist/index.js",
31
+ "types": "dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "contract/registry.snapshot.json",
41
+ "README.md",
42
+ "LICENSE",
43
+ "CHANGELOG.md"
44
+ ],
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "scripts": {
52
+ "build": "tsup",
53
+ "dev": "tsup --watch",
54
+ "start": "node dist/index.js",
55
+ "typecheck": "tsc --noEmit",
56
+ "lint": "eslint .",
57
+ "lint:fix": "eslint . --fix",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
60
+ "sync:contract": "node scripts/sync-contract.mjs",
61
+ "prepublishOnly": "npm run build"
62
+ },
63
+ "dependencies": {
64
+ "@modelcontextprotocol/sdk": "^1.0.0",
65
+ "zod": "^3.23.8"
66
+ },
67
+ "devDependencies": {
68
+ "@eslint/js": "^9.9.0",
69
+ "@semantic-release/changelog": "^6.0.3",
70
+ "@semantic-release/git": "^10.0.1",
71
+ "@types/node": "^20.14.0",
72
+ "eslint": "^9.9.0",
73
+ "semantic-release": "^24.0.0",
74
+ "tsup": "^8.2.0",
75
+ "typescript": "^5.5.0",
76
+ "typescript-eslint": "^8.0.0",
77
+ "vitest": "^2.0.0"
78
+ }
79
+ }