@systemdox/mcp-server 0.1.14 → 0.1.16
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 +3 -2
- package/dist/cli.js +270 -48
- package/dist/http.js +251 -47
- package/dist/index.js +251 -47
- package/dist/lib/config-links.d.ts +18 -0
- package/dist/lib/config-links.js +21 -0
- package/dist/tools/get-plan-board.d.ts +27 -0
- package/dist/tools/get-plan-board.js +208 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,8 @@ HTTP. Point any HTTP-capable MCP client at it — nothing to install or update.
|
|
|
45
45
|
which suits serverless hosting. There is no long-lived SSE stream.
|
|
46
46
|
- **Scope** — the key is the boundary. A key bound to a single space (a
|
|
47
47
|
"department" key) sees only that space's context; org-wide planner endpoints
|
|
48
|
-
(`get_planning_context`) are barred to it and answer `403`.
|
|
48
|
+
(`get_planning_context`, `get_plan_board`) are barred to it and answer `403`.
|
|
49
|
+
Give each
|
|
49
50
|
department's repo its own key via `${SYSTEMDOX_API_KEY}`, and an agent in one
|
|
50
51
|
department never sees another's checks, specs, or docs.
|
|
51
52
|
|
|
@@ -89,7 +90,7 @@ npx @systemdox/mcp-server setup
|
|
|
89
90
|
## Tools
|
|
90
91
|
|
|
91
92
|
Read: `get_checks`, `get_specs`, `get_docs`, `get_tech_stack`,
|
|
92
|
-
`get_planning_context`, `get_context`, `get_project_rules`,
|
|
93
|
+
`get_planning_context`, `get_plan_board`, `get_context`, `get_project_rules`,
|
|
93
94
|
`get_prompt_templates`. Write: `create_check`, `update_spec`,
|
|
94
95
|
`report_decision`, `create_project_rule`, `create_prompt_template`. Write tools
|
|
95
96
|
require an API key with `editor`+ role.
|
package/dist/cli.js
CHANGED
|
@@ -246,6 +246,27 @@ var init_deprecation = __esm({
|
|
|
246
246
|
}
|
|
247
247
|
});
|
|
248
248
|
|
|
249
|
+
// src/lib/config-links.ts
|
|
250
|
+
async function workspaceBase(apiClient2) {
|
|
251
|
+
try {
|
|
252
|
+
const access = await apiClient2.get(
|
|
253
|
+
"/v1/docs/portal-access"
|
|
254
|
+
);
|
|
255
|
+
const slug = (access.host ?? "").split(".")[0];
|
|
256
|
+
return slug ? `https://${slug}.systemdox.com` : null;
|
|
257
|
+
} catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function configLink(path, base) {
|
|
262
|
+
return base ? `${base}${path}` : `${path} in your SystemDox workspace`;
|
|
263
|
+
}
|
|
264
|
+
var init_config_links = __esm({
|
|
265
|
+
"src/lib/config-links.ts"() {
|
|
266
|
+
"use strict";
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
249
270
|
// src/tools/get-guardrails.ts
|
|
250
271
|
import { z } from "zod";
|
|
251
272
|
function registerGetGuardrails(server2, apiClient2) {
|
|
@@ -268,13 +289,9 @@ function registerGetGuardrails(server2, apiClient2) {
|
|
|
268
289
|
);
|
|
269
290
|
}
|
|
270
291
|
if (filtered.length === 0) {
|
|
292
|
+
const text2 = guardrails.length === 0 ? `No checks found. Add checks at ${configLink("/docs/guardrails", await workspaceBase(apiClient2))}` : `No checks found${lang ? ` for language "${lang}"` : ""}${category ? ` in category "${category}"` : ""}.`;
|
|
271
293
|
return {
|
|
272
|
-
content: [
|
|
273
|
-
{
|
|
274
|
-
type: "text",
|
|
275
|
-
text: `No checks found${lang ? ` for language "${lang}"` : ""}${category ? ` in category "${category}"` : ""}.`
|
|
276
|
-
}
|
|
277
|
-
]
|
|
294
|
+
content: [{ type: "text", text: text2 }]
|
|
278
295
|
};
|
|
279
296
|
}
|
|
280
297
|
const text = filtered.map(
|
|
@@ -331,6 +348,7 @@ var init_get_guardrails = __esm({
|
|
|
331
348
|
"use strict";
|
|
332
349
|
init_language();
|
|
333
350
|
init_deprecation();
|
|
351
|
+
init_config_links();
|
|
334
352
|
DESCRIPTION = "Retrieve SystemDox checks (banned patterns, required patterns, coding rules). Filter by language, category, or file path to get only relevant checks.";
|
|
335
353
|
inputSchema = () => ({
|
|
336
354
|
language: z.string().optional().describe(
|
|
@@ -559,7 +577,7 @@ function registerGetTechStack(server2, apiClient2) {
|
|
|
559
577
|
content: [
|
|
560
578
|
{
|
|
561
579
|
type: "text",
|
|
562
|
-
text:
|
|
580
|
+
text: `No tech stack entries configured. Add your stack at ${configLink("/docs/tech-stack", await workspaceBase(apiClient2))}`
|
|
563
581
|
}
|
|
564
582
|
]
|
|
565
583
|
};
|
|
@@ -593,6 +611,7 @@ ${sections}`
|
|
|
593
611
|
var init_get_tech_stack = __esm({
|
|
594
612
|
"src/tools/get-tech-stack.ts"() {
|
|
595
613
|
"use strict";
|
|
614
|
+
init_config_links();
|
|
596
615
|
}
|
|
597
616
|
});
|
|
598
617
|
|
|
@@ -650,11 +669,21 @@ ${adrs}`);
|
|
|
650
669
|
|
|
651
670
|
${specs}`);
|
|
652
671
|
}
|
|
672
|
+
if (sections.length === 0) {
|
|
673
|
+
return {
|
|
674
|
+
content: [
|
|
675
|
+
{
|
|
676
|
+
type: "text",
|
|
677
|
+
text: `No planning context available. Connect repositories at ${configLink("/settings/github", await workspaceBase(apiClient2))}`
|
|
678
|
+
}
|
|
679
|
+
]
|
|
680
|
+
};
|
|
681
|
+
}
|
|
653
682
|
return {
|
|
654
683
|
content: [
|
|
655
684
|
{
|
|
656
685
|
type: "text",
|
|
657
|
-
text: sections.join("\n\n")
|
|
686
|
+
text: sections.join("\n\n")
|
|
658
687
|
}
|
|
659
688
|
]
|
|
660
689
|
};
|
|
@@ -664,6 +693,199 @@ ${specs}`);
|
|
|
664
693
|
var init_get_planning_context = __esm({
|
|
665
694
|
"src/tools/get-planning-context.ts"() {
|
|
666
695
|
"use strict";
|
|
696
|
+
init_config_links();
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// src/tools/get-plan-board.ts
|
|
701
|
+
import { z as z5 } from "zod";
|
|
702
|
+
function bucketOf(item) {
|
|
703
|
+
const status = (item.fields?.Status ?? "").trim().toLowerCase();
|
|
704
|
+
if (status) {
|
|
705
|
+
if (status.includes("done") || status.includes("ship") || status.includes("complete")) {
|
|
706
|
+
return "done";
|
|
707
|
+
}
|
|
708
|
+
if (status.includes("progress") || status.includes("doing") || status.includes("review")) {
|
|
709
|
+
return "inProgress";
|
|
710
|
+
}
|
|
711
|
+
return "todo";
|
|
712
|
+
}
|
|
713
|
+
return item.state === "CLOSED" ? "done" : "todo";
|
|
714
|
+
}
|
|
715
|
+
function formatBoards(boards) {
|
|
716
|
+
const lines = boards.map((b) => {
|
|
717
|
+
const c = b.statusCounts ?? { todo: 0, inProgress: 0, done: 0 };
|
|
718
|
+
return `- **${b.title}** \u2014 board #${b.number} \xB7 ${b.itemCount} item(s) (todo ${c.todo} \xB7 in progress ${c.inProgress} \xB7 done ${c.done})` + (b.description ? `
|
|
719
|
+
${b.description}` : "") + `
|
|
720
|
+
${b.url}`;
|
|
721
|
+
});
|
|
722
|
+
return lines.join("\n");
|
|
723
|
+
}
|
|
724
|
+
function formatItem(item) {
|
|
725
|
+
const meta = [];
|
|
726
|
+
if (item.labels.length > 0) meta.push(`labels: ${item.labels.join(", ")}`);
|
|
727
|
+
if (item.assignees.length > 0)
|
|
728
|
+
meta.push(`assignees: ${item.assignees.join(", ")}`);
|
|
729
|
+
const epic = item.fields?.Epic;
|
|
730
|
+
if (epic) meta.push(`epic: ${epic}`);
|
|
731
|
+
return `- #${item.number} ${item.title}${item.repo ? ` \u2014 ${item.repo}` : ""}` + (meta.length > 0 ? `
|
|
732
|
+
${meta.join(" \xB7 ")}` : "") + (item.url ? `
|
|
733
|
+
${item.url}` : "");
|
|
734
|
+
}
|
|
735
|
+
function registerGetPlanBoard(server2, apiClient2) {
|
|
736
|
+
server2.registerTool(
|
|
737
|
+
"get_plan_board",
|
|
738
|
+
{
|
|
739
|
+
title: "Get Plan Board",
|
|
740
|
+
description: "Read the GitHub Projects boards connected to this workspace. With no arguments, lists the open boards with their item counts by status. With project_number, lists that board's issues grouped by status (In Progress / Todo / Done), each with issue number, title, repo, labels, assignees and URL \u2014 so you can see what is planned or in flight and pick up the right issue. Read-only: this tool cannot create, move, or close board items.",
|
|
741
|
+
inputSchema: {
|
|
742
|
+
project_number: z5.number().int().positive().optional().describe(
|
|
743
|
+
"Board number (the N in github.com/orgs/{org}/projects/N). Omit to list the open boards."
|
|
744
|
+
),
|
|
745
|
+
status: z5.enum(["todo", "in_progress", "done"]).optional().describe(
|
|
746
|
+
"With project_number, return only the items in this status group"
|
|
747
|
+
),
|
|
748
|
+
limit: z5.number().int().positive().max(200).optional().describe(
|
|
749
|
+
`Max items to list (default ${DEFAULT_LIMIT}). Group headings always show the true totals.`
|
|
750
|
+
)
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
async ({ project_number, status, limit }) => {
|
|
754
|
+
if (project_number === void 0) {
|
|
755
|
+
const boards = await apiClient2.get(
|
|
756
|
+
"/v1/planning/projects"
|
|
757
|
+
);
|
|
758
|
+
if (boards.length === 0) {
|
|
759
|
+
return {
|
|
760
|
+
content: [
|
|
761
|
+
{
|
|
762
|
+
type: "text",
|
|
763
|
+
text: `No project boards found. Connect a GitHub organisation and create a board at ${configLink("/plan", await workspaceBase(apiClient2))}`
|
|
764
|
+
}
|
|
765
|
+
]
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
const open = boards.filter((b) => !b.closed);
|
|
769
|
+
if (open.length === 0) {
|
|
770
|
+
return {
|
|
771
|
+
content: [
|
|
772
|
+
{
|
|
773
|
+
type: "text",
|
|
774
|
+
text: `No open project boards (${boards.length} closed). Reopen one, or create a board at ${configLink("/plan", await workspaceBase(apiClient2))}`
|
|
775
|
+
}
|
|
776
|
+
]
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
const closedNote = boards.length > open.length ? `
|
|
780
|
+
|
|
781
|
+
_${boards.length - open.length} closed board(s) not listed._` : "";
|
|
782
|
+
return {
|
|
783
|
+
content: [
|
|
784
|
+
{
|
|
785
|
+
type: "text",
|
|
786
|
+
text: `${open.length} open project board(s):
|
|
787
|
+
|
|
788
|
+
${formatBoards(open)}` + closedNote + `
|
|
789
|
+
|
|
790
|
+
Call get_plan_board with project_number to list a board's issues.`
|
|
791
|
+
}
|
|
792
|
+
]
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
const response = await apiClient2.get(
|
|
796
|
+
`/v1/planning/projects/${project_number}/items`
|
|
797
|
+
);
|
|
798
|
+
const allItems = response.items ?? [];
|
|
799
|
+
const boardName = response.projectTitle ? `**${response.projectTitle}** \u2014 board #${project_number}` : `Board #${project_number}`;
|
|
800
|
+
if (allItems.length === 0) {
|
|
801
|
+
return {
|
|
802
|
+
content: [
|
|
803
|
+
{
|
|
804
|
+
type: "text",
|
|
805
|
+
text: `${boardName} has no issues, or it is not visible to this workspace's GitHub connection. Check the board at ${configLink("/plan", await workspaceBase(apiClient2))}`
|
|
806
|
+
}
|
|
807
|
+
]
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
const grouped = {
|
|
811
|
+
inProgress: [],
|
|
812
|
+
todo: [],
|
|
813
|
+
done: []
|
|
814
|
+
};
|
|
815
|
+
for (const item of allItems) grouped[bucketOf(item)].push(item);
|
|
816
|
+
const wanted = status ? [STATUS_ARG_TO_BUCKET[status]] : BUCKET_ORDER;
|
|
817
|
+
const shown = wanted.reduce((n, b) => n + grouped[b].length, 0);
|
|
818
|
+
if (shown === 0) {
|
|
819
|
+
return {
|
|
820
|
+
content: [
|
|
821
|
+
{
|
|
822
|
+
type: "text",
|
|
823
|
+
text: `${boardName}: no items with status "${status}" (of ${allItems.length} item(s) on the board).`
|
|
824
|
+
}
|
|
825
|
+
]
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const cap = limit ?? DEFAULT_LIMIT;
|
|
829
|
+
let budget = cap;
|
|
830
|
+
const sections = [];
|
|
831
|
+
let rendered = 0;
|
|
832
|
+
for (const b of wanted) {
|
|
833
|
+
const items = grouped[b];
|
|
834
|
+
if (items.length === 0) continue;
|
|
835
|
+
const slice = items.slice(0, Math.max(budget, 0));
|
|
836
|
+
budget -= slice.length;
|
|
837
|
+
rendered += slice.length;
|
|
838
|
+
const omitted = items.length - slice.length;
|
|
839
|
+
sections.push(
|
|
840
|
+
`### ${BUCKET_LABELS[b]} (${items.length})
|
|
841
|
+
` + (slice.length > 0 ? slice.map(formatItem).join("\n") : "_(not listed \u2014 item limit reached)_") + (omitted > 0 && slice.length > 0 ? `
|
|
842
|
+
_\u2026${omitted} more not listed._` : "")
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
const notes = [];
|
|
846
|
+
if (rendered < shown) {
|
|
847
|
+
notes.push(
|
|
848
|
+
`_Listing ${rendered} of ${shown} matching item(s) \u2014 raise limit (max 200) or filter by status to see the rest._`
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
if (allItems.length >= API_ITEM_PAGE) {
|
|
852
|
+
notes.push(
|
|
853
|
+
`_The API reads the board's first ${API_ITEM_PAGE} items; a larger board may have more._`
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
return {
|
|
857
|
+
content: [
|
|
858
|
+
{
|
|
859
|
+
type: "text",
|
|
860
|
+
text: `${boardName} \u2014 ${allItems.length} item(s)
|
|
861
|
+
|
|
862
|
+
` + sections.join("\n\n") + (notes.length > 0 ? `
|
|
863
|
+
|
|
864
|
+
${notes.join("\n")}` : "")
|
|
865
|
+
}
|
|
866
|
+
]
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
var API_ITEM_PAGE, DEFAULT_LIMIT, BUCKET_LABELS, BUCKET_ORDER, STATUS_ARG_TO_BUCKET;
|
|
872
|
+
var init_get_plan_board = __esm({
|
|
873
|
+
"src/tools/get-plan-board.ts"() {
|
|
874
|
+
"use strict";
|
|
875
|
+
init_config_links();
|
|
876
|
+
API_ITEM_PAGE = 100;
|
|
877
|
+
DEFAULT_LIMIT = 50;
|
|
878
|
+
BUCKET_LABELS = {
|
|
879
|
+
inProgress: "In Progress",
|
|
880
|
+
todo: "Todo",
|
|
881
|
+
done: "Done"
|
|
882
|
+
};
|
|
883
|
+
BUCKET_ORDER = ["inProgress", "todo", "done"];
|
|
884
|
+
STATUS_ARG_TO_BUCKET = {
|
|
885
|
+
todo: "todo",
|
|
886
|
+
in_progress: "inProgress",
|
|
887
|
+
done: "done"
|
|
888
|
+
};
|
|
667
889
|
}
|
|
668
890
|
});
|
|
669
891
|
|
|
@@ -1240,7 +1462,7 @@ var init_context_builder = __esm({
|
|
|
1240
1462
|
});
|
|
1241
1463
|
|
|
1242
1464
|
// src/tools/get-context.ts
|
|
1243
|
-
import { z as
|
|
1465
|
+
import { z as z6 } from "zod";
|
|
1244
1466
|
function registerGetContext(server2, apiClient2) {
|
|
1245
1467
|
server2.registerTool(
|
|
1246
1468
|
"get_context",
|
|
@@ -1248,13 +1470,13 @@ function registerGetContext(server2, apiClient2) {
|
|
|
1248
1470
|
title: "Get Context",
|
|
1249
1471
|
description: "Intelligent context aggregator: fetches checks, specs, docs, and tech stack, ranks them by relevance to your current task, and returns the full text of the highest-ranked items within a token budget \u2014 each with the ID needed to fetch the rest. Use this instead of calling individual tools when you want curated, relevant context.",
|
|
1250
1472
|
inputSchema: {
|
|
1251
|
-
task_description:
|
|
1473
|
+
task_description: z6.string().describe(
|
|
1252
1474
|
"What you're working on (e.g., 'add a new REST endpoint for user preferences')"
|
|
1253
1475
|
),
|
|
1254
|
-
file_paths:
|
|
1476
|
+
file_paths: z6.array(z6.string()).optional().describe(
|
|
1255
1477
|
"File paths being edited \u2014 used for language/category detection"
|
|
1256
1478
|
),
|
|
1257
|
-
max_tokens:
|
|
1479
|
+
max_tokens: z6.number().optional().describe("Maximum token budget for the response (default: 4000)")
|
|
1258
1480
|
}
|
|
1259
1481
|
},
|
|
1260
1482
|
async ({ task_description, file_paths, max_tokens }) => {
|
|
@@ -1277,7 +1499,7 @@ var init_get_context = __esm({
|
|
|
1277
1499
|
});
|
|
1278
1500
|
|
|
1279
1501
|
// src/tools/create-guardrail.ts
|
|
1280
|
-
import { z as
|
|
1502
|
+
import { z as z7 } from "zod";
|
|
1281
1503
|
function registerCreateGuardrail(server2, apiClient2) {
|
|
1282
1504
|
const handler = async ({
|
|
1283
1505
|
title,
|
|
@@ -1357,19 +1579,19 @@ var init_create_guardrail = __esm({
|
|
|
1357
1579
|
"data-access"
|
|
1358
1580
|
];
|
|
1359
1581
|
inputSchema2 = () => ({
|
|
1360
|
-
title:
|
|
1361
|
-
description:
|
|
1362
|
-
severity:
|
|
1363
|
-
category:
|
|
1364
|
-
pattern:
|
|
1365
|
-
fix:
|
|
1366
|
-
fitness_test:
|
|
1582
|
+
title: z7.string().describe("Short title for the check"),
|
|
1583
|
+
description: z7.string().describe("Detailed description of what the check enforces and why"),
|
|
1584
|
+
severity: z7.enum(["critical", "high", "medium", "low"]).describe("Severity level"),
|
|
1585
|
+
category: z7.enum(CATEGORIES).optional().describe("Category (defaults to conventions)"),
|
|
1586
|
+
pattern: z7.string().optional().describe("Bad code pattern that should be avoided"),
|
|
1587
|
+
fix: z7.string().optional().describe("Correct code pattern to use instead"),
|
|
1588
|
+
fitness_test: z7.string().optional().describe("Shell command to detect violations in CI")
|
|
1367
1589
|
});
|
|
1368
1590
|
}
|
|
1369
1591
|
});
|
|
1370
1592
|
|
|
1371
1593
|
// src/tools/update-spec.ts
|
|
1372
|
-
import { z as
|
|
1594
|
+
import { z as z8 } from "zod";
|
|
1373
1595
|
function registerUpdateSpec(server2, apiClient2) {
|
|
1374
1596
|
server2.registerTool(
|
|
1375
1597
|
"update_spec",
|
|
@@ -1377,12 +1599,12 @@ function registerUpdateSpec(server2, apiClient2) {
|
|
|
1377
1599
|
title: "Update Spec",
|
|
1378
1600
|
description: "Update an existing SystemDox spec's proposal, tasks, or requirements. Only draft specs can be modified \u2014 published specs must be versioned first.",
|
|
1379
1601
|
inputSchema: {
|
|
1380
|
-
spec_id:
|
|
1381
|
-
section:
|
|
1382
|
-
content:
|
|
1602
|
+
spec_id: z8.string().describe("ID of the spec to update"),
|
|
1603
|
+
section: z8.enum(["proposal", "tasks", "requirements"]).describe("Which section to update"),
|
|
1604
|
+
content: z8.string().describe(
|
|
1383
1605
|
"New markdown content for the section. For requirements, provide the full requirement content."
|
|
1384
1606
|
),
|
|
1385
|
-
requirement_name:
|
|
1607
|
+
requirement_name: z8.string().optional().describe(
|
|
1386
1608
|
"Name of the requirement (required when section is 'requirements')"
|
|
1387
1609
|
)
|
|
1388
1610
|
},
|
|
@@ -1437,7 +1659,7 @@ var init_update_spec = __esm({
|
|
|
1437
1659
|
});
|
|
1438
1660
|
|
|
1439
1661
|
// src/tools/report-decision.ts
|
|
1440
|
-
import { z as
|
|
1662
|
+
import { z as z9 } from "zod";
|
|
1441
1663
|
function registerReportDecision(server2, apiClient2) {
|
|
1442
1664
|
server2.registerTool(
|
|
1443
1665
|
"report_decision",
|
|
@@ -1445,11 +1667,11 @@ function registerReportDecision(server2, apiClient2) {
|
|
|
1445
1667
|
title: "Report Decision",
|
|
1446
1668
|
description: "Record an architecture decision made during development. Creates a draft ADR in SystemDox for human review.",
|
|
1447
1669
|
inputSchema: {
|
|
1448
|
-
title:
|
|
1449
|
-
context:
|
|
1450
|
-
decision:
|
|
1451
|
-
consequences:
|
|
1452
|
-
related_files:
|
|
1670
|
+
title: z9.string().describe("Decision title (e.g., 'Use WebSocket for real-time updates')"),
|
|
1671
|
+
context: z9.string().describe("What prompted this decision \u2014 the problem or constraint"),
|
|
1672
|
+
decision: z9.string().describe("What was decided and why"),
|
|
1673
|
+
consequences: z9.string().describe("What this decision means going forward \u2014 trade-offs, follow-up work"),
|
|
1674
|
+
related_files: z9.array(z9.string()).optional().describe("File paths related to this decision")
|
|
1453
1675
|
},
|
|
1454
1676
|
annotations: {
|
|
1455
1677
|
readOnlyHint: false
|
|
@@ -1505,7 +1727,7 @@ var init_report_decision = __esm({
|
|
|
1505
1727
|
});
|
|
1506
1728
|
|
|
1507
1729
|
// src/tools/get-project-rules.ts
|
|
1508
|
-
import { z as
|
|
1730
|
+
import { z as z10 } from "zod";
|
|
1509
1731
|
function registerGetProjectRules(server2, apiClient2) {
|
|
1510
1732
|
server2.registerTool(
|
|
1511
1733
|
"get_project_rules",
|
|
@@ -1513,7 +1735,7 @@ function registerGetProjectRules(server2, apiClient2) {
|
|
|
1513
1735
|
title: "Get Project Rules",
|
|
1514
1736
|
description: "Retrieve project rules defined in SystemDox Standards. These are team-defined principles covering deployment, architecture, security, testing, and process. Filter by category to get only relevant rules.",
|
|
1515
1737
|
inputSchema: {
|
|
1516
|
-
category:
|
|
1738
|
+
category: z10.enum(["deployment", "architecture", "security", "testing", "process", "other"]).optional().describe("Filter by rule category")
|
|
1517
1739
|
}
|
|
1518
1740
|
},
|
|
1519
1741
|
async ({ category }) => {
|
|
@@ -1527,13 +1749,9 @@ function registerGetProjectRules(server2, apiClient2) {
|
|
|
1527
1749
|
);
|
|
1528
1750
|
}
|
|
1529
1751
|
if (filtered.length === 0) {
|
|
1752
|
+
const text2 = rules.length === 0 ? `No project rules found. Add rules at ${configLink("/docs/rules", await workspaceBase(apiClient2))}` : `No project rules found${category ? ` in category "${category}"` : ""}.`;
|
|
1530
1753
|
return {
|
|
1531
|
-
content: [
|
|
1532
|
-
{
|
|
1533
|
-
type: "text",
|
|
1534
|
-
text: `No project rules found${category ? ` in category "${category}"` : ""}.`
|
|
1535
|
-
}
|
|
1536
|
-
]
|
|
1754
|
+
content: [{ type: "text", text: text2 }]
|
|
1537
1755
|
};
|
|
1538
1756
|
}
|
|
1539
1757
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -1571,6 +1789,7 @@ var CATEGORY_LABELS;
|
|
|
1571
1789
|
var init_get_project_rules = __esm({
|
|
1572
1790
|
"src/tools/get-project-rules.ts"() {
|
|
1573
1791
|
"use strict";
|
|
1792
|
+
init_config_links();
|
|
1574
1793
|
CATEGORY_LABELS = {
|
|
1575
1794
|
deployment: "Deployment",
|
|
1576
1795
|
architecture: "Architecture",
|
|
@@ -1583,7 +1802,7 @@ var init_get_project_rules = __esm({
|
|
|
1583
1802
|
});
|
|
1584
1803
|
|
|
1585
1804
|
// src/tools/create-project-rule.ts
|
|
1586
|
-
import { z as
|
|
1805
|
+
import { z as z11 } from "zod";
|
|
1587
1806
|
function registerCreateProjectRule(server2, apiClient2) {
|
|
1588
1807
|
server2.registerTool(
|
|
1589
1808
|
"create_project_rule",
|
|
@@ -1591,10 +1810,10 @@ function registerCreateProjectRule(server2, apiClient2) {
|
|
|
1591
1810
|
title: "Create Project Rule",
|
|
1592
1811
|
description: "Create a new project rule in SystemDox Standards. Use this when you discover a team principle or convention that should be enforced across the project.",
|
|
1593
1812
|
inputSchema: {
|
|
1594
|
-
text:
|
|
1813
|
+
text: z11.string().describe(
|
|
1595
1814
|
"The rule statement \u2014 one enforceable principle per rule (e.g. 'All deployments MUST go through GitHub Actions workflows')"
|
|
1596
1815
|
),
|
|
1597
|
-
category:
|
|
1816
|
+
category: z11.enum(["deployment", "architecture", "security", "testing", "process", "other"]).describe("Rule category")
|
|
1598
1817
|
},
|
|
1599
1818
|
annotations: {
|
|
1600
1819
|
readOnlyHint: false
|
|
@@ -1641,7 +1860,7 @@ function registerGetPromptTemplates(server2, apiClient2) {
|
|
|
1641
1860
|
content: [
|
|
1642
1861
|
{
|
|
1643
1862
|
type: "text",
|
|
1644
|
-
text:
|
|
1863
|
+
text: `No prompt templates found. Add templates at ${configLink("/docs/prompts", await workspaceBase(apiClient2))}`
|
|
1645
1864
|
}
|
|
1646
1865
|
]
|
|
1647
1866
|
};
|
|
@@ -1671,11 +1890,12 @@ ${text}`
|
|
|
1671
1890
|
var init_get_prompt_templates = __esm({
|
|
1672
1891
|
"src/tools/get-prompt-templates.ts"() {
|
|
1673
1892
|
"use strict";
|
|
1893
|
+
init_config_links();
|
|
1674
1894
|
}
|
|
1675
1895
|
});
|
|
1676
1896
|
|
|
1677
1897
|
// src/tools/create-prompt-template.ts
|
|
1678
|
-
import { z as
|
|
1898
|
+
import { z as z12 } from "zod";
|
|
1679
1899
|
function registerCreatePromptTemplate(server2, apiClient2) {
|
|
1680
1900
|
server2.registerTool(
|
|
1681
1901
|
"create_prompt_template",
|
|
@@ -1683,9 +1903,9 @@ function registerCreatePromptTemplate(server2, apiClient2) {
|
|
|
1683
1903
|
title: "Create Prompt Template",
|
|
1684
1904
|
description: "Create a new reusable prompt template in SystemDox Standards. Use this to store preconfigured prompts for common AI tasks like code reviews, documentation generation, or analysis frameworks.",
|
|
1685
1905
|
inputSchema: {
|
|
1686
|
-
name:
|
|
1687
|
-
description:
|
|
1688
|
-
prompt:
|
|
1906
|
+
name: z12.string().describe("Short name for the template"),
|
|
1907
|
+
description: z12.string().optional().describe("Brief description of when to use this template"),
|
|
1908
|
+
prompt: z12.string().describe("The full prompt text that Gen AI will use when generating content")
|
|
1689
1909
|
},
|
|
1690
1910
|
annotations: {
|
|
1691
1911
|
readOnlyHint: false
|
|
@@ -1730,6 +1950,7 @@ function createServer(apiClient2) {
|
|
|
1730
1950
|
registerGetDocs(server2, apiClient2);
|
|
1731
1951
|
registerGetTechStack(server2, apiClient2);
|
|
1732
1952
|
registerGetPlanningContext(server2, apiClient2);
|
|
1953
|
+
registerGetPlanBoard(server2, apiClient2);
|
|
1733
1954
|
registerGetContext(server2, apiClient2);
|
|
1734
1955
|
registerGetProjectRules(server2, apiClient2);
|
|
1735
1956
|
registerGetPromptTemplates(server2, apiClient2);
|
|
@@ -1748,6 +1969,7 @@ var init_server = __esm({
|
|
|
1748
1969
|
init_get_docs();
|
|
1749
1970
|
init_get_tech_stack();
|
|
1750
1971
|
init_get_planning_context();
|
|
1972
|
+
init_get_plan_board();
|
|
1751
1973
|
init_get_context();
|
|
1752
1974
|
init_create_guardrail();
|
|
1753
1975
|
init_update_spec();
|
|
@@ -1935,7 +2157,7 @@ async function setup() {
|
|
|
1935
2157
|
console.log(` Configured: ${target.name}`);
|
|
1936
2158
|
}
|
|
1937
2159
|
console.log(
|
|
1938
|
-
"\n Done! Restart your AI coding tool to activate the SystemDox MCP server.\n The server provides
|
|
2160
|
+
"\n Done! Restart your AI coding tool to activate the SystemDox MCP server.\n The server provides 14 tools: get_context, get_checks,\n get_project_rules, get_prompt_templates, get_specs, get_docs,\n get_tech_stack, get_planning_context, get_plan_board, create_check,\n create_project_rule, create_prompt_template, update_spec,\n report_decision.\n\n get_guardrails and create_guardrail remain registered as deprecated\n aliases of get_checks and create_check \u2014 existing configs keep working.\n"
|
|
1939
2161
|
);
|
|
1940
2162
|
}
|
|
1941
2163
|
var args = process.argv.slice(2);
|