/** * Object Storage Well-Known Binding * * Defines the interface for S3-compatible object storage operations. * Any MCP that implements this binding can provide file/object management * for buckets and objects. * * This binding includes: * - LIST_OBJECTS: List objects with pagination and prefix filtering * - GET_OBJECT_METADATA: Get object metadata (HEAD operation) * - GET_PRESIGNED_URL: Generate presigned URL for downloading * - PUT_PRESIGNED_URL: Generate presigned URL for uploading * - DELETE_OBJECT: Delete a single object * - DELETE_OBJECTS: Batch delete multiple objects */ import { z } from "zod"; import type { Binder, ToolBinder } from "../core/binder"; // ============================================================================ // Tool Schemas // ============================================================================ /** * LIST_OBJECTS - List objects in the bucket with pagination support */ const ListObjectsInputSchema = z.object({ prefix: z .string() .optional() .describe("Filter objects by prefix (e.g., 'folder/' for folder contents)"), maxKeys: z .number() .optional() .default(1000) .describe("Maximum number of keys to return (default: 1000)"), continuationToken: z .string() .optional() .describe("Token for pagination from previous response"), delimiter: z .string() .optional() .describe( "Delimiter for grouping keys (typically '/'). When set, commonPrefixes returns folder paths.", ), }); const ListObjectsOutputSchema = z.object({ objects: z.array( z.object({ key: z.string().describe("Object key/path"), size: z.number().describe("Object size in bytes"), lastModified: z.string().describe("Last modified timestamp"), etag: z.string().describe("Entity tag for the object"), }), ), nextContinuationToken: z .string() .optional() .describe("Token for fetching next page of results"), isTruncated: z.boolean().describe("Whether there are more results available"), commonPrefixes: z .array(z.string()) .optional() .describe( "Folder paths when delimiter is used (e.g., ['photos/2024/', 'photos/2025/'])", ), }); export type ListObjectsInput = z.infer; export type ListObjectsOutput = z.infer; /** * GET_OBJECT_METADATA - Get object metadata using HEAD operation */ const GetObjectMetadataInputSchema = z.object({ key: z.string().describe("Object key/path to get metadata for"), }); const GetObjectMetadataOutputSchema = z.object({ contentType: z.string().optional().describe("MIME type of the object"), contentLength: z.number().describe("Size of the object in bytes"), lastModified: z.string().describe("Last modified timestamp"), etag: z.string().describe("Entity tag for the object"), metadata: z .record(z.string(), z.string()) .optional() .describe("Custom metadata key-value pairs"), }); export type GetObjectMetadataInput = z.infer< typeof GetObjectMetadataInputSchema >; export type GetObjectMetadataOutput = z.infer< typeof GetObjectMetadataOutputSchema >; /** * GET_PRESIGNED_URL - Generate a presigned URL for downloading an object */ const GetPresignedUrlInputSchema = z.object({ key: z.string().describe("Object key/path to generate URL for"), expiresIn: z .number() .optional() .describe( "URL expiration time in seconds (default: from state config or 3600)", ), }); const GetPresignedUrlOutputSchema = z.object({ url: z.string().describe("Presigned URL for downloading the object"), expiresIn: z.number().describe("Expiration time in seconds that was used"), }); export type GetPresignedUrlInput = z.infer; export type GetPresignedUrlOutput = z.infer; /** * PUT_PRESIGNED_URL - Generate a presigned URL for uploading an object */ const PutPresignedUrlInputSchema = z.object({ key: z.string().describe("Object key/path for the upload"), expiresIn: z .number() .optional() .describe( "URL expiration time in seconds (default: from state config or 3600)", ), contentType: z .string() .optional() .describe("MIME type for the object being uploaded"), }); const PutPresignedUrlOutputSchema = z.object({ url: z.string().describe("Presigned URL for uploading the object"), expiresIn: z.number().describe("Expiration time in seconds that was used"), }); export type PutPresignedUrlInput = z.infer; export type PutPresignedUrlOutput = z.infer; /** * DELETE_OBJECT - Delete a single object */ const DeleteObjectInputSchema = z.object({ key: z.string().describe("Object key/path to delete"), }); const DeleteObjectOutputSchema = z.object({ success: z.boolean().describe("Whether the deletion was successful"), key: z.string().describe("The key that was deleted"), }); export type DeleteObjectInput = z.infer; export type DeleteObjectOutput = z.infer; /** * DELETE_OBJECTS - Delete multiple objects in batch */ const DeleteObjectsInputSchema = z.object({ keys: z .array(z.string()) .max(1000) .describe("Array of object keys/paths to delete (max 1000)"), }); const DeleteObjectsOutputSchema = z.object({ deleted: z.array(z.string()).describe("Array of successfully deleted keys"), errors: z .array( z.object({ key: z.string(), message: z.string(), }), ) .describe("Array of errors for failed deletions"), }); export type DeleteObjectsInput = z.infer; export type DeleteObjectsOutput = z.infer; // ============================================================================ // Binding Definition // ============================================================================ /** * Object Storage Binding * * Defines the interface for S3-compatible object storage operations. * Any MCP that implements this binding can be used with the Object Storage plugin * to provide a file browser UI. * * Required tools: * - LIST_OBJECTS: List objects with prefix filtering and pagination * - GET_OBJECT_METADATA: Get object metadata (HEAD) * - GET_PRESIGNED_URL: Generate download URL * - PUT_PRESIGNED_URL: Generate upload URL * - DELETE_OBJECT: Delete single object * - DELETE_OBJECTS: Batch delete objects */ export const OBJECT_STORAGE_BINDING = [ { name: "LIST_OBJECTS" as const, inputSchema: ListObjectsInputSchema, outputSchema: ListObjectsOutputSchema, } satisfies ToolBinder<"LIST_OBJECTS", ListObjectsInput, ListObjectsOutput>, { name: "GET_OBJECT_METADATA" as const, inputSchema: GetObjectMetadataInputSchema, outputSchema: GetObjectMetadataOutputSchema, } satisfies ToolBinder< "GET_OBJECT_METADATA", GetObjectMetadataInput, GetObjectMetadataOutput >, { name: "GET_PRESIGNED_URL" as const, inputSchema: GetPresignedUrlInputSchema, outputSchema: GetPresignedUrlOutputSchema, } satisfies ToolBinder< "GET_PRESIGNED_URL", GetPresignedUrlInput, GetPresignedUrlOutput >, { name: "PUT_PRESIGNED_URL" as const, inputSchema: PutPresignedUrlInputSchema, outputSchema: PutPresignedUrlOutputSchema, } satisfies ToolBinder< "PUT_PRESIGNED_URL", PutPresignedUrlInput, PutPresignedUrlOutput >, { name: "DELETE_OBJECT" as const, inputSchema: DeleteObjectInputSchema, outputSchema: DeleteObjectOutputSchema, } satisfies ToolBinder< "DELETE_OBJECT", DeleteObjectInput, DeleteObjectOutput >, { name: "DELETE_OBJECTS" as const, inputSchema: DeleteObjectsInputSchema, outputSchema: DeleteObjectsOutputSchema, } satisfies ToolBinder< "DELETE_OBJECTS", DeleteObjectsInput, DeleteObjectsOutput >, ] as const satisfies Binder; export type ObjectStorageBinding = typeof OBJECT_STORAGE_BINDING;