loopctl-mcp-server 2.25.0 → 2.27.0
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/index.js +143 -6
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1027,9 +1027,9 @@ async function knowledgeBulkDelete({
|
|
|
1027
1027
|
|
|
1028
1028
|
async function knowledgeDrafts({ limit, offset, project_id }) {
|
|
1029
1029
|
const params = new URLSearchParams();
|
|
1030
|
-
// Pass `limit` through verbatim (like knowledge_list/index/search) so the
|
|
1031
|
-
//
|
|
1032
|
-
//
|
|
1030
|
+
// Pass `limit` through verbatim (like knowledge_list/index/search) so the server
|
|
1031
|
+
// honors it up to its max page size, clamping above it server-side rather than
|
|
1032
|
+
// silently clamping client-side (which would truncate draft enumeration).
|
|
1033
1033
|
if (limit != null) params.set("limit", String(limit));
|
|
1034
1034
|
if (offset != null) params.set("offset", String(offset));
|
|
1035
1035
|
if (project_id) params.set("project_id", project_id);
|
|
@@ -1038,6 +1038,41 @@ async function knowledgeDrafts({ limit, offset, project_id }) {
|
|
|
1038
1038
|
return toContent(result);
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
+
async function knowledgeConflicts({ limit, offset }) {
|
|
1042
|
+
const params = new URLSearchParams();
|
|
1043
|
+
if (limit != null) params.set("limit", String(limit));
|
|
1044
|
+
if (offset != null) params.set("offset", String(offset));
|
|
1045
|
+
const qs = params.toString();
|
|
1046
|
+
const path = qs
|
|
1047
|
+
? `/api/v1/knowledge/conflicts?${qs}`
|
|
1048
|
+
: "/api/v1/knowledge/conflicts";
|
|
1049
|
+
const result = await apiCall("GET", path, null, process.env.LOOPCTL_AGENT_KEY);
|
|
1050
|
+
return toContent(result);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
async function knowledgeResolveConflict({
|
|
1054
|
+
source_article_id,
|
|
1055
|
+
target_article_id,
|
|
1056
|
+
disposition,
|
|
1057
|
+
authoritative_article_id,
|
|
1058
|
+
classification,
|
|
1059
|
+
evidence,
|
|
1060
|
+
confidence,
|
|
1061
|
+
}) {
|
|
1062
|
+
const payload = { source_article_id, target_article_id, disposition };
|
|
1063
|
+
if (authoritative_article_id) payload.authoritative_article_id = authoritative_article_id;
|
|
1064
|
+
if (classification) payload.classification = classification;
|
|
1065
|
+
if (evidence) payload.evidence = evidence;
|
|
1066
|
+
if (confidence) payload.confidence = confidence;
|
|
1067
|
+
const result = await apiCall(
|
|
1068
|
+
"POST",
|
|
1069
|
+
"/api/v1/knowledge/conflicts/resolve",
|
|
1070
|
+
payload,
|
|
1071
|
+
process.env.LOOPCTL_AGENT_KEY,
|
|
1072
|
+
);
|
|
1073
|
+
return toContent(result);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1041
1076
|
async function knowledgeLint({ project_id, stale_days, min_coverage, max_per_category }) {
|
|
1042
1077
|
const params = new URLSearchParams();
|
|
1043
1078
|
if (stale_days != null) params.set("stale_days", String(stale_days));
|
|
@@ -2852,8 +2887,8 @@ const TOOLS = [
|
|
|
2852
2887
|
description:
|
|
2853
2888
|
"List draft (unpublished) knowledge articles. Requires orchestrator role. " +
|
|
2854
2889
|
"Returns paginated drafts with total_count in meta. Paginate via offset/limit " +
|
|
2855
|
-
"(limit honored up to 1000; a limit above the max is
|
|
2856
|
-
"
|
|
2890
|
+
"(limit honored up to 1000; a limit above the max is clamped to the maximum, " +
|
|
2891
|
+
"never rejected, so pagination stays complete).",
|
|
2857
2892
|
inputSchema: {
|
|
2858
2893
|
type: "object",
|
|
2859
2894
|
properties: {
|
|
@@ -2861,7 +2896,7 @@ const TOOLS = [
|
|
|
2861
2896
|
type: "integer",
|
|
2862
2897
|
description:
|
|
2863
2898
|
"Max drafts per page (default 20, max 1000). A limit above the max is " +
|
|
2864
|
-
"
|
|
2899
|
+
"clamped to the maximum — never rejected — so offset pagination stays complete.",
|
|
2865
2900
|
default: 20,
|
|
2866
2901
|
minimum: 1,
|
|
2867
2902
|
maximum: 1000,
|
|
@@ -2880,6 +2915,102 @@ const TOOLS = [
|
|
|
2880
2915
|
required: [],
|
|
2881
2916
|
},
|
|
2882
2917
|
},
|
|
2918
|
+
{
|
|
2919
|
+
name: "knowledge_conflicts",
|
|
2920
|
+
description:
|
|
2921
|
+
"List potential-conflict article pairs — published articles flagged 'too similar " +
|
|
2922
|
+
"to comfortably coexist' by the auto-linker / nightly lint sweep, highest-overlap " +
|
|
2923
|
+
"first. The KB only FLAGS the pair (via a mechanical similarity threshold); it does " +
|
|
2924
|
+
"NOT decide whether it's a redundancy to merge or a real contradiction — that's your " +
|
|
2925
|
+
"call, with the live context. Each entry has the two articles (id/title/status/" +
|
|
2926
|
+
"category) and their similarity. Read both, then merge (supersede one, knowledge_create " +
|
|
2927
|
+
"the merged article, or PATCH) or, if they genuinely disagree, reconcile. Paginated " +
|
|
2928
|
+
"with total_count in meta. Agent role.",
|
|
2929
|
+
inputSchema: {
|
|
2930
|
+
type: "object",
|
|
2931
|
+
properties: {
|
|
2932
|
+
limit: {
|
|
2933
|
+
type: "integer",
|
|
2934
|
+
description:
|
|
2935
|
+
"Max pairs per page (default 50, max 1000). A limit above the max is clamped " +
|
|
2936
|
+
"to the maximum — never rejected — so offset pagination stays complete.",
|
|
2937
|
+
default: 50,
|
|
2938
|
+
minimum: 1,
|
|
2939
|
+
maximum: 1000,
|
|
2940
|
+
},
|
|
2941
|
+
offset: {
|
|
2942
|
+
type: "integer",
|
|
2943
|
+
description: "Pagination offset. Default 0.",
|
|
2944
|
+
default: 0,
|
|
2945
|
+
minimum: 0,
|
|
2946
|
+
},
|
|
2947
|
+
},
|
|
2948
|
+
required: [],
|
|
2949
|
+
},
|
|
2950
|
+
},
|
|
2951
|
+
{
|
|
2952
|
+
name: "knowledge_resolve_conflict",
|
|
2953
|
+
description:
|
|
2954
|
+
"Record YOUR verdict on a potential-conflict pair (from knowledge_conflicts or an " +
|
|
2955
|
+
"article's potential_conflicts). You have the live context the KB lacks — it never " +
|
|
2956
|
+
"re-judges, it acts on what you record. Dispositions: 'dismiss' (a false positive — " +
|
|
2957
|
+
"the two don't actually conflict; drops out of the queue immediately); 'supersede' " +
|
|
2958
|
+
"(one article wins — pass authoritative_article_id, the winner; the nightly executor " +
|
|
2959
|
+
"creates a supersedes link and retires the loser, but ONLY at confidence:\"high\" — " +
|
|
2960
|
+
"reversible and audited); 'merge' (recorded for the later merge step). Non-destructive " +
|
|
2961
|
+
"at agent role — you record intent; the privileged nightly job executes it. " +
|
|
2962
|
+
"Last-write-wins per pair, so re-recording with fresher ground truth overrides. " +
|
|
2963
|
+
"Resolve only conflicts material to your current task; adjudicate against the actual " +
|
|
2964
|
+
"system, and if you can't tell which is right, leave it (or record low confidence) " +
|
|
2965
|
+
"rather than guessing.",
|
|
2966
|
+
inputSchema: {
|
|
2967
|
+
type: "object",
|
|
2968
|
+
properties: {
|
|
2969
|
+
source_article_id: {
|
|
2970
|
+
type: "string",
|
|
2971
|
+
description: "One article of the conflict pair (UUID). Order does not matter.",
|
|
2972
|
+
},
|
|
2973
|
+
target_article_id: {
|
|
2974
|
+
type: "string",
|
|
2975
|
+
description: "The other article of the conflict pair (UUID).",
|
|
2976
|
+
},
|
|
2977
|
+
disposition: {
|
|
2978
|
+
type: "string",
|
|
2979
|
+
enum: ["dismiss", "supersede", "merge"],
|
|
2980
|
+
description:
|
|
2981
|
+
"dismiss = false positive; supersede = one wins (set authoritative_article_id); " +
|
|
2982
|
+
"merge = combine (recorded for the later merge step).",
|
|
2983
|
+
},
|
|
2984
|
+
authoritative_article_id: {
|
|
2985
|
+
type: "string",
|
|
2986
|
+
description:
|
|
2987
|
+
"For supersede/merge: the WINNING article (must be one of the pair). The other " +
|
|
2988
|
+
"is the loser to retire/merge into the winner.",
|
|
2989
|
+
},
|
|
2990
|
+
classification: {
|
|
2991
|
+
type: "string",
|
|
2992
|
+
enum: ["redundant", "complementary", "contradictory"],
|
|
2993
|
+
description:
|
|
2994
|
+
"Your judgment of the relationship: redundant (same claim), complementary (same " +
|
|
2995
|
+
"topic, different facets — usually a dismiss), or contradictory (can't both be true).",
|
|
2996
|
+
},
|
|
2997
|
+
evidence: {
|
|
2998
|
+
type: "string",
|
|
2999
|
+
description:
|
|
3000
|
+
"Why you're sure — ideally a ground-truth reference (commit, file:line, URL, or the " +
|
|
3001
|
+
"observed behavior). Recorded for audit and for a human reviewing low-confidence calls.",
|
|
3002
|
+
},
|
|
3003
|
+
confidence: {
|
|
3004
|
+
type: "string",
|
|
3005
|
+
enum: ["high", "medium", "low"],
|
|
3006
|
+
description:
|
|
3007
|
+
"high, medium, or low. supersede auto-executes only at 'high'; lower confidence is " +
|
|
3008
|
+
"recorded but left for review. Default medium.",
|
|
3009
|
+
},
|
|
3010
|
+
},
|
|
3011
|
+
required: ["source_article_id", "target_article_id", "disposition"],
|
|
3012
|
+
},
|
|
3013
|
+
},
|
|
2883
3014
|
{
|
|
2884
3015
|
name: "knowledge_lint",
|
|
2885
3016
|
description:
|
|
@@ -3500,6 +3631,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3500
3631
|
case "knowledge_drafts":
|
|
3501
3632
|
return await knowledgeDrafts(args);
|
|
3502
3633
|
|
|
3634
|
+
case "knowledge_conflicts":
|
|
3635
|
+
return await knowledgeConflicts(args);
|
|
3636
|
+
|
|
3637
|
+
case "knowledge_resolve_conflict":
|
|
3638
|
+
return await knowledgeResolveConflict(args);
|
|
3639
|
+
|
|
3503
3640
|
case "knowledge_lint":
|
|
3504
3641
|
return await knowledgeLint(args);
|
|
3505
3642
|
|