imagekit-mcp-server 1.0.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.
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/index.js +352 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ronaldo Lima
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# ImageKit MCP Server
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for [ImageKit.io](https://imagekit.io) that lets AI assistants upload, search, transform, and manage media assets in your ImageKit media library.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx imagekit-mcp-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires three environment variables:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
IMAGEKIT_PUBLIC_KEY=public_xxx
|
|
15
|
+
IMAGEKIT_PRIVATE_KEY=private_xxx
|
|
16
|
+
IMAGEKIT_URL_ENDPOINT=https://ik.imagekit.io/your_id
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Get your credentials from the [ImageKit Dashboard](https://imagekit.io/dashboard/developer/api-keys).
|
|
20
|
+
|
|
21
|
+
## MCP Client Configuration
|
|
22
|
+
|
|
23
|
+
### Claude Desktop
|
|
24
|
+
|
|
25
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"imagekit": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["-y", "imagekit-mcp-server"],
|
|
33
|
+
"env": {
|
|
34
|
+
"IMAGEKIT_PUBLIC_KEY": "public_xxx",
|
|
35
|
+
"IMAGEKIT_PRIVATE_KEY": "private_xxx",
|
|
36
|
+
"IMAGEKIT_URL_ENDPOINT": "https://ik.imagekit.io/your_id"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Claude Code
|
|
44
|
+
|
|
45
|
+
Add to `~/.claude/settings.json`:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mcpServers": {
|
|
50
|
+
"imagekit": {
|
|
51
|
+
"command": "npx",
|
|
52
|
+
"args": ["-y", "imagekit-mcp-server"],
|
|
53
|
+
"env": {
|
|
54
|
+
"IMAGEKIT_PUBLIC_KEY": "public_xxx",
|
|
55
|
+
"IMAGEKIT_PRIVATE_KEY": "private_xxx",
|
|
56
|
+
"IMAGEKIT_URL_ENDPOINT": "https://ik.imagekit.io/your_id"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Cursor
|
|
64
|
+
|
|
65
|
+
Add to `.cursor/mcp.json` in your project:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"imagekit": {
|
|
71
|
+
"command": "npx",
|
|
72
|
+
"args": ["-y", "imagekit-mcp-server"],
|
|
73
|
+
"env": {
|
|
74
|
+
"IMAGEKIT_PUBLIC_KEY": "public_xxx",
|
|
75
|
+
"IMAGEKIT_PRIVATE_KEY": "private_xxx",
|
|
76
|
+
"IMAGEKIT_URL_ENDPOINT": "https://ik.imagekit.io/your_id"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Tools
|
|
84
|
+
|
|
85
|
+
| Tool | Description |
|
|
86
|
+
|------|-------------|
|
|
87
|
+
| `list_files` | List and search files with filters (path, type, search query, pagination, sort) |
|
|
88
|
+
| `list_folders` | List folders at a given path |
|
|
89
|
+
| `get_file_details` | Get full metadata for a file (dimensions, tags, URLs, dates) |
|
|
90
|
+
| `upload_file` | Upload a local file |
|
|
91
|
+
| `upload_file_from_url` | Upload from a remote URL |
|
|
92
|
+
| `upload_base64_file` | Upload from base64 data |
|
|
93
|
+
| `delete_file` | Delete a file by ID |
|
|
94
|
+
| `create_folder` | Create a new folder |
|
|
95
|
+
| `delete_folder` | Delete a folder |
|
|
96
|
+
| `move_file` | Move a file to a different folder |
|
|
97
|
+
| `generate_url` | Generate URLs with transformations (resize, crop, format, signed URLs) |
|
|
98
|
+
|
|
99
|
+
## Examples
|
|
100
|
+
|
|
101
|
+
Ask your AI assistant:
|
|
102
|
+
|
|
103
|
+
- "List all images in the /products/ folder"
|
|
104
|
+
- "Upload this screenshot to /screenshots/2026/"
|
|
105
|
+
- "Generate a 300x300 thumbnail for /hero-banner.jpg"
|
|
106
|
+
- "Search for files named 'logo'"
|
|
107
|
+
- "Move /old-folder/image.jpg to /new-folder/"
|
|
108
|
+
- "Delete the file with ID abc123"
|
|
109
|
+
|
|
110
|
+
## Development
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
git clone https://github.com/limaronaldo/imagekit-mcp-server.git
|
|
114
|
+
cd imagekit-mcp-server
|
|
115
|
+
npm install
|
|
116
|
+
|
|
117
|
+
# Set credentials
|
|
118
|
+
export IMAGEKIT_PUBLIC_KEY=public_xxx
|
|
119
|
+
export IMAGEKIT_PRIVATE_KEY=private_xxx
|
|
120
|
+
export IMAGEKIT_URL_ENDPOINT=https://ik.imagekit.io/your_id
|
|
121
|
+
|
|
122
|
+
# Run
|
|
123
|
+
node index.js
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import ImageKit from "imagekit";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
|
|
10
|
+
// --- ImageKit client (lazy init, fail-fast on missing credentials) ---
|
|
11
|
+
|
|
12
|
+
let client = null;
|
|
13
|
+
|
|
14
|
+
function getClient() {
|
|
15
|
+
if (client) return client;
|
|
16
|
+
|
|
17
|
+
const publicKey = process.env.IMAGEKIT_PUBLIC_KEY;
|
|
18
|
+
const privateKey = process.env.IMAGEKIT_PRIVATE_KEY;
|
|
19
|
+
const urlEndpoint = process.env.IMAGEKIT_URL_ENDPOINT;
|
|
20
|
+
|
|
21
|
+
if (!publicKey || !privateKey || !urlEndpoint) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"Missing credentials. Set IMAGEKIT_PUBLIC_KEY, IMAGEKIT_PRIVATE_KEY, and IMAGEKIT_URL_ENDPOINT."
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
client = new ImageKit({ publicKey, privateKey, urlEndpoint });
|
|
28
|
+
return client;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// --- Helpers ---
|
|
32
|
+
|
|
33
|
+
function trimSlash(folder) {
|
|
34
|
+
if (!folder) return "/";
|
|
35
|
+
return folder.endsWith("/") ? folder.slice(0, -1) : folder;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function formatResult(text) {
|
|
39
|
+
return { content: [{ type: "text", text }] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function formatError(message) {
|
|
43
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function formatFileInfo(file) {
|
|
47
|
+
const size = file.size ? `${(file.size / 1024).toFixed(1)} KB` : "N/A";
|
|
48
|
+
const dims =
|
|
49
|
+
file.width && file.height ? `${file.width}x${file.height}` : "";
|
|
50
|
+
const tags =
|
|
51
|
+
file.tags && file.tags.length > 0 ? `\n Tags: ${file.tags.join(", ")}` : "";
|
|
52
|
+
return `${file.name} (${file.fileType || "file"}, ${size}${dims ? ", " + dims : ""})${tags}\n ID: ${file.fileId}\n URL: ${file.url}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function uploadToImageKit(fileData, fileName, options = {}) {
|
|
56
|
+
const ik = getClient();
|
|
57
|
+
|
|
58
|
+
const uploadOptions = {
|
|
59
|
+
file: fileData,
|
|
60
|
+
fileName,
|
|
61
|
+
folder: trimSlash(options.folder),
|
|
62
|
+
useUniqueFileName: options.useUniqueFileName ?? true,
|
|
63
|
+
isPrivateFile: options.isPrivateFile ?? false,
|
|
64
|
+
overwriteFile: options.overwriteFile ?? false,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (options.tags?.length) uploadOptions.tags = options.tags;
|
|
68
|
+
if (options.customCoordinates) uploadOptions.customCoordinates = options.customCoordinates;
|
|
69
|
+
if (options.customMetadata) uploadOptions.customMetadata = options.customMetadata;
|
|
70
|
+
|
|
71
|
+
const result = await ik.upload(uploadOptions);
|
|
72
|
+
|
|
73
|
+
return formatResult(
|
|
74
|
+
`File uploaded successfully.\n\n` +
|
|
75
|
+
` Name: ${result.name}\n` +
|
|
76
|
+
` ID: ${result.fileId}\n` +
|
|
77
|
+
` URL: ${result.url}\n` +
|
|
78
|
+
` Size: ${(result.size / 1024).toFixed(1)} KB\n` +
|
|
79
|
+
` Path: ${result.filePath}\n` +
|
|
80
|
+
` Type: ${result.fileType}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- Upload option schemas (shared) ---
|
|
85
|
+
|
|
86
|
+
const uploadOptionsSchema = {
|
|
87
|
+
folder: z.string().default("/").describe("Destination folder path"),
|
|
88
|
+
useUniqueFileName: z.boolean().default(true).describe("Generate unique filename to avoid conflicts"),
|
|
89
|
+
isPrivateFile: z.boolean().default(false).describe("Whether file should be private"),
|
|
90
|
+
overwriteFile: z.boolean().default(false).describe("Whether to overwrite existing file with same name"),
|
|
91
|
+
tags: z.array(z.string()).optional().describe("Tags to associate with the file"),
|
|
92
|
+
customCoordinates: z.string().optional().describe("Custom focus coordinates for cropping (x,y,width,height)"),
|
|
93
|
+
customMetadata: z.record(z.string(), z.any()).optional().describe("Custom metadata key-value pairs"),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// --- Server setup ---
|
|
97
|
+
|
|
98
|
+
const server = new McpServer({
|
|
99
|
+
name: "imagekit-mcp-server",
|
|
100
|
+
version: "1.0.0",
|
|
101
|
+
description: "MCP server for ImageKit.io media management",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// --- Tools ---
|
|
105
|
+
|
|
106
|
+
server.tool(
|
|
107
|
+
"list_files",
|
|
108
|
+
"List and search files in ImageKit media library",
|
|
109
|
+
{
|
|
110
|
+
path: z.string().default("/").describe("Folder path to list files from"),
|
|
111
|
+
searchQuery: z.string().optional().describe("Search query for file names (supports ImageKit search syntax)"),
|
|
112
|
+
limit: z.number().min(1).max(1000).default(20).describe("Number of files to return"),
|
|
113
|
+
skip: z.number().min(0).default(0).describe("Number of files to skip for pagination"),
|
|
114
|
+
fileType: z.enum(["image", "non-image", "all"]).default("all").describe("Filter by file type"),
|
|
115
|
+
sort: z.string().default("DESC_CREATED").describe("Sort order (ASC_CREATED, DESC_CREATED, ASC_NAME, DESC_NAME, ASC_SIZE, DESC_SIZE)"),
|
|
116
|
+
},
|
|
117
|
+
async ({ path: filePath, searchQuery, limit, skip, fileType, sort }) => {
|
|
118
|
+
const ik = getClient();
|
|
119
|
+
|
|
120
|
+
const params = {
|
|
121
|
+
path: filePath,
|
|
122
|
+
limit,
|
|
123
|
+
skip,
|
|
124
|
+
sort,
|
|
125
|
+
includeFolder: true,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (searchQuery) params.searchQuery = searchQuery;
|
|
129
|
+
if (fileType !== "all") params.fileType = fileType;
|
|
130
|
+
|
|
131
|
+
const result = await ik.listFiles(params);
|
|
132
|
+
const count = result.length;
|
|
133
|
+
|
|
134
|
+
if (count === 0) {
|
|
135
|
+
return formatResult(`No files found in "${filePath}"${searchQuery ? ` matching "${searchQuery}"` : ""}.`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const lines = result.map((f) => formatFileInfo(f));
|
|
139
|
+
const header = searchQuery
|
|
140
|
+
? `Found ${count} file(s) matching "${searchQuery}" in "${filePath}":`
|
|
141
|
+
: `Found ${count} file(s) in "${filePath}":`;
|
|
142
|
+
|
|
143
|
+
return formatResult(`${header}\n\n${lines.join("\n\n")}`);
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
server.tool(
|
|
148
|
+
"list_folders",
|
|
149
|
+
"List folders in ImageKit",
|
|
150
|
+
{
|
|
151
|
+
path: z.string().default("/").describe("Folder path to list (default is root)"),
|
|
152
|
+
},
|
|
153
|
+
async ({ path: folderPath }) => {
|
|
154
|
+
const ik = getClient();
|
|
155
|
+
|
|
156
|
+
const result = await ik.listFiles({
|
|
157
|
+
path: folderPath,
|
|
158
|
+
includeFolder: true,
|
|
159
|
+
type: "folder",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const folders = result.filter((item) => item.type === "folder");
|
|
163
|
+
|
|
164
|
+
if (folders.length === 0) {
|
|
165
|
+
return formatResult(`No folders found in "${folderPath}".`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const lines = folders.map(
|
|
169
|
+
(f) => `${f.name}\n ID: ${f.folderId}\n Path: ${f.folderPath}`
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return formatResult(`Found ${folders.length} folder(s) in "${folderPath}":\n\n${lines.join("\n\n")}`);
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
server.tool(
|
|
177
|
+
"get_file_details",
|
|
178
|
+
"Get detailed information about a specific file",
|
|
179
|
+
{
|
|
180
|
+
fileId: z.string().describe("The unique ID of the file"),
|
|
181
|
+
},
|
|
182
|
+
async ({ fileId }) => {
|
|
183
|
+
const ik = getClient();
|
|
184
|
+
const f = await ik.getFileDetails(fileId);
|
|
185
|
+
|
|
186
|
+
const dims = f.width && f.height ? `\n Dimensions: ${f.width}x${f.height}` : "";
|
|
187
|
+
const tags = f.tags?.length ? `\n Tags: ${f.tags.join(", ")}` : "";
|
|
188
|
+
|
|
189
|
+
return formatResult(
|
|
190
|
+
`File details:\n\n` +
|
|
191
|
+
` Name: ${f.name}\n` +
|
|
192
|
+
` ID: ${f.fileId}\n` +
|
|
193
|
+
` Type: ${f.fileType}\n` +
|
|
194
|
+
` Size: ${(f.size / 1024).toFixed(1)} KB${dims}\n` +
|
|
195
|
+
` URL: ${f.url}\n` +
|
|
196
|
+
` Path: ${f.filePath}${tags}\n` +
|
|
197
|
+
` Created: ${f.createdAt}\n` +
|
|
198
|
+
` Updated: ${f.updatedAt}`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
server.tool(
|
|
204
|
+
"upload_file",
|
|
205
|
+
"Upload a local file to ImageKit",
|
|
206
|
+
{
|
|
207
|
+
filePath: z.string().describe("Local path to the file to upload"),
|
|
208
|
+
fileName: z.string().optional().describe("Name for the uploaded file (defaults to original filename)"),
|
|
209
|
+
...uploadOptionsSchema,
|
|
210
|
+
},
|
|
211
|
+
async ({ filePath, fileName, ...options }) => {
|
|
212
|
+
if (!fs.existsSync(filePath)) {
|
|
213
|
+
return formatError(`File not found: ${filePath}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const buffer = fs.readFileSync(filePath);
|
|
217
|
+
const base64 = buffer.toString("base64");
|
|
218
|
+
const name = fileName || path.basename(filePath);
|
|
219
|
+
|
|
220
|
+
return uploadToImageKit(base64, name, options);
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
server.tool(
|
|
225
|
+
"upload_file_from_url",
|
|
226
|
+
"Upload a file to ImageKit from a URL",
|
|
227
|
+
{
|
|
228
|
+
url: z.string().url().describe("URL of the file to upload"),
|
|
229
|
+
fileName: z.string().describe("Name for the uploaded file"),
|
|
230
|
+
...uploadOptionsSchema,
|
|
231
|
+
},
|
|
232
|
+
async ({ url, fileName, ...options }) => {
|
|
233
|
+
return uploadToImageKit(url, fileName, options);
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
server.tool(
|
|
238
|
+
"upload_base64_file",
|
|
239
|
+
"Upload a file to ImageKit from base64 encoded data",
|
|
240
|
+
{
|
|
241
|
+
base64Data: z.string().describe("Base64 encoded file content (with or without data URI prefix)"),
|
|
242
|
+
fileName: z.string().describe("Name for the uploaded file"),
|
|
243
|
+
...uploadOptionsSchema,
|
|
244
|
+
},
|
|
245
|
+
async ({ base64Data, fileName, ...options }) => {
|
|
246
|
+
// Strip data URI prefix if present
|
|
247
|
+
const clean = base64Data.startsWith("data:")
|
|
248
|
+
? base64Data.split(",")[1]
|
|
249
|
+
: base64Data;
|
|
250
|
+
|
|
251
|
+
return uploadToImageKit(clean, fileName, options);
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
server.tool(
|
|
256
|
+
"delete_file",
|
|
257
|
+
"Delete a file from ImageKit",
|
|
258
|
+
{
|
|
259
|
+
fileId: z.string().describe("The unique ID of the file to delete"),
|
|
260
|
+
},
|
|
261
|
+
async ({ fileId }) => {
|
|
262
|
+
const ik = getClient();
|
|
263
|
+
await ik.deleteFile(fileId);
|
|
264
|
+
return formatResult(`File deleted successfully. ID: ${fileId}`);
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
server.tool(
|
|
269
|
+
"create_folder",
|
|
270
|
+
"Create a new folder in ImageKit",
|
|
271
|
+
{
|
|
272
|
+
folderName: z.string().describe("Name of the folder to create"),
|
|
273
|
+
parentFolderPath: z.string().default("/").describe("Parent folder path"),
|
|
274
|
+
},
|
|
275
|
+
async ({ folderName, parentFolderPath }) => {
|
|
276
|
+
const ik = getClient();
|
|
277
|
+
await ik.createFolder({ folderName, parentFolderPath });
|
|
278
|
+
return formatResult(`Folder "${folderName}" created in "${parentFolderPath}".`);
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
server.tool(
|
|
283
|
+
"delete_folder",
|
|
284
|
+
"Delete a folder from ImageKit",
|
|
285
|
+
{
|
|
286
|
+
folderPath: z.string().describe("Full path of the folder to delete"),
|
|
287
|
+
},
|
|
288
|
+
async ({ folderPath }) => {
|
|
289
|
+
const ik = getClient();
|
|
290
|
+
await ik.deleteFolder(folderPath);
|
|
291
|
+
return formatResult(`Folder deleted: ${folderPath}`);
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
server.tool(
|
|
296
|
+
"move_file",
|
|
297
|
+
"Move a file to a different folder in ImageKit",
|
|
298
|
+
{
|
|
299
|
+
sourceFilePath: z.string().describe("Current full path of the file (e.g., /folder/image.jpg)"),
|
|
300
|
+
destinationPath: z.string().describe("Destination folder path (e.g., /new-folder/)"),
|
|
301
|
+
},
|
|
302
|
+
async ({ sourceFilePath, destinationPath }) => {
|
|
303
|
+
const ik = getClient();
|
|
304
|
+
await ik.moveFile({ sourceFilePath, destinationPath });
|
|
305
|
+
return formatResult(`File moved from "${sourceFilePath}" to "${destinationPath}".`);
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
server.tool(
|
|
310
|
+
"generate_url",
|
|
311
|
+
"Generate an ImageKit URL with transformations (resize, crop, format, quality, etc.)",
|
|
312
|
+
{
|
|
313
|
+
path: z.string().optional().describe("Image path relative to URL endpoint (e.g., /folder/image.jpg)"),
|
|
314
|
+
src: z.string().optional().describe("Absolute image URL (alternative to path)"),
|
|
315
|
+
transformation: z
|
|
316
|
+
.array(z.record(z.string(), z.any()))
|
|
317
|
+
.optional()
|
|
318
|
+
.describe("Array of transformation objects (e.g., [{height: 300, width: 300}])"),
|
|
319
|
+
transformationPosition: z.enum(["path", "query"]).default("path").describe("Where to place transformation params"),
|
|
320
|
+
signed: z.boolean().default(false).describe("Generate a signed URL"),
|
|
321
|
+
expireSeconds: z.number().default(300).describe("Signed URL expiry in seconds"),
|
|
322
|
+
},
|
|
323
|
+
async ({ path: imgPath, src, transformation, transformationPosition, signed, expireSeconds }) => {
|
|
324
|
+
if (!imgPath && !src) {
|
|
325
|
+
return formatError("Either 'path' or 'src' is required.");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const ik = getClient();
|
|
329
|
+
|
|
330
|
+
const options = { transformation_position: transformationPosition };
|
|
331
|
+
if (imgPath) options.path = imgPath;
|
|
332
|
+
if (src) options.src = src;
|
|
333
|
+
if (transformation) options.transformation = transformation;
|
|
334
|
+
if (signed) {
|
|
335
|
+
options.signed = true;
|
|
336
|
+
options.expire_seconds = expireSeconds;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const url = ik.url(options);
|
|
340
|
+
|
|
341
|
+
return formatResult(
|
|
342
|
+
`Generated URL:\n\n ${url}` +
|
|
343
|
+
(signed ? `\n\n Signed: yes, expires in ${expireSeconds}s` : "")
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// --- Start ---
|
|
349
|
+
|
|
350
|
+
const transport = new StdioServerTransport();
|
|
351
|
+
await server.connect(transport);
|
|
352
|
+
console.error("ImageKit MCP server running on stdio");
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "imagekit-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for ImageKit.io — upload, search, transform, and manage media assets",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"imagekit-mcp-server": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "node index.js"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
20
|
+
"imagekit": "^6.0.0",
|
|
21
|
+
"zod": "^3.24.0"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"mcp-server",
|
|
26
|
+
"imagekit",
|
|
27
|
+
"media",
|
|
28
|
+
"cdn",
|
|
29
|
+
"image-management",
|
|
30
|
+
"model-context-protocol",
|
|
31
|
+
"ai",
|
|
32
|
+
"claude"
|
|
33
|
+
],
|
|
34
|
+
"author": "Ronaldo Lima",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/limaronaldo/imagekit-mcp-server.git"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/limaronaldo/imagekit-mcp-server#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/limaronaldo/imagekit-mcp-server/issues"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
}
|
|
47
|
+
}
|