@mediagraph/mcp 1.0.2 → 1.0.4

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.
Files changed (2) hide show
  1. package/dist/index.js +247 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -69,10 +69,11 @@ var OAuthHandler = class {
69
69
  });
70
70
  return `${this.config.oauthUrl}/oauth/authorize?${params.toString()}`;
71
71
  }
72
+ callbackPromise = null;
72
73
  /**
73
- * Start a local HTTP server to receive the OAuth callback
74
+ * Start the callback server and wait for it to be ready
74
75
  */
75
- async waitForCallback() {
76
+ async startCallbackServer() {
76
77
  return new Promise((resolve, reject) => {
77
78
  this.callbackServer = createServer((req, res) => {
78
79
  const url = new URL2(req.url || "/", `http://localhost:${this.config.redirectPort}`);
@@ -93,7 +94,8 @@ var OAuthHandler = class {
93
94
  </html>
94
95
  `);
95
96
  this.stopCallbackServer();
96
- reject(new Error(errorDescription || error));
97
+ this.callbackPromise?.reject(new Error(errorDescription || error));
98
+ this.callbackPromise = null;
97
99
  return;
98
100
  }
99
101
  if (!code || !state) {
@@ -108,7 +110,8 @@ var OAuthHandler = class {
108
110
  </html>
109
111
  `);
110
112
  this.stopCallbackServer();
111
- reject(new Error("Missing authorization code or state"));
113
+ this.callbackPromise?.reject(new Error("Missing authorization code or state"));
114
+ this.callbackPromise = null;
112
115
  return;
113
116
  }
114
117
  if (state !== this.state) {
@@ -123,7 +126,8 @@ var OAuthHandler = class {
123
126
  </html>
124
127
  `);
125
128
  this.stopCallbackServer();
126
- reject(new Error("State parameter mismatch"));
129
+ this.callbackPromise?.reject(new Error("State parameter mismatch"));
130
+ this.callbackPromise = null;
127
131
  return;
128
132
  }
129
133
  res.writeHead(200, { "Content-Type": "text/html" });
@@ -137,20 +141,43 @@ var OAuthHandler = class {
137
141
  </html>
138
142
  `);
139
143
  this.stopCallbackServer();
140
- resolve({ code, state });
144
+ this.callbackPromise?.resolve({ code, state });
145
+ this.callbackPromise = null;
141
146
  } else {
142
147
  res.writeHead(404);
143
148
  res.end("Not found");
144
149
  }
145
150
  });
151
+ this.callbackServer.on("error", (err) => {
152
+ reject(err);
153
+ });
146
154
  this.callbackServer.listen(this.config.redirectPort, () => {
155
+ resolve();
147
156
  });
157
+ });
158
+ }
159
+ /**
160
+ * Wait for the OAuth callback (server must be started first)
161
+ */
162
+ async waitForCallback() {
163
+ return new Promise((resolve, reject) => {
164
+ this.callbackPromise = { resolve, reject };
148
165
  setTimeout(() => {
149
- this.stopCallbackServer();
150
- reject(new Error("Authorization timed out"));
166
+ if (this.callbackPromise) {
167
+ this.stopCallbackServer();
168
+ this.callbackPromise.reject(new Error("Authorization timed out"));
169
+ this.callbackPromise = null;
170
+ }
151
171
  }, 5 * 60 * 1e3);
152
172
  });
153
173
  }
174
+ /**
175
+ * Start server and wait for callback (convenience method for CLI)
176
+ */
177
+ async startAndWaitForCallback() {
178
+ await this.startCallbackServer();
179
+ return this.waitForCallback();
180
+ }
154
181
  /**
155
182
  * Stop the callback server
156
183
  */
@@ -984,6 +1011,36 @@ var MediagraphClient = class {
984
1011
  async addAssetsToUpload(guid, assetIds) {
985
1012
  await this.request("POST", `/api/uploads/${guid}/assets`, { body: { asset_ids: assetIds } });
986
1013
  }
1014
+ /**
1015
+ * Prepare an asset for upload - returns a signed URL for direct S3 upload
1016
+ */
1017
+ async prepareAssetUpload(uploadGuid, data) {
1018
+ return this.request("POST", `/api/uploads/${uploadGuid}/assets`, {
1019
+ body: { asset: data }
1020
+ });
1021
+ }
1022
+ /**
1023
+ * Mark an asset as uploaded (triggers processing)
1024
+ */
1025
+ async setAssetUploaded(assetGuid, skipMeta) {
1026
+ const params = skipMeta ? { skip_meta: "true" } : void 0;
1027
+ return this.request("GET", `/api/assets/${assetGuid}/set_uploaded`, { params });
1028
+ }
1029
+ /**
1030
+ * Upload file data directly to a signed S3 URL
1031
+ */
1032
+ async uploadToSignedUrl(signedUrl, fileData, contentType) {
1033
+ const response = await fetch(signedUrl, {
1034
+ method: "PUT",
1035
+ body: fileData,
1036
+ headers: {
1037
+ "Content-Type": contentType
1038
+ }
1039
+ });
1040
+ if (!response.ok) {
1041
+ throw new Error(`Failed to upload to S3: ${response.status} ${response.statusText}`);
1042
+ }
1043
+ }
987
1044
  async setUploadDone(id) {
988
1045
  return this.request("POST", `/api/uploads/${id}/set_done`);
989
1046
  }
@@ -2441,6 +2498,181 @@ var downloadTools = {
2441
2498
  }
2442
2499
  };
2443
2500
 
2501
+ // src/tools/uploads.ts
2502
+ import { readFile, stat } from "fs/promises";
2503
+ import { basename, extname } from "path";
2504
+ var MIME_TYPES = {
2505
+ ".jpg": "image/jpeg",
2506
+ ".jpeg": "image/jpeg",
2507
+ ".png": "image/png",
2508
+ ".gif": "image/gif",
2509
+ ".webp": "image/webp",
2510
+ ".svg": "image/svg+xml",
2511
+ ".bmp": "image/bmp",
2512
+ ".tiff": "image/tiff",
2513
+ ".tif": "image/tiff",
2514
+ ".heic": "image/heic",
2515
+ ".heif": "image/heif",
2516
+ ".mp4": "video/mp4",
2517
+ ".mov": "video/quicktime",
2518
+ ".avi": "video/x-msvideo",
2519
+ ".webm": "video/webm",
2520
+ ".mkv": "video/x-matroska",
2521
+ ".mp3": "audio/mpeg",
2522
+ ".wav": "audio/wav",
2523
+ ".aac": "audio/aac",
2524
+ ".flac": "audio/flac",
2525
+ ".pdf": "application/pdf",
2526
+ ".doc": "application/msword",
2527
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
2528
+ ".xls": "application/vnd.ms-excel",
2529
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
2530
+ ".ppt": "application/vnd.ms-powerpoint",
2531
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
2532
+ ".zip": "application/zip",
2533
+ ".psd": "image/vnd.adobe.photoshop",
2534
+ ".ai": "application/postscript",
2535
+ ".eps": "application/postscript",
2536
+ ".raw": "image/x-raw",
2537
+ ".cr2": "image/x-canon-cr2",
2538
+ ".nef": "image/x-nikon-nef",
2539
+ ".arw": "image/x-sony-arw",
2540
+ ".dng": "image/x-adobe-dng"
2541
+ };
2542
+ function getMimeType(filename) {
2543
+ const ext = extname(filename).toLowerCase();
2544
+ return MIME_TYPES[ext] || "application/octet-stream";
2545
+ }
2546
+ var uploadTools = {
2547
+ definitions: [
2548
+ {
2549
+ name: "upload_file",
2550
+ description: `Upload a file from the local filesystem to Mediagraph.
2551
+ This creates a new asset in the user's Mediagraph library.
2552
+ Supports images, videos, audio, documents, and other media files.
2553
+ The file will be processed and thumbnails/previews generated automatically.`,
2554
+ inputSchema: {
2555
+ type: "object",
2556
+ properties: {
2557
+ file_path: {
2558
+ type: "string",
2559
+ description: "Absolute path to the file on the local filesystem"
2560
+ },
2561
+ storage_folder_id: {
2562
+ type: "number",
2563
+ description: "Optional: ID of the storage folder to upload into"
2564
+ }
2565
+ },
2566
+ required: ["file_path"]
2567
+ }
2568
+ },
2569
+ {
2570
+ name: "upload_files",
2571
+ description: `Upload multiple files from the local filesystem to Mediagraph.
2572
+ Creates new assets for each file in the user's Mediagraph library.
2573
+ All files are uploaded in a single upload session.`,
2574
+ inputSchema: {
2575
+ type: "object",
2576
+ properties: {
2577
+ file_paths: {
2578
+ type: "array",
2579
+ items: { type: "string" },
2580
+ description: "Array of absolute paths to files on the local filesystem"
2581
+ },
2582
+ storage_folder_id: {
2583
+ type: "number",
2584
+ description: "Optional: ID of the storage folder to upload into"
2585
+ }
2586
+ },
2587
+ required: ["file_paths"]
2588
+ }
2589
+ }
2590
+ ],
2591
+ handlers: {
2592
+ async upload_file(args, { client: client2 }) {
2593
+ const filePath = args.file_path;
2594
+ let fileStats;
2595
+ try {
2596
+ fileStats = await stat(filePath);
2597
+ } catch {
2598
+ return errorResult(`File not found: ${filePath}`);
2599
+ }
2600
+ if (!fileStats.isFile()) {
2601
+ return errorResult(`Not a file: ${filePath}`);
2602
+ }
2603
+ const fileData = await readFile(filePath);
2604
+ const filename = basename(filePath);
2605
+ const contentType = getMimeType(filename);
2606
+ const upload = await client2.createUpload();
2607
+ const preparedAsset = await client2.prepareAssetUpload(upload.guid, {
2608
+ filename,
2609
+ file_size: fileStats.size,
2610
+ created_via: "mcp"
2611
+ });
2612
+ await client2.uploadToSignedUrl(preparedAsset.signed_upload_url, fileData, contentType);
2613
+ const asset = await client2.setAssetUploaded(preparedAsset.guid);
2614
+ await client2.setUploadDone(upload.id);
2615
+ return successResult({
2616
+ message: `Successfully uploaded ${filename}`,
2617
+ asset: {
2618
+ id: asset.id,
2619
+ guid: asset.guid,
2620
+ filename: asset.filename,
2621
+ file_size: asset.file_size,
2622
+ content_type: asset.content_type
2623
+ },
2624
+ upload_guid: upload.guid
2625
+ });
2626
+ },
2627
+ async upload_files(args, { client: client2 }) {
2628
+ const filePaths = args.file_paths;
2629
+ if (!filePaths || filePaths.length === 0) {
2630
+ return errorResult("No files provided");
2631
+ }
2632
+ const upload = await client2.createUpload();
2633
+ const results = [];
2634
+ for (const filePath of filePaths) {
2635
+ try {
2636
+ const fileStats = await stat(filePath);
2637
+ if (!fileStats.isFile()) {
2638
+ results.push({ filename: filePath, success: false, error: "Not a file" });
2639
+ continue;
2640
+ }
2641
+ const fileData = await readFile(filePath);
2642
+ const filename = basename(filePath);
2643
+ const contentType = getMimeType(filename);
2644
+ const preparedAsset = await client2.prepareAssetUpload(upload.guid, {
2645
+ filename,
2646
+ file_size: fileStats.size,
2647
+ created_via: "mcp"
2648
+ });
2649
+ await client2.uploadToSignedUrl(preparedAsset.signed_upload_url, fileData, contentType);
2650
+ const asset = await client2.setAssetUploaded(preparedAsset.guid);
2651
+ results.push({
2652
+ filename,
2653
+ success: true,
2654
+ asset_id: asset.id,
2655
+ asset_guid: asset.guid
2656
+ });
2657
+ } catch (error) {
2658
+ results.push({
2659
+ filename: basename(filePath),
2660
+ success: false,
2661
+ error: error instanceof Error ? error.message : "Unknown error"
2662
+ });
2663
+ }
2664
+ }
2665
+ await client2.setUploadDone(upload.id);
2666
+ const successCount = results.filter((r) => r.success).length;
2667
+ return successResult({
2668
+ message: `Uploaded ${successCount} of ${filePaths.length} files`,
2669
+ upload_guid: upload.guid,
2670
+ results
2671
+ });
2672
+ }
2673
+ }
2674
+ };
2675
+
2444
2676
  // src/tools/webhooks.ts
2445
2677
  var webhookTools = {
2446
2678
  definitions: [
@@ -2754,6 +2986,7 @@ var allToolModules = [
2754
2986
  workflowTools,
2755
2987
  socialTools,
2756
2988
  downloadTools,
2989
+ uploadTools,
2757
2990
  webhookTools,
2758
2991
  adminTools
2759
2992
  ];
@@ -2962,6 +3195,7 @@ async function runAutoAuth() {
2962
3195
  isAuthInProgress = true;
2963
3196
  try {
2964
3197
  const authUrl = oauthHandler.getAuthorizationUrl();
3198
+ await oauthHandler.startCallbackServer();
2965
3199
  openBrowser(authUrl);
2966
3200
  const { code } = await oauthHandler.waitForCallback();
2967
3201
  const tokens = await oauthHandler.exchangeCode(code);
@@ -2978,6 +3212,7 @@ async function runAutoAuth() {
2978
3212
  return true;
2979
3213
  } catch (error) {
2980
3214
  console.error("Auto-auth failed:", error);
3215
+ oauthHandler.stopCallbackServer();
2981
3216
  return false;
2982
3217
  } finally {
2983
3218
  isAuthInProgress = false;
@@ -3131,7 +3366,10 @@ async function runAuthorize() {
3131
3366
  console.log("Starting Mediagraph OAuth authorization...");
3132
3367
  console.log("");
3133
3368
  const authUrl = oauthHandler.getAuthorizationUrl();
3134
- console.log("Please open this URL in your browser:");
3369
+ await oauthHandler.startCallbackServer();
3370
+ openBrowser(authUrl);
3371
+ console.log("Opening browser for authorization...");
3372
+ console.log("If the browser does not open, please visit:");
3135
3373
  console.log("");
3136
3374
  console.log(authUrl);
3137
3375
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mediagraph/mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "MCP server for Mediagraph - Media Asset Management Platform",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",