Skip to main content
Glama

Cloudinary MCP Server

index.ts6.06 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js"; import { v2 as cloudinary, UploadApiResponse } from "cloudinary"; import { readFile } from 'node:fs/promises'; // Get Cloudinary config from environment variables const CLOUD_NAME = process.env.CLOUDINARY_CLOUD_NAME; const API_KEY = process.env.CLOUDINARY_API_KEY; const API_SECRET = process.env.CLOUDINARY_API_SECRET; if (!CLOUD_NAME || !API_KEY || !API_SECRET) { throw new Error("Missing required Cloudinary environment variables"); } // Configure Cloudinary cloudinary.config({ cloud_name: CLOUD_NAME, api_key: API_KEY, api_secret: API_SECRET, }); class CloudinaryServer { private server: Server; constructor() { this.server = new Server( { name: "cloudinary-server", version: "0.1.0", }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); // Error handling this.server.onerror = (error) => console.error("[MCP Error]", error); process.on("SIGINT", async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "upload", description: "Upload media (images/videos) to Cloudinary. For large files, the upload is processed in chunks and returns a streaming response. The uploaded asset will be available at:\n- HTTP: http://res.cloudinary.com/{cloud_name}/{resource_type}/upload/v1/{public_id}.{format}\n- HTTPS: https://res.cloudinary.com/{cloud_name}/{resource_type}/upload/v1/{public_id}.{format}\nwhere {cloud_name} is your Cloudinary cloud name, resource_type is 'image' or 'video', and format is determined by the file extension.", inputSchema: { type: "object", properties: { file: { type: "string", description: "Path to file, URL, or base64 data URI to upload" }, resource_type: { type: "string", enum: ["image", "video", "raw"], description: "Type of resource to upload. For videos, the upload will return a streaming response as it processes in chunks." }, public_id: { type: "string", description: "Public ID to assign to the uploaded asset. This will be used in the final URL. If not provided, Cloudinary will generate one." }, overwrite: { type: "boolean", description: "Whether to overwrite existing assets with the same public ID" }, tags: { type: "array", items: { type: "string" }, description: "Tags to assign to the uploaded asset" } }, required: ["file"] } } ] })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name !== "upload") { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } const args = request.params.arguments as { file: string; resource_type?: "image" | "video" | "raw"; public_id?: string; overwrite?: boolean; tags?: string[]; }; try { // For videos, show that upload is in progress if (args.resource_type === "video") { console.error("Starting video upload, this may take a while..."); } const options: any = { resource_type: args.resource_type || "auto", public_id: args.public_id, overwrite: args.overwrite, tags: args.tags, chunk_size: 20000000 // 20MB chunks }; // Read the file into a buffer const buffer = await readFile(args.file); // Upload the file buffer const result = await new Promise<UploadApiResponse>((resolve, reject) => { cloudinary.uploader.upload_stream( options, (error: Error | undefined, result: UploadApiResponse | undefined) => { if (error) { reject(error); return; } if (!result) { reject(new Error("No result received from upload")); return; } resolve(result); } ).end(buffer); }); console.error("Upload completed successfully!"); // Extract relevant information from result const response = { public_id: result.public_id, version: result.version, signature: result.signature, format: result.format, resource_type: result.resource_type, created_at: result.created_at, bytes: result.bytes, type: result.type, url: result.url, secure_url: result.secure_url }; return { content: [ { type: "text", text: JSON.stringify(response, null, 2) } ] }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Upload failed: ${error instanceof Error ? error.message : String(error)}` ); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("Cloudinary MCP server running on stdio"); } } const server = new CloudinaryServer(); server.run().catch((error) => { console.error("Server error:", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/felores/cloudinary-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server