@systemdox/mcp-server 0.1.49 → 0.1.50
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 +30 -1
- package/dist/cli.js +565 -161
- package/dist/http.js +1102 -728
- package/dist/index.d.ts +2 -2
- package/dist/index.js +747 -370
- package/dist/install-skill.js +9 -15
- package/dist/lib/api-client.d.ts +2 -3
- package/dist/lib/api-client.js +7 -14
- package/dist/lib/context-builder.js +14 -7
- package/dist/lib/session-brief.d.ts +59 -0
- package/dist/lib/session-brief.js +174 -0
- package/dist/lib/skills.js +2 -1
- package/dist/lib/team-setup-types.d.ts +18 -0
- package/dist/plugin-path.js +7 -14
- package/dist/server.d.ts +5 -1
- package/dist/server.js +3579 -0
- package/dist/setup-mcp.js +9 -15
- package/dist/setup-team.js +7 -14
- package/dist/sync-memory.js +7 -14
- package/dist/tools/document-graph.d.ts +87 -0
- package/dist/tools/get-team-setup.d.ts +6 -0
- package/dist/tools/skills.js +2 -1
- package/dist/tools/team-setup.js +2 -1
- package/package.json +1 -1
package/dist/setup-mcp.js
CHANGED
|
@@ -64,14 +64,6 @@ var SystemDoxApiClient = class {
|
|
|
64
64
|
this.config = config;
|
|
65
65
|
this.cache = new ResponseCache(config.cacheTtlMs);
|
|
66
66
|
}
|
|
67
|
-
async validate() {
|
|
68
|
-
const res = await this.fetchRaw("/v1/health");
|
|
69
|
-
if (!res.ok) {
|
|
70
|
-
throw new Error(
|
|
71
|
-
`SystemDox API authentication failed (${res.status}). Check your SYSTEMDOX_API_KEY.`
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
67
|
async get(path, params) {
|
|
76
68
|
const url = this.buildUrl(path, params);
|
|
77
69
|
const cacheKey = url.toString();
|
|
@@ -85,8 +77,8 @@ var SystemDoxApiClient = class {
|
|
|
85
77
|
}
|
|
86
78
|
/** A read that skips the cache: an install must take the version that is
|
|
87
79
|
* latest now, not the one cached before someone published another. */
|
|
88
|
-
async getFresh(path) {
|
|
89
|
-
const res = await this.fetchRaw(path);
|
|
80
|
+
async getFresh(path, signal) {
|
|
81
|
+
const res = await this.fetchRaw(path, void 0, signal);
|
|
90
82
|
if (!res.ok) throw new SystemDoxApiError(res.status, await res.text());
|
|
91
83
|
return await res.json();
|
|
92
84
|
}
|
|
@@ -116,8 +108,8 @@ var SystemDoxApiClient = class {
|
|
|
116
108
|
// One method per operationId. Every read skips the cache: setup, sync and
|
|
117
109
|
// plugin-path act on what the workspace holds now.
|
|
118
110
|
/** getOwnTeamSetup: the key's own workspace, named in `workspace`. */
|
|
119
|
-
getOwnTeamSetup() {
|
|
120
|
-
return this.getFresh(TEAM_SETUP);
|
|
111
|
+
getOwnTeamSetup(signal) {
|
|
112
|
+
return this.getFresh(TEAM_SETUP, signal);
|
|
121
113
|
}
|
|
122
114
|
getTeamSetup(workspace) {
|
|
123
115
|
return this.getFresh(`${TEAM_SETUP}/${seg(workspace)}`);
|
|
@@ -221,14 +213,15 @@ var SystemDoxApiClient = class {
|
|
|
221
213
|
}
|
|
222
214
|
return url;
|
|
223
215
|
}
|
|
224
|
-
async fetchRaw(path, params) {
|
|
216
|
+
async fetchRaw(path, params, signal) {
|
|
225
217
|
const url = this.buildUrl(path, params);
|
|
226
218
|
return fetch(url, {
|
|
227
219
|
headers: {
|
|
228
220
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
229
221
|
"X-API-Key": this.config.apiKey,
|
|
230
222
|
Accept: "application/json"
|
|
231
|
-
}
|
|
223
|
+
},
|
|
224
|
+
signal
|
|
232
225
|
});
|
|
233
226
|
}
|
|
234
227
|
};
|
|
@@ -615,9 +608,10 @@ async function setupMcpServer(opts) {
|
|
|
615
608
|
Your key for ${workspace} is in ${credentialsPath()} (readable by you alone), not in these configs.
|
|
616
609
|
|
|
617
610
|
Done! Restart your AI coding tool to activate the SystemDox MCP server.
|
|
618
|
-
The server provides
|
|
611
|
+
The server provides 25 tools: get_context, get_team_setup, get_checks,
|
|
619
612
|
get_project_rules, get_prompt_templates, get_specs, get_docs,
|
|
620
613
|
get_tech_stack, get_planning_context, get_plan_board, get_change,
|
|
614
|
+
get_document_links, trace_document_path, get_graph_stats,
|
|
621
615
|
create_check, create_project_rule, create_prompt_template,
|
|
622
616
|
update_spec, report_decision, publish_skill, list_skills,
|
|
623
617
|
install_skill, publish_setup_part, resolve_person, sync_memory.
|
package/dist/setup-team.js
CHANGED
|
@@ -68,14 +68,6 @@ var SystemDoxApiClient = class {
|
|
|
68
68
|
this.config = config;
|
|
69
69
|
this.cache = new ResponseCache(config.cacheTtlMs);
|
|
70
70
|
}
|
|
71
|
-
async validate() {
|
|
72
|
-
const res = await this.fetchRaw("/v1/health");
|
|
73
|
-
if (!res.ok) {
|
|
74
|
-
throw new Error(
|
|
75
|
-
`SystemDox API authentication failed (${res.status}). Check your SYSTEMDOX_API_KEY.`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
71
|
async get(path, params) {
|
|
80
72
|
const url = this.buildUrl(path, params);
|
|
81
73
|
const cacheKey = url.toString();
|
|
@@ -89,8 +81,8 @@ var SystemDoxApiClient = class {
|
|
|
89
81
|
}
|
|
90
82
|
/** A read that skips the cache: an install must take the version that is
|
|
91
83
|
* latest now, not the one cached before someone published another. */
|
|
92
|
-
async getFresh(path) {
|
|
93
|
-
const res = await this.fetchRaw(path);
|
|
84
|
+
async getFresh(path, signal) {
|
|
85
|
+
const res = await this.fetchRaw(path, void 0, signal);
|
|
94
86
|
if (!res.ok) throw new SystemDoxApiError(res.status, await res.text());
|
|
95
87
|
return await res.json();
|
|
96
88
|
}
|
|
@@ -120,8 +112,8 @@ var SystemDoxApiClient = class {
|
|
|
120
112
|
// One method per operationId. Every read skips the cache: setup, sync and
|
|
121
113
|
// plugin-path act on what the workspace holds now.
|
|
122
114
|
/** getOwnTeamSetup: the key's own workspace, named in `workspace`. */
|
|
123
|
-
getOwnTeamSetup() {
|
|
124
|
-
return this.getFresh(TEAM_SETUP);
|
|
115
|
+
getOwnTeamSetup(signal) {
|
|
116
|
+
return this.getFresh(TEAM_SETUP, signal);
|
|
125
117
|
}
|
|
126
118
|
getTeamSetup(workspace) {
|
|
127
119
|
return this.getFresh(`${TEAM_SETUP}/${seg(workspace)}`);
|
|
@@ -225,14 +217,15 @@ var SystemDoxApiClient = class {
|
|
|
225
217
|
}
|
|
226
218
|
return url;
|
|
227
219
|
}
|
|
228
|
-
async fetchRaw(path, params) {
|
|
220
|
+
async fetchRaw(path, params, signal) {
|
|
229
221
|
const url = this.buildUrl(path, params);
|
|
230
222
|
return fetch(url, {
|
|
231
223
|
headers: {
|
|
232
224
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
233
225
|
"X-API-Key": this.config.apiKey,
|
|
234
226
|
Accept: "application/json"
|
|
235
|
-
}
|
|
227
|
+
},
|
|
228
|
+
signal
|
|
236
229
|
});
|
|
237
230
|
}
|
|
238
231
|
};
|
package/dist/sync-memory.js
CHANGED
|
@@ -65,14 +65,6 @@ var SystemDoxApiClient = class {
|
|
|
65
65
|
this.config = config;
|
|
66
66
|
this.cache = new ResponseCache(config.cacheTtlMs);
|
|
67
67
|
}
|
|
68
|
-
async validate() {
|
|
69
|
-
const res = await this.fetchRaw("/v1/health");
|
|
70
|
-
if (!res.ok) {
|
|
71
|
-
throw new Error(
|
|
72
|
-
`SystemDox API authentication failed (${res.status}). Check your SYSTEMDOX_API_KEY.`
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
68
|
async get(path, params) {
|
|
77
69
|
const url = this.buildUrl(path, params);
|
|
78
70
|
const cacheKey = url.toString();
|
|
@@ -86,8 +78,8 @@ var SystemDoxApiClient = class {
|
|
|
86
78
|
}
|
|
87
79
|
/** A read that skips the cache: an install must take the version that is
|
|
88
80
|
* latest now, not the one cached before someone published another. */
|
|
89
|
-
async getFresh(path) {
|
|
90
|
-
const res = await this.fetchRaw(path);
|
|
81
|
+
async getFresh(path, signal) {
|
|
82
|
+
const res = await this.fetchRaw(path, void 0, signal);
|
|
91
83
|
if (!res.ok) throw new SystemDoxApiError(res.status, await res.text());
|
|
92
84
|
return await res.json();
|
|
93
85
|
}
|
|
@@ -117,8 +109,8 @@ var SystemDoxApiClient = class {
|
|
|
117
109
|
// One method per operationId. Every read skips the cache: setup, sync and
|
|
118
110
|
// plugin-path act on what the workspace holds now.
|
|
119
111
|
/** getOwnTeamSetup: the key's own workspace, named in `workspace`. */
|
|
120
|
-
getOwnTeamSetup() {
|
|
121
|
-
return this.getFresh(TEAM_SETUP);
|
|
112
|
+
getOwnTeamSetup(signal) {
|
|
113
|
+
return this.getFresh(TEAM_SETUP, signal);
|
|
122
114
|
}
|
|
123
115
|
getTeamSetup(workspace) {
|
|
124
116
|
return this.getFresh(`${TEAM_SETUP}/${seg(workspace)}`);
|
|
@@ -222,14 +214,15 @@ var SystemDoxApiClient = class {
|
|
|
222
214
|
}
|
|
223
215
|
return url;
|
|
224
216
|
}
|
|
225
|
-
async fetchRaw(path, params) {
|
|
217
|
+
async fetchRaw(path, params, signal) {
|
|
226
218
|
const url = this.buildUrl(path, params);
|
|
227
219
|
return fetch(url, {
|
|
228
220
|
headers: {
|
|
229
221
|
Authorization: `Bearer ${this.config.apiKey}`,
|
|
230
222
|
"X-API-Key": this.config.apiKey,
|
|
231
223
|
Accept: "application/json"
|
|
232
|
-
}
|
|
224
|
+
},
|
|
225
|
+
signal
|
|
233
226
|
});
|
|
234
227
|
}
|
|
235
228
|
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { SystemDoxApiClient } from "../lib/api-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* The document graph over MCP (ADR-224, specs/changes/document-graph/contract.md §5).
|
|
5
|
+
* Three read-only tools on the hosted and the local server. Every answer says
|
|
6
|
+
* how each link is known, and one that was cut says so.
|
|
7
|
+
*/
|
|
8
|
+
interface GraphNode {
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
type: string;
|
|
12
|
+
repo: string;
|
|
13
|
+
path: string;
|
|
14
|
+
}
|
|
15
|
+
interface NeighbourLink {
|
|
16
|
+
direction: "out" | "in";
|
|
17
|
+
relation: string;
|
|
18
|
+
provenance: string;
|
|
19
|
+
confidence: number;
|
|
20
|
+
file: string;
|
|
21
|
+
line: number;
|
|
22
|
+
other: GraphNode;
|
|
23
|
+
}
|
|
24
|
+
interface AmbiguousLink {
|
|
25
|
+
reference: string;
|
|
26
|
+
file: string;
|
|
27
|
+
line: number;
|
|
28
|
+
candidates: GraphNode[];
|
|
29
|
+
}
|
|
30
|
+
interface Neighbours {
|
|
31
|
+
doc: GraphNode;
|
|
32
|
+
links: NeighbourLink[];
|
|
33
|
+
ambiguous: AmbiguousLink[];
|
|
34
|
+
total: number;
|
|
35
|
+
truncated: boolean;
|
|
36
|
+
}
|
|
37
|
+
interface PathStep {
|
|
38
|
+
from: GraphNode;
|
|
39
|
+
to: GraphNode;
|
|
40
|
+
relation: string;
|
|
41
|
+
provenance: string;
|
|
42
|
+
confidence: number;
|
|
43
|
+
file: string;
|
|
44
|
+
line: number;
|
|
45
|
+
reversed: boolean;
|
|
46
|
+
}
|
|
47
|
+
interface GraphPath {
|
|
48
|
+
found: boolean;
|
|
49
|
+
hops: number;
|
|
50
|
+
confidence: number;
|
|
51
|
+
steps: PathStep[];
|
|
52
|
+
}
|
|
53
|
+
interface RepoFreshness {
|
|
54
|
+
repo: string;
|
|
55
|
+
documents: number;
|
|
56
|
+
links: number;
|
|
57
|
+
indexedCommit: string;
|
|
58
|
+
indexedAt: string;
|
|
59
|
+
headCommit: string;
|
|
60
|
+
pushedAt: string;
|
|
61
|
+
behind: boolean;
|
|
62
|
+
}
|
|
63
|
+
interface GraphStats {
|
|
64
|
+
nodeCount: number;
|
|
65
|
+
edgeCount: number;
|
|
66
|
+
links: {
|
|
67
|
+
declared: number;
|
|
68
|
+
extracted: number;
|
|
69
|
+
inferred: number;
|
|
70
|
+
ambiguous: number;
|
|
71
|
+
};
|
|
72
|
+
repos: RepoFreshness[];
|
|
73
|
+
}
|
|
74
|
+
interface RepoLink {
|
|
75
|
+
source: string;
|
|
76
|
+
target: string;
|
|
77
|
+
relation: string;
|
|
78
|
+
provenance: string;
|
|
79
|
+
confidence: number;
|
|
80
|
+
file: string;
|
|
81
|
+
line: number;
|
|
82
|
+
}
|
|
83
|
+
export declare function formatNeighbours(answer: Neighbours): string;
|
|
84
|
+
export declare function formatPath(answer: GraphPath, maxHops: number): string;
|
|
85
|
+
export declare function formatStats(stats: GraphStats, repoLinks: RepoLink[]): string;
|
|
86
|
+
export declare function registerDocumentGraph(server: McpServer, apiClient: SystemDoxApiClient): void;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { SystemDoxApiClient } from "../lib/api-client.js";
|
|
3
|
+
export declare function registerGetTeamSetup(server: McpServer, apiClient: SystemDoxApiClient, { localFiles, keyProblem }: {
|
|
4
|
+
localFiles: boolean;
|
|
5
|
+
keyProblem?: string;
|
|
6
|
+
}): void;
|
package/dist/tools/skills.js
CHANGED
|
@@ -29,6 +29,7 @@ import { homedir } from "os";
|
|
|
29
29
|
import { dirname, join, relative, resolve, sep } from "path";
|
|
30
30
|
var MANIFEST_FILE = ".systemdox-skill.json";
|
|
31
31
|
var ENTRY_FILE = "SKILL.md";
|
|
32
|
+
var PLUGIN_MANIFEST_FOLDER = ".claude-plugin";
|
|
32
33
|
var MAX_FILES = 100;
|
|
33
34
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
34
35
|
var MAX_TOTAL_BYTES = 1024 * 1024;
|
|
@@ -53,7 +54,7 @@ function isValidName(name) {
|
|
|
53
54
|
return name.length <= 64 && NAME_RE.test(name);
|
|
54
55
|
}
|
|
55
56
|
function isValidPath(path) {
|
|
56
|
-
return path.length > 0 && path.length <= 512 && !path.startsWith("/") && path.split("/").every((s) => SEGMENT_RE.test(s) && s !== "." && s !== "..");
|
|
57
|
+
return path.length > 0 && path.length <= 512 && !path.startsWith("/") && path.split("/").every((s) => SEGMENT_RE.test(s) && s !== "." && s !== ".." && s !== PLUGIN_MANIFEST_FOLDER);
|
|
57
58
|
}
|
|
58
59
|
function frontmatterName(skillMd) {
|
|
59
60
|
const block = /^---\r?\n([\s\S]*?)\r?\n---/.exec(skillMd);
|
package/dist/tools/team-setup.js
CHANGED
|
@@ -14,13 +14,14 @@ import { existsSync } from "fs";
|
|
|
14
14
|
import { homedir } from "os";
|
|
15
15
|
import { dirname, join, relative, resolve, sep } from "path";
|
|
16
16
|
var MANIFEST_FILE = ".systemdox-skill.json";
|
|
17
|
+
var PLUGIN_MANIFEST_FOLDER = ".claude-plugin";
|
|
17
18
|
var MAX_FILES = 100;
|
|
18
19
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
19
20
|
var MAX_TOTAL_BYTES = 1024 * 1024;
|
|
20
21
|
var SEGMENT_RE = /^[A-Za-z0-9._-]+$/;
|
|
21
22
|
var SKIPPED = /* @__PURE__ */ new Set([MANIFEST_FILE, ".git", "node_modules", "__pycache__", ".DS_Store"]);
|
|
22
23
|
function isValidPath(path) {
|
|
23
|
-
return path.length > 0 && path.length <= 512 && !path.startsWith("/") && path.split("/").every((s) => SEGMENT_RE.test(s) && s !== "." && s !== "..");
|
|
24
|
+
return path.length > 0 && path.length <= 512 && !path.startsWith("/") && path.split("/").every((s) => SEGMENT_RE.test(s) && s !== "." && s !== ".." && s !== PLUGIN_MANIFEST_FOLDER);
|
|
24
25
|
}
|
|
25
26
|
async function walk(root, { skipHidden }, dir = root, out = { files: [], links: [], hidden: [] }) {
|
|
26
27
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemdox/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
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",
|