@latellu/atlas-mcp 1.0.3 → 1.0.5
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/README.md +1 -1
- package/dist/index.js +64 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ AI: → Calls upload_media with file_path='/Users/you/Pictures/tokyo.png'
|
|
|
172
172
|
|
|
173
173
|
### Best Practices
|
|
174
174
|
|
|
175
|
-
1. **Scope API keys**:
|
|
175
|
+
1. **Scope API keys**: For management keys, grant only the scopes each tool needs — `content:write` for create/update/delete/reorder, `content:publish` for publish/unpublish/archive/schedule, `media:write` for uploads/deletes. Leave unused scopes off.
|
|
176
176
|
2. **Restrict upload paths**: Only allow necessary directories
|
|
177
177
|
3. **Rotate keys**: Regularly rotate API keys
|
|
178
178
|
4. **Monitor usage**: Review MCP tool calls for suspicious activity
|
package/dist/index.js
CHANGED
|
@@ -5,13 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var
|
|
9
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
-
}) : x)(function(x) {
|
|
11
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
-
});
|
|
14
|
-
var __commonJS = (cb, mod) => function __require2() {
|
|
8
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
15
9
|
try {
|
|
16
10
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
11
|
} catch (e) {
|
|
@@ -3231,8 +3225,8 @@ var require_utils = __commonJS({
|
|
|
3231
3225
|
}
|
|
3232
3226
|
return ind;
|
|
3233
3227
|
}
|
|
3234
|
-
function removeDotSegments(
|
|
3235
|
-
let input =
|
|
3228
|
+
function removeDotSegments(path2) {
|
|
3229
|
+
let input = path2;
|
|
3236
3230
|
const output = [];
|
|
3237
3231
|
let nextSlash = -1;
|
|
3238
3232
|
let len = 0;
|
|
@@ -3484,8 +3478,8 @@ var require_schemes = __commonJS({
|
|
|
3484
3478
|
wsComponent.secure = void 0;
|
|
3485
3479
|
}
|
|
3486
3480
|
if (wsComponent.resourceName) {
|
|
3487
|
-
const [
|
|
3488
|
-
wsComponent.path =
|
|
3481
|
+
const [path2, query] = wsComponent.resourceName.split("?");
|
|
3482
|
+
wsComponent.path = path2 && path2 !== "/" ? path2 : void 0;
|
|
3489
3483
|
wsComponent.query = query;
|
|
3490
3484
|
wsComponent.resourceName = void 0;
|
|
3491
3485
|
}
|
|
@@ -12536,6 +12530,30 @@ var StdioServerTransport = class {
|
|
|
12536
12530
|
import { z as z2 } from "zod";
|
|
12537
12531
|
|
|
12538
12532
|
// src/client.ts
|
|
12533
|
+
function detectMimeType(fileName) {
|
|
12534
|
+
const ext = fileName.slice(fileName.lastIndexOf(".")).toLowerCase();
|
|
12535
|
+
switch (ext) {
|
|
12536
|
+
case ".jpg":
|
|
12537
|
+
case ".jpeg":
|
|
12538
|
+
return "image/jpeg";
|
|
12539
|
+
case ".png":
|
|
12540
|
+
return "image/png";
|
|
12541
|
+
case ".gif":
|
|
12542
|
+
return "image/gif";
|
|
12543
|
+
case ".webp":
|
|
12544
|
+
return "image/webp";
|
|
12545
|
+
case ".mp4":
|
|
12546
|
+
return "video/mp4";
|
|
12547
|
+
case ".webm":
|
|
12548
|
+
return "video/webm";
|
|
12549
|
+
case ".mov":
|
|
12550
|
+
return "video/quicktime";
|
|
12551
|
+
case ".pdf":
|
|
12552
|
+
return "application/pdf";
|
|
12553
|
+
default:
|
|
12554
|
+
return "application/octet-stream";
|
|
12555
|
+
}
|
|
12556
|
+
}
|
|
12539
12557
|
var AtlasClient = class {
|
|
12540
12558
|
baseUrl;
|
|
12541
12559
|
apiKey;
|
|
@@ -12543,8 +12561,8 @@ var AtlasClient = class {
|
|
|
12543
12561
|
this.baseUrl = baseUrl;
|
|
12544
12562
|
this.apiKey = apiKey;
|
|
12545
12563
|
}
|
|
12546
|
-
async request(
|
|
12547
|
-
const url = `${this.baseUrl}${
|
|
12564
|
+
async request(path2, options) {
|
|
12565
|
+
const url = `${this.baseUrl}${path2}`;
|
|
12548
12566
|
const response = await fetch(url, {
|
|
12549
12567
|
...options,
|
|
12550
12568
|
headers: {
|
|
@@ -12553,6 +12571,20 @@ var AtlasClient = class {
|
|
|
12553
12571
|
...options?.headers
|
|
12554
12572
|
}
|
|
12555
12573
|
});
|
|
12574
|
+
return this.handleResponse(response);
|
|
12575
|
+
}
|
|
12576
|
+
// Write requests to /api/v1/manage/* carry an Idempotency-Key so that
|
|
12577
|
+
// AI-agent-driven retries don't risk double-create/double-publish.
|
|
12578
|
+
async writeRequest(path2, options) {
|
|
12579
|
+
return this.request(path2, {
|
|
12580
|
+
...options,
|
|
12581
|
+
headers: {
|
|
12582
|
+
"Idempotency-Key": crypto.randomUUID(),
|
|
12583
|
+
...options.headers
|
|
12584
|
+
}
|
|
12585
|
+
});
|
|
12586
|
+
}
|
|
12587
|
+
async handleResponse(response) {
|
|
12556
12588
|
if (!response.ok) {
|
|
12557
12589
|
const error = await response.json().catch(() => ({}));
|
|
12558
12590
|
throw new Error(
|
|
@@ -12591,39 +12623,39 @@ var AtlasClient = class {
|
|
|
12591
12623
|
return this.request(`/api/v1/public/entries/${slug}`);
|
|
12592
12624
|
}
|
|
12593
12625
|
async createEntry(contentType, data, slug) {
|
|
12594
|
-
return this.
|
|
12626
|
+
return this.writeRequest(`/api/v1/manage/content-types/${contentType}/entries`, {
|
|
12595
12627
|
method: "POST",
|
|
12596
12628
|
body: JSON.stringify({ slug, data })
|
|
12597
12629
|
});
|
|
12598
12630
|
}
|
|
12599
12631
|
async updateEntry(contentType, slug, data) {
|
|
12600
|
-
return this.
|
|
12632
|
+
return this.writeRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}`, {
|
|
12601
12633
|
method: "PUT",
|
|
12602
12634
|
body: JSON.stringify({ data })
|
|
12603
12635
|
});
|
|
12604
12636
|
}
|
|
12605
12637
|
async deleteEntry(contentType, slug) {
|
|
12606
|
-
return this.
|
|
12638
|
+
return this.writeRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}`, {
|
|
12607
12639
|
method: "DELETE"
|
|
12608
12640
|
});
|
|
12609
12641
|
}
|
|
12610
12642
|
async publishEntry(contentType, slug) {
|
|
12611
|
-
return this.
|
|
12643
|
+
return this.writeRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/publish`, {
|
|
12612
12644
|
method: "PATCH"
|
|
12613
12645
|
});
|
|
12614
12646
|
}
|
|
12615
12647
|
async unpublishEntry(contentType, slug) {
|
|
12616
|
-
return this.
|
|
12648
|
+
return this.writeRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/unpublish`, {
|
|
12617
12649
|
method: "PATCH"
|
|
12618
12650
|
});
|
|
12619
12651
|
}
|
|
12620
12652
|
async archiveEntry(contentType, slug) {
|
|
12621
|
-
return this.
|
|
12653
|
+
return this.writeRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/archive`, {
|
|
12622
12654
|
method: "PATCH"
|
|
12623
12655
|
});
|
|
12624
12656
|
}
|
|
12625
12657
|
async duplicateEntry(contentType, slug) {
|
|
12626
|
-
return this.
|
|
12658
|
+
return this.writeRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/duplicate`, {
|
|
12627
12659
|
method: "POST"
|
|
12628
12660
|
});
|
|
12629
12661
|
}
|
|
@@ -12638,24 +12670,24 @@ var AtlasClient = class {
|
|
|
12638
12670
|
return this.request(`/api/v1/public/pages/${slug}`);
|
|
12639
12671
|
}
|
|
12640
12672
|
async createPage(data) {
|
|
12641
|
-
return this.
|
|
12673
|
+
return this.writeRequest("/api/v1/manage/pages", {
|
|
12642
12674
|
method: "POST",
|
|
12643
12675
|
body: JSON.stringify(data)
|
|
12644
12676
|
});
|
|
12645
12677
|
}
|
|
12646
12678
|
async updatePage(slug, data) {
|
|
12647
|
-
return this.
|
|
12679
|
+
return this.writeRequest(`/api/v1/manage/pages/${slug}`, {
|
|
12648
12680
|
method: "PUT",
|
|
12649
12681
|
body: JSON.stringify(data)
|
|
12650
12682
|
});
|
|
12651
12683
|
}
|
|
12652
12684
|
async deletePage(slug) {
|
|
12653
|
-
return this.
|
|
12685
|
+
return this.writeRequest(`/api/v1/manage/pages/${slug}`, {
|
|
12654
12686
|
method: "DELETE"
|
|
12655
12687
|
});
|
|
12656
12688
|
}
|
|
12657
12689
|
async publishPage(slug) {
|
|
12658
|
-
return this.
|
|
12690
|
+
return this.writeRequest(`/api/v1/manage/pages/${slug}/publish`, {
|
|
12659
12691
|
method: "PATCH"
|
|
12660
12692
|
});
|
|
12661
12693
|
}
|
|
@@ -12665,38 +12697,35 @@ var AtlasClient = class {
|
|
|
12665
12697
|
}
|
|
12666
12698
|
async uploadMedia(filePath, folder, altText) {
|
|
12667
12699
|
const fs = await import("fs");
|
|
12668
|
-
const
|
|
12700
|
+
const path2 = await import("path");
|
|
12669
12701
|
if (!fs.existsSync(filePath)) {
|
|
12670
12702
|
throw new Error(`File not found: ${filePath}`);
|
|
12671
12703
|
}
|
|
12672
|
-
const fileName =
|
|
12704
|
+
const fileName = path2.basename(filePath);
|
|
12673
12705
|
const fileBuffer = fs.readFileSync(filePath);
|
|
12706
|
+
const mimeType = detectMimeType(fileName);
|
|
12674
12707
|
const formData = new FormData();
|
|
12675
|
-
formData.append("file", new Blob([fileBuffer]), fileName);
|
|
12708
|
+
formData.append("file", new Blob([fileBuffer], { type: mimeType }), fileName);
|
|
12676
12709
|
if (folder) formData.append("folder", folder);
|
|
12677
12710
|
if (altText) formData.append("alt_text", altText);
|
|
12678
12711
|
const response = await fetch(`${this.baseUrl}/api/v1/manage/media/upload`, {
|
|
12679
12712
|
method: "POST",
|
|
12680
12713
|
headers: {
|
|
12681
|
-
"X-API-Key": this.apiKey
|
|
12714
|
+
"X-API-Key": this.apiKey,
|
|
12715
|
+
"Idempotency-Key": crypto.randomUUID()
|
|
12682
12716
|
},
|
|
12683
12717
|
body: formData
|
|
12684
12718
|
});
|
|
12685
|
-
|
|
12686
|
-
const error = await response.json().catch(() => ({}));
|
|
12687
|
-
throw new Error(`Upload failed: ${response.status} - ${error.message || response.statusText}`);
|
|
12688
|
-
}
|
|
12689
|
-
const data = await response.json();
|
|
12690
|
-
return data.data || data;
|
|
12719
|
+
return this.handleResponse(response);
|
|
12691
12720
|
}
|
|
12692
12721
|
};
|
|
12693
12722
|
|
|
12694
12723
|
// src/security.ts
|
|
12724
|
+
import path from "node:path";
|
|
12695
12725
|
function validateFilePath(filePath, allowedPaths) {
|
|
12696
12726
|
if (allowedPaths.length === 0) {
|
|
12697
12727
|
return;
|
|
12698
12728
|
}
|
|
12699
|
-
const path = __require("path");
|
|
12700
12729
|
const absPath = path.resolve(filePath);
|
|
12701
12730
|
const isAllowed = allowedPaths.some((allowed) => {
|
|
12702
12731
|
const absAllowed = path.resolve(allowed);
|