converteverything-mcp 1.2.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 +668 -0
- package/dist/client.d.ts +129 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +594 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +169 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* ConvertEverything MCP Server
|
|
5
|
+
*
|
|
6
|
+
* An MCP server that enables AI assistants to convert files between 93+ formats
|
|
7
|
+
* using the ConvertEverything.io API.
|
|
8
|
+
*
|
|
9
|
+
* Security: This server only uses the public API with user-provided API keys.
|
|
10
|
+
* No internal endpoints, secrets, or admin functionality is exposed.
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
47
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
48
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
49
|
+
const zod_1 = require("zod");
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const client_js_1 = require("./client.js");
|
|
53
|
+
const types_js_2 = require("./types.js");
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Package Info
|
|
56
|
+
// ============================================================================
|
|
57
|
+
const PACKAGE_VERSION = "1.2.0";
|
|
58
|
+
const PACKAGE_NAME = "converteverything-mcp";
|
|
59
|
+
function parseArgs() {
|
|
60
|
+
const args = process.argv.slice(2);
|
|
61
|
+
const result = {};
|
|
62
|
+
for (let i = 0; i < args.length; i++) {
|
|
63
|
+
const arg = args[i];
|
|
64
|
+
const nextArg = args[i + 1];
|
|
65
|
+
if (arg === "--help" || arg === "-h") {
|
|
66
|
+
result.help = true;
|
|
67
|
+
}
|
|
68
|
+
else if (arg === "--version" || arg === "-v") {
|
|
69
|
+
result.version = true;
|
|
70
|
+
}
|
|
71
|
+
else if ((arg === "--api-key" || arg === "-k") && nextArg) {
|
|
72
|
+
result.apiKey = nextArg;
|
|
73
|
+
i++;
|
|
74
|
+
}
|
|
75
|
+
else if ((arg === "--base-url" || arg === "-u") && nextArg) {
|
|
76
|
+
result.baseUrl = nextArg;
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
else if (arg.startsWith("--api-key=")) {
|
|
80
|
+
result.apiKey = arg.split("=")[1];
|
|
81
|
+
}
|
|
82
|
+
else if (arg.startsWith("--base-url=")) {
|
|
83
|
+
result.baseUrl = arg.split("=")[1];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
function showHelp() {
|
|
89
|
+
console.log(`
|
|
90
|
+
${PACKAGE_NAME} v${PACKAGE_VERSION}
|
|
91
|
+
|
|
92
|
+
MCP server for ConvertEverything.io - Convert files between 93+ formats
|
|
93
|
+
|
|
94
|
+
USAGE:
|
|
95
|
+
npx ${PACKAGE_NAME} [OPTIONS]
|
|
96
|
+
|
|
97
|
+
OPTIONS:
|
|
98
|
+
-k, --api-key <key> ConvertEverything.io API key (required)
|
|
99
|
+
-u, --base-url <url> Custom API URL (default: https://converteverything.io)
|
|
100
|
+
-h, --help Show this help message
|
|
101
|
+
-v, --version Show version number
|
|
102
|
+
|
|
103
|
+
ENVIRONMENT VARIABLES:
|
|
104
|
+
CONVERTEVERYTHING_API_KEY API key (alternative to --api-key)
|
|
105
|
+
CONVERTEVERYTHING_BASE_URL Base URL (alternative to --base-url)
|
|
106
|
+
|
|
107
|
+
EXAMPLES:
|
|
108
|
+
npx ${PACKAGE_NAME} --api-key ce_your_key_here
|
|
109
|
+
npx ${PACKAGE_NAME} -k ce_your_key_here
|
|
110
|
+
|
|
111
|
+
GET AN API KEY:
|
|
112
|
+
1. Sign up at https://converteverything.io/register
|
|
113
|
+
2. Subscribe to Silver ($9.99/mo) or Gold ($19.99/mo)
|
|
114
|
+
3. Create a key at https://converteverything.io/api-keys
|
|
115
|
+
|
|
116
|
+
MORE INFO:
|
|
117
|
+
https://github.com/converteverything/converteverything-mcp
|
|
118
|
+
`);
|
|
119
|
+
}
|
|
120
|
+
function showVersion() {
|
|
121
|
+
console.log(`${PACKAGE_NAME} v${PACKAGE_VERSION}`);
|
|
122
|
+
}
|
|
123
|
+
const cliArgs = parseArgs();
|
|
124
|
+
// Handle --help and --version before anything else
|
|
125
|
+
if (cliArgs.help) {
|
|
126
|
+
showHelp();
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
if (cliArgs.version) {
|
|
130
|
+
showVersion();
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Configuration (CLI args take precedence over env vars)
|
|
135
|
+
// ============================================================================
|
|
136
|
+
const API_KEY = cliArgs.apiKey || process.env.CONVERTEVERYTHING_API_KEY;
|
|
137
|
+
const BASE_URL = cliArgs.baseUrl || process.env.CONVERTEVERYTHING_BASE_URL || types_js_2.DEFAULT_BASE_URL;
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Input Validation Schemas
|
|
140
|
+
// ============================================================================
|
|
141
|
+
const ConvertFileSchema = zod_1.z.object({
|
|
142
|
+
file_path: zod_1.z.string().min(1).describe("Path to the file to convert"),
|
|
143
|
+
target_format: zod_1.z.string().min(1).max(10).describe("Target format (e.g., 'mp3', 'pdf', 'png')"),
|
|
144
|
+
options: zod_1.z.record(zod_1.z.unknown()).optional().describe("Conversion settings"),
|
|
145
|
+
preset: zod_1.z.string().optional().describe("Preset name: web-optimized, high-quality, smallest-size, balanced, print-ready, archive"),
|
|
146
|
+
});
|
|
147
|
+
const ConvertBase64Schema = zod_1.z.object({
|
|
148
|
+
data: zod_1.z.string().min(1).describe("Base64-encoded file data"),
|
|
149
|
+
filename: zod_1.z.string().min(1).max(255).describe("Original filename with extension"),
|
|
150
|
+
target_format: zod_1.z.string().min(1).max(10).describe("Target format"),
|
|
151
|
+
options: zod_1.z.record(zod_1.z.unknown()).optional().describe("Conversion settings"),
|
|
152
|
+
preset: zod_1.z.string().optional().describe("Preset name"),
|
|
153
|
+
});
|
|
154
|
+
const BatchConvertSchema = zod_1.z.object({
|
|
155
|
+
files: zod_1.z.array(zod_1.z.object({
|
|
156
|
+
file_path: zod_1.z.string().min(1),
|
|
157
|
+
target_format: zod_1.z.string().min(1).max(10),
|
|
158
|
+
})).min(1).max(50).describe("Array of files to convert"),
|
|
159
|
+
options: zod_1.z.record(zod_1.z.unknown()).optional().describe("Shared conversion settings"),
|
|
160
|
+
preset: zod_1.z.string().optional().describe("Preset name to apply to all files"),
|
|
161
|
+
});
|
|
162
|
+
const GetConversionStatusSchema = zod_1.z.object({
|
|
163
|
+
conversion_id: zod_1.z.string().uuid().describe("The conversion ID"),
|
|
164
|
+
});
|
|
165
|
+
const WaitForConversionSchema = zod_1.z.object({
|
|
166
|
+
conversion_id: zod_1.z.string().uuid().describe("The conversion ID"),
|
|
167
|
+
timeout: zod_1.z.number().optional().describe("Max wait time in seconds (default: 300)"),
|
|
168
|
+
poll_interval: zod_1.z.number().optional().describe("Poll interval in seconds (default: 2)"),
|
|
169
|
+
});
|
|
170
|
+
const DownloadFileSchema = zod_1.z.object({
|
|
171
|
+
conversion_id: zod_1.z.string().uuid().describe("The conversion ID"),
|
|
172
|
+
save_path: zod_1.z.string().optional().describe("Optional path to save the file"),
|
|
173
|
+
});
|
|
174
|
+
const ListConversionsSchema = zod_1.z.object({
|
|
175
|
+
page: zod_1.z.number().optional().describe("Page number (default: 1)"),
|
|
176
|
+
per_page: zod_1.z.number().optional().describe("Items per page (default: 10, max: 100)"),
|
|
177
|
+
});
|
|
178
|
+
const CancelConversionSchema = zod_1.z.object({
|
|
179
|
+
conversion_id: zod_1.z.string().uuid().describe("The conversion ID to cancel"),
|
|
180
|
+
});
|
|
181
|
+
const GetFileInfoSchema = zod_1.z.object({
|
|
182
|
+
file_path: zod_1.z.string().min(1).describe("Path to the file to analyze"),
|
|
183
|
+
});
|
|
184
|
+
const EstimateOutputSizeSchema = zod_1.z.object({
|
|
185
|
+
file_path: zod_1.z.string().min(1).describe("Path to the source file"),
|
|
186
|
+
target_format: zod_1.z.string().min(1).max(10).describe("Target format"),
|
|
187
|
+
options: zod_1.z.record(zod_1.z.unknown()).optional().describe("Conversion options"),
|
|
188
|
+
preset: zod_1.z.string().optional().describe("Preset name"),
|
|
189
|
+
});
|
|
190
|
+
// ============================================================================
|
|
191
|
+
// Tool Definitions
|
|
192
|
+
// ============================================================================
|
|
193
|
+
const TOOLS = [
|
|
194
|
+
{
|
|
195
|
+
name: "get_supported_formats",
|
|
196
|
+
description: "Get a list of all supported file formats for conversion, organized by category " +
|
|
197
|
+
"(audio, video, image, document, ebook, data, 3d, font, archive, cad).",
|
|
198
|
+
inputSchema: {
|
|
199
|
+
type: "object",
|
|
200
|
+
properties: {},
|
|
201
|
+
required: [],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "get_usage",
|
|
206
|
+
description: "Get your current API usage statistics and limits. Shows daily conversions used, " +
|
|
207
|
+
"daily limit, maximum file size for your tier, and your subscription tier.",
|
|
208
|
+
inputSchema: {
|
|
209
|
+
type: "object",
|
|
210
|
+
properties: {},
|
|
211
|
+
required: [],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: "list_presets",
|
|
216
|
+
description: "List available conversion presets with their settings. " +
|
|
217
|
+
"Presets: web-optimized, high-quality, smallest-size, balanced, print-ready, archive.",
|
|
218
|
+
inputSchema: {
|
|
219
|
+
type: "object",
|
|
220
|
+
properties: {},
|
|
221
|
+
required: [],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "convert_file",
|
|
226
|
+
description: "Convert a file from one format to another. Supports 93+ formats. " +
|
|
227
|
+
"Use 'preset' for quick settings or 'options' for fine-grained control.",
|
|
228
|
+
inputSchema: {
|
|
229
|
+
type: "object",
|
|
230
|
+
properties: {
|
|
231
|
+
file_path: { type: "string", description: "Path to the file to convert" },
|
|
232
|
+
target_format: { type: "string", description: "Target format (e.g., 'mp3', 'pdf')" },
|
|
233
|
+
options: { type: "object", description: "Conversion settings (bitrate, quality, etc.)" },
|
|
234
|
+
preset: { type: "string", description: "Preset: web-optimized, high-quality, smallest-size, balanced, print-ready, archive" },
|
|
235
|
+
},
|
|
236
|
+
required: ["file_path", "target_format"],
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "convert_base64",
|
|
241
|
+
description: "Convert a file provided as base64-encoded data. Useful for in-memory files.",
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: "object",
|
|
244
|
+
properties: {
|
|
245
|
+
data: { type: "string", description: "Base64-encoded file data" },
|
|
246
|
+
filename: { type: "string", description: "Original filename with extension" },
|
|
247
|
+
target_format: { type: "string", description: "Target format" },
|
|
248
|
+
options: { type: "object", description: "Conversion settings" },
|
|
249
|
+
preset: { type: "string", description: "Preset name" },
|
|
250
|
+
},
|
|
251
|
+
required: ["data", "filename", "target_format"],
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: "batch_convert",
|
|
256
|
+
description: "Convert multiple files at once. Provide an array of file paths and target formats. " +
|
|
257
|
+
"Returns conversion IDs for each file.",
|
|
258
|
+
inputSchema: {
|
|
259
|
+
type: "object",
|
|
260
|
+
properties: {
|
|
261
|
+
files: {
|
|
262
|
+
type: "array",
|
|
263
|
+
items: {
|
|
264
|
+
type: "object",
|
|
265
|
+
properties: {
|
|
266
|
+
file_path: { type: "string" },
|
|
267
|
+
target_format: { type: "string" },
|
|
268
|
+
},
|
|
269
|
+
required: ["file_path", "target_format"],
|
|
270
|
+
},
|
|
271
|
+
description: "Array of {file_path, target_format} objects",
|
|
272
|
+
},
|
|
273
|
+
options: { type: "object", description: "Shared conversion settings" },
|
|
274
|
+
preset: { type: "string", description: "Preset to apply to all files" },
|
|
275
|
+
},
|
|
276
|
+
required: ["files"],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: "get_conversion_status",
|
|
281
|
+
description: "Check the status of a conversion. Returns: pending, processing, completed, or failed.",
|
|
282
|
+
inputSchema: {
|
|
283
|
+
type: "object",
|
|
284
|
+
properties: {
|
|
285
|
+
conversion_id: { type: "string", description: "The conversion ID" },
|
|
286
|
+
},
|
|
287
|
+
required: ["conversion_id"],
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: "wait_for_conversion",
|
|
292
|
+
description: "Wait for a conversion to complete, polling until done. " +
|
|
293
|
+
"Returns the final status when complete or failed.",
|
|
294
|
+
inputSchema: {
|
|
295
|
+
type: "object",
|
|
296
|
+
properties: {
|
|
297
|
+
conversion_id: { type: "string", description: "The conversion ID" },
|
|
298
|
+
timeout: { type: "number", description: "Max wait time in seconds (default: 300)" },
|
|
299
|
+
poll_interval: { type: "number", description: "Poll interval in seconds (default: 2)" },
|
|
300
|
+
},
|
|
301
|
+
required: ["conversion_id"],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: "download_file",
|
|
306
|
+
description: "Download a completed conversion. Returns base64 data or saves to disk.",
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: "object",
|
|
309
|
+
properties: {
|
|
310
|
+
conversion_id: { type: "string", description: "The conversion ID" },
|
|
311
|
+
save_path: { type: "string", description: "Optional path to save the file" },
|
|
312
|
+
},
|
|
313
|
+
required: ["conversion_id"],
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: "list_conversions",
|
|
318
|
+
description: "List your recent conversions with pagination.",
|
|
319
|
+
inputSchema: {
|
|
320
|
+
type: "object",
|
|
321
|
+
properties: {
|
|
322
|
+
page: { type: "number", description: "Page number (default: 1)" },
|
|
323
|
+
per_page: { type: "number", description: "Items per page (default: 10, max: 100)" },
|
|
324
|
+
},
|
|
325
|
+
required: [],
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "cancel_conversion",
|
|
330
|
+
description: "Cancel an in-progress conversion. Cannot cancel completed or failed conversions.",
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {
|
|
334
|
+
conversion_id: { type: "string", description: "The conversion ID to cancel" },
|
|
335
|
+
},
|
|
336
|
+
required: ["conversion_id"],
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "get_file_info",
|
|
341
|
+
description: "Get file information including size, format, and MIME type before conversion.",
|
|
342
|
+
inputSchema: {
|
|
343
|
+
type: "object",
|
|
344
|
+
properties: {
|
|
345
|
+
file_path: { type: "string", description: "Path to the file to analyze" },
|
|
346
|
+
},
|
|
347
|
+
required: ["file_path"],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: "estimate_output_size",
|
|
352
|
+
description: "Estimate the output file size after conversion based on format and options.",
|
|
353
|
+
inputSchema: {
|
|
354
|
+
type: "object",
|
|
355
|
+
properties: {
|
|
356
|
+
file_path: { type: "string", description: "Path to the source file" },
|
|
357
|
+
target_format: { type: "string", description: "Target format" },
|
|
358
|
+
options: { type: "object", description: "Conversion options" },
|
|
359
|
+
preset: { type: "string", description: "Preset name" },
|
|
360
|
+
},
|
|
361
|
+
required: ["file_path", "target_format"],
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
];
|
|
365
|
+
// ============================================================================
|
|
366
|
+
// Resource Definitions
|
|
367
|
+
// ============================================================================
|
|
368
|
+
const RESOURCES = [
|
|
369
|
+
{
|
|
370
|
+
uri: "converteverything://formats",
|
|
371
|
+
name: "Supported Formats",
|
|
372
|
+
description: "Complete list of all 93+ supported file formats by category",
|
|
373
|
+
mimeType: "text/plain",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
uri: "converteverything://formats/audio",
|
|
377
|
+
name: "Audio Formats",
|
|
378
|
+
description: "Supported audio formats: mp3, wav, flac, aac, ogg, m4a, etc.",
|
|
379
|
+
mimeType: "text/plain",
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
uri: "converteverything://formats/video",
|
|
383
|
+
name: "Video Formats",
|
|
384
|
+
description: "Supported video formats: mp4, avi, mkv, mov, webm, etc.",
|
|
385
|
+
mimeType: "text/plain",
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
uri: "converteverything://formats/image",
|
|
389
|
+
name: "Image Formats",
|
|
390
|
+
description: "Supported image formats: jpg, png, gif, webp, svg, etc.",
|
|
391
|
+
mimeType: "text/plain",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
uri: "converteverything://formats/document",
|
|
395
|
+
name: "Document Formats",
|
|
396
|
+
description: "Supported document formats: pdf, docx, xlsx, pptx, etc.",
|
|
397
|
+
mimeType: "text/plain",
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
uri: "converteverything://presets",
|
|
401
|
+
name: "Conversion Presets",
|
|
402
|
+
description: "Available presets: web-optimized, high-quality, smallest-size, etc.",
|
|
403
|
+
mimeType: "text/plain",
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
uri: "converteverything://subscription",
|
|
407
|
+
name: "Subscription Info",
|
|
408
|
+
description: "Your current subscription tier, limits, and usage",
|
|
409
|
+
mimeType: "text/plain",
|
|
410
|
+
},
|
|
411
|
+
];
|
|
412
|
+
// Format categories for resources (derived from types.ts)
|
|
413
|
+
const FORMAT_CATEGORY_KEYS = [
|
|
414
|
+
"audio", "video", "image", "document", "ebook", "data", "3d", "font", "archive", "cad"
|
|
415
|
+
];
|
|
416
|
+
// ============================================================================
|
|
417
|
+
// MCP Prompts
|
|
418
|
+
// ============================================================================
|
|
419
|
+
const PROMPTS = [
|
|
420
|
+
{
|
|
421
|
+
name: "convert-for-web",
|
|
422
|
+
description: "Convert files to web-optimized formats",
|
|
423
|
+
arguments: [
|
|
424
|
+
{
|
|
425
|
+
name: "file_path",
|
|
426
|
+
description: "Path to the file or folder to convert",
|
|
427
|
+
required: true,
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: "batch-convert-folder",
|
|
433
|
+
description: "Convert all files in a folder to a target format",
|
|
434
|
+
arguments: [
|
|
435
|
+
{
|
|
436
|
+
name: "folder_path",
|
|
437
|
+
description: "Path to the folder containing files",
|
|
438
|
+
required: true,
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: "target_format",
|
|
442
|
+
description: "Target format for all files",
|
|
443
|
+
required: true,
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: "optimize-images",
|
|
449
|
+
description: "Optimize images for web or print",
|
|
450
|
+
arguments: [
|
|
451
|
+
{
|
|
452
|
+
name: "file_path",
|
|
453
|
+
description: "Path to the image or folder of images",
|
|
454
|
+
required: true,
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
name: "purpose",
|
|
458
|
+
description: "Purpose: 'web' for smaller files, 'print' for high quality",
|
|
459
|
+
required: false,
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
name: "convert-video-for-streaming",
|
|
465
|
+
description: "Convert video to streaming-friendly format (MP4/WebM)",
|
|
466
|
+
arguments: [
|
|
467
|
+
{
|
|
468
|
+
name: "file_path",
|
|
469
|
+
description: "Path to the video file",
|
|
470
|
+
required: true,
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
name: "quality",
|
|
474
|
+
description: "Quality level: 'low', 'medium', 'high'",
|
|
475
|
+
required: false,
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: "document-to-pdf",
|
|
481
|
+
description: "Convert documents to PDF format",
|
|
482
|
+
arguments: [
|
|
483
|
+
{
|
|
484
|
+
name: "file_path",
|
|
485
|
+
description: "Path to the document file",
|
|
486
|
+
required: true,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
name: "quality",
|
|
490
|
+
description: "PDF quality: 'screen', 'ebook', 'printer', 'prepress'",
|
|
491
|
+
required: false,
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
},
|
|
495
|
+
];
|
|
496
|
+
// ============================================================================
|
|
497
|
+
// Server Implementation
|
|
498
|
+
// ============================================================================
|
|
499
|
+
class ConvertEverythingServer {
|
|
500
|
+
server;
|
|
501
|
+
client = null;
|
|
502
|
+
constructor() {
|
|
503
|
+
this.server = new index_js_1.Server({
|
|
504
|
+
name: PACKAGE_NAME,
|
|
505
|
+
version: PACKAGE_VERSION,
|
|
506
|
+
}, {
|
|
507
|
+
capabilities: {
|
|
508
|
+
tools: {},
|
|
509
|
+
resources: {},
|
|
510
|
+
prompts: {},
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
this.setupHandlers();
|
|
514
|
+
}
|
|
515
|
+
getClient() {
|
|
516
|
+
if (!this.client) {
|
|
517
|
+
if (!API_KEY) {
|
|
518
|
+
throw new Error("API key required. Use --api-key or set CONVERTEVERYTHING_API_KEY. " +
|
|
519
|
+
"Get your key at https://converteverything.io/api-keys");
|
|
520
|
+
}
|
|
521
|
+
this.client = new client_js_1.ConvertEverythingClient({
|
|
522
|
+
apiKey: API_KEY,
|
|
523
|
+
baseUrl: BASE_URL,
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
return this.client;
|
|
527
|
+
}
|
|
528
|
+
setupHandlers() {
|
|
529
|
+
// List available tools
|
|
530
|
+
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
531
|
+
tools: TOOLS,
|
|
532
|
+
}));
|
|
533
|
+
// List available resources
|
|
534
|
+
this.server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => ({
|
|
535
|
+
resources: RESOURCES,
|
|
536
|
+
}));
|
|
537
|
+
// Read resource content
|
|
538
|
+
this.server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
|
|
539
|
+
const uri = request.params.uri;
|
|
540
|
+
return this.handleReadResource(uri);
|
|
541
|
+
});
|
|
542
|
+
// List available prompts
|
|
543
|
+
this.server.setRequestHandler(types_js_1.ListPromptsRequestSchema, async () => ({
|
|
544
|
+
prompts: PROMPTS,
|
|
545
|
+
}));
|
|
546
|
+
// Get prompt content
|
|
547
|
+
this.server.setRequestHandler(types_js_1.GetPromptRequestSchema, async (request) => {
|
|
548
|
+
const { name, arguments: promptArgs } = request.params;
|
|
549
|
+
return this.handleGetPrompt(name, promptArgs || {});
|
|
550
|
+
});
|
|
551
|
+
// Handle tool calls
|
|
552
|
+
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
553
|
+
const { name, arguments: args } = request.params;
|
|
554
|
+
try {
|
|
555
|
+
switch (name) {
|
|
556
|
+
case "get_supported_formats":
|
|
557
|
+
return await this.handleGetSupportedFormats();
|
|
558
|
+
case "get_usage":
|
|
559
|
+
return await this.handleGetUsage();
|
|
560
|
+
case "list_presets":
|
|
561
|
+
return this.handleListPresets();
|
|
562
|
+
case "convert_file":
|
|
563
|
+
return await this.handleConvertFile(args);
|
|
564
|
+
case "convert_base64":
|
|
565
|
+
return await this.handleConvertBase64(args);
|
|
566
|
+
case "batch_convert":
|
|
567
|
+
return await this.handleBatchConvert(args);
|
|
568
|
+
case "get_conversion_status":
|
|
569
|
+
return await this.handleGetConversionStatus(args);
|
|
570
|
+
case "wait_for_conversion":
|
|
571
|
+
return await this.handleWaitForConversion(args);
|
|
572
|
+
case "download_file":
|
|
573
|
+
return await this.handleDownloadFile(args);
|
|
574
|
+
case "list_conversions":
|
|
575
|
+
return await this.handleListConversions(args);
|
|
576
|
+
case "cancel_conversion":
|
|
577
|
+
return await this.handleCancelConversion(args);
|
|
578
|
+
case "get_file_info":
|
|
579
|
+
return await this.handleGetFileInfo(args);
|
|
580
|
+
case "estimate_output_size":
|
|
581
|
+
return await this.handleEstimateOutputSize(args);
|
|
582
|
+
default:
|
|
583
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
588
|
+
return {
|
|
589
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
590
|
+
isError: true,
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
handleReadResource(uri) {
|
|
596
|
+
let text = "";
|
|
597
|
+
if (uri === "converteverything://formats") {
|
|
598
|
+
text = "SUPPORTED FORMATS (93+ total)\n\n";
|
|
599
|
+
for (const category of FORMAT_CATEGORY_KEYS) {
|
|
600
|
+
const formats = (0, types_js_2.getFormatsByCategory)(category);
|
|
601
|
+
const description = types_js_2.FORMAT_CATEGORY_DESCRIPTIONS[category];
|
|
602
|
+
text += `${category.toUpperCase()}: ${formats.join(", ")}\n`;
|
|
603
|
+
text += ` ${description}\n\n`;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
else if (uri === "converteverything://presets") {
|
|
607
|
+
text = "CONVERSION PRESETS\n\n";
|
|
608
|
+
for (const [name, config] of Object.entries(types_js_2.PRESET_CONFIGS)) {
|
|
609
|
+
text += `${name}: ${config.description}\n`;
|
|
610
|
+
for (const [category, options] of Object.entries(config.options)) {
|
|
611
|
+
text += ` ${category}: ${JSON.stringify(options)}\n`;
|
|
612
|
+
}
|
|
613
|
+
text += "\n";
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else if (uri.startsWith("converteverything://formats/")) {
|
|
617
|
+
const category = uri.replace("converteverything://formats/", "");
|
|
618
|
+
if (types_js_2.FORMAT_CATEGORY_DESCRIPTIONS[category]) {
|
|
619
|
+
const formats = (0, types_js_2.getFormatsByCategory)(category);
|
|
620
|
+
const description = types_js_2.FORMAT_CATEGORY_DESCRIPTIONS[category];
|
|
621
|
+
text = `${category.toUpperCase()} FORMATS\n\n`;
|
|
622
|
+
text += `${description}\n\n`;
|
|
623
|
+
text += `Formats: ${formats.join(", ")}\n`;
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
throw new Error(`Unknown format category: ${category}`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else if (uri === "converteverything://subscription") {
|
|
630
|
+
// This is a dynamic resource - fetch usage info
|
|
631
|
+
return this.handleSubscriptionResource(uri);
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
contents: [{ uri, mimeType: "text/plain", text }],
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
async handleSubscriptionResource(uri) {
|
|
641
|
+
try {
|
|
642
|
+
const client = this.getClient();
|
|
643
|
+
const usage = await client.getUsage();
|
|
644
|
+
const limitText = usage.conversions_limit === -1
|
|
645
|
+
? "Unlimited"
|
|
646
|
+
: usage.conversions_limit.toString();
|
|
647
|
+
const remainingText = usage.conversions_remaining === Infinity
|
|
648
|
+
? "Unlimited"
|
|
649
|
+
: usage.conversions_remaining.toString();
|
|
650
|
+
const text = `SUBSCRIPTION INFO\n\n` +
|
|
651
|
+
`Tier: ${usage.tier.toUpperCase()}\n\n` +
|
|
652
|
+
`Daily Conversions:\n` +
|
|
653
|
+
` Used: ${usage.conversions_used}\n` +
|
|
654
|
+
` Limit: ${limitText}\n` +
|
|
655
|
+
` Remaining: ${remainingText}\n\n` +
|
|
656
|
+
`Max File Size: ${usage.max_file_size_mb} MB\n` +
|
|
657
|
+
`File Retention: ${usage.file_retention_hours} hours\n\n` +
|
|
658
|
+
`Upgrade: https://converteverything.io/pricing`;
|
|
659
|
+
return {
|
|
660
|
+
contents: [{ uri, mimeType: "text/plain", text }],
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
665
|
+
return {
|
|
666
|
+
contents: [{ uri, mimeType: "text/plain", text: `Error fetching subscription info: ${message}` }],
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
async handleGetSupportedFormats() {
|
|
671
|
+
const client = this.getClient();
|
|
672
|
+
const formats = await client.getSupportedFormats();
|
|
673
|
+
let text = `Supported Formats (${formats.total_formats} total):\n\n`;
|
|
674
|
+
for (const [category, formatList] of Object.entries(formats.formats)) {
|
|
675
|
+
if (Array.isArray(formatList) && formatList.length > 0) {
|
|
676
|
+
text += `${category.toUpperCase()}: ${formatList.join(", ")}\n`;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return { content: [{ type: "text", text }] };
|
|
680
|
+
}
|
|
681
|
+
async handleGetUsage() {
|
|
682
|
+
const client = this.getClient();
|
|
683
|
+
const usage = await client.getUsage();
|
|
684
|
+
const limitText = usage.conversions_limit === -1
|
|
685
|
+
? "Unlimited"
|
|
686
|
+
: usage.conversions_limit.toString();
|
|
687
|
+
const remainingText = usage.conversions_remaining === Infinity
|
|
688
|
+
? "Unlimited"
|
|
689
|
+
: usage.conversions_remaining.toString();
|
|
690
|
+
const text = `API Usage:\n` +
|
|
691
|
+
` Tier: ${usage.tier}\n` +
|
|
692
|
+
` Daily conversions: ${usage.conversions_used} / ${limitText}\n` +
|
|
693
|
+
` Remaining: ${remainingText}\n` +
|
|
694
|
+
` Max file size: ${usage.max_file_size_mb} MB\n` +
|
|
695
|
+
` File retention: ${usage.file_retention_hours} hours`;
|
|
696
|
+
return { content: [{ type: "text", text }] };
|
|
697
|
+
}
|
|
698
|
+
handleListPresets() {
|
|
699
|
+
let text = "Available Presets:\n\n";
|
|
700
|
+
for (const [name, config] of Object.entries(types_js_2.PRESET_CONFIGS)) {
|
|
701
|
+
text += `${name}:\n ${config.description}\n`;
|
|
702
|
+
for (const [category, options] of Object.entries(config.options)) {
|
|
703
|
+
text += ` ${category}: ${JSON.stringify(options)}\n`;
|
|
704
|
+
}
|
|
705
|
+
text += "\n";
|
|
706
|
+
}
|
|
707
|
+
return { content: [{ type: "text", text }] };
|
|
708
|
+
}
|
|
709
|
+
resolveOptions(targetFormat, preset, options) {
|
|
710
|
+
let resolvedOptions = options;
|
|
711
|
+
if (preset) {
|
|
712
|
+
const category = (0, types_js_2.getFormatCategory)(targetFormat);
|
|
713
|
+
if (category) {
|
|
714
|
+
const presetOptions = (0, types_js_2.getPresetOptions)(preset, category);
|
|
715
|
+
if (presetOptions) {
|
|
716
|
+
resolvedOptions = { ...presetOptions, ...options };
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return resolvedOptions;
|
|
721
|
+
}
|
|
722
|
+
async handleConvertFile(args) {
|
|
723
|
+
const parsed = ConvertFileSchema.parse(args);
|
|
724
|
+
const client = this.getClient();
|
|
725
|
+
const options = this.resolveOptions(parsed.target_format, parsed.preset, parsed.options);
|
|
726
|
+
const result = await client.convertFile(parsed.file_path, parsed.target_format, options);
|
|
727
|
+
const text = `Conversion started:\n` +
|
|
728
|
+
` ID: ${result.id}\n` +
|
|
729
|
+
` Status: ${result.status}\n` +
|
|
730
|
+
` From: ${result.source_format} → ${result.target_format}\n` +
|
|
731
|
+
` File: ${result.original_filename}\n` +
|
|
732
|
+
(parsed.preset ? ` Preset: ${parsed.preset}\n` : "") +
|
|
733
|
+
`\nUse wait_for_conversion to wait for completion, or get_conversion_status to check progress.`;
|
|
734
|
+
return { content: [{ type: "text", text }] };
|
|
735
|
+
}
|
|
736
|
+
async handleConvertBase64(args) {
|
|
737
|
+
const parsed = ConvertBase64Schema.parse(args);
|
|
738
|
+
const client = this.getClient();
|
|
739
|
+
const options = this.resolveOptions(parsed.target_format, parsed.preset, parsed.options);
|
|
740
|
+
const result = await client.convertFileBuffer(parsed.data, parsed.filename, parsed.target_format, options);
|
|
741
|
+
const text = `Conversion started:\n` +
|
|
742
|
+
` ID: ${result.id}\n` +
|
|
743
|
+
` Status: ${result.status}\n` +
|
|
744
|
+
` From: ${result.source_format} → ${result.target_format}\n` +
|
|
745
|
+
` File: ${result.original_filename}\n` +
|
|
746
|
+
(parsed.preset ? ` Preset: ${parsed.preset}\n` : "");
|
|
747
|
+
return { content: [{ type: "text", text }] };
|
|
748
|
+
}
|
|
749
|
+
async handleBatchConvert(args) {
|
|
750
|
+
const parsed = BatchConvertSchema.parse(args);
|
|
751
|
+
const client = this.getClient();
|
|
752
|
+
const results = [];
|
|
753
|
+
for (const file of parsed.files) {
|
|
754
|
+
try {
|
|
755
|
+
const options = this.resolveOptions(file.target_format, parsed.preset, parsed.options);
|
|
756
|
+
const result = await client.convertFile(file.file_path, file.target_format, options);
|
|
757
|
+
results.push({ file: file.file_path, id: result.id });
|
|
758
|
+
}
|
|
759
|
+
catch (error) {
|
|
760
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
761
|
+
results.push({ file: file.file_path, error: message });
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const successful = results.filter((r) => r.id);
|
|
765
|
+
const failed = results.filter((r) => r.error);
|
|
766
|
+
let text = `Batch conversion started:\n`;
|
|
767
|
+
text += ` Total: ${results.length}\n`;
|
|
768
|
+
text += ` Started: ${successful.length}\n`;
|
|
769
|
+
text += ` Failed: ${failed.length}\n\n`;
|
|
770
|
+
if (successful.length > 0) {
|
|
771
|
+
text += "Conversions:\n";
|
|
772
|
+
for (const r of successful) {
|
|
773
|
+
text += ` ${path.basename(r.file)} → ${r.id}\n`;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (failed.length > 0) {
|
|
777
|
+
text += "\nErrors:\n";
|
|
778
|
+
for (const r of failed) {
|
|
779
|
+
text += ` ${path.basename(r.file)}: ${r.error}\n`;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return { content: [{ type: "text", text }] };
|
|
783
|
+
}
|
|
784
|
+
async handleGetConversionStatus(args) {
|
|
785
|
+
const parsed = GetConversionStatusSchema.parse(args);
|
|
786
|
+
const client = this.getClient();
|
|
787
|
+
const result = await client.getConversionStatus(parsed.conversion_id);
|
|
788
|
+
let text = `Conversion Status:\n` +
|
|
789
|
+
` ID: ${result.id}\n` +
|
|
790
|
+
` Status: ${result.status}\n` +
|
|
791
|
+
` From: ${result.source_format} → ${result.target_format}\n` +
|
|
792
|
+
` File: ${result.original_filename}\n`;
|
|
793
|
+
if (result.created_at)
|
|
794
|
+
text += ` Created: ${result.created_at}\n`;
|
|
795
|
+
if (result.started_at)
|
|
796
|
+
text += ` Started: ${result.started_at}\n`;
|
|
797
|
+
if (result.completed_at)
|
|
798
|
+
text += ` Completed: ${result.completed_at}\n`;
|
|
799
|
+
if (result.error_message)
|
|
800
|
+
text += ` Error: ${result.error_message}\n`;
|
|
801
|
+
if (result.download_url) {
|
|
802
|
+
text += `\nFile is ready for download. Use download_file to get the converted file.`;
|
|
803
|
+
if (result.download_expires_at) {
|
|
804
|
+
text += `\nDownload expires: ${result.download_expires_at}`;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
return { content: [{ type: "text", text }] };
|
|
808
|
+
}
|
|
809
|
+
async handleWaitForConversion(args) {
|
|
810
|
+
const parsed = WaitForConversionSchema.parse(args);
|
|
811
|
+
const client = this.getClient();
|
|
812
|
+
const timeout = (parsed.timeout || 300) * 1000;
|
|
813
|
+
const pollInterval = (parsed.poll_interval || 2) * 1000;
|
|
814
|
+
const result = await client.waitForConversion(parsed.conversion_id, {
|
|
815
|
+
timeout,
|
|
816
|
+
pollInterval,
|
|
817
|
+
});
|
|
818
|
+
let text = `Conversion ${result.status}:\n` +
|
|
819
|
+
` ID: ${result.id}\n` +
|
|
820
|
+
` Status: ${result.status}\n` +
|
|
821
|
+
` From: ${result.source_format} → ${result.target_format}\n` +
|
|
822
|
+
` File: ${result.original_filename}\n`;
|
|
823
|
+
if (result.completed_at)
|
|
824
|
+
text += ` Completed: ${result.completed_at}\n`;
|
|
825
|
+
if (result.error_message)
|
|
826
|
+
text += ` Error: ${result.error_message}\n`;
|
|
827
|
+
if (result.status === "completed") {
|
|
828
|
+
text += `\nFile is ready! Use download_file to get the converted file.`;
|
|
829
|
+
}
|
|
830
|
+
return { content: [{ type: "text", text }] };
|
|
831
|
+
}
|
|
832
|
+
async handleDownloadFile(args) {
|
|
833
|
+
const parsed = DownloadFileSchema.parse(args);
|
|
834
|
+
const client = this.getClient();
|
|
835
|
+
const { data, filename, contentType } = await client.downloadFile(parsed.conversion_id);
|
|
836
|
+
if (parsed.save_path) {
|
|
837
|
+
// Validate save path
|
|
838
|
+
const savePath = path.resolve(parsed.save_path);
|
|
839
|
+
// Security: Check for null bytes
|
|
840
|
+
if (savePath.includes("\0")) {
|
|
841
|
+
throw new Error("Invalid save path: contains null bytes");
|
|
842
|
+
}
|
|
843
|
+
// Security: Ensure the path doesn't contain traversal sequences after resolution
|
|
844
|
+
if (savePath.includes("..")) {
|
|
845
|
+
throw new Error("Invalid save path: contains directory traversal");
|
|
846
|
+
}
|
|
847
|
+
// Determine final path (if directory, append filename)
|
|
848
|
+
const stats = fs.statSync(savePath, { throwIfNoEntry: false });
|
|
849
|
+
let finalPath;
|
|
850
|
+
if (stats?.isDirectory()) {
|
|
851
|
+
// Sanitize the filename before joining
|
|
852
|
+
const sanitizedFilename = path.basename(filename).replace(/[\x00-\x1f]/g, "");
|
|
853
|
+
finalPath = path.join(savePath, sanitizedFilename);
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
// Ensure parent directory exists
|
|
857
|
+
const parentDir = path.dirname(savePath);
|
|
858
|
+
if (!fs.existsSync(parentDir)) {
|
|
859
|
+
throw new Error(`Parent directory does not exist: ${parentDir}`);
|
|
860
|
+
}
|
|
861
|
+
finalPath = savePath;
|
|
862
|
+
}
|
|
863
|
+
fs.writeFileSync(finalPath, data);
|
|
864
|
+
return {
|
|
865
|
+
content: [{
|
|
866
|
+
type: "text",
|
|
867
|
+
text: `File saved to: ${finalPath}\nSize: ${data.length} bytes\nType: ${contentType}`,
|
|
868
|
+
}],
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
const base64 = data.toString("base64");
|
|
872
|
+
return {
|
|
873
|
+
content: [{
|
|
874
|
+
type: "text",
|
|
875
|
+
text: `Filename: ${filename}\nSize: ${data.length} bytes\nType: ${contentType}\nData (base64):\n${base64}`,
|
|
876
|
+
}],
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
async handleListConversions(args) {
|
|
880
|
+
const parsed = ListConversionsSchema.parse(args);
|
|
881
|
+
const client = this.getClient();
|
|
882
|
+
const result = await client.listConversions(parsed.page, parsed.per_page);
|
|
883
|
+
let text = `Recent Conversions (page ${result.page}, ${result.conversions.length} of ${result.total}):\n\n`;
|
|
884
|
+
if (result.conversions.length === 0) {
|
|
885
|
+
text += "No conversions found.";
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
for (const conv of result.conversions) {
|
|
889
|
+
text += `${conv.id}\n`;
|
|
890
|
+
text += ` ${conv.original_filename}: ${conv.source_format} → ${conv.target_format}\n`;
|
|
891
|
+
text += ` Status: ${conv.status}`;
|
|
892
|
+
if (conv.created_at)
|
|
893
|
+
text += ` | Created: ${conv.created_at}`;
|
|
894
|
+
text += "\n\n";
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return { content: [{ type: "text", text }] };
|
|
898
|
+
}
|
|
899
|
+
async handleCancelConversion(args) {
|
|
900
|
+
const parsed = CancelConversionSchema.parse(args);
|
|
901
|
+
const client = this.getClient();
|
|
902
|
+
const result = await client.cancelConversion(parsed.conversion_id);
|
|
903
|
+
const text = result.success
|
|
904
|
+
? `✓ ${result.message}`
|
|
905
|
+
: `✗ ${result.message}`;
|
|
906
|
+
return { content: [{ type: "text", text }] };
|
|
907
|
+
}
|
|
908
|
+
async handleGetFileInfo(args) {
|
|
909
|
+
const parsed = GetFileInfoSchema.parse(args);
|
|
910
|
+
const client = this.getClient();
|
|
911
|
+
const info = await client.getFileInfo(parsed.file_path);
|
|
912
|
+
const sizeFormatted = info.size < 1024
|
|
913
|
+
? `${info.size} bytes`
|
|
914
|
+
: info.size < 1024 * 1024
|
|
915
|
+
? `${(info.size / 1024).toFixed(1)} KB`
|
|
916
|
+
: info.size < 1024 * 1024 * 1024
|
|
917
|
+
? `${(info.size / 1024 / 1024).toFixed(1)} MB`
|
|
918
|
+
: `${(info.size / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
919
|
+
const text = `File Information:\n` +
|
|
920
|
+
` Name: ${info.filename}\n` +
|
|
921
|
+
` Size: ${sizeFormatted} (${info.size} bytes)\n` +
|
|
922
|
+
` Format: ${info.format.toUpperCase()}\n` +
|
|
923
|
+
` MIME Type: ${info.mimeType}`;
|
|
924
|
+
return { content: [{ type: "text", text }] };
|
|
925
|
+
}
|
|
926
|
+
async handleEstimateOutputSize(args) {
|
|
927
|
+
const parsed = EstimateOutputSizeSchema.parse(args);
|
|
928
|
+
const client = this.getClient();
|
|
929
|
+
const info = await client.getFileInfo(parsed.file_path);
|
|
930
|
+
const options = this.resolveOptions(parsed.target_format, parsed.preset, parsed.options);
|
|
931
|
+
const estimate = client.estimateOutputSize(info.size, info.format, parsed.target_format, options);
|
|
932
|
+
const formatSize = (size) => {
|
|
933
|
+
if (size < 1024)
|
|
934
|
+
return `${size} bytes`;
|
|
935
|
+
if (size < 1024 * 1024)
|
|
936
|
+
return `${(size / 1024).toFixed(1)} KB`;
|
|
937
|
+
if (size < 1024 * 1024 * 1024)
|
|
938
|
+
return `${(size / 1024 / 1024).toFixed(1)} MB`;
|
|
939
|
+
return `${(size / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
940
|
+
};
|
|
941
|
+
const text = `Output Size Estimate:\n` +
|
|
942
|
+
` Input: ${formatSize(info.size)} (${info.format.toUpperCase()})\n` +
|
|
943
|
+
` Target: ${parsed.target_format.toUpperCase()}\n` +
|
|
944
|
+
` Estimated Output: ${formatSize(estimate.estimatedSize)}\n` +
|
|
945
|
+
` Confidence: ${estimate.confidence}\n` +
|
|
946
|
+
(estimate.notes ? ` Notes: ${estimate.notes}` : "");
|
|
947
|
+
return { content: [{ type: "text", text }] };
|
|
948
|
+
}
|
|
949
|
+
handleGetPrompt(name, args) {
|
|
950
|
+
switch (name) {
|
|
951
|
+
case "convert-for-web":
|
|
952
|
+
return {
|
|
953
|
+
messages: [
|
|
954
|
+
{
|
|
955
|
+
role: "user",
|
|
956
|
+
content: {
|
|
957
|
+
type: "text",
|
|
958
|
+
text: `I need to convert the file at "${args.file_path}" to a web-optimized format.
|
|
959
|
+
|
|
960
|
+
Please:
|
|
961
|
+
1. First use get_file_info to check the file details
|
|
962
|
+
2. Determine the best web format (e.g., JPG/WebP for images, MP4 for video, MP3 for audio)
|
|
963
|
+
3. Use the "web-optimized" preset for optimal web delivery
|
|
964
|
+
4. Convert the file and let me know when it's ready`,
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
],
|
|
968
|
+
};
|
|
969
|
+
case "batch-convert-folder":
|
|
970
|
+
return {
|
|
971
|
+
messages: [
|
|
972
|
+
{
|
|
973
|
+
role: "user",
|
|
974
|
+
content: {
|
|
975
|
+
type: "text",
|
|
976
|
+
text: `Please convert all files in the folder "${args.folder_path}" to ${args.target_format} format.
|
|
977
|
+
|
|
978
|
+
Steps:
|
|
979
|
+
1. List all files in the folder
|
|
980
|
+
2. Filter for convertible files
|
|
981
|
+
3. Use batch_convert to convert them all to ${args.target_format}
|
|
982
|
+
4. Report the results when complete`,
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
],
|
|
986
|
+
};
|
|
987
|
+
case "optimize-images":
|
|
988
|
+
const purpose = args.purpose || "web";
|
|
989
|
+
const preset = purpose === "print" ? "print-ready" : "web-optimized";
|
|
990
|
+
return {
|
|
991
|
+
messages: [
|
|
992
|
+
{
|
|
993
|
+
role: "user",
|
|
994
|
+
content: {
|
|
995
|
+
type: "text",
|
|
996
|
+
text: `Optimize the images at "${args.file_path}" for ${purpose} use.
|
|
997
|
+
|
|
998
|
+
Please:
|
|
999
|
+
1. Check the image(s) using get_file_info
|
|
1000
|
+
2. Use the "${preset}" preset
|
|
1001
|
+
3. Convert to the appropriate format (WebP/JPG for web, PNG/TIFF for print)
|
|
1002
|
+
4. Report the size savings`,
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
],
|
|
1006
|
+
};
|
|
1007
|
+
case "convert-video-for-streaming":
|
|
1008
|
+
const quality = args.quality || "medium";
|
|
1009
|
+
const crf = quality === "high" ? 18 : quality === "low" ? 28 : 23;
|
|
1010
|
+
return {
|
|
1011
|
+
messages: [
|
|
1012
|
+
{
|
|
1013
|
+
role: "user",
|
|
1014
|
+
content: {
|
|
1015
|
+
type: "text",
|
|
1016
|
+
text: `Convert the video at "${args.file_path}" to MP4 format optimized for streaming.
|
|
1017
|
+
|
|
1018
|
+
Requirements:
|
|
1019
|
+
1. Get the video file info first
|
|
1020
|
+
2. Convert to MP4 with CRF ${crf} (${quality} quality)
|
|
1021
|
+
3. Use the "fast" preset for quicker encoding
|
|
1022
|
+
4. Wait for completion and report the result`,
|
|
1023
|
+
},
|
|
1024
|
+
},
|
|
1025
|
+
],
|
|
1026
|
+
};
|
|
1027
|
+
case "document-to-pdf":
|
|
1028
|
+
const pdfQuality = args.quality || "ebook";
|
|
1029
|
+
return {
|
|
1030
|
+
messages: [
|
|
1031
|
+
{
|
|
1032
|
+
role: "user",
|
|
1033
|
+
content: {
|
|
1034
|
+
type: "text",
|
|
1035
|
+
text: `Convert the document at "${args.file_path}" to PDF format.
|
|
1036
|
+
|
|
1037
|
+
Settings:
|
|
1038
|
+
1. Use PDF quality: "${pdfQuality}"
|
|
1039
|
+
2. Convert to PDF
|
|
1040
|
+
3. Report when complete with the file size`,
|
|
1041
|
+
},
|
|
1042
|
+
},
|
|
1043
|
+
],
|
|
1044
|
+
};
|
|
1045
|
+
default:
|
|
1046
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
async run() {
|
|
1050
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
1051
|
+
await this.server.connect(transport);
|
|
1052
|
+
console.error(`ConvertEverything MCP server v${PACKAGE_VERSION} running on stdio`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
// ============================================================================
|
|
1056
|
+
// Main Entry Point
|
|
1057
|
+
// ============================================================================
|
|
1058
|
+
const server = new ConvertEverythingServer();
|
|
1059
|
+
server.run().catch((error) => {
|
|
1060
|
+
console.error("Fatal error:", error);
|
|
1061
|
+
process.exit(1);
|
|
1062
|
+
});
|
|
1063
|
+
//# sourceMappingURL=index.js.map
|