@systemdox/mcp-server 0.1.54 → 0.1.56
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 +15 -0
- package/README.md +37 -5
- package/dist/cli.js +80 -13
- package/dist/http.js +1825 -1752
- package/dist/index.js +79 -12
- package/dist/install-skill.js +28 -3
- package/dist/lib/api-client.d.ts +4 -0
- package/dist/lib/api-client.js +28 -3
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/mcp-answer.d.ts +18 -0
- package/dist/plugin-path.js +28 -3
- package/dist/server.d.ts +5 -1
- package/dist/server.js +51 -9
- package/dist/setup-mcp.js +28 -3
- package/dist/setup-team.js +28 -3
- package/dist/sync-memory.js +28 -3
- package/package.json +15 -3
package/dist/setup-team.js
CHANGED
|
@@ -59,6 +59,17 @@ var SystemDoxApiError = class extends Error {
|
|
|
59
59
|
code;
|
|
60
60
|
body;
|
|
61
61
|
};
|
|
62
|
+
function unescapedPath(path) {
|
|
63
|
+
let current = path.split(/[?#]/, 1)[0];
|
|
64
|
+
for (; ; ) {
|
|
65
|
+
const next = current.replace(/[\t\n\r]/g, "").replace(/%(?:2e|2f|5c|25|09|0a|0d)/gi, (escape) => decodeURIComponent(escape));
|
|
66
|
+
if (next === current) return current;
|
|
67
|
+
current = next;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function walksThePath(path) {
|
|
71
|
+
return unescapedPath(path).split(/[/\\]/).some((segment) => segment === "." || segment === "..");
|
|
72
|
+
}
|
|
62
73
|
var TEAM_SETUP = "/v1/team-setup";
|
|
63
74
|
var seg = (s) => encodeURIComponent(String(s));
|
|
64
75
|
var SystemDoxApiClient = class {
|
|
@@ -94,7 +105,7 @@ var SystemDoxApiClient = class {
|
|
|
94
105
|
}
|
|
95
106
|
async mutate(method, path, body) {
|
|
96
107
|
const url = this.buildUrl(path);
|
|
97
|
-
const res = await
|
|
108
|
+
const res = await this.call(url, {
|
|
98
109
|
method,
|
|
99
110
|
headers: {
|
|
100
111
|
// The API Gateway authorizer only reads the Authorization header;
|
|
@@ -144,7 +155,7 @@ var SystemDoxApiClient = class {
|
|
|
144
155
|
/** getPluginZip: the zip's bytes as served, and its X-Content-SHA256
|
|
145
156
|
* header. The caller checks the BYTES against the hash it was given. */
|
|
146
157
|
async getPluginZip(workspace, version) {
|
|
147
|
-
const res = await
|
|
158
|
+
const res = await this.call(
|
|
148
159
|
this.buildUrl(`${TEAM_SETUP}/${seg(workspace)}/plugin/versions/${seg(version)}/zip`),
|
|
149
160
|
{
|
|
150
161
|
headers: {
|
|
@@ -210,6 +221,10 @@ var SystemDoxApiClient = class {
|
|
|
210
221
|
buildUrl(path, params) {
|
|
211
222
|
const base = this.config.apiUrl.replace(/\/+$/, "");
|
|
212
223
|
const url = new URL(`${base}${path.startsWith("/") ? "" : "/"}${path}`);
|
|
224
|
+
const { origin, pathname } = new URL(base);
|
|
225
|
+
if (walksThePath(path) || url.origin !== origin || !url.pathname.startsWith(`${pathname.replace(/\/+$/, "")}/`)) {
|
|
226
|
+
throw new Error("Refused: this id leaves the API's own path.");
|
|
227
|
+
}
|
|
213
228
|
if (params) {
|
|
214
229
|
for (const [key, value] of Object.entries(params)) {
|
|
215
230
|
url.searchParams.set(key, value);
|
|
@@ -219,7 +234,7 @@ var SystemDoxApiClient = class {
|
|
|
219
234
|
}
|
|
220
235
|
async fetchRaw(path, params, signal) {
|
|
221
236
|
const url = this.buildUrl(path, params);
|
|
222
|
-
return
|
|
237
|
+
return this.call(url, {
|
|
223
238
|
headers: {
|
|
224
239
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
225
240
|
"X-API-Key": this.config.apiKey,
|
|
@@ -228,6 +243,16 @@ var SystemDoxApiClient = class {
|
|
|
228
243
|
signal
|
|
229
244
|
});
|
|
230
245
|
}
|
|
246
|
+
/** Every call of this client, so `onAnswer` hears each answer. */
|
|
247
|
+
async call(url, init) {
|
|
248
|
+
const res = await fetch(url, init);
|
|
249
|
+
const { onAnswer } = this.config;
|
|
250
|
+
if (onAnswer) {
|
|
251
|
+
const refused = res.ok ? void 0 : new SystemDoxApiError(res.status, await res.clone().text());
|
|
252
|
+
onAnswer({ status: res.status, code: refused?.code });
|
|
253
|
+
}
|
|
254
|
+
return res;
|
|
255
|
+
}
|
|
231
256
|
};
|
|
232
257
|
|
|
233
258
|
// src/lib/memory-sync.ts
|
package/dist/sync-memory.js
CHANGED
|
@@ -56,6 +56,17 @@ var SystemDoxApiError = class extends Error {
|
|
|
56
56
|
code;
|
|
57
57
|
body;
|
|
58
58
|
};
|
|
59
|
+
function unescapedPath(path) {
|
|
60
|
+
let current = path.split(/[?#]/, 1)[0];
|
|
61
|
+
for (; ; ) {
|
|
62
|
+
const next = current.replace(/[\t\n\r]/g, "").replace(/%(?:2e|2f|5c|25|09|0a|0d)/gi, (escape) => decodeURIComponent(escape));
|
|
63
|
+
if (next === current) return current;
|
|
64
|
+
current = next;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function walksThePath(path) {
|
|
68
|
+
return unescapedPath(path).split(/[/\\]/).some((segment) => segment === "." || segment === "..");
|
|
69
|
+
}
|
|
59
70
|
var TEAM_SETUP = "/v1/team-setup";
|
|
60
71
|
var seg = (s) => encodeURIComponent(String(s));
|
|
61
72
|
var SystemDoxApiClient = class {
|
|
@@ -91,7 +102,7 @@ var SystemDoxApiClient = class {
|
|
|
91
102
|
}
|
|
92
103
|
async mutate(method, path, body) {
|
|
93
104
|
const url = this.buildUrl(path);
|
|
94
|
-
const res = await
|
|
105
|
+
const res = await this.call(url, {
|
|
95
106
|
method,
|
|
96
107
|
headers: {
|
|
97
108
|
// The API Gateway authorizer only reads the Authorization header;
|
|
@@ -141,7 +152,7 @@ var SystemDoxApiClient = class {
|
|
|
141
152
|
/** getPluginZip: the zip's bytes as served, and its X-Content-SHA256
|
|
142
153
|
* header. The caller checks the BYTES against the hash it was given. */
|
|
143
154
|
async getPluginZip(workspace, version) {
|
|
144
|
-
const res = await
|
|
155
|
+
const res = await this.call(
|
|
145
156
|
this.buildUrl(`${TEAM_SETUP}/${seg(workspace)}/plugin/versions/${seg(version)}/zip`),
|
|
146
157
|
{
|
|
147
158
|
headers: {
|
|
@@ -207,6 +218,10 @@ var SystemDoxApiClient = class {
|
|
|
207
218
|
buildUrl(path, params) {
|
|
208
219
|
const base = this.config.apiUrl.replace(/\/+$/, "");
|
|
209
220
|
const url = new URL(`${base}${path.startsWith("/") ? "" : "/"}${path}`);
|
|
221
|
+
const { origin, pathname } = new URL(base);
|
|
222
|
+
if (walksThePath(path) || url.origin !== origin || !url.pathname.startsWith(`${pathname.replace(/\/+$/, "")}/`)) {
|
|
223
|
+
throw new Error("Refused: this id leaves the API's own path.");
|
|
224
|
+
}
|
|
210
225
|
if (params) {
|
|
211
226
|
for (const [key, value] of Object.entries(params)) {
|
|
212
227
|
url.searchParams.set(key, value);
|
|
@@ -216,7 +231,7 @@ var SystemDoxApiClient = class {
|
|
|
216
231
|
}
|
|
217
232
|
async fetchRaw(path, params, signal) {
|
|
218
233
|
const url = this.buildUrl(path, params);
|
|
219
|
-
return
|
|
234
|
+
return this.call(url, {
|
|
220
235
|
headers: {
|
|
221
236
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
222
237
|
"X-API-Key": this.config.apiKey,
|
|
@@ -225,6 +240,16 @@ var SystemDoxApiClient = class {
|
|
|
225
240
|
signal
|
|
226
241
|
});
|
|
227
242
|
}
|
|
243
|
+
/** Every call of this client, so `onAnswer` hears each answer. */
|
|
244
|
+
async call(url, init) {
|
|
245
|
+
const res = await fetch(url, init);
|
|
246
|
+
const { onAnswer } = this.config;
|
|
247
|
+
if (onAnswer) {
|
|
248
|
+
const refused = res.ok ? void 0 : new SystemDoxApiError(res.status, await res.clone().text());
|
|
249
|
+
onAnswer({ status: res.status, code: refused?.code });
|
|
250
|
+
}
|
|
251
|
+
return res;
|
|
252
|
+
}
|
|
228
253
|
};
|
|
229
254
|
|
|
230
255
|
// src/lib/memory-sync.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemdox/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.56",
|
|
4
4
|
"mcpName": "io.github.puglieseweb/systemdox",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Give AI coding agents your architecture context over MCP — checks, specs, ADRs, project rules, and docs from SystemDox, delivered at write time",
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"dist",
|
|
24
|
+
"!dist/oauth.*",
|
|
25
|
+
"!dist/connect.*",
|
|
26
|
+
"!dist/lib/aws-deps.*",
|
|
27
|
+
"!dist/lib/connection-store.*",
|
|
28
|
+
"!dist/lib/oauth-*",
|
|
24
29
|
"README.md"
|
|
25
30
|
],
|
|
26
31
|
"scripts": {
|
|
@@ -35,8 +40,11 @@
|
|
|
35
40
|
},
|
|
36
41
|
"repository": {
|
|
37
42
|
"type": "git",
|
|
38
|
-
"url": "https://github.com/puglieseweb/
|
|
39
|
-
|
|
43
|
+
"url": "https://github.com/puglieseweb/systemdox-mcp-server.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://www.systemdox.com",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/puglieseweb/systemdox-mcp-server/issues"
|
|
40
48
|
},
|
|
41
49
|
"publishConfig": {
|
|
42
50
|
"access": "public",
|
|
@@ -65,6 +73,10 @@
|
|
|
65
73
|
"zod": "^3.25.67"
|
|
66
74
|
},
|
|
67
75
|
"devDependencies": {
|
|
76
|
+
"@aws-sdk/client-dynamodb": "3.1159.0",
|
|
77
|
+
"@aws-sdk/client-kms": "3.1159.0",
|
|
78
|
+
"@aws-sdk/client-secrets-manager": "3.1159.0",
|
|
79
|
+
"@aws-sdk/lib-dynamodb": "3.1159.0",
|
|
68
80
|
"@types/node": "^26.0.1",
|
|
69
81
|
"esbuild": "^0.25.0",
|
|
70
82
|
"tsup": "^8.5.1",
|