@mgsoftwarebv/mg-dashboard-mcp 7.4.1 → 7.4.3
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/dist/index.js +442 -470
- package/package.json +2 -2
- package/dist/index.d.ts +0 -27
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -674,26 +674,47 @@ var TRIGGER_TOOLS = [
|
|
|
674
674
|
},
|
|
675
675
|
required: ["action", "project"]
|
|
676
676
|
}
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
name: "trigger-env",
|
|
680
|
+
description: 'Manage a Trigger.dev project\'s environment variables via the official Management API (the webapp encrypts/decrypts \u2014 no direct SecretStore/DB writes, upgrade-safe). SUPERADMIN ONLY: this reads and writes production secrets across every project. Pick the operation with `action`:\n- "list": all variable names + a secret flag for the env (values are NOT returned \u2014 use "get").\n- "get": one variable. Secret values are masked unless includeSecret=true. Required: key.\n- "set": create or update a variable (upsert) + read-back verify. Required: key, value. Optional: isSecret (only sent when provided, so existing vars keep their flag).\n- "delete": remove a variable. Required: key.\nOn the self-hosted instance only the "prod" environment exists (env defaults to "prod").',
|
|
681
|
+
inputSchema: {
|
|
682
|
+
type: "object",
|
|
683
|
+
properties: {
|
|
684
|
+
action: { type: "string", enum: ["list", "get", "set", "delete"], description: "Which env-var operation to perform." },
|
|
685
|
+
project: { type: "string", description: 'Project slug from trigger-list (e.g. "domeinflits-vsJ2")' },
|
|
686
|
+
key: { type: "string", description: "Environment variable name. Required for get/set/delete." },
|
|
687
|
+
value: { type: "string", description: 'Value to store. Required for action="set".' },
|
|
688
|
+
env: { type: "string", description: 'Environment slug. Default "prod" (only prod exists on self-hosted).' },
|
|
689
|
+
isSecret: { type: "boolean", description: `action="set" only: mark the variable as secret. Omit to leave an existing var's flag unchanged.` },
|
|
690
|
+
includeSecret: { type: "boolean", description: 'action="get" only: reveal a secret value instead of masking it (default false).' }
|
|
691
|
+
},
|
|
692
|
+
required: ["action", "project"]
|
|
693
|
+
}
|
|
677
694
|
}
|
|
678
695
|
];
|
|
679
696
|
var TRIGGER_TOOL_NAMES = new Set(TRIGGER_TOOLS.map((t) => t.name));
|
|
680
697
|
var TRIGGER_TOOL_MODULE_MAP = {
|
|
681
698
|
"trigger-list": "ci_cd",
|
|
682
699
|
"trigger-runs": "ci_cd",
|
|
683
|
-
"trigger-run": "ci_cd"
|
|
700
|
+
"trigger-run": "ci_cd",
|
|
701
|
+
// trigger-env manages production secrets → gated behind the admin `settings`
|
|
702
|
+
// module AND an extra superadmin-only check in index.ts (defense in depth).
|
|
703
|
+
"trigger-env": "settings"
|
|
684
704
|
};
|
|
685
705
|
async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
686
|
-
const sql26 = `SELECT re.\\"apiKey\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
|
|
706
|
+
const sql26 = `SELECT re.\\"apiKey\\" || '~~' || p.\\"externalRef\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
|
|
687
707
|
const cmd = [
|
|
688
708
|
`PORT=$(docker port "${WA_CONTAINER}" 3000/tcp 2>/dev/null | head -1 | sed 's/.*://')`,
|
|
689
|
-
`
|
|
690
|
-
'echo "$PORT|$
|
|
709
|
+
`ROW=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql26}" 2>/dev/null | tr -d '[:space:]')`,
|
|
710
|
+
'echo "$PORT|$ROW"'
|
|
691
711
|
].join(" && ");
|
|
692
712
|
const result = await sshExec2(conn, cmd, proxy);
|
|
693
713
|
const output = result.stdout.trim();
|
|
694
714
|
const sepIdx = output.indexOf("|");
|
|
695
715
|
const port = sepIdx > 0 ? output.substring(0, sepIdx) : "";
|
|
696
|
-
const
|
|
716
|
+
const combined = sepIdx > 0 ? output.substring(sepIdx + 1) : "";
|
|
717
|
+
const [apiKey2, projectRef] = combined.split("~~");
|
|
697
718
|
if (!port) {
|
|
698
719
|
throw new Error(
|
|
699
720
|
`Could not find webapp port for ${WA_CONTAINER}. Is the container running?`
|
|
@@ -704,7 +725,7 @@ async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
|
704
725
|
`Could not get API key for project "${projectSlug}". Check if the project slug is correct (use trigger-list).`
|
|
705
726
|
);
|
|
706
727
|
}
|
|
707
|
-
return { port, apiKey: apiKey2 };
|
|
728
|
+
return { port, apiKey: apiKey2, projectRef: projectRef || "" };
|
|
708
729
|
}
|
|
709
730
|
async function fetchRunLogs(runId, conn, proxy, sshExec2) {
|
|
710
731
|
const sql26 = `SELECT level, message, \\"isError\\", \\"createdAt\\" FROM \\"TaskEvent\\" WHERE \\"runId\\" = '${runId}' AND level IN ('INFO','WARN','ERROR','DEBUG','LOG','TRACE') ORDER BY \\"startTime\\" ASC LIMIT 200`;
|
|
@@ -940,6 +961,108 @@ ${rawJson.substring(0, 500)}` }] };
|
|
|
940
961
|
return await waitForCompletion(conn, proxy, sshExec2, instance, result.id, waitSeconds);
|
|
941
962
|
}
|
|
942
963
|
// -----------------------------------------------------------------
|
|
964
|
+
case "trigger-env": {
|
|
965
|
+
const action = String(args2.action);
|
|
966
|
+
if (!["list", "get", "set", "delete"].includes(action)) {
|
|
967
|
+
return { content: [{ type: "text", text: "Error: action must be one of: list, get, set, delete" }] };
|
|
968
|
+
}
|
|
969
|
+
const project = String(args2.project);
|
|
970
|
+
const env = String(args2.env || "prod");
|
|
971
|
+
const instance = await discoverInstance(project, conn, proxy, sshExec2);
|
|
972
|
+
if (!instance.projectRef) {
|
|
973
|
+
return { content: [{ type: "text", text: `Error: could not resolve project ref for "${project}" (use trigger-list).` }] };
|
|
974
|
+
}
|
|
975
|
+
const apiBase = `/api/v1/projects/${instance.projectRef}/envvars/${encodeURIComponent(env)}`;
|
|
976
|
+
if (action === "list") {
|
|
977
|
+
const raw2 = await triggerApi(conn, proxy, sshExec2, instance, "GET", apiBase);
|
|
978
|
+
let vars;
|
|
979
|
+
try {
|
|
980
|
+
vars = JSON.parse(raw2);
|
|
981
|
+
} catch {
|
|
982
|
+
return { content: [{ type: "text", text: `Invalid API response:
|
|
983
|
+
${raw2.substring(0, 500)}` }] };
|
|
984
|
+
}
|
|
985
|
+
if (!Array.isArray(vars)) {
|
|
986
|
+
return { content: [{ type: "text", text: `Unexpected API response:
|
|
987
|
+
${raw2.substring(0, 500)}` }] };
|
|
988
|
+
}
|
|
989
|
+
if (vars.length === 0) {
|
|
990
|
+
return { content: [{ type: "text", text: `No environment variables for ${project}/${env}.` }] };
|
|
991
|
+
}
|
|
992
|
+
const lines = vars.map((v) => `${v.isSecret ? "[secret]" : " "} ${v.name}`).sort();
|
|
993
|
+
const text9 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
|
|
994
|
+
` + "-".repeat(50) + "\n" + lines.join("\n");
|
|
995
|
+
return { content: [{ type: "text", text: text9 }] };
|
|
996
|
+
}
|
|
997
|
+
if (action === "get") {
|
|
998
|
+
const key2 = String(args2.key ?? "");
|
|
999
|
+
if (!key2) return { content: [{ type: "text", text: 'Error: action="get" requires key' }] };
|
|
1000
|
+
const raw2 = await triggerApi(conn, proxy, sshExec2, instance, "GET", `${apiBase}/${encodeURIComponent(key2)}`);
|
|
1001
|
+
let v;
|
|
1002
|
+
try {
|
|
1003
|
+
v = JSON.parse(raw2);
|
|
1004
|
+
} catch {
|
|
1005
|
+
return { content: [{ type: "text", text: `Invalid API response:
|
|
1006
|
+
${raw2.substring(0, 500)}` }] };
|
|
1007
|
+
}
|
|
1008
|
+
if (v.error) return { content: [{ type: "text", text: `Error: ${v.error}` }] };
|
|
1009
|
+
const includeSecret = args2.includeSecret === true;
|
|
1010
|
+
const shown = v.isSecret && !includeSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (secret \u2014 pass includeSecret:true to reveal)" : v.value ?? "";
|
|
1011
|
+
const label = v.isSecret ? " [secret]" : "";
|
|
1012
|
+
return { content: [{ type: "text", text: `${project}/${env} ${key2}${label}
|
|
1013
|
+
${shown}` }] };
|
|
1014
|
+
}
|
|
1015
|
+
if (action === "set") {
|
|
1016
|
+
const key2 = String(args2.key ?? "");
|
|
1017
|
+
if (!key2) return { content: [{ type: "text", text: 'Error: action="set" requires key' }] };
|
|
1018
|
+
if (args2.value === void 0) return { content: [{ type: "text", text: 'Error: action="set" requires value' }] };
|
|
1019
|
+
const value = String(args2.value);
|
|
1020
|
+
const bodyObj = { name: key2, value };
|
|
1021
|
+
if (typeof args2.isSecret === "boolean") bodyObj.isSecret = args2.isSecret;
|
|
1022
|
+
const raw2 = await triggerApi(conn, proxy, sshExec2, instance, "POST", apiBase, JSON.stringify(bodyObj));
|
|
1023
|
+
let resp2;
|
|
1024
|
+
try {
|
|
1025
|
+
resp2 = JSON.parse(raw2);
|
|
1026
|
+
} catch {
|
|
1027
|
+
return { content: [{ type: "text", text: `Unexpected API response:
|
|
1028
|
+
${raw2.substring(0, 500)}` }] };
|
|
1029
|
+
}
|
|
1030
|
+
if (!resp2.success) {
|
|
1031
|
+
return { content: [{ type: "text", text: `Error: ${resp2.error || raw2.substring(0, 500)}` }] };
|
|
1032
|
+
}
|
|
1033
|
+
const verifyRaw = await triggerApi(conn, proxy, sshExec2, instance, "GET", `${apiBase}/${encodeURIComponent(key2)}`);
|
|
1034
|
+
let vv;
|
|
1035
|
+
try {
|
|
1036
|
+
vv = JSON.parse(verifyRaw);
|
|
1037
|
+
} catch {
|
|
1038
|
+
vv = {};
|
|
1039
|
+
}
|
|
1040
|
+
const verified = vv.value === value;
|
|
1041
|
+
const flag = typeof args2.isSecret === "boolean" ? ` isSecret=${args2.isSecret}` : "";
|
|
1042
|
+
return {
|
|
1043
|
+
content: [{
|
|
1044
|
+
type: "text",
|
|
1045
|
+
text: `Set ${project}/${env} ${key2}${flag}
|
|
1046
|
+
Verify read-back: ${verified ? "OK" : "MISMATCH \u2014 value did not persist as expected"}`
|
|
1047
|
+
}]
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
const key = String(args2.key ?? "");
|
|
1051
|
+
if (!key) return { content: [{ type: "text", text: 'Error: action="delete" requires key' }] };
|
|
1052
|
+
const raw = await triggerApi(conn, proxy, sshExec2, instance, "DELETE", `${apiBase}/${encodeURIComponent(key)}`);
|
|
1053
|
+
let resp;
|
|
1054
|
+
try {
|
|
1055
|
+
resp = JSON.parse(raw);
|
|
1056
|
+
} catch {
|
|
1057
|
+
return { content: [{ type: "text", text: `Unexpected API response:
|
|
1058
|
+
${raw.substring(0, 500)}` }] };
|
|
1059
|
+
}
|
|
1060
|
+
if (!resp.success) {
|
|
1061
|
+
return { content: [{ type: "text", text: `Error: ${resp.error || raw.substring(0, 500)}` }] };
|
|
1062
|
+
}
|
|
1063
|
+
return { content: [{ type: "text", text: `Deleted ${project}/${env} ${key}` }] };
|
|
1064
|
+
}
|
|
1065
|
+
// -----------------------------------------------------------------
|
|
943
1066
|
default:
|
|
944
1067
|
return { content: [{ type: "text", text: `Unknown trigger tool: ${name}` }] };
|
|
945
1068
|
}
|
|
@@ -6970,6 +7093,75 @@ var TOOL_MODULE_MAP = {
|
|
|
6970
7093
|
"cursor-remote-run": "cursor_remote",
|
|
6971
7094
|
...TRIGGER_TOOL_MODULE_MAP
|
|
6972
7095
|
};
|
|
7096
|
+
var SUPERADMIN_ONLY_TOOLS = /* @__PURE__ */ new Set(["trigger-env"]);
|
|
7097
|
+
function resolveContentDispatch(tool, a) {
|
|
7098
|
+
const action = typeof a.action === "string" ? a.action : "";
|
|
7099
|
+
const { action: _drop, ...params } = a;
|
|
7100
|
+
const registry = (backendAction) => ({
|
|
7101
|
+
route: "content-registry",
|
|
7102
|
+
body: { action: backendAction, params }
|
|
7103
|
+
});
|
|
7104
|
+
const managed = (backendAction) => ({
|
|
7105
|
+
route: "managed-site",
|
|
7106
|
+
body: { action: backendAction, params }
|
|
7107
|
+
});
|
|
7108
|
+
switch (tool) {
|
|
7109
|
+
// Singletons keep their original names (no `action` discriminator).
|
|
7110
|
+
case "save_content_quality_run":
|
|
7111
|
+
return registry("save_content_quality_run");
|
|
7112
|
+
case "link_content_pack_to_ticket":
|
|
7113
|
+
return registry("link_content_pack_to_ticket");
|
|
7114
|
+
case "content-source":
|
|
7115
|
+
if (action === "save") return registry("save_content_source");
|
|
7116
|
+
if (action === "search") return registry("search_content_sources");
|
|
7117
|
+
if (action === "get") {
|
|
7118
|
+
if (typeof a.id === "string" && a.id) return registry("get_content_source_by_id");
|
|
7119
|
+
if (typeof a.url === "string" && a.url) return registry("get_content_source_by_url");
|
|
7120
|
+
return { error: "content-source action=get needs `id` or `url`" };
|
|
7121
|
+
}
|
|
7122
|
+
return { error: "content-source: action must be save | get | search" };
|
|
7123
|
+
case "research":
|
|
7124
|
+
if (action === "topic")
|
|
7125
|
+
return {
|
|
7126
|
+
route: "research-topic",
|
|
7127
|
+
body: {
|
|
7128
|
+
topic: typeof a.topic === "string" ? a.topic : void 0,
|
|
7129
|
+
urls: Array.isArray(a.urls) ? a.urls : void 0,
|
|
7130
|
+
discover: a.discover && typeof a.discover === "object" ? a.discover : void 0,
|
|
7131
|
+
includeRawText: typeof a.includeRawText === "boolean" ? a.includeRawText : void 0,
|
|
7132
|
+
maxRawTextChars: typeof a.maxRawTextChars === "number" ? a.maxRawTextChars : void 0
|
|
7133
|
+
}
|
|
7134
|
+
};
|
|
7135
|
+
if (action === "save") return registry("save_research_pack");
|
|
7136
|
+
if (action === "get") return registry("get_research_pack");
|
|
7137
|
+
return { error: "research: action must be topic | save | get" };
|
|
7138
|
+
case "content-corpus":
|
|
7139
|
+
if (action === "save") return registry("save_content_corpus_item");
|
|
7140
|
+
if (action === "list") return registry("list_project_content_corpus");
|
|
7141
|
+
if (action === "context") return registry("get_content_context_for_project");
|
|
7142
|
+
return { error: "content-corpus: action must be save | list | context" };
|
|
7143
|
+
case "content-snapshot":
|
|
7144
|
+
if (action === "list") return registry("list_content_snapshots");
|
|
7145
|
+
if (action === "prune") return registry("prune_content_snapshots");
|
|
7146
|
+
return { error: "content-snapshot: action must be list | prune" };
|
|
7147
|
+
case "site-candidate":
|
|
7148
|
+
if (action === "add") return registry("add_site_candidates");
|
|
7149
|
+
if (action === "list") return registry("list_site_candidates");
|
|
7150
|
+
if (action === "set-status") return registry("set_candidate_status");
|
|
7151
|
+
return { error: "site-candidate: action must be add | list | set-status" };
|
|
7152
|
+
case "managed-site":
|
|
7153
|
+
if (action === "register") return managed("register_managed_site");
|
|
7154
|
+
if (action === "update") return managed("update_managed_site");
|
|
7155
|
+
if (action === "list") return managed("list_managed_sites");
|
|
7156
|
+
return { error: "managed-site: action must be register | update | list" };
|
|
7157
|
+
case "content-sync":
|
|
7158
|
+
if (action === "trigger") return managed("trigger_content_sync");
|
|
7159
|
+
if (action === "status") return managed("get_content_sync_status");
|
|
7160
|
+
return { error: "content-sync: action must be trigger | status" };
|
|
7161
|
+
default:
|
|
7162
|
+
return { error: `unknown content tool ${tool}` };
|
|
7163
|
+
}
|
|
7164
|
+
}
|
|
6973
7165
|
function normalizeAllowedTools(value) {
|
|
6974
7166
|
if (!Array.isArray(value)) return null;
|
|
6975
7167
|
const tools = value.filter((t) => typeof t === "string").map((t) => t.trim()).filter(Boolean);
|
|
@@ -8596,30 +8788,19 @@ var NO_FOOTER_TOOLS = /* @__PURE__ */ new Set();
|
|
|
8596
8788
|
var RAW_JSON_TOOLS = /* @__PURE__ */ new Set([
|
|
8597
8789
|
"get_mg_dashboard_commits",
|
|
8598
8790
|
"extract_article",
|
|
8599
|
-
|
|
8600
|
-
//
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
"
|
|
8604
|
-
"
|
|
8605
|
-
"
|
|
8606
|
-
"
|
|
8607
|
-
"
|
|
8608
|
-
"list_project_content_corpus",
|
|
8609
|
-
"get_content_context_for_project",
|
|
8791
|
+
// Content family (2026-DASMG-040/041/042) — compact JSON consumed verbatim.
|
|
8792
|
+
// Consolidated action-based tools (the Cursor surface); each proxies to the
|
|
8793
|
+
// backend content-registry / managed-site / research-topic dispatchers, which
|
|
8794
|
+
// still speak the original fine-grained action names.
|
|
8795
|
+
"research",
|
|
8796
|
+
"content-source",
|
|
8797
|
+
"content-corpus",
|
|
8798
|
+
"content-snapshot",
|
|
8799
|
+
"site-candidate",
|
|
8610
8800
|
"save_content_quality_run",
|
|
8611
|
-
"list_content_snapshots",
|
|
8612
8801
|
"link_content_pack_to_ticket",
|
|
8613
|
-
"
|
|
8614
|
-
"
|
|
8615
|
-
"list_site_candidates",
|
|
8616
|
-
"set_candidate_status",
|
|
8617
|
-
// Managed site content sync (2026-DASMG-042) — compact JSON consumed verbatim.
|
|
8618
|
-
"register_managed_site",
|
|
8619
|
-
"update_managed_site",
|
|
8620
|
-
"list_managed_sites",
|
|
8621
|
-
"trigger_content_sync",
|
|
8622
|
-
"get_content_sync_status"
|
|
8802
|
+
"managed-site",
|
|
8803
|
+
"content-sync"
|
|
8623
8804
|
]);
|
|
8624
8805
|
var TOOL_CACHE_TTL_MS = {
|
|
8625
8806
|
"list-servers": 6e4,
|
|
@@ -10770,7 +10951,7 @@ var TOOLS = [
|
|
|
10770
10951
|
},
|
|
10771
10952
|
{
|
|
10772
10953
|
name: "extract_article",
|
|
10773
|
-
description: "Extract readable article/content data from ONE PUBLIC URL, with a browser-render fallback for pages that block a normal fetch (403) or only render with JavaScript (ticket 2026-DASMG-039). Returns title, author, publishedAt, source, summary, facts[], optional rawText (capped), canonicalUrl, the `extractionMethod` used (http | browser | jsonld | rss | amp | metadata_only) and an explicit `limitations` array (e.g. blocked_without_browser, paywall_detected, partial_content, rawtext_truncated). PUBLIC URLs only \u2014 SSRF-guarded (private/loopback/metadata addresses rejected), no paywall bypass, no credentials; the source/canonical URL is always preserved. Use it to turn a news/article link into safe factual source material for agents; it never auto-publishes or rewrites. For several sources + cross-checking, use
|
|
10954
|
+
description: "Extract readable article/content data from ONE PUBLIC URL, with a browser-render fallback for pages that block a normal fetch (403) or only render with JavaScript (ticket 2026-DASMG-039). Returns title, author, publishedAt, source, summary, facts[], optional rawText (capped), canonicalUrl, the `extractionMethod` used (http | browser | jsonld | rss | amp | metadata_only) and an explicit `limitations` array (e.g. blocked_without_browser, paywall_detected, partial_content, rawtext_truncated). PUBLIC URLs only \u2014 SSRF-guarded (private/loopback/metadata addresses rejected), no paywall bypass, no credentials; the source/canonical URL is always preserved. Use it to turn a news/article link into safe factual source material for agents; it never auto-publishes or rewrites. For several sources + cross-checking, use `research` with action=topic.",
|
|
10774
10955
|
inputSchema: {
|
|
10775
10956
|
type: "object",
|
|
10776
10957
|
properties: {
|
|
@@ -10790,30 +10971,44 @@ var TOOLS = [
|
|
|
10790
10971
|
required: ["url"]
|
|
10791
10972
|
}
|
|
10792
10973
|
},
|
|
10974
|
+
// ----- Research + evidence packs (2026-DASMG-040/041), consolidated -----
|
|
10793
10975
|
{
|
|
10794
|
-
name: "
|
|
10795
|
-
description: "
|
|
10976
|
+
name: "research",
|
|
10977
|
+
description: 'Research + evidence-pack toolkit (2026-DASMG-040/041). Pick the operation with `action`:\n- "topic": turn SEVERAL public sources on one topic into a single cross-checked, sourced \'evidence pack\'. Composes extract_article per URL: collect, extract, cross-check, then hand a downstream agent a safe base to write ORIGINAL content from. Modes: curated (`urls` + optional `topic`), discovery (`topic` + `discover.enabled` \u2014 finds source URLs for you), hybrid (`urls` + `discover`). Returns `sources`, `discovery` report, `verifiedFacts`, `conflicts`, `uniqueClaims`, `openQuestions`, `verbatimWarnings`, `limitations`. Structured EVIDENCE ONLY \u2014 never ready-to-publish prose; every claim traces to a source id. PUBLIC URLs only (SSRF-guarded).\n- "save": persist an evidence pack. Pass an already-built `pack`, or `urls`/`topic` (+ optional `discover`) to research then save. Each source is deduped into content_sources and linked so packs are reusable without re-crawling. Returns pack id + compact counts + linked source ids.\n- "get": fetch a stored pack by `id`. Compact by default; set `includeSources` / `includeClaims` for the full arrays.',
|
|
10796
10978
|
inputSchema: {
|
|
10797
10979
|
type: "object",
|
|
10798
10980
|
properties: {
|
|
10981
|
+
action: {
|
|
10982
|
+
type: "string",
|
|
10983
|
+
enum: ["topic", "save", "get"],
|
|
10984
|
+
description: "topic = build a pack; save = persist a pack; get = fetch a stored pack."
|
|
10985
|
+
},
|
|
10986
|
+
id: {
|
|
10987
|
+
type: "string",
|
|
10988
|
+
description: "action=get: content_evidence_packs.id (uuid)."
|
|
10989
|
+
},
|
|
10799
10990
|
topic: {
|
|
10800
10991
|
type: "string",
|
|
10801
|
-
description: "
|
|
10992
|
+
description: "action=topic/save: the topic the pack is about (also the search query in discovery mode)."
|
|
10802
10993
|
},
|
|
10803
10994
|
urls: {
|
|
10804
10995
|
type: "array",
|
|
10805
10996
|
items: { type: "string" },
|
|
10806
|
-
description: "
|
|
10997
|
+
description: "action=topic/save: curated public http(s) URLs to extract and cross-check (max 20)."
|
|
10998
|
+
},
|
|
10999
|
+
pack: {
|
|
11000
|
+
type: "object",
|
|
11001
|
+
description: "action=save: an already-built pack payload (skips re-researching)."
|
|
10807
11002
|
},
|
|
10808
11003
|
discover: {
|
|
10809
11004
|
type: "object",
|
|
10810
|
-
description: "
|
|
11005
|
+
description: "action=topic/save: discovery options. enabled=true auto-finds source URLs for `topic` via an env-gated web-search backend + keyless sitemap crawl over allowDomains. Curated `urls` are kept and topped up to maxSources; degrades gracefully when no backend is configured.",
|
|
10811
11006
|
properties: {
|
|
10812
11007
|
enabled: { type: "boolean", description: "Turn discovery on (default off)." },
|
|
10813
11008
|
maxSources: { type: "number", description: "Cap on total sources, curated + discovered (1-12, default 8)." },
|
|
10814
|
-
recencyDays: { type: "number", description: "Prefer/keep sources no older than this (
|
|
11009
|
+
recencyDays: { type: "number", description: "Prefer/keep sources no older than this (sitemap lastmod)." },
|
|
10815
11010
|
locale: { type: "string", description: "Locale hint, e.g. nl-NL." },
|
|
10816
|
-
allowDomains: { type: "array", items: { type: "string" }, description: "Prefer/guarantee domains
|
|
11011
|
+
allowDomains: { type: "array", items: { type: "string" }, description: "Prefer/guarantee domains; targets for sitemap top-up." },
|
|
10817
11012
|
blockDomains: { type: "array", items: { type: "string" }, description: "Domains to exclude from discovery." },
|
|
10818
11013
|
sourcePolicy: {
|
|
10819
11014
|
type: "string",
|
|
@@ -10824,24 +11019,21 @@ var TOOLS = [
|
|
|
10824
11019
|
},
|
|
10825
11020
|
includeRawText: {
|
|
10826
11021
|
type: "boolean",
|
|
10827
|
-
description: "
|
|
11022
|
+
description: "action=topic: reserved; claim mining always reads body text up to maxRawTextChars."
|
|
10828
11023
|
},
|
|
10829
11024
|
maxRawTextChars: {
|
|
10830
11025
|
type: "number",
|
|
10831
|
-
description: "
|
|
10832
|
-
}
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
properties: {
|
|
10843
|
-
url: { type: "string", description: "Source URL (required)." },
|
|
10844
|
-
canonicalUrl: { type: "string", description: "Canonical URL used for dedupe." },
|
|
11026
|
+
description: "action=topic: per-source body cap for claim mining (default 4000, max 20000)."
|
|
11027
|
+
},
|
|
11028
|
+
includeSources: { type: "boolean", description: "action=get: return the source list." },
|
|
11029
|
+
includeClaims: {
|
|
11030
|
+
type: "boolean",
|
|
11031
|
+
description: "action=get: return verified facts / conflicts / unique claims."
|
|
11032
|
+
},
|
|
11033
|
+
locale: { type: "string", description: "action=save: locale of the pack." },
|
|
11034
|
+
projectKey: { type: "string", description: "action=save: project identifier." },
|
|
11035
|
+
siteKey: { type: "string", description: "action=save: site identifier." },
|
|
11036
|
+
ticketNumber: { type: "string", description: "action=save: related ticket number." },
|
|
10845
11037
|
sourceType: {
|
|
10846
11038
|
type: "string",
|
|
10847
11039
|
enum: [
|
|
@@ -10854,113 +11046,31 @@ var TOOLS = [
|
|
|
10854
11046
|
"supplier",
|
|
10855
11047
|
"unknown"
|
|
10856
11048
|
],
|
|
10857
|
-
description: "
|
|
11049
|
+
description: "action=save: provenance label for linked sources."
|
|
10858
11050
|
},
|
|
10859
|
-
|
|
10860
|
-
author: { type: "string", description: "Author." },
|
|
10861
|
-
publishedAt: { type: "string", description: "Publish date (any parseable format)." },
|
|
10862
|
-
summary: { type: "string", description: "Short factual summary." },
|
|
10863
|
-
facts: { type: "array", items: { type: "string" }, description: "Key facts." },
|
|
10864
|
-
limitations: { type: "array", items: { type: "string" } },
|
|
10865
|
-
fullText: { type: "string", description: "Extracted body text." },
|
|
10866
|
-
language: { type: "string", description: "Language code." },
|
|
10867
|
-
projectKey: { type: "string", description: "Project/site identifier." },
|
|
10868
|
-
siteKey: { type: "string", description: "Site identifier." },
|
|
10869
|
-
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10870
|
-
metadata: { type: "object" },
|
|
10871
|
-
reExtract: {
|
|
10872
|
-
type: "boolean",
|
|
10873
|
-
description: "Force a re-fetch even when fields are supplied."
|
|
10874
|
-
}
|
|
11051
|
+
metadata: { type: "object", description: "action=save: arbitrary metadata." }
|
|
10875
11052
|
},
|
|
10876
|
-
required: ["
|
|
10877
|
-
}
|
|
10878
|
-
},
|
|
10879
|
-
{
|
|
10880
|
-
name: "get_content_source_by_url",
|
|
10881
|
-
description: "Look up a stored source by URL or canonical URL (2026-DASMG-041). Compact by default (id + summary + counts); set `includeFullText` for the body. Use to answer 'was this link already scanned?' before re-extracting.",
|
|
10882
|
-
inputSchema: {
|
|
10883
|
-
type: "object",
|
|
10884
|
-
properties: {
|
|
10885
|
-
url: { type: "string", description: "URL or canonical URL." },
|
|
10886
|
-
includeFullText: { type: "boolean", description: "Return full_text." }
|
|
10887
|
-
},
|
|
10888
|
-
required: ["url"]
|
|
10889
|
-
}
|
|
10890
|
-
},
|
|
10891
|
-
{
|
|
10892
|
-
name: "get_content_source_by_id",
|
|
10893
|
-
description: "Fetch a stored source by id (2026-DASMG-041). Compact by default; set `includeFullText` for the body.",
|
|
10894
|
-
inputSchema: {
|
|
10895
|
-
type: "object",
|
|
10896
|
-
properties: {
|
|
10897
|
-
id: { type: "string", description: "content_sources.id (uuid)." },
|
|
10898
|
-
includeFullText: { type: "boolean", description: "Return full_text." }
|
|
10899
|
-
},
|
|
10900
|
-
required: ["id"]
|
|
11053
|
+
required: ["action"]
|
|
10901
11054
|
}
|
|
10902
11055
|
},
|
|
11056
|
+
// ----- Content sources registry (2026-DASMG-041), consolidated -----
|
|
10903
11057
|
{
|
|
10904
|
-
name: "
|
|
10905
|
-
description:
|
|
11058
|
+
name: "content-source",
|
|
11059
|
+
description: 'Stored external/evidence sources registry (2026-DASMG-041). Pick the operation with `action`:\n- "save": persist (dedupe + upsert) ONE fetched source \u2014 the durable companion to extract_article. Pass a `url` (re-extracts internally) and/or already-extracted fields (title/summary/facts/fullText). Dedupes on canonical URL then text hash, snapshots the previous version when text changed. Returns the stored id + compact summary + created/updated/snapshotted. Required: url.\n- "get": look up ONE stored source by `id` OR by `url`/canonical url (\'was this link already scanned?\'). Compact by default; set `includeFullText` for the body. Required: id or url.\n- "search": full-text + filter search over stored sources (query, domain, sourceType, projectKey, ticketNumber, dateFrom/dateTo). Compact, paginated. Use to reuse evidence already in the registry.',
|
|
10906
11060
|
inputSchema: {
|
|
10907
11061
|
type: "object",
|
|
10908
11062
|
properties: {
|
|
10909
|
-
|
|
10910
|
-
domain: { type: "string", description: "Exact domain filter." },
|
|
10911
|
-
sourceType: {
|
|
11063
|
+
action: {
|
|
10912
11064
|
type: "string",
|
|
10913
|
-
enum: [
|
|
10914
|
-
|
|
10915
|
-
"client_site",
|
|
10916
|
-
"external_source",
|
|
10917
|
-
"competitor",
|
|
10918
|
-
"news",
|
|
10919
|
-
"government",
|
|
10920
|
-
"supplier",
|
|
10921
|
-
"unknown"
|
|
10922
|
-
]
|
|
11065
|
+
enum: ["save", "get", "search"],
|
|
11066
|
+
description: "save = upsert one source; get = fetch by id/url; search = filtered search."
|
|
10923
11067
|
},
|
|
10924
|
-
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
10928
|
-
limit: { type: "integer", description: "Max rows (default 20, max 100)." },
|
|
10929
|
-
offset: { type: "integer", description: "Pagination offset." },
|
|
10930
|
-
includeFullText: { type: "boolean", description: "Return full_text per row." }
|
|
10931
|
-
}
|
|
10932
|
-
}
|
|
10933
|
-
},
|
|
10934
|
-
{
|
|
10935
|
-
name: "save_research_pack",
|
|
10936
|
-
description: "Persist a research_topic evidence pack (2026-DASMG-041): cross-checked verified facts, conflicts, unique claims, open questions. Pass an already-built `pack`, or `urls`/`topic` to research then save. Add `discover` (with a topic) to auto-find sources before saving \u2014 the same discovery research_topic does (2026-DASMG-040). Each source is deduped into content_sources and linked via the join table, so packs can be reused without re-crawling. Returns the pack id + compact counts + linked source ids.",
|
|
10937
|
-
inputSchema: {
|
|
10938
|
-
type: "object",
|
|
10939
|
-
properties: {
|
|
10940
|
-
pack: { type: "object", description: "A research_topic result payload." },
|
|
10941
|
-
urls: {
|
|
10942
|
-
type: "array",
|
|
10943
|
-
items: { type: "string" },
|
|
10944
|
-
description: "URLs to research if no pack is given."
|
|
10945
|
-
},
|
|
10946
|
-
topic: { type: "string", description: "Topic to research if no pack is given." },
|
|
10947
|
-
discover: {
|
|
10948
|
-
type: "object",
|
|
10949
|
-
description: "Discovery options (same shape as research_topic). With a topic, enabled=true auto-finds source URLs before saving. Ignored when `pack` is supplied.",
|
|
10950
|
-
properties: {
|
|
10951
|
-
enabled: { type: "boolean" },
|
|
10952
|
-
maxSources: { type: "number" },
|
|
10953
|
-
recencyDays: { type: "number" },
|
|
10954
|
-
locale: { type: "string" },
|
|
10955
|
-
allowDomains: { type: "array", items: { type: "string" } },
|
|
10956
|
-
blockDomains: { type: "array", items: { type: "string" } },
|
|
10957
|
-
sourcePolicy: { type: "string", enum: ["official_only"] }
|
|
10958
|
-
}
|
|
11068
|
+
id: { type: "string", description: "action=get: content_sources.id (uuid)." },
|
|
11069
|
+
url: {
|
|
11070
|
+
type: "string",
|
|
11071
|
+
description: "action=save (required) / action=get: source URL or canonical URL."
|
|
10959
11072
|
},
|
|
10960
|
-
|
|
10961
|
-
projectKey: { type: "string", description: "Project identifier." },
|
|
10962
|
-
siteKey: { type: "string", description: "Site identifier." },
|
|
10963
|
-
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
11073
|
+
canonicalUrl: { type: "string", description: "action=save: canonical URL used for dedupe." },
|
|
10964
11074
|
sourceType: {
|
|
10965
11075
|
type: "string",
|
|
10966
11076
|
enum: [
|
|
@@ -10972,38 +11082,55 @@ var TOOLS = [
|
|
|
10972
11082
|
"government",
|
|
10973
11083
|
"supplier",
|
|
10974
11084
|
"unknown"
|
|
10975
|
-
]
|
|
11085
|
+
],
|
|
11086
|
+
description: "Provenance label. own_site/client_site = managed content; the rest are evidence only. (save/search filter)"
|
|
10976
11087
|
},
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
11088
|
+
title: { type: "string", description: "action=save: title." },
|
|
11089
|
+
author: { type: "string", description: "action=save: author." },
|
|
11090
|
+
publishedAt: { type: "string", description: "action=save: publish date (any parseable format)." },
|
|
11091
|
+
summary: { type: "string", description: "action=save: short factual summary." },
|
|
11092
|
+
facts: { type: "array", items: { type: "string" }, description: "action=save: key facts." },
|
|
11093
|
+
limitations: { type: "array", items: { type: "string" } },
|
|
11094
|
+
fullText: { type: "string", description: "action=save: extracted body text." },
|
|
11095
|
+
language: { type: "string", description: "action=save: language code." },
|
|
11096
|
+
projectKey: { type: "string", description: "Project/site identifier (save/search)." },
|
|
11097
|
+
siteKey: { type: "string", description: "action=save: site identifier." },
|
|
11098
|
+
ticketNumber: { type: "string", description: "Related ticket number (save/search filter)." },
|
|
11099
|
+
metadata: { type: "object" },
|
|
11100
|
+
reExtract: {
|
|
11101
|
+
type: "boolean",
|
|
11102
|
+
description: "action=save: force a re-fetch even when fields are supplied."
|
|
11103
|
+
},
|
|
11104
|
+
query: { type: "string", description: "action=search: free-text search (title/summary/body)." },
|
|
11105
|
+
domain: { type: "string", description: "action=search: exact domain filter." },
|
|
11106
|
+
dateFrom: { type: "string", description: "action=search: last-seen lower bound (ISO/date)." },
|
|
11107
|
+
dateTo: { type: "string", description: "action=search: last-seen upper bound (ISO/date)." },
|
|
11108
|
+
limit: { type: "integer", description: "action=search: max rows (default 20, max 100)." },
|
|
11109
|
+
offset: { type: "integer", description: "action=search: pagination offset." },
|
|
11110
|
+
includeFullText: {
|
|
10990
11111
|
type: "boolean",
|
|
10991
|
-
description: "
|
|
11112
|
+
description: "action=get/search: return full_text (per row for search)."
|
|
10992
11113
|
}
|
|
10993
11114
|
},
|
|
10994
|
-
required: ["
|
|
11115
|
+
required: ["action"]
|
|
10995
11116
|
}
|
|
10996
11117
|
},
|
|
11118
|
+
// ----- Own/managed content corpus (2026-DASMG-041), consolidated -----
|
|
10997
11119
|
{
|
|
10998
|
-
name: "
|
|
10999
|
-
description:
|
|
11120
|
+
name: "content-corpus",
|
|
11121
|
+
description: 'Your/your client\'s OWN managed pages (the corpus), 2026-DASMG-041. Pick the operation with `action`:\n- "save": upsert ONE own/managed page keyed by (projectKey, canonical url) \u2014 the current state of content you own. Pass a `url` (re-extracts) and/or title/h1/headings/fullText/internalLinks. Snapshots the previous version when text changed. For your pages, not external evidence. Required: projectKey, url.\n- "list": list corpus items for a project, filter by domain/sourceType/query. Compact, paginated. Required: projectKey.\n- "context": aggregate brief for a content agent \u2014 recent corpus items, recent evidence packs and related sources for a project (optionally focused by topic/url), plus totals. One call to brief an agent on what already exists. Required: projectKey.',
|
|
11000
11122
|
inputSchema: {
|
|
11001
11123
|
type: "object",
|
|
11002
11124
|
properties: {
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11125
|
+
action: {
|
|
11126
|
+
type: "string",
|
|
11127
|
+
enum: ["save", "list", "context"],
|
|
11128
|
+
description: "save = upsert one own page; list = list corpus items; context = aggregate agent brief."
|
|
11129
|
+
},
|
|
11130
|
+
projectKey: { type: "string", description: "Project identifier (required for all actions)." },
|
|
11131
|
+
url: { type: "string", description: "action=save (required) / action=context: page URL focus." },
|
|
11132
|
+
siteKey: { type: "string", description: "action=save: site identifier." },
|
|
11133
|
+
canonicalUrl: { type: "string", description: "action=save: canonical URL." },
|
|
11007
11134
|
sourceType: {
|
|
11008
11135
|
type: "string",
|
|
11009
11136
|
enum: [
|
|
@@ -11016,76 +11143,42 @@ var TOOLS = [
|
|
|
11016
11143
|
"supplier",
|
|
11017
11144
|
"unknown"
|
|
11018
11145
|
],
|
|
11019
|
-
description: "
|
|
11146
|
+
description: "action=save: defaults to own_site. action=list: filter."
|
|
11020
11147
|
},
|
|
11021
|
-
pagePath: { type: "string", description: "
|
|
11022
|
-
title: { type: "string", description: "
|
|
11023
|
-
h1: { type: "string", description: "
|
|
11148
|
+
pagePath: { type: "string", description: "action=save: path within the site." },
|
|
11149
|
+
title: { type: "string", description: "action=save: title." },
|
|
11150
|
+
h1: { type: "string", description: "action=save: main heading." },
|
|
11024
11151
|
headings: {
|
|
11025
11152
|
type: "array",
|
|
11026
11153
|
items: { type: "object" },
|
|
11027
|
-
description: "
|
|
11154
|
+
description: "action=save: headings ({level,text})."
|
|
11028
11155
|
},
|
|
11029
|
-
wordCount: { type: "integer", description: "
|
|
11030
|
-
fullText: { type: "string", description: "
|
|
11156
|
+
wordCount: { type: "integer", description: "action=save: word count." },
|
|
11157
|
+
fullText: { type: "string", description: "action=save: full page text." },
|
|
11031
11158
|
internalLinks: {
|
|
11032
11159
|
type: "array",
|
|
11033
11160
|
items: { type: "object" },
|
|
11034
|
-
description: "
|
|
11161
|
+
description: "action=save: internal links ({url,text})."
|
|
11035
11162
|
},
|
|
11036
11163
|
externalLinks: { type: "array", items: { type: "object" } },
|
|
11037
|
-
language: { type: "string", description: "
|
|
11038
|
-
ticketNumber: { type: "string", description: "
|
|
11164
|
+
language: { type: "string", description: "action=save: language code." },
|
|
11165
|
+
ticketNumber: { type: "string", description: "action=save: related ticket number." },
|
|
11039
11166
|
metadata: { type: "object" },
|
|
11040
11167
|
reExtract: {
|
|
11041
11168
|
type: "boolean",
|
|
11042
|
-
description: "
|
|
11043
|
-
}
|
|
11044
|
-
},
|
|
11045
|
-
required: ["projectKey", "url"]
|
|
11046
|
-
}
|
|
11047
|
-
},
|
|
11048
|
-
{
|
|
11049
|
-
name: "list_project_content_corpus",
|
|
11050
|
-
description: "List corpus items for a project (2026-DASMG-041), filter by domain/sourceType/query. Compact, paginated. Use to see content coverage and reuse own pages.",
|
|
11051
|
-
inputSchema: {
|
|
11052
|
-
type: "object",
|
|
11053
|
-
properties: {
|
|
11054
|
-
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
11055
|
-
domain: { type: "string", description: "Domain filter." },
|
|
11056
|
-
sourceType: {
|
|
11057
|
-
type: "string",
|
|
11058
|
-
enum: [
|
|
11059
|
-
"own_site",
|
|
11060
|
-
"client_site",
|
|
11061
|
-
"external_source",
|
|
11062
|
-
"competitor",
|
|
11063
|
-
"news",
|
|
11064
|
-
"government",
|
|
11065
|
-
"supplier",
|
|
11066
|
-
"unknown"
|
|
11067
|
-
]
|
|
11169
|
+
description: "action=save: force a re-fetch even when fields are supplied."
|
|
11068
11170
|
},
|
|
11069
|
-
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
name: "get_content_context_for_project",
|
|
11079
|
-
description: "Aggregate context for a content agent (2026-DASMG-041): recent corpus items, recent evidence packs and related sources for a project (optionally focused by topic or url), plus totals. One call to brief an agent on what already exists before it writes.",
|
|
11080
|
-
inputSchema: {
|
|
11081
|
-
type: "object",
|
|
11082
|
-
properties: {
|
|
11083
|
-
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
11084
|
-
topic: { type: "string", description: "Optional topic focus." },
|
|
11085
|
-
url: { type: "string", description: "Optional url focus." },
|
|
11086
|
-
limit: { type: "integer", description: "Max items per section (default 10)." }
|
|
11171
|
+
domain: { type: "string", description: "action=list: domain filter." },
|
|
11172
|
+
query: { type: "string", description: "action=list: free-text search." },
|
|
11173
|
+
topic: { type: "string", description: "action=context: optional topic focus." },
|
|
11174
|
+
limit: {
|
|
11175
|
+
type: "integer",
|
|
11176
|
+
description: "action=list: max rows (default 20). action=context: max items per section (default 10)."
|
|
11177
|
+
},
|
|
11178
|
+
offset: { type: "integer", description: "action=list: pagination offset." },
|
|
11179
|
+
includeFullText: { type: "boolean", description: "action=list: return full_text per row." }
|
|
11087
11180
|
},
|
|
11088
|
-
required: ["projectKey"]
|
|
11181
|
+
required: ["action", "projectKey"]
|
|
11089
11182
|
}
|
|
11090
11183
|
},
|
|
11091
11184
|
{
|
|
@@ -11116,19 +11209,31 @@ var TOOLS = [
|
|
|
11116
11209
|
required: ["tool"]
|
|
11117
11210
|
}
|
|
11118
11211
|
},
|
|
11212
|
+
// ----- Content snapshots / version history (2026-DASMG-041), consolidated -----
|
|
11119
11213
|
{
|
|
11120
|
-
name: "
|
|
11121
|
-
description:
|
|
11214
|
+
name: "content-snapshot",
|
|
11215
|
+
description: 'Historical version snapshots of corpus items / sources (2026-DASMG-041). Pick the operation with `action`:\n- "list": list snapshots for a corpus item or source by id, url or canonical url, newest first. Compact; set `includeFullText` for the captured body. Use to see how content changed over time.\n- "prune": retention cleanup \u2014 keep the newest N snapshots per item and drop the rest older than the retention window (defaults keepPerItem 20, olderThanDays 365). Returns how many were pruned.',
|
|
11122
11216
|
inputSchema: {
|
|
11123
11217
|
type: "object",
|
|
11124
11218
|
properties: {
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11219
|
+
action: {
|
|
11220
|
+
type: "string",
|
|
11221
|
+
enum: ["list", "prune"],
|
|
11222
|
+
description: "list = show snapshots; prune = retention cleanup."
|
|
11223
|
+
},
|
|
11224
|
+
corpusItemId: { type: "string", description: "action=list: content_corpus_items.id." },
|
|
11225
|
+
contentSourceId: { type: "string", description: "action=list: content_sources.id." },
|
|
11226
|
+
url: { type: "string", description: "action=list: URL to match." },
|
|
11227
|
+
canonicalUrl: { type: "string", description: "action=list: canonical URL to match." },
|
|
11228
|
+
limit: { type: "integer", description: "action=list: max rows (default 20)." },
|
|
11229
|
+
includeFullText: { type: "boolean", description: "action=list: return captured full_text." },
|
|
11230
|
+
keepPerItem: { type: "integer", description: "action=prune: snapshots to keep per item (default 20)." },
|
|
11231
|
+
olderThanDays: {
|
|
11232
|
+
type: "integer",
|
|
11233
|
+
description: "action=prune: only prune snapshots older than this many days (default 365)."
|
|
11234
|
+
}
|
|
11235
|
+
},
|
|
11236
|
+
required: ["action"]
|
|
11132
11237
|
}
|
|
11133
11238
|
},
|
|
11134
11239
|
{
|
|
@@ -11143,31 +11248,41 @@ var TOOLS = [
|
|
|
11143
11248
|
required: ["packId", "ticketNumber"]
|
|
11144
11249
|
}
|
|
11145
11250
|
},
|
|
11251
|
+
// ----- Content/site candidates (2026-DASMG-041), consolidated -----
|
|
11146
11252
|
{
|
|
11147
|
-
name: "
|
|
11148
|
-
description:
|
|
11253
|
+
name: "site-candidate",
|
|
11254
|
+
description: 'Which site(s) a stored source/pack could be used on (2026-DASMG-041). Pick the operation with `action`:\n- "add": record candidates \u2014 attach one or more to a contentSourceId and/or packId; each candidate targets a managedSiteId (preferred) or projectKey/domain. Re-adding the same (subject, target) updates instead of duplicating. Required: candidates.\n- "list": list candidates, filter by managedSiteId / projectKey / domain, contentSourceId, packId, status. Use status=candidate to find unused content suggested for a site. Compact, paginated; includes source title / pack topic.\n- "set-status": update a candidate\'s status. status=used marks it published on the site (stamps usedAt + usedCorpusItemId / usedUrl); rejected = not a fit. Required: id, status.',
|
|
11149
11255
|
inputSchema: {
|
|
11150
11256
|
type: "object",
|
|
11151
11257
|
properties: {
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
description: "
|
|
11156
|
-
}
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11258
|
+
action: {
|
|
11259
|
+
type: "string",
|
|
11260
|
+
enum: ["add", "list", "set-status"],
|
|
11261
|
+
description: "add = record candidates; list = list candidates; set-status = update one candidate."
|
|
11262
|
+
},
|
|
11263
|
+
id: { type: "string", description: "action=set-status: content_site_candidates.id (uuid)." },
|
|
11264
|
+
status: {
|
|
11265
|
+
type: "string",
|
|
11266
|
+
enum: ["candidate", "used", "rejected"],
|
|
11267
|
+
description: "action=set-status: new status (required). action=list: filter \u2014 candidate = suggested, used = published, rejected = not a fit."
|
|
11268
|
+
},
|
|
11269
|
+
usedCorpusItemId: {
|
|
11270
|
+
type: "string",
|
|
11271
|
+
description: "action=set-status: content_corpus_items.id of the page produced (when used)."
|
|
11272
|
+
},
|
|
11273
|
+
usedUrl: { type: "string", description: "action=set-status: URL of the page produced (when used)." },
|
|
11274
|
+
reason: { type: "string", description: "action=set-status: optional note for the status change." },
|
|
11275
|
+
contentSourceId: {
|
|
11276
|
+
type: "string",
|
|
11277
|
+
description: "action=add: the source to suggest. action=list: filter."
|
|
11278
|
+
},
|
|
11279
|
+
packId: {
|
|
11280
|
+
type: "string",
|
|
11281
|
+
description: "action=add: content_evidence_packs.id to suggest. action=list: filter."
|
|
11282
|
+
},
|
|
11168
11283
|
candidates: {
|
|
11169
11284
|
type: "array",
|
|
11170
|
-
description: "
|
|
11285
|
+
description: "action=add: target sites. Each needs a managedSiteId, projectKey or domain.",
|
|
11171
11286
|
items: {
|
|
11172
11287
|
type: "object",
|
|
11173
11288
|
properties: {
|
|
@@ -11180,66 +11295,44 @@ var TOOLS = [
|
|
|
11180
11295
|
}
|
|
11181
11296
|
}
|
|
11182
11297
|
},
|
|
11183
|
-
|
|
11298
|
+
managedSiteId: { type: "string", description: "action=list: managed_sites.id filter." },
|
|
11299
|
+
projectKey: { type: "string", description: "action=list: project filter." },
|
|
11300
|
+
domain: { type: "string", description: "action=list: domain filter." },
|
|
11301
|
+
limit: { type: "integer", description: "action=list: max rows (default 20, max 100)." },
|
|
11302
|
+
offset: { type: "integer", description: "action=list: pagination offset." },
|
|
11303
|
+
ticketNumber: { type: "string", description: "action=add: related ticket number." },
|
|
11184
11304
|
metadata: { type: "object" }
|
|
11185
11305
|
},
|
|
11186
|
-
required: ["
|
|
11306
|
+
required: ["action"]
|
|
11187
11307
|
}
|
|
11188
11308
|
},
|
|
11309
|
+
// ----- Managed sites (2026-DASMG-042), consolidated -----
|
|
11189
11310
|
{
|
|
11190
|
-
name: "
|
|
11191
|
-
description:
|
|
11311
|
+
name: "managed-site",
|
|
11312
|
+
description: 'Sites registered for content sync (2026-DASMG-042). Pick the operation with `action`:\n- "register": register (or upsert by projectKey+domain) a site: domain, optional repo/branch + sitemapUrl, syncMode and a git pathUrlMap. Optional + additive \u2014 external/unmanaged sites still work via extract_article without this. Enables git-push/cron/manual ingestion that re-indexes only CHANGED pages. Required: projectKey, domain.\n- "update": update an existing site by id \u2014 only provided fields change (enable/disable, switch syncMode, set sitemapUrl/pathUrlMap, \u2026). Required: id.\n- "list": list managed sites, filter by projectKey/domain/repo/enabledOnly. Compact, paginated.\nTrigger/poll ingestion runs with the `content-sync` tool.',
|
|
11192
11313
|
inputSchema: {
|
|
11193
11314
|
type: "object",
|
|
11194
11315
|
properties: {
|
|
11195
|
-
|
|
11196
|
-
projectKey: { type: "string", description: "Project filter." },
|
|
11197
|
-
domain: { type: "string", description: "Domain filter." },
|
|
11198
|
-
contentSourceId: { type: "string", description: "content_sources.id filter." },
|
|
11199
|
-
packId: { type: "string", description: "content_evidence_packs.id filter." },
|
|
11200
|
-
status: {
|
|
11316
|
+
action: {
|
|
11201
11317
|
type: "string",
|
|
11202
|
-
enum: ["
|
|
11203
|
-
description: "
|
|
11318
|
+
enum: ["register", "update", "list"],
|
|
11319
|
+
description: "register = upsert a site; update = patch a site by id; list = list sites."
|
|
11204
11320
|
},
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
}
|
|
11208
|
-
}
|
|
11209
|
-
},
|
|
11210
|
-
{
|
|
11211
|
-
name: "set_candidate_status",
|
|
11212
|
-
description: "Update a candidate's status (2026-DASMG-041). Set status=used to mark the content as used on the site (stamps usedAt and records usedCorpusItemId / usedUrl), or rejected when it is not a fit.",
|
|
11213
|
-
inputSchema: {
|
|
11214
|
-
type: "object",
|
|
11215
|
-
properties: {
|
|
11216
|
-
id: { type: "string", description: "content_site_candidates.id (uuid)." },
|
|
11217
|
-
status: {
|
|
11321
|
+
id: { type: "string", description: "action=update: managed_sites.id (uuid, required)." },
|
|
11322
|
+
projectKey: {
|
|
11218
11323
|
type: "string",
|
|
11219
|
-
|
|
11220
|
-
description: "New status for the candidate."
|
|
11324
|
+
description: "action=register (required): project identifier. action=list: filter."
|
|
11221
11325
|
},
|
|
11222
|
-
|
|
11326
|
+
domain: {
|
|
11223
11327
|
type: "string",
|
|
11224
|
-
description: "
|
|
11328
|
+
description: "action=register (required): site domain, e.g. example.nl. action=list: filter."
|
|
11225
11329
|
},
|
|
11226
|
-
|
|
11227
|
-
reason: { type: "string", description: "Optional note for the status change." }
|
|
11228
|
-
},
|
|
11229
|
-
required: ["id", "status"]
|
|
11230
|
-
}
|
|
11231
|
-
},
|
|
11232
|
-
{
|
|
11233
|
-
name: "register_managed_site",
|
|
11234
|
-
description: "Register (or upsert by projectKey+domain) a site for content sync (2026-DASMG-042): domain, optional repo/branch + sitemapUrl, syncMode and a git pathUrlMap. Optional + additive \u2014 external/unmanaged sites still work via extract_article without this. Enables git-push/cron/manual ingestion that re-indexes only CHANGED pages into the corpus (hash-skips unchanged ones).",
|
|
11235
|
-
inputSchema: {
|
|
11236
|
-
type: "object",
|
|
11237
|
-
properties: {
|
|
11238
|
-
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
11239
|
-
domain: { type: "string", description: "Site domain, e.g. example.nl (required)." },
|
|
11240
|
-
siteKey: { type: "string", description: "Optional site identifier within the project." },
|
|
11330
|
+
siteKey: { type: "string", description: "Site identifier within the project." },
|
|
11241
11331
|
name: { type: "string", description: "Human-friendly site name." },
|
|
11242
|
-
repo: {
|
|
11332
|
+
repo: {
|
|
11333
|
+
type: "string",
|
|
11334
|
+
description: "GitHub repo owner/repo that publishes the site (for git pushes). action=list: filter."
|
|
11335
|
+
},
|
|
11243
11336
|
branch: { type: "string", description: "Branch whose pushes trigger a sync; omit for any branch." },
|
|
11244
11337
|
sitemapUrl: { type: "string", description: "Sitemap URL for sitemap/hybrid detection." },
|
|
11245
11338
|
sourceType: {
|
|
@@ -11256,11 +11349,12 @@ var TOOLS = [
|
|
|
11256
11349
|
],
|
|
11257
11350
|
description: "Provenance label for produced corpus items (default own_site)."
|
|
11258
11351
|
},
|
|
11259
|
-
enabled: { type: "boolean", description: "
|
|
11352
|
+
enabled: { type: "boolean", description: "register/update: whether syncs run (default true)." },
|
|
11353
|
+
enabledOnly: { type: "boolean", description: "action=list: only enabled sites." },
|
|
11260
11354
|
syncMode: {
|
|
11261
11355
|
type: "string",
|
|
11262
11356
|
enum: ["sitemap", "git", "hybrid", "manual"],
|
|
11263
|
-
description: "Change detection: sitemap (lastmod/hash), git (changed paths via pathUrlMap), hybrid (both), manual (only
|
|
11357
|
+
description: "Change detection: sitemap (lastmod/hash), git (changed paths via pathUrlMap), hybrid (both), manual (only via content-sync trigger)."
|
|
11264
11358
|
},
|
|
11265
11359
|
pathUrlMap: {
|
|
11266
11360
|
type: "array",
|
|
@@ -11272,106 +11366,49 @@ var TOOLS = [
|
|
|
11272
11366
|
type: "boolean",
|
|
11273
11367
|
description: "Flag changed pages for an MG SEO CLI follow-up (soft handoff)."
|
|
11274
11368
|
},
|
|
11369
|
+
limit: { type: "integer", description: "action=list: max rows (default 50, max 200)." },
|
|
11370
|
+
offset: { type: "integer", description: "action=list: pagination offset." },
|
|
11275
11371
|
metadata: { type: "object" }
|
|
11276
11372
|
},
|
|
11277
|
-
required: ["
|
|
11373
|
+
required: ["action"]
|
|
11278
11374
|
}
|
|
11279
11375
|
},
|
|
11376
|
+
// ----- Content ingestion runs (2026-DASMG-042), consolidated -----
|
|
11280
11377
|
{
|
|
11281
|
-
name: "
|
|
11282
|
-
description:
|
|
11378
|
+
name: "content-sync",
|
|
11379
|
+
description: 'Content ingestion runs for a managed site (2026-DASMG-042). Pick the operation with `action`:\n- "trigger": start a run by siteId, or projectKey+domain. Detects changed pages (sitemap/git/hash), re-indexes only changes into the corpus and snapshots the previous version. Returns the runId; poll with action=status.\n- "status": read run status by runId (that run), or by siteId / projectKey+domain (recent runs). Returns changed/skipped/failed counts, the changed URLs, limitations and the site summary.',
|
|
11283
11380
|
inputSchema: {
|
|
11284
11381
|
type: "object",
|
|
11285
11382
|
properties: {
|
|
11286
|
-
|
|
11287
|
-
name: { type: "string", description: "Human-friendly site name." },
|
|
11288
|
-
domain: { type: "string", description: "Site domain." },
|
|
11289
|
-
siteKey: { type: "string", description: "Site identifier within the project." },
|
|
11290
|
-
repo: { type: "string", description: "GitHub repo owner/repo." },
|
|
11291
|
-
branch: { type: "string", description: "Branch whose pushes trigger a sync." },
|
|
11292
|
-
sitemapUrl: { type: "string", description: "Sitemap URL." },
|
|
11293
|
-
sourceType: {
|
|
11294
|
-
type: "string",
|
|
11295
|
-
enum: [
|
|
11296
|
-
"own_site",
|
|
11297
|
-
"client_site",
|
|
11298
|
-
"external_source",
|
|
11299
|
-
"competitor",
|
|
11300
|
-
"news",
|
|
11301
|
-
"government",
|
|
11302
|
-
"supplier",
|
|
11303
|
-
"unknown"
|
|
11304
|
-
]
|
|
11305
|
-
},
|
|
11306
|
-
enabled: { type: "boolean", description: "Enable/disable syncs." },
|
|
11307
|
-
syncMode: {
|
|
11383
|
+
action: {
|
|
11308
11384
|
type: "string",
|
|
11309
|
-
enum: ["
|
|
11385
|
+
enum: ["trigger", "status"],
|
|
11386
|
+
description: "trigger = start an ingestion run; status = read run status."
|
|
11310
11387
|
},
|
|
11311
|
-
pathUrlMap: { type: "array", items: { type: "object" } },
|
|
11312
|
-
maxUrlsPerRun: { type: "integer", description: "Per-run URL cap." },
|
|
11313
|
-
exportToSeoCli: { type: "boolean" },
|
|
11314
|
-
metadata: { type: "object" }
|
|
11315
|
-
},
|
|
11316
|
-
required: ["id"]
|
|
11317
|
-
}
|
|
11318
|
-
},
|
|
11319
|
-
{
|
|
11320
|
-
name: "list_managed_sites",
|
|
11321
|
-
description: "List managed sites (2026-DASMG-042), filter by projectKey/domain/repo/enabledOnly. Compact, paginated.",
|
|
11322
|
-
inputSchema: {
|
|
11323
|
-
type: "object",
|
|
11324
|
-
properties: {
|
|
11325
|
-
projectKey: { type: "string", description: "Project filter." },
|
|
11326
|
-
domain: { type: "string", description: "Domain filter." },
|
|
11327
|
-
repo: { type: "string", description: "Repo filter (owner/repo)." },
|
|
11328
|
-
enabledOnly: { type: "boolean", description: "Only enabled sites." },
|
|
11329
|
-
limit: { type: "integer", description: "Max rows (default 50, max 200)." },
|
|
11330
|
-
offset: { type: "integer", description: "Pagination offset." }
|
|
11331
|
-
}
|
|
11332
|
-
}
|
|
11333
|
-
},
|
|
11334
|
-
{
|
|
11335
|
-
name: "trigger_content_sync",
|
|
11336
|
-
description: "Start a content ingestion run for a managed site (2026-DASMG-042) by siteId, or projectKey+domain. Detects changed pages (sitemap/git/hash), re-indexes only changes into the corpus and snapshots the previous version. Returns the runId; poll get_content_sync_status for results.",
|
|
11337
|
-
inputSchema: {
|
|
11338
|
-
type: "object",
|
|
11339
|
-
properties: {
|
|
11340
11388
|
siteId: { type: "string", description: "managed_sites.id (uuid)." },
|
|
11341
11389
|
projectKey: { type: "string", description: "Project identifier (with domain)." },
|
|
11342
11390
|
domain: { type: "string", description: "Site domain (with projectKey)." },
|
|
11391
|
+
runId: { type: "string", description: "action=status: content_ingestion_runs.id (uuid)." },
|
|
11343
11392
|
triggerType: {
|
|
11344
11393
|
type: "string",
|
|
11345
11394
|
enum: ["manual", "git_push", "deploy", "cron", "sitemap"],
|
|
11346
|
-
description: "
|
|
11395
|
+
description: "action=trigger: label recorded on the run (default manual)."
|
|
11347
11396
|
},
|
|
11348
|
-
sourceCommit: { type: "string", description: "
|
|
11349
|
-
sourceRef: { type: "string", description: "
|
|
11397
|
+
sourceCommit: { type: "string", description: "action=trigger: source git commit/deploy id, when known." },
|
|
11398
|
+
sourceRef: { type: "string", description: "action=trigger: source git ref, when known." },
|
|
11350
11399
|
urls: {
|
|
11351
11400
|
type: "array",
|
|
11352
11401
|
items: { type: "string" },
|
|
11353
|
-
description: "
|
|
11402
|
+
description: "action=trigger: restrict the run to these URLs (skips detection)."
|
|
11354
11403
|
},
|
|
11355
11404
|
changedPaths: {
|
|
11356
11405
|
type: "array",
|
|
11357
11406
|
items: { type: "string" },
|
|
11358
|
-
description: "
|
|
11359
|
-
}
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
{
|
|
11364
|
-
name: "get_content_sync_status",
|
|
11365
|
-
description: "Read ingestion run status (2026-DASMG-042): by runId (that run), or by siteId / projectKey+domain (recent runs). Returns changed/skipped/failed counts, the changed URLs, limitations and the site summary.",
|
|
11366
|
-
inputSchema: {
|
|
11367
|
-
type: "object",
|
|
11368
|
-
properties: {
|
|
11369
|
-
runId: { type: "string", description: "content_ingestion_runs.id (uuid)." },
|
|
11370
|
-
siteId: { type: "string", description: "managed_sites.id (uuid)." },
|
|
11371
|
-
projectKey: { type: "string", description: "Project identifier (with domain)." },
|
|
11372
|
-
domain: { type: "string", description: "Site domain (with projectKey)." },
|
|
11373
|
-
limit: { type: "integer", description: "Max recent runs to return (default 10)." }
|
|
11374
|
-
}
|
|
11407
|
+
description: "action=trigger: changed repo paths to map to URLs via pathUrlMap."
|
|
11408
|
+
},
|
|
11409
|
+
limit: { type: "integer", description: "action=status: max recent runs to return (default 10)." }
|
|
11410
|
+
},
|
|
11411
|
+
required: ["action"]
|
|
11375
11412
|
}
|
|
11376
11413
|
},
|
|
11377
11414
|
{
|
|
@@ -11478,8 +11515,10 @@ var MCP_VERSION = "7.3.0";
|
|
|
11478
11515
|
async function handleListTools() {
|
|
11479
11516
|
if (!authContext) return { tools: TOOLS };
|
|
11480
11517
|
const allowedTools = authContext.allowedTools;
|
|
11518
|
+
const isSuperadmin = authContext.roleName === "superadmin";
|
|
11481
11519
|
const accessible = TOOLS.filter((tool) => {
|
|
11482
11520
|
if (allowedTools && !allowedTools.includes(tool.name)) return false;
|
|
11521
|
+
if (SUPERADMIN_ONLY_TOOLS.has(tool.name) && !isSuperadmin) return false;
|
|
11483
11522
|
const requiredModule = TOOL_MODULE_MAP[tool.name];
|
|
11484
11523
|
if (!requiredModule) return true;
|
|
11485
11524
|
return authContext.permissions.modules[requiredModule] === true;
|
|
@@ -11502,6 +11541,16 @@ async function handleCallTool(request) {
|
|
|
11502
11541
|
]
|
|
11503
11542
|
};
|
|
11504
11543
|
}
|
|
11544
|
+
if (SUPERADMIN_ONLY_TOOLS.has(name) && authContext.roleName !== "superadmin") {
|
|
11545
|
+
return {
|
|
11546
|
+
content: [
|
|
11547
|
+
{
|
|
11548
|
+
type: "text",
|
|
11549
|
+
text: `Access denied: "${name}" is restricted to superadmins (it manages production secrets).`
|
|
11550
|
+
}
|
|
11551
|
+
]
|
|
11552
|
+
};
|
|
11553
|
+
}
|
|
11505
11554
|
const requiredModule = TOOL_MODULE_MAP[name];
|
|
11506
11555
|
if (requiredModule && authContext.permissions.modules[requiredModule] !== true) {
|
|
11507
11556
|
return {
|
|
@@ -11637,106 +11686,31 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
11637
11686
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11638
11687
|
};
|
|
11639
11688
|
}
|
|
11640
|
-
|
|
11641
|
-
|
|
11642
|
-
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
headers: {
|
|
11653
|
-
"content-type": "application/json",
|
|
11654
|
-
authorization: `Bearer ${apiKey}`
|
|
11655
|
-
},
|
|
11656
|
-
body: JSON.stringify({
|
|
11657
|
-
topic,
|
|
11658
|
-
urls,
|
|
11659
|
-
discover: a.discover && typeof a.discover === "object" ? a.discover : void 0,
|
|
11660
|
-
includeRawText: typeof a.includeRawText === "boolean" ? a.includeRawText : void 0,
|
|
11661
|
-
maxRawTextChars: typeof a.maxRawTextChars === "number" ? a.maxRawTextChars : void 0
|
|
11662
|
-
})
|
|
11663
|
-
});
|
|
11664
|
-
if (!res.ok) {
|
|
11665
|
-
const detail = await res.text().catch(() => "");
|
|
11666
|
-
return {
|
|
11667
|
-
content: [
|
|
11668
|
-
{
|
|
11669
|
-
type: "text",
|
|
11670
|
-
text: `Error: research_topic failed (${res.status}). ${detail.slice(0, 300)}`
|
|
11671
|
-
}
|
|
11672
|
-
]
|
|
11673
|
-
};
|
|
11674
|
-
}
|
|
11675
|
-
const data = await res.json();
|
|
11676
|
-
return {
|
|
11677
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11678
|
-
};
|
|
11679
|
-
}
|
|
11680
|
-
// ----- Content registry / corpus persistence (2026-DASMG-041) -----
|
|
11681
|
-
// All content-registry tools proxy to one dispatcher route; the tool name
|
|
11682
|
-
// is the dispatcher `action` and the args are forwarded verbatim as
|
|
11683
|
-
// `params` (the route validates them with the matching Zod schema).
|
|
11684
|
-
case "save_content_source":
|
|
11685
|
-
case "get_content_source_by_url":
|
|
11686
|
-
case "get_content_source_by_id":
|
|
11687
|
-
case "search_content_sources":
|
|
11688
|
-
case "save_research_pack":
|
|
11689
|
-
case "get_research_pack":
|
|
11690
|
-
case "save_content_corpus_item":
|
|
11691
|
-
case "list_project_content_corpus":
|
|
11692
|
-
case "get_content_context_for_project":
|
|
11689
|
+
// ----- Content family (2026-DASMG-040/041/042), consolidated -----
|
|
11690
|
+
// Action-based tools + the two singletons, mapped to the backend
|
|
11691
|
+
// content-registry / managed-site / research-topic dispatchers by
|
|
11692
|
+
// resolveContentDispatch (the backend still speaks the granular action
|
|
11693
|
+
// names, so no server change is needed).
|
|
11694
|
+
case "research":
|
|
11695
|
+
case "content-source":
|
|
11696
|
+
case "content-corpus":
|
|
11697
|
+
case "content-snapshot":
|
|
11698
|
+
case "site-candidate":
|
|
11699
|
+
case "managed-site":
|
|
11700
|
+
case "content-sync":
|
|
11693
11701
|
case "save_content_quality_run":
|
|
11694
|
-
case "
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
case "list_site_candidates":
|
|
11699
|
-
case "set_candidate_status": {
|
|
11700
|
-
const res = await fetch(`${dashboardBaseUrl}/api/tools/content-registry`, {
|
|
11701
|
-
method: "POST",
|
|
11702
|
-
headers: {
|
|
11703
|
-
"content-type": "application/json",
|
|
11704
|
-
authorization: `Bearer ${apiKey}`
|
|
11705
|
-
},
|
|
11706
|
-
body: JSON.stringify({ action: name, params: a })
|
|
11707
|
-
});
|
|
11708
|
-
if (!res.ok) {
|
|
11709
|
-
const detail = await res.text().catch(() => "");
|
|
11710
|
-
return {
|
|
11711
|
-
content: [
|
|
11712
|
-
{
|
|
11713
|
-
type: "text",
|
|
11714
|
-
text: `Error: ${name} failed (${res.status}). ${detail.slice(0, 300)}`
|
|
11715
|
-
}
|
|
11716
|
-
]
|
|
11717
|
-
};
|
|
11702
|
+
case "link_content_pack_to_ticket": {
|
|
11703
|
+
const dispatch = resolveContentDispatch(name, a);
|
|
11704
|
+
if ("error" in dispatch) {
|
|
11705
|
+
return { content: [{ type: "text", text: `Error: ${dispatch.error}` }] };
|
|
11718
11706
|
}
|
|
11719
|
-
const
|
|
11720
|
-
return {
|
|
11721
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11722
|
-
};
|
|
11723
|
-
}
|
|
11724
|
-
// ----- Managed site content sync (2026-DASMG-042) -----
|
|
11725
|
-
// The 5 PUBLIC managed-site tools proxy to one dispatcher route; the tool
|
|
11726
|
-
// name is the dispatcher `action`, args forwarded verbatim as `params`
|
|
11727
|
-
// (the route validates them and only allows the public actions).
|
|
11728
|
-
case "register_managed_site":
|
|
11729
|
-
case "update_managed_site":
|
|
11730
|
-
case "list_managed_sites":
|
|
11731
|
-
case "trigger_content_sync":
|
|
11732
|
-
case "get_content_sync_status": {
|
|
11733
|
-
const res = await fetch(`${dashboardBaseUrl}/api/tools/managed-site`, {
|
|
11707
|
+
const res = await fetch(`${dashboardBaseUrl}/api/tools/${dispatch.route}`, {
|
|
11734
11708
|
method: "POST",
|
|
11735
11709
|
headers: {
|
|
11736
11710
|
"content-type": "application/json",
|
|
11737
11711
|
authorization: `Bearer ${apiKey}`
|
|
11738
11712
|
},
|
|
11739
|
-
body: JSON.stringify(
|
|
11713
|
+
body: JSON.stringify(dispatch.body)
|
|
11740
11714
|
});
|
|
11741
11715
|
if (!res.ok) {
|
|
11742
11716
|
const detail = await res.text().catch(() => "");
|
|
@@ -14775,5 +14749,3 @@ main().catch((err) => {
|
|
|
14775
14749
|
console.error("Fatal error:", err);
|
|
14776
14750
|
process.exit(1);
|
|
14777
14751
|
});
|
|
14778
|
-
//# sourceMappingURL=index.js.map
|
|
14779
|
-
//# sourceMappingURL=index.js.map
|