@latellu/atlas-mcp 1.0.5 → 1.0.6
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 +18 -7
- package/dist/index.js +68 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,17 +69,23 @@ atlas-mcp
|
|
|
69
69
|
4. Copy the key — it is only shown once
|
|
70
70
|
|
|
71
71
|
**Key types:**
|
|
72
|
-
- `atlas_live_*` —
|
|
73
|
-
- `atlas_mgmt_*` —
|
|
72
|
+
- `atlas_live_*` — Delivery API key. Required for every **read** tool (`get_workspace_schema`, `list_*`, `get_*`).
|
|
73
|
+
- `atlas_mgmt_*` — Management API key. Required for every **write** tool (`create_*`, `update_*`, `delete_*`, `publish_*`, `unpublish_*`, `archive_*`, `duplicate_*`, `upload_media`).
|
|
74
|
+
|
|
75
|
+
The backend enforces one key type per route group — a management key cannot read `/api/v1/public/*`, and a delivery key cannot write to `/api/v1/manage/*`. **For the full tool set (read + write), configure both keys.** A single key only unlocks the half of the tools matching its type; calling a tool that needs the other key type returns a clear error naming the missing env var.
|
|
74
76
|
|
|
75
77
|
### Environment Variables
|
|
76
78
|
|
|
77
79
|
| Variable | Required | Description |
|
|
78
80
|
|----------|----------|-------------|
|
|
79
|
-
| `ATLAS_API_KEY` |
|
|
81
|
+
| `ATLAS_API_KEY` | One of the three below | Single key, auto-detected by prefix (`atlas_live_*` enables read tools, `atlas_mgmt_*` enables write tools) — kept for backward compatibility |
|
|
82
|
+
| `ATLAS_LIVE_API_KEY` | No | Delivery key (`atlas_live_*`), explicitly enables read tools |
|
|
83
|
+
| `ATLAS_MGMT_API_KEY` | No | Management key (`atlas_mgmt_*`), explicitly enables write tools |
|
|
80
84
|
| `ATLAS_API_URL` | No | Atlas API base URL (default: `https://api.atlas.latellu.com`) |
|
|
81
85
|
| `MCP_ALLOWED_UPLOAD_PATHS` | No | Comma-separated list of allowed directories for media upload |
|
|
82
86
|
|
|
87
|
+
At least one of `ATLAS_API_KEY`, `ATLAS_LIVE_API_KEY`, or `ATLAS_MGMT_API_KEY` must be set. Set `ATLAS_LIVE_API_KEY` and `ATLAS_MGMT_API_KEY` together to enable both read and write tools in the same server.
|
|
88
|
+
|
|
83
89
|
### Claude Desktop Configuration
|
|
84
90
|
|
|
85
91
|
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
@@ -91,7 +97,8 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
91
97
|
"command": "npx",
|
|
92
98
|
"args": ["-y", "@latellu/atlas-mcp"],
|
|
93
99
|
"env": {
|
|
94
|
-
"
|
|
100
|
+
"ATLAS_LIVE_API_KEY": "atlas_live_xxx...",
|
|
101
|
+
"ATLAS_MGMT_API_KEY": "atlas_mgmt_xxx...",
|
|
95
102
|
"MCP_ALLOWED_UPLOAD_PATHS": "/Users/you/Documents,/Users/you/Pictures"
|
|
96
103
|
}
|
|
97
104
|
}
|
|
@@ -99,6 +106,8 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
99
106
|
}
|
|
100
107
|
```
|
|
101
108
|
|
|
109
|
+
Write-only setups (no read tools needed) can still set just `ATLAS_API_KEY` to a single `atlas_mgmt_*` key.
|
|
110
|
+
|
|
102
111
|
### Cursor Configuration
|
|
103
112
|
|
|
104
113
|
Add to `.cursor/mcp.json`:
|
|
@@ -110,7 +119,8 @@ Add to `.cursor/mcp.json`:
|
|
|
110
119
|
"command": "npx",
|
|
111
120
|
"args": ["-y", "@latellu/atlas-mcp"],
|
|
112
121
|
"env": {
|
|
113
|
-
"
|
|
122
|
+
"ATLAS_LIVE_API_KEY": "atlas_live_xxx...",
|
|
123
|
+
"ATLAS_MGMT_API_KEY": "atlas_mgmt_xxx..."
|
|
114
124
|
}
|
|
115
125
|
}
|
|
116
126
|
}
|
|
@@ -159,8 +169,9 @@ AI: → Calls upload_media with file_path='/Users/you/Pictures/tokyo.png'
|
|
|
159
169
|
### Permission Model
|
|
160
170
|
|
|
161
171
|
**Layer 1: API Key**
|
|
162
|
-
- `atlas_live_*` — Read
|
|
163
|
-
- `atlas_mgmt_*` —
|
|
172
|
+
- `atlas_live_*` — Read tools only (delivery API)
|
|
173
|
+
- `atlas_mgmt_*` — Write tools only (management API)
|
|
174
|
+
- Configure both (`ATLAS_LIVE_API_KEY` + `ATLAS_MGMT_API_KEY`) for the full tool set
|
|
164
175
|
|
|
165
176
|
**Layer 2: Filesystem Permission**
|
|
166
177
|
- Configurable via `MCP_ALLOWED_UPLOAD_PATHS` env var
|
package/dist/index.js
CHANGED
|
@@ -12556,27 +12556,44 @@ function detectMimeType(fileName) {
|
|
|
12556
12556
|
}
|
|
12557
12557
|
var AtlasClient = class {
|
|
12558
12558
|
baseUrl;
|
|
12559
|
-
|
|
12560
|
-
|
|
12559
|
+
liveKey;
|
|
12560
|
+
mgmtKey;
|
|
12561
|
+
constructor(baseUrl, keys) {
|
|
12561
12562
|
this.baseUrl = baseUrl;
|
|
12562
|
-
this.
|
|
12563
|
+
this.liveKey = keys.liveKey;
|
|
12564
|
+
this.mgmtKey = keys.mgmtKey;
|
|
12563
12565
|
}
|
|
12564
|
-
async
|
|
12566
|
+
async httpRequest(path2, apiKey, options) {
|
|
12565
12567
|
const url = `${this.baseUrl}${path2}`;
|
|
12566
12568
|
const response = await fetch(url, {
|
|
12567
12569
|
...options,
|
|
12568
12570
|
headers: {
|
|
12569
12571
|
"Content-Type": "application/json",
|
|
12570
|
-
"X-API-Key":
|
|
12572
|
+
"X-API-Key": apiKey,
|
|
12571
12573
|
...options?.headers
|
|
12572
12574
|
}
|
|
12573
12575
|
});
|
|
12574
12576
|
return this.handleResponse(response);
|
|
12575
12577
|
}
|
|
12576
|
-
//
|
|
12577
|
-
|
|
12578
|
-
|
|
12579
|
-
|
|
12578
|
+
// Reads against /api/v1/public/* require a delivery key (atlas_live_*).
|
|
12579
|
+
async publicRequest(path2, options) {
|
|
12580
|
+
if (!this.liveKey) {
|
|
12581
|
+
throw new Error(
|
|
12582
|
+
"This operation requires a delivery API key (atlas_live_*). Set ATLAS_LIVE_API_KEY (or ATLAS_API_KEY to an atlas_live_* key) to enable read operations."
|
|
12583
|
+
);
|
|
12584
|
+
}
|
|
12585
|
+
return this.httpRequest(path2, this.liveKey, options);
|
|
12586
|
+
}
|
|
12587
|
+
// Writes against /api/v1/manage/* require a management key (atlas_mgmt_*)
|
|
12588
|
+
// and carry an Idempotency-Key so AI-agent-driven retries don't risk
|
|
12589
|
+
// double-create/double-publish.
|
|
12590
|
+
async manageRequest(path2, options) {
|
|
12591
|
+
if (!this.mgmtKey) {
|
|
12592
|
+
throw new Error(
|
|
12593
|
+
"This operation requires a management API key (atlas_mgmt_*). Set ATLAS_MGMT_API_KEY (or ATLAS_API_KEY to an atlas_mgmt_* key) to enable write operations."
|
|
12594
|
+
);
|
|
12595
|
+
}
|
|
12596
|
+
return this.httpRequest(path2, this.mgmtKey, {
|
|
12580
12597
|
...options,
|
|
12581
12598
|
headers: {
|
|
12582
12599
|
"Idempotency-Key": crypto.randomUUID(),
|
|
@@ -12596,7 +12613,7 @@ var AtlasClient = class {
|
|
|
12596
12613
|
}
|
|
12597
12614
|
// Schema endpoints
|
|
12598
12615
|
async getWorkspaceSchema() {
|
|
12599
|
-
return this.
|
|
12616
|
+
return this.publicRequest("/api/v1/public/schema");
|
|
12600
12617
|
}
|
|
12601
12618
|
async listContentTypes() {
|
|
12602
12619
|
const schema = await this.getWorkspaceSchema();
|
|
@@ -12617,45 +12634,45 @@ var AtlasClient = class {
|
|
|
12617
12634
|
if (params?.page) searchParams.set("page", params.page.toString());
|
|
12618
12635
|
if (params?.limit) searchParams.set("limit", params.limit.toString());
|
|
12619
12636
|
if (params?.status) searchParams.set("status", params.status);
|
|
12620
|
-
return this.
|
|
12637
|
+
return this.publicRequest(`/api/v1/public/entries?${searchParams.toString()}`);
|
|
12621
12638
|
}
|
|
12622
12639
|
async getEntry(contentType, slug) {
|
|
12623
|
-
return this.
|
|
12640
|
+
return this.publicRequest(`/api/v1/public/entries/${slug}`);
|
|
12624
12641
|
}
|
|
12625
12642
|
async createEntry(contentType, data, slug) {
|
|
12626
|
-
return this.
|
|
12643
|
+
return this.manageRequest(`/api/v1/manage/content-types/${contentType}/entries`, {
|
|
12627
12644
|
method: "POST",
|
|
12628
12645
|
body: JSON.stringify({ slug, data })
|
|
12629
12646
|
});
|
|
12630
12647
|
}
|
|
12631
12648
|
async updateEntry(contentType, slug, data) {
|
|
12632
|
-
return this.
|
|
12649
|
+
return this.manageRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}`, {
|
|
12633
12650
|
method: "PUT",
|
|
12634
12651
|
body: JSON.stringify({ data })
|
|
12635
12652
|
});
|
|
12636
12653
|
}
|
|
12637
12654
|
async deleteEntry(contentType, slug) {
|
|
12638
|
-
return this.
|
|
12655
|
+
return this.manageRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}`, {
|
|
12639
12656
|
method: "DELETE"
|
|
12640
12657
|
});
|
|
12641
12658
|
}
|
|
12642
12659
|
async publishEntry(contentType, slug) {
|
|
12643
|
-
return this.
|
|
12660
|
+
return this.manageRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/publish`, {
|
|
12644
12661
|
method: "PATCH"
|
|
12645
12662
|
});
|
|
12646
12663
|
}
|
|
12647
12664
|
async unpublishEntry(contentType, slug) {
|
|
12648
|
-
return this.
|
|
12665
|
+
return this.manageRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/unpublish`, {
|
|
12649
12666
|
method: "PATCH"
|
|
12650
12667
|
});
|
|
12651
12668
|
}
|
|
12652
12669
|
async archiveEntry(contentType, slug) {
|
|
12653
|
-
return this.
|
|
12670
|
+
return this.manageRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/archive`, {
|
|
12654
12671
|
method: "PATCH"
|
|
12655
12672
|
});
|
|
12656
12673
|
}
|
|
12657
12674
|
async duplicateEntry(contentType, slug) {
|
|
12658
|
-
return this.
|
|
12675
|
+
return this.manageRequest(`/api/v1/manage/content-types/${contentType}/entries/${slug}/duplicate`, {
|
|
12659
12676
|
method: "POST"
|
|
12660
12677
|
});
|
|
12661
12678
|
}
|
|
@@ -12664,38 +12681,43 @@ var AtlasClient = class {
|
|
|
12664
12681
|
const searchParams = new URLSearchParams();
|
|
12665
12682
|
if (params?.page) searchParams.set("page", params.page.toString());
|
|
12666
12683
|
if (params?.limit) searchParams.set("limit", params.limit.toString());
|
|
12667
|
-
return this.
|
|
12684
|
+
return this.publicRequest(`/api/v1/public/pages?${searchParams.toString()}`);
|
|
12668
12685
|
}
|
|
12669
12686
|
async getPage(slug) {
|
|
12670
|
-
return this.
|
|
12687
|
+
return this.publicRequest(`/api/v1/public/pages/${slug}`);
|
|
12671
12688
|
}
|
|
12672
12689
|
async createPage(data) {
|
|
12673
|
-
return this.
|
|
12690
|
+
return this.manageRequest("/api/v1/manage/pages", {
|
|
12674
12691
|
method: "POST",
|
|
12675
12692
|
body: JSON.stringify(data)
|
|
12676
12693
|
});
|
|
12677
12694
|
}
|
|
12678
12695
|
async updatePage(slug, data) {
|
|
12679
|
-
return this.
|
|
12696
|
+
return this.manageRequest(`/api/v1/manage/pages/${slug}`, {
|
|
12680
12697
|
method: "PUT",
|
|
12681
12698
|
body: JSON.stringify(data)
|
|
12682
12699
|
});
|
|
12683
12700
|
}
|
|
12684
12701
|
async deletePage(slug) {
|
|
12685
|
-
return this.
|
|
12702
|
+
return this.manageRequest(`/api/v1/manage/pages/${slug}`, {
|
|
12686
12703
|
method: "DELETE"
|
|
12687
12704
|
});
|
|
12688
12705
|
}
|
|
12689
12706
|
async publishPage(slug) {
|
|
12690
|
-
return this.
|
|
12707
|
+
return this.manageRequest(`/api/v1/manage/pages/${slug}/publish`, {
|
|
12691
12708
|
method: "PATCH"
|
|
12692
12709
|
});
|
|
12693
12710
|
}
|
|
12694
12711
|
// Media endpoints
|
|
12695
12712
|
async getMedia(id) {
|
|
12696
|
-
return this.
|
|
12713
|
+
return this.publicRequest(`/api/v1/public/media/${id}`);
|
|
12697
12714
|
}
|
|
12698
12715
|
async uploadMedia(filePath, folder, altText) {
|
|
12716
|
+
if (!this.mgmtKey) {
|
|
12717
|
+
throw new Error(
|
|
12718
|
+
"This operation requires a management API key (atlas_mgmt_*). Set ATLAS_MGMT_API_KEY (or ATLAS_API_KEY to an atlas_mgmt_* key) to enable write operations."
|
|
12719
|
+
);
|
|
12720
|
+
}
|
|
12699
12721
|
const fs = await import("fs");
|
|
12700
12722
|
const path2 = await import("path");
|
|
12701
12723
|
if (!fs.existsSync(filePath)) {
|
|
@@ -12711,7 +12733,7 @@ var AtlasClient = class {
|
|
|
12711
12733
|
const response = await fetch(`${this.baseUrl}/api/v1/manage/media/upload`, {
|
|
12712
12734
|
method: "POST",
|
|
12713
12735
|
headers: {
|
|
12714
|
-
"X-API-Key": this.
|
|
12736
|
+
"X-API-Key": this.mgmtKey,
|
|
12715
12737
|
"Idempotency-Key": crypto.randomUUID()
|
|
12716
12738
|
},
|
|
12717
12739
|
body: formData
|
|
@@ -12740,13 +12762,25 @@ function validateFilePath(filePath, allowedPaths) {
|
|
|
12740
12762
|
|
|
12741
12763
|
// src/index.ts
|
|
12742
12764
|
var ATLAS_API_URL = process.env.ATLAS_API_URL || "https://api.atlas.latellu.com";
|
|
12743
|
-
var ATLAS_API_KEY = process.env.ATLAS_API_KEY;
|
|
12744
12765
|
var MCP_ALLOWED_UPLOAD_PATHS = process.env.MCP_ALLOWED_UPLOAD_PATHS ? process.env.MCP_ALLOWED_UPLOAD_PATHS.split(",").map((p) => p.trim()) : [];
|
|
12745
|
-
|
|
12746
|
-
|
|
12766
|
+
var LIVE_KEY_PREFIX = "atlas_live_";
|
|
12767
|
+
var MGMT_KEY_PREFIX = "atlas_mgmt_";
|
|
12768
|
+
function resolveKeys() {
|
|
12769
|
+
const legacyCandidates = [process.env.ATLAS_LIVE_API_KEY, process.env.ATLAS_MGMT_API_KEY, process.env.ATLAS_API_KEY].filter(
|
|
12770
|
+
(v) => !!v
|
|
12771
|
+
);
|
|
12772
|
+
const liveKey2 = process.env.ATLAS_LIVE_API_KEY ?? legacyCandidates.find((k) => k.startsWith(LIVE_KEY_PREFIX));
|
|
12773
|
+
const mgmtKey2 = process.env.ATLAS_MGMT_API_KEY ?? legacyCandidates.find((k) => k.startsWith(MGMT_KEY_PREFIX));
|
|
12774
|
+
return { liveKey: liveKey2, mgmtKey: mgmtKey2 };
|
|
12775
|
+
}
|
|
12776
|
+
var { liveKey, mgmtKey } = resolveKeys();
|
|
12777
|
+
if (!liveKey && !mgmtKey) {
|
|
12778
|
+
console.error(
|
|
12779
|
+
"No valid Atlas API key found. Set ATLAS_API_KEY to an atlas_live_* or atlas_mgmt_* key, or set ATLAS_LIVE_API_KEY / ATLAS_MGMT_API_KEY explicitly (both, for full read+write access)."
|
|
12780
|
+
);
|
|
12747
12781
|
process.exit(1);
|
|
12748
12782
|
}
|
|
12749
|
-
var atlasClient = new AtlasClient(ATLAS_API_URL,
|
|
12783
|
+
var atlasClient = new AtlasClient(ATLAS_API_URL, { liveKey, mgmtKey });
|
|
12750
12784
|
var server = new McpServer({
|
|
12751
12785
|
name: "atlas-mcp",
|
|
12752
12786
|
version: "1.0.0"
|
|
@@ -13279,7 +13313,9 @@ server.tool(
|
|
|
13279
13313
|
async function main() {
|
|
13280
13314
|
const transport = new StdioServerTransport();
|
|
13281
13315
|
await server.connect(transport);
|
|
13282
|
-
console.error(
|
|
13316
|
+
console.error(
|
|
13317
|
+
`Atlas MCP server started (read tools: ${liveKey ? "enabled" : "disabled, needs atlas_live_*"}, write tools: ${mgmtKey ? "enabled" : "disabled, needs atlas_mgmt_*"})`
|
|
13318
|
+
);
|
|
13283
13319
|
}
|
|
13284
13320
|
main().catch((error) => {
|
|
13285
13321
|
console.error("Fatal error:", error);
|