@systemdox/mcp-server 0.1.28 → 0.1.30
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/cli.js +21 -21
- package/dist/http.js +48 -22
- package/dist/index.js +21 -21
- package/dist/lib/context-builder.d.ts +1 -1
- package/dist/lib/context-builder.js +8 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,7 +130,7 @@ require an API key with `editor`+ role.
|
|
|
130
130
|
The aggregator. It ranks checks, specs and docs against the task, then
|
|
131
131
|
spends the token budget **fetching the bodies** of the highest-ranked specs and
|
|
132
132
|
docs (`/v1/specs/{id}`, `/v1/docs/{id}/content`) — concurrently, clipped to a
|
|
133
|
-
fair share of what's left. Every item carries the `
|
|
133
|
+
fair share of what's left. Every item carries the `draft_id` / `doc_id` needed to
|
|
134
134
|
fetch it in full, so nothing is a dead end: items whose body did not fit, or
|
|
135
135
|
whose fetch failed, still appear as a title + ID. Tech stack and project rules
|
|
136
136
|
are always included; the budget is spent on bodies, never on truncating the
|
package/dist/cli.js
CHANGED
|
@@ -394,15 +394,15 @@ function registerGetSpecs(server2, apiClient2) {
|
|
|
394
394
|
title: "Get Specs",
|
|
395
395
|
description: "Retrieve SystemDox specifications (proposal, requirements with RFC 2119 keywords, Given/When/Then scenarios). Get a specific spec by ID, filter by status, or search by keyword.",
|
|
396
396
|
inputSchema: {
|
|
397
|
-
|
|
397
|
+
draft_id: z2.string().optional().describe("Specific spec ID to retrieve with full content"),
|
|
398
398
|
status: z2.enum(["draft", "ready", "archived"]).optional().describe("Filter by status"),
|
|
399
399
|
search: z2.string().optional().describe("Keyword search across spec names")
|
|
400
400
|
}
|
|
401
401
|
},
|
|
402
|
-
async ({
|
|
403
|
-
if (
|
|
402
|
+
async ({ draft_id, status, search }) => {
|
|
403
|
+
if (draft_id) {
|
|
404
404
|
const spec = await apiClient2.get(
|
|
405
|
-
`/v1/specs/${
|
|
405
|
+
`/v1/specs/${draft_id}`
|
|
406
406
|
);
|
|
407
407
|
const sections = [`# ${spec.name}
|
|
408
408
|
|
|
@@ -451,7 +451,7 @@ ${spec.taskContent}`);
|
|
|
451
451
|
};
|
|
452
452
|
}
|
|
453
453
|
const text = filtered.map(
|
|
454
|
-
(s) => `- **${s.name}** (${s.
|
|
454
|
+
(s) => `- **${s.name}** (${s.draftId}) \u2014 ${s.status}, ${s.requirementCount} requirements` + (s.repoFullName ? `, repo: ${s.repoFullName}` : "")
|
|
455
455
|
).join("\n");
|
|
456
456
|
return {
|
|
457
457
|
content: [
|
|
@@ -1117,7 +1117,7 @@ ${g.fix}
|
|
|
1117
1117
|
}
|
|
1118
1118
|
function formatSpecHeadline(s) {
|
|
1119
1119
|
return `### ${s.name}
|
|
1120
|
-
\`
|
|
1120
|
+
\`draft_id: ${s.draftId}\` \xB7 ${s.status} \xB7 ${s.requirementCount} requirements`;
|
|
1121
1121
|
}
|
|
1122
1122
|
function formatDocHeadline(d) {
|
|
1123
1123
|
return `### ${d.title}
|
|
@@ -1149,9 +1149,9 @@ function formatRules(rules) {
|
|
|
1149
1149
|
|
|
1150
1150
|
${text.trim()}`;
|
|
1151
1151
|
}
|
|
1152
|
-
async function fetchSpecBody(api,
|
|
1152
|
+
async function fetchSpecBody(api, draftId) {
|
|
1153
1153
|
const spec = await api.get(
|
|
1154
|
-
`/v1/specs/${encodeURIComponent(
|
|
1154
|
+
`/v1/specs/${encodeURIComponent(draftId)}`
|
|
1155
1155
|
);
|
|
1156
1156
|
const parts = [];
|
|
1157
1157
|
if (spec.proposalContent) {
|
|
@@ -1245,16 +1245,16 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
1245
1245
|
updatedAt: g.updatedAt
|
|
1246
1246
|
})),
|
|
1247
1247
|
...specs.filter((s) => s.status !== "archived").map((s) => {
|
|
1248
|
-
refs.set(key("spec", s.
|
|
1248
|
+
refs.set(key("spec", s.draftId), {
|
|
1249
1249
|
type: "spec",
|
|
1250
|
-
id: s.
|
|
1250
|
+
id: s.draftId,
|
|
1251
1251
|
title: s.name,
|
|
1252
|
-
listLine: `- **${s.name}** \u2014 \`
|
|
1253
|
-
fetchHint: `get_specs(
|
|
1252
|
+
listLine: `- **${s.name}** \u2014 \`draft_id: ${s.draftId}\` (${s.status}, ${s.requirementCount} requirements)`,
|
|
1253
|
+
fetchHint: `get_specs(draft_id="${s.draftId}")`
|
|
1254
1254
|
});
|
|
1255
1255
|
return {
|
|
1256
1256
|
type: "spec",
|
|
1257
|
-
id: s.
|
|
1257
|
+
id: s.draftId,
|
|
1258
1258
|
title: s.name,
|
|
1259
1259
|
content: formatSpecHeadline(s),
|
|
1260
1260
|
updatedAt: s.updatedAt
|
|
@@ -1620,7 +1620,7 @@ function registerUpdateSpec(server2, apiClient2) {
|
|
|
1620
1620
|
title: "Update Spec",
|
|
1621
1621
|
description: "Update an existing SystemDox spec's proposal, tasks, or requirements. Only draft specs can be modified \u2014 published specs must be versioned first.",
|
|
1622
1622
|
inputSchema: {
|
|
1623
|
-
|
|
1623
|
+
draft_id: z8.string().describe("ID of the spec to update"),
|
|
1624
1624
|
section: z8.enum(["proposal", "tasks", "requirements"]).describe("Which section to update"),
|
|
1625
1625
|
content: z8.string().describe(
|
|
1626
1626
|
"New markdown content for the section. For requirements, provide the full requirement content."
|
|
@@ -1633,8 +1633,8 @@ function registerUpdateSpec(server2, apiClient2) {
|
|
|
1633
1633
|
readOnlyHint: false
|
|
1634
1634
|
}
|
|
1635
1635
|
},
|
|
1636
|
-
async ({
|
|
1637
|
-
const spec = await apiClient2.get(`/v1/specs/${
|
|
1636
|
+
async ({ draft_id, section, content, requirement_name }) => {
|
|
1637
|
+
const spec = await apiClient2.get(`/v1/specs/${draft_id}`);
|
|
1638
1638
|
if (spec.status !== "draft") {
|
|
1639
1639
|
return {
|
|
1640
1640
|
content: [
|
|
@@ -1659,8 +1659,8 @@ function registerUpdateSpec(server2, apiClient2) {
|
|
|
1659
1659
|
};
|
|
1660
1660
|
body.requirements = [...existing, newReq];
|
|
1661
1661
|
}
|
|
1662
|
-
await apiClient2.put(`/v1/specs/${
|
|
1663
|
-
apiClient2.invalidateCache(`/v1/specs/${
|
|
1662
|
+
await apiClient2.put(`/v1/specs/${draft_id}`, body);
|
|
1663
|
+
apiClient2.invalidateCache(`/v1/specs/${draft_id}`);
|
|
1664
1664
|
apiClient2.invalidateCache("/v1/specs");
|
|
1665
1665
|
return {
|
|
1666
1666
|
content: [
|
|
@@ -1724,7 +1724,7 @@ ${decision}
|
|
|
1724
1724
|
## Consequences
|
|
1725
1725
|
|
|
1726
1726
|
${consequences}` + relatedSection;
|
|
1727
|
-
await apiClient2.put(`/v1/specs/${created.
|
|
1727
|
+
await apiClient2.put(`/v1/specs/${created.draftId}`, {
|
|
1728
1728
|
proposalContent: adrContent
|
|
1729
1729
|
});
|
|
1730
1730
|
apiClient2.invalidateCache("/v1/specs");
|
|
@@ -1732,7 +1732,7 @@ ${consequences}` + relatedSection;
|
|
|
1732
1732
|
content: [
|
|
1733
1733
|
{
|
|
1734
1734
|
type: "text",
|
|
1735
|
-
text: `Architecture decision recorded: **${adrTitle}** (spec id: ${created.
|
|
1735
|
+
text: `Architecture decision recorded: **${adrTitle}** (spec id: ${created.draftId})
|
|
1736
1736
|
|
|
1737
1737
|
Status: draft \u2014 review in SystemDox before publishing.`
|
|
1738
1738
|
}
|
|
@@ -2000,7 +2000,7 @@ var init_server = __esm({
|
|
|
2000
2000
|
init_create_project_rule();
|
|
2001
2001
|
init_get_prompt_templates();
|
|
2002
2002
|
init_create_prompt_template();
|
|
2003
|
-
SERVER_VERSION = true ? "0.1.
|
|
2003
|
+
SERVER_VERSION = true ? "0.1.30" : "0.0.0-dev";
|
|
2004
2004
|
}
|
|
2005
2005
|
});
|
|
2006
2006
|
|
package/dist/http.js
CHANGED
|
@@ -117,6 +117,25 @@ var SystemDoxApiClient = class {
|
|
|
117
117
|
}
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
// src/lib/config.ts
|
|
121
|
+
var API_URL_DEFAULT = "https://api.puglieseweb.com/systemdox";
|
|
122
|
+
function requireHttpsApiUrl(apiUrl) {
|
|
123
|
+
let parsed;
|
|
124
|
+
try {
|
|
125
|
+
parsed = new URL(apiUrl);
|
|
126
|
+
} catch {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`SYSTEMDOX_API_URL is not a valid URL: ${apiUrl}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (parsed.protocol !== "https:") {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`SYSTEMDOX_API_URL must use https:// \u2014 refusing to send the API key over ${parsed.protocol}// in cleartext. Got: ${apiUrl}`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return apiUrl;
|
|
137
|
+
}
|
|
138
|
+
|
|
120
139
|
// src/lib/metrics.ts
|
|
121
140
|
var NAMESPACE = "SystemDox/Mcp";
|
|
122
141
|
var METRIC_NAME = "McpToolCall";
|
|
@@ -347,15 +366,15 @@ function registerGetSpecs(server, apiClient) {
|
|
|
347
366
|
title: "Get Specs",
|
|
348
367
|
description: "Retrieve SystemDox specifications (proposal, requirements with RFC 2119 keywords, Given/When/Then scenarios). Get a specific spec by ID, filter by status, or search by keyword.",
|
|
349
368
|
inputSchema: {
|
|
350
|
-
|
|
369
|
+
draft_id: z2.string().optional().describe("Specific spec ID to retrieve with full content"),
|
|
351
370
|
status: z2.enum(["draft", "ready", "archived"]).optional().describe("Filter by status"),
|
|
352
371
|
search: z2.string().optional().describe("Keyword search across spec names")
|
|
353
372
|
}
|
|
354
373
|
},
|
|
355
|
-
async ({
|
|
356
|
-
if (
|
|
374
|
+
async ({ draft_id, status, search }) => {
|
|
375
|
+
if (draft_id) {
|
|
357
376
|
const spec = await apiClient.get(
|
|
358
|
-
`/v1/specs/${
|
|
377
|
+
`/v1/specs/${draft_id}`
|
|
359
378
|
);
|
|
360
379
|
const sections = [`# ${spec.name}
|
|
361
380
|
|
|
@@ -404,7 +423,7 @@ ${spec.taskContent}`);
|
|
|
404
423
|
};
|
|
405
424
|
}
|
|
406
425
|
const text = filtered.map(
|
|
407
|
-
(s) => `- **${s.name}** (${s.
|
|
426
|
+
(s) => `- **${s.name}** (${s.draftId}) \u2014 ${s.status}, ${s.requirementCount} requirements` + (s.repoFullName ? `, repo: ${s.repoFullName}` : "")
|
|
408
427
|
).join("\n");
|
|
409
428
|
return {
|
|
410
429
|
content: [
|
|
@@ -1045,7 +1064,7 @@ ${g.fix}
|
|
|
1045
1064
|
}
|
|
1046
1065
|
function formatSpecHeadline(s) {
|
|
1047
1066
|
return `### ${s.name}
|
|
1048
|
-
\`
|
|
1067
|
+
\`draft_id: ${s.draftId}\` \xB7 ${s.status} \xB7 ${s.requirementCount} requirements`;
|
|
1049
1068
|
}
|
|
1050
1069
|
function formatDocHeadline(d) {
|
|
1051
1070
|
return `### ${d.title}
|
|
@@ -1077,9 +1096,9 @@ function formatRules(rules) {
|
|
|
1077
1096
|
|
|
1078
1097
|
${text.trim()}`;
|
|
1079
1098
|
}
|
|
1080
|
-
async function fetchSpecBody(api,
|
|
1099
|
+
async function fetchSpecBody(api, draftId) {
|
|
1081
1100
|
const spec = await api.get(
|
|
1082
|
-
`/v1/specs/${encodeURIComponent(
|
|
1101
|
+
`/v1/specs/${encodeURIComponent(draftId)}`
|
|
1083
1102
|
);
|
|
1084
1103
|
const parts = [];
|
|
1085
1104
|
if (spec.proposalContent) {
|
|
@@ -1195,16 +1214,16 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
1195
1214
|
updatedAt: g.updatedAt
|
|
1196
1215
|
})),
|
|
1197
1216
|
...specs.filter((s) => s.status !== "archived").map((s) => {
|
|
1198
|
-
refs.set(key("spec", s.
|
|
1217
|
+
refs.set(key("spec", s.draftId), {
|
|
1199
1218
|
type: "spec",
|
|
1200
|
-
id: s.
|
|
1219
|
+
id: s.draftId,
|
|
1201
1220
|
title: s.name,
|
|
1202
|
-
listLine: `- **${s.name}** \u2014 \`
|
|
1203
|
-
fetchHint: `get_specs(
|
|
1221
|
+
listLine: `- **${s.name}** \u2014 \`draft_id: ${s.draftId}\` (${s.status}, ${s.requirementCount} requirements)`,
|
|
1222
|
+
fetchHint: `get_specs(draft_id="${s.draftId}")`
|
|
1204
1223
|
});
|
|
1205
1224
|
return {
|
|
1206
1225
|
type: "spec",
|
|
1207
|
-
id: s.
|
|
1226
|
+
id: s.draftId,
|
|
1208
1227
|
title: s.name,
|
|
1209
1228
|
content: formatSpecHeadline(s),
|
|
1210
1229
|
updatedAt: s.updatedAt
|
|
@@ -1518,7 +1537,7 @@ function registerUpdateSpec(server, apiClient) {
|
|
|
1518
1537
|
title: "Update Spec",
|
|
1519
1538
|
description: "Update an existing SystemDox spec's proposal, tasks, or requirements. Only draft specs can be modified \u2014 published specs must be versioned first.",
|
|
1520
1539
|
inputSchema: {
|
|
1521
|
-
|
|
1540
|
+
draft_id: z8.string().describe("ID of the spec to update"),
|
|
1522
1541
|
section: z8.enum(["proposal", "tasks", "requirements"]).describe("Which section to update"),
|
|
1523
1542
|
content: z8.string().describe(
|
|
1524
1543
|
"New markdown content for the section. For requirements, provide the full requirement content."
|
|
@@ -1531,8 +1550,8 @@ function registerUpdateSpec(server, apiClient) {
|
|
|
1531
1550
|
readOnlyHint: false
|
|
1532
1551
|
}
|
|
1533
1552
|
},
|
|
1534
|
-
async ({
|
|
1535
|
-
const spec = await apiClient.get(`/v1/specs/${
|
|
1553
|
+
async ({ draft_id, section, content, requirement_name }) => {
|
|
1554
|
+
const spec = await apiClient.get(`/v1/specs/${draft_id}`);
|
|
1536
1555
|
if (spec.status !== "draft") {
|
|
1537
1556
|
return {
|
|
1538
1557
|
content: [
|
|
@@ -1557,8 +1576,8 @@ function registerUpdateSpec(server, apiClient) {
|
|
|
1557
1576
|
};
|
|
1558
1577
|
body.requirements = [...existing, newReq];
|
|
1559
1578
|
}
|
|
1560
|
-
await apiClient.put(`/v1/specs/${
|
|
1561
|
-
apiClient.invalidateCache(`/v1/specs/${
|
|
1579
|
+
await apiClient.put(`/v1/specs/${draft_id}`, body);
|
|
1580
|
+
apiClient.invalidateCache(`/v1/specs/${draft_id}`);
|
|
1562
1581
|
apiClient.invalidateCache("/v1/specs");
|
|
1563
1582
|
return {
|
|
1564
1583
|
content: [
|
|
@@ -1617,7 +1636,7 @@ ${decision}
|
|
|
1617
1636
|
## Consequences
|
|
1618
1637
|
|
|
1619
1638
|
${consequences}` + relatedSection;
|
|
1620
|
-
await apiClient.put(`/v1/specs/${created.
|
|
1639
|
+
await apiClient.put(`/v1/specs/${created.draftId}`, {
|
|
1621
1640
|
proposalContent: adrContent
|
|
1622
1641
|
});
|
|
1623
1642
|
apiClient.invalidateCache("/v1/specs");
|
|
@@ -1625,7 +1644,7 @@ ${consequences}` + relatedSection;
|
|
|
1625
1644
|
content: [
|
|
1626
1645
|
{
|
|
1627
1646
|
type: "text",
|
|
1628
|
-
text: `Architecture decision recorded: **${adrTitle}** (spec id: ${created.
|
|
1647
|
+
text: `Architecture decision recorded: **${adrTitle}** (spec id: ${created.draftId})
|
|
1629
1648
|
|
|
1630
1649
|
Status: draft \u2014 review in SystemDox before publishing.`
|
|
1631
1650
|
}
|
|
@@ -1825,7 +1844,7 @@ function registerCreatePromptTemplate(server, apiClient) {
|
|
|
1825
1844
|
}
|
|
1826
1845
|
|
|
1827
1846
|
// src/server.ts
|
|
1828
|
-
var SERVER_VERSION = true ? "0.1.
|
|
1847
|
+
var SERVER_VERSION = true ? "0.1.30" : "0.0.0-dev";
|
|
1829
1848
|
function createServer(apiClient) {
|
|
1830
1849
|
const server = new McpServer({
|
|
1831
1850
|
name: "systemdox-mcp-server",
|
|
@@ -1896,9 +1915,16 @@ async function handler(event) {
|
|
|
1896
1915
|
}
|
|
1897
1916
|
const rawBody = event.isBase64Encoded ? Buffer.from(event.body ?? "", "base64").toString("utf8") : event.body ?? "";
|
|
1898
1917
|
emitMcpToolCalls(keyTenant, rawBody);
|
|
1918
|
+
let apiUrl;
|
|
1919
|
+
try {
|
|
1920
|
+
apiUrl = requireHttpsApiUrl(process.env.SYSTEMDOX_API_URL ?? API_URL_DEFAULT);
|
|
1921
|
+
} catch (err) {
|
|
1922
|
+
console.error("Refusing to proxy MCP tool calls:", err);
|
|
1923
|
+
return jsonError(500, "Server misconfigured: API URL must use https://");
|
|
1924
|
+
}
|
|
1899
1925
|
const apiClient = new SystemDoxApiClient({
|
|
1900
1926
|
apiKey,
|
|
1901
|
-
apiUrl
|
|
1927
|
+
apiUrl,
|
|
1902
1928
|
cacheTtlMs: 0,
|
|
1903
1929
|
// per-request client; never cache across invocations
|
|
1904
1930
|
defaultOrg: void 0
|
package/dist/index.js
CHANGED
|
@@ -348,15 +348,15 @@ function registerGetSpecs(server2, apiClient2) {
|
|
|
348
348
|
title: "Get Specs",
|
|
349
349
|
description: "Retrieve SystemDox specifications (proposal, requirements with RFC 2119 keywords, Given/When/Then scenarios). Get a specific spec by ID, filter by status, or search by keyword.",
|
|
350
350
|
inputSchema: {
|
|
351
|
-
|
|
351
|
+
draft_id: z2.string().optional().describe("Specific spec ID to retrieve with full content"),
|
|
352
352
|
status: z2.enum(["draft", "ready", "archived"]).optional().describe("Filter by status"),
|
|
353
353
|
search: z2.string().optional().describe("Keyword search across spec names")
|
|
354
354
|
}
|
|
355
355
|
},
|
|
356
|
-
async ({
|
|
357
|
-
if (
|
|
356
|
+
async ({ draft_id, status, search }) => {
|
|
357
|
+
if (draft_id) {
|
|
358
358
|
const spec = await apiClient2.get(
|
|
359
|
-
`/v1/specs/${
|
|
359
|
+
`/v1/specs/${draft_id}`
|
|
360
360
|
);
|
|
361
361
|
const sections = [`# ${spec.name}
|
|
362
362
|
|
|
@@ -405,7 +405,7 @@ ${spec.taskContent}`);
|
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
407
|
const text = filtered.map(
|
|
408
|
-
(s) => `- **${s.name}** (${s.
|
|
408
|
+
(s) => `- **${s.name}** (${s.draftId}) \u2014 ${s.status}, ${s.requirementCount} requirements` + (s.repoFullName ? `, repo: ${s.repoFullName}` : "")
|
|
409
409
|
).join("\n");
|
|
410
410
|
return {
|
|
411
411
|
content: [
|
|
@@ -1046,7 +1046,7 @@ ${g.fix}
|
|
|
1046
1046
|
}
|
|
1047
1047
|
function formatSpecHeadline(s) {
|
|
1048
1048
|
return `### ${s.name}
|
|
1049
|
-
\`
|
|
1049
|
+
\`draft_id: ${s.draftId}\` \xB7 ${s.status} \xB7 ${s.requirementCount} requirements`;
|
|
1050
1050
|
}
|
|
1051
1051
|
function formatDocHeadline(d) {
|
|
1052
1052
|
return `### ${d.title}
|
|
@@ -1078,9 +1078,9 @@ function formatRules(rules) {
|
|
|
1078
1078
|
|
|
1079
1079
|
${text.trim()}`;
|
|
1080
1080
|
}
|
|
1081
|
-
async function fetchSpecBody(api,
|
|
1081
|
+
async function fetchSpecBody(api, draftId) {
|
|
1082
1082
|
const spec = await api.get(
|
|
1083
|
-
`/v1/specs/${encodeURIComponent(
|
|
1083
|
+
`/v1/specs/${encodeURIComponent(draftId)}`
|
|
1084
1084
|
);
|
|
1085
1085
|
const parts = [];
|
|
1086
1086
|
if (spec.proposalContent) {
|
|
@@ -1196,16 +1196,16 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
1196
1196
|
updatedAt: g.updatedAt
|
|
1197
1197
|
})),
|
|
1198
1198
|
...specs.filter((s) => s.status !== "archived").map((s) => {
|
|
1199
|
-
refs.set(key("spec", s.
|
|
1199
|
+
refs.set(key("spec", s.draftId), {
|
|
1200
1200
|
type: "spec",
|
|
1201
|
-
id: s.
|
|
1201
|
+
id: s.draftId,
|
|
1202
1202
|
title: s.name,
|
|
1203
|
-
listLine: `- **${s.name}** \u2014 \`
|
|
1204
|
-
fetchHint: `get_specs(
|
|
1203
|
+
listLine: `- **${s.name}** \u2014 \`draft_id: ${s.draftId}\` (${s.status}, ${s.requirementCount} requirements)`,
|
|
1204
|
+
fetchHint: `get_specs(draft_id="${s.draftId}")`
|
|
1205
1205
|
});
|
|
1206
1206
|
return {
|
|
1207
1207
|
type: "spec",
|
|
1208
|
-
id: s.
|
|
1208
|
+
id: s.draftId,
|
|
1209
1209
|
title: s.name,
|
|
1210
1210
|
content: formatSpecHeadline(s),
|
|
1211
1211
|
updatedAt: s.updatedAt
|
|
@@ -1519,7 +1519,7 @@ function registerUpdateSpec(server2, apiClient2) {
|
|
|
1519
1519
|
title: "Update Spec",
|
|
1520
1520
|
description: "Update an existing SystemDox spec's proposal, tasks, or requirements. Only draft specs can be modified \u2014 published specs must be versioned first.",
|
|
1521
1521
|
inputSchema: {
|
|
1522
|
-
|
|
1522
|
+
draft_id: z8.string().describe("ID of the spec to update"),
|
|
1523
1523
|
section: z8.enum(["proposal", "tasks", "requirements"]).describe("Which section to update"),
|
|
1524
1524
|
content: z8.string().describe(
|
|
1525
1525
|
"New markdown content for the section. For requirements, provide the full requirement content."
|
|
@@ -1532,8 +1532,8 @@ function registerUpdateSpec(server2, apiClient2) {
|
|
|
1532
1532
|
readOnlyHint: false
|
|
1533
1533
|
}
|
|
1534
1534
|
},
|
|
1535
|
-
async ({
|
|
1536
|
-
const spec = await apiClient2.get(`/v1/specs/${
|
|
1535
|
+
async ({ draft_id, section, content, requirement_name }) => {
|
|
1536
|
+
const spec = await apiClient2.get(`/v1/specs/${draft_id}`);
|
|
1537
1537
|
if (spec.status !== "draft") {
|
|
1538
1538
|
return {
|
|
1539
1539
|
content: [
|
|
@@ -1558,8 +1558,8 @@ function registerUpdateSpec(server2, apiClient2) {
|
|
|
1558
1558
|
};
|
|
1559
1559
|
body.requirements = [...existing, newReq];
|
|
1560
1560
|
}
|
|
1561
|
-
await apiClient2.put(`/v1/specs/${
|
|
1562
|
-
apiClient2.invalidateCache(`/v1/specs/${
|
|
1561
|
+
await apiClient2.put(`/v1/specs/${draft_id}`, body);
|
|
1562
|
+
apiClient2.invalidateCache(`/v1/specs/${draft_id}`);
|
|
1563
1563
|
apiClient2.invalidateCache("/v1/specs");
|
|
1564
1564
|
return {
|
|
1565
1565
|
content: [
|
|
@@ -1618,7 +1618,7 @@ ${decision}
|
|
|
1618
1618
|
## Consequences
|
|
1619
1619
|
|
|
1620
1620
|
${consequences}` + relatedSection;
|
|
1621
|
-
await apiClient2.put(`/v1/specs/${created.
|
|
1621
|
+
await apiClient2.put(`/v1/specs/${created.draftId}`, {
|
|
1622
1622
|
proposalContent: adrContent
|
|
1623
1623
|
});
|
|
1624
1624
|
apiClient2.invalidateCache("/v1/specs");
|
|
@@ -1626,7 +1626,7 @@ ${consequences}` + relatedSection;
|
|
|
1626
1626
|
content: [
|
|
1627
1627
|
{
|
|
1628
1628
|
type: "text",
|
|
1629
|
-
text: `Architecture decision recorded: **${adrTitle}** (spec id: ${created.
|
|
1629
|
+
text: `Architecture decision recorded: **${adrTitle}** (spec id: ${created.draftId})
|
|
1630
1630
|
|
|
1631
1631
|
Status: draft \u2014 review in SystemDox before publishing.`
|
|
1632
1632
|
}
|
|
@@ -1826,7 +1826,7 @@ function registerCreatePromptTemplate(server2, apiClient2) {
|
|
|
1826
1826
|
}
|
|
1827
1827
|
|
|
1828
1828
|
// src/server.ts
|
|
1829
|
-
var SERVER_VERSION = true ? "0.1.
|
|
1829
|
+
var SERVER_VERSION = true ? "0.1.30" : "0.0.0-dev";
|
|
1830
1830
|
function createServer(apiClient2) {
|
|
1831
1831
|
const server2 = new McpServer({
|
|
1832
1832
|
name: "systemdox-mcp-server",
|
|
@@ -279,7 +279,7 @@ ${g.fix}
|
|
|
279
279
|
}
|
|
280
280
|
function formatSpecHeadline(s) {
|
|
281
281
|
return `### ${s.name}
|
|
282
|
-
\`
|
|
282
|
+
\`draft_id: ${s.draftId}\` \xB7 ${s.status} \xB7 ${s.requirementCount} requirements`;
|
|
283
283
|
}
|
|
284
284
|
function formatDocHeadline(d) {
|
|
285
285
|
return `### ${d.title}
|
|
@@ -311,9 +311,9 @@ function formatRules(rules) {
|
|
|
311
311
|
|
|
312
312
|
${text.trim()}`;
|
|
313
313
|
}
|
|
314
|
-
async function fetchSpecBody(api,
|
|
314
|
+
async function fetchSpecBody(api, draftId) {
|
|
315
315
|
const spec = await api.get(
|
|
316
|
-
`/v1/specs/${encodeURIComponent(
|
|
316
|
+
`/v1/specs/${encodeURIComponent(draftId)}`
|
|
317
317
|
);
|
|
318
318
|
const parts = [];
|
|
319
319
|
if (spec.proposalContent) {
|
|
@@ -429,16 +429,16 @@ async function buildContext(api, { taskDescription, filePaths = [], maxTokens })
|
|
|
429
429
|
updatedAt: g.updatedAt
|
|
430
430
|
})),
|
|
431
431
|
...specs.filter((s) => s.status !== "archived").map((s) => {
|
|
432
|
-
refs.set(key("spec", s.
|
|
432
|
+
refs.set(key("spec", s.draftId), {
|
|
433
433
|
type: "spec",
|
|
434
|
-
id: s.
|
|
434
|
+
id: s.draftId,
|
|
435
435
|
title: s.name,
|
|
436
|
-
listLine: `- **${s.name}** \u2014 \`
|
|
437
|
-
fetchHint: `get_specs(
|
|
436
|
+
listLine: `- **${s.name}** \u2014 \`draft_id: ${s.draftId}\` (${s.status}, ${s.requirementCount} requirements)`,
|
|
437
|
+
fetchHint: `get_specs(draft_id="${s.draftId}")`
|
|
438
438
|
});
|
|
439
439
|
return {
|
|
440
440
|
type: "spec",
|
|
441
|
-
id: s.
|
|
441
|
+
id: s.draftId,
|
|
442
442
|
title: s.name,
|
|
443
443
|
content: formatSpecHeadline(s),
|
|
444
444
|
updatedAt: s.updatedAt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@systemdox/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
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",
|