@contextstream/mcp-server 0.3.53 → 0.3.55
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 +6 -0
- package/dist/index.js +257 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,9 +28,15 @@ No special syntax. No commands to learn. Just ask.
|
|
|
28
28
|
- Memory capture and recall (decisions, preferences, tasks, bugs, lessons)
|
|
29
29
|
- Code search (semantic, hybrid, keyword, pattern)
|
|
30
30
|
- Knowledge graph and code analysis (dependencies, impact, call paths, circular deps, unused code)
|
|
31
|
+
- Graph ingestion for full graph builds (`graph_ingest`)
|
|
31
32
|
- Local repo ingestion for indexing (`projects_ingest_local`)
|
|
32
33
|
- Auto-context: on the first tool call in a new session, the server can auto-initialize context
|
|
33
34
|
|
|
35
|
+
## Graph tiers
|
|
36
|
+
|
|
37
|
+
- **Pro (Graph-Lite):** module-level import graph, dependencies, and 1-hop impact
|
|
38
|
+
- **Elite/Team (Full Graph):** module + call + dataflow + type layers, plus `graph_ingest`
|
|
39
|
+
|
|
34
40
|
## Requirements
|
|
35
41
|
|
|
36
42
|
- Node.js 18+
|
package/dist/index.js
CHANGED
|
@@ -4701,6 +4701,8 @@ import { createRequire } from "module";
|
|
|
4701
4701
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
4702
4702
|
import { homedir } from "os";
|
|
4703
4703
|
import { join as join3 } from "path";
|
|
4704
|
+
var UPGRADE_COMMAND = "npm update -g @contextstream/mcp-server";
|
|
4705
|
+
var NPM_LATEST_URL = "https://registry.npmjs.org/@contextstream/mcp-server/latest";
|
|
4704
4706
|
function getVersion() {
|
|
4705
4707
|
try {
|
|
4706
4708
|
const require2 = createRequire(import.meta.url);
|
|
@@ -4724,6 +4726,7 @@ function compareVersions(v1, v2) {
|
|
|
4724
4726
|
return 0;
|
|
4725
4727
|
}
|
|
4726
4728
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4729
|
+
var latestVersionPromise = null;
|
|
4727
4730
|
function getCacheFilePath() {
|
|
4728
4731
|
return join3(homedir(), ".contextstream", "version-cache.json");
|
|
4729
4732
|
}
|
|
@@ -4752,33 +4755,40 @@ function writeCache(latestVersion) {
|
|
|
4752
4755
|
} catch {
|
|
4753
4756
|
}
|
|
4754
4757
|
}
|
|
4755
|
-
async function
|
|
4756
|
-
const currentVersion = VERSION;
|
|
4757
|
-
if (currentVersion === "unknown") return;
|
|
4758
|
+
async function fetchLatestVersion() {
|
|
4758
4759
|
try {
|
|
4759
|
-
const cached = readCache();
|
|
4760
|
-
if (cached) {
|
|
4761
|
-
if (compareVersions(currentVersion, cached.latestVersion) < 0) {
|
|
4762
|
-
showUpdateWarning(currentVersion, cached.latestVersion);
|
|
4763
|
-
}
|
|
4764
|
-
return;
|
|
4765
|
-
}
|
|
4766
4760
|
const controller = new AbortController();
|
|
4767
4761
|
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
4768
|
-
const response = await fetch(
|
|
4762
|
+
const response = await fetch(NPM_LATEST_URL, {
|
|
4769
4763
|
signal: controller.signal,
|
|
4770
4764
|
headers: { "Accept": "application/json" }
|
|
4771
4765
|
});
|
|
4772
4766
|
clearTimeout(timeout);
|
|
4773
|
-
if (!response.ok) return;
|
|
4767
|
+
if (!response.ok) return null;
|
|
4774
4768
|
const data = await response.json();
|
|
4775
|
-
|
|
4776
|
-
if (typeof latestVersion !== "string") return;
|
|
4777
|
-
writeCache(latestVersion);
|
|
4778
|
-
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
4779
|
-
showUpdateWarning(currentVersion, latestVersion);
|
|
4780
|
-
}
|
|
4769
|
+
return typeof data.version === "string" ? data.version : null;
|
|
4781
4770
|
} catch {
|
|
4771
|
+
return null;
|
|
4772
|
+
}
|
|
4773
|
+
}
|
|
4774
|
+
async function resolveLatestVersion() {
|
|
4775
|
+
const cached = readCache();
|
|
4776
|
+
if (cached) return cached.latestVersion;
|
|
4777
|
+
if (!latestVersionPromise) {
|
|
4778
|
+
latestVersionPromise = fetchLatestVersion().finally(() => {
|
|
4779
|
+
latestVersionPromise = null;
|
|
4780
|
+
});
|
|
4781
|
+
}
|
|
4782
|
+
const latestVersion = await latestVersionPromise;
|
|
4783
|
+
if (latestVersion) {
|
|
4784
|
+
writeCache(latestVersion);
|
|
4785
|
+
}
|
|
4786
|
+
return latestVersion;
|
|
4787
|
+
}
|
|
4788
|
+
async function checkForUpdates() {
|
|
4789
|
+
const notice = await getUpdateNotice();
|
|
4790
|
+
if (notice?.behind) {
|
|
4791
|
+
showUpdateWarning(notice.current, notice.latest);
|
|
4782
4792
|
}
|
|
4783
4793
|
}
|
|
4784
4794
|
function showUpdateWarning(currentVersion, latestVersion) {
|
|
@@ -4786,12 +4796,30 @@ function showUpdateWarning(currentVersion, latestVersion) {
|
|
|
4786
4796
|
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
4787
4797
|
console.error(`\u26A0\uFE0F Update available: v${currentVersion} \u2192 v${latestVersion}`);
|
|
4788
4798
|
console.error("");
|
|
4789
|
-
console.error(
|
|
4799
|
+
console.error(` Run: ${UPGRADE_COMMAND}`);
|
|
4790
4800
|
console.error("");
|
|
4791
4801
|
console.error(" Then restart your AI tool to use the new version.");
|
|
4792
4802
|
console.error("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
4793
4803
|
console.error("");
|
|
4794
4804
|
}
|
|
4805
|
+
async function getUpdateNotice() {
|
|
4806
|
+
const currentVersion = VERSION;
|
|
4807
|
+
if (currentVersion === "unknown") return null;
|
|
4808
|
+
try {
|
|
4809
|
+
const latestVersion = await resolveLatestVersion();
|
|
4810
|
+
if (!latestVersion) return null;
|
|
4811
|
+
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
4812
|
+
return {
|
|
4813
|
+
current: currentVersion,
|
|
4814
|
+
latest: latestVersion,
|
|
4815
|
+
behind: true,
|
|
4816
|
+
upgrade_command: UPGRADE_COMMAND
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4819
|
+
} catch {
|
|
4820
|
+
}
|
|
4821
|
+
return null;
|
|
4822
|
+
}
|
|
4795
4823
|
|
|
4796
4824
|
// src/client.ts
|
|
4797
4825
|
var uuidSchema = external_exports.string().uuid();
|
|
@@ -4826,6 +4854,19 @@ function normalizeNodeType(input) {
|
|
|
4826
4854
|
);
|
|
4827
4855
|
}
|
|
4828
4856
|
}
|
|
4857
|
+
function pickString(value) {
|
|
4858
|
+
if (typeof value !== "string") return null;
|
|
4859
|
+
const trimmed = value.trim();
|
|
4860
|
+
return trimmed ? trimmed : null;
|
|
4861
|
+
}
|
|
4862
|
+
function normalizeGraphTier(value) {
|
|
4863
|
+
const normalized = value.trim().toLowerCase();
|
|
4864
|
+
if (!normalized) return null;
|
|
4865
|
+
if (normalized.includes("full") || normalized.includes("elite") || normalized.includes("team")) return "full";
|
|
4866
|
+
if (normalized.includes("lite") || normalized.includes("light") || normalized.includes("basic") || normalized.includes("module")) return "lite";
|
|
4867
|
+
if (normalized.includes("none") || normalized.includes("off") || normalized.includes("disabled") || normalized.includes("free")) return "none";
|
|
4868
|
+
return null;
|
|
4869
|
+
}
|
|
4829
4870
|
var AI_PLAN_TIMEOUT_MS = 5e4;
|
|
4830
4871
|
var AI_PLAN_RETRIES = 0;
|
|
4831
4872
|
var ContextStreamClient = class {
|
|
@@ -4920,6 +4961,26 @@ var ContextStreamClient = class {
|
|
|
4920
4961
|
return null;
|
|
4921
4962
|
}
|
|
4922
4963
|
}
|
|
4964
|
+
async getGraphTier() {
|
|
4965
|
+
try {
|
|
4966
|
+
const balance = await this.getCreditBalance();
|
|
4967
|
+
const plan = balance?.plan ?? {};
|
|
4968
|
+
const features = plan?.features ?? {};
|
|
4969
|
+
const tierCandidate = pickString(plan.graph_tier) || pickString(plan.graphTier) || pickString(features.graph_tier) || pickString(features.graphTier) || pickString(balance?.graph_tier) || pickString(balance?.graphTier);
|
|
4970
|
+
const normalizedTier = tierCandidate ? normalizeGraphTier(tierCandidate) : null;
|
|
4971
|
+
if (normalizedTier) return normalizedTier;
|
|
4972
|
+
const planName = pickString(plan.name)?.toLowerCase() ?? null;
|
|
4973
|
+
if (!planName) return "none";
|
|
4974
|
+
if (planName.includes("elite") || planName.includes("team") || planName.includes("enterprise") || planName.includes("business")) {
|
|
4975
|
+
return "full";
|
|
4976
|
+
}
|
|
4977
|
+
if (planName.includes("pro")) return "lite";
|
|
4978
|
+
if (planName.includes("free")) return "none";
|
|
4979
|
+
return "lite";
|
|
4980
|
+
} catch {
|
|
4981
|
+
return "none";
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4923
4984
|
// Workspaces & Projects
|
|
4924
4985
|
listWorkspaces(params) {
|
|
4925
4986
|
const query = new URLSearchParams();
|
|
@@ -6611,6 +6672,11 @@ W:${wsHint}
|
|
|
6611
6672
|
[/CTX]`;
|
|
6612
6673
|
candidateContext = context;
|
|
6613
6674
|
}
|
|
6675
|
+
let versionNotice = null;
|
|
6676
|
+
try {
|
|
6677
|
+
versionNotice = await getUpdateNotice();
|
|
6678
|
+
} catch {
|
|
6679
|
+
}
|
|
6614
6680
|
this.trackTokenSavings({
|
|
6615
6681
|
tool: "context_smart",
|
|
6616
6682
|
workspace_id: withDefaults.workspace_id,
|
|
@@ -6631,6 +6697,7 @@ W:${wsHint}
|
|
|
6631
6697
|
token_estimate: Math.ceil(context.length / 4),
|
|
6632
6698
|
format,
|
|
6633
6699
|
sources_used: items.filter((i) => context.includes(i.value.slice(0, 20))).length,
|
|
6700
|
+
...versionNotice ? { version_notice: versionNotice } : {},
|
|
6634
6701
|
...errors.length > 0 && { errors }
|
|
6635
6702
|
// Include errors for debugging
|
|
6636
6703
|
};
|
|
@@ -7939,6 +8006,35 @@ function toStructured(data) {
|
|
|
7939
8006
|
}
|
|
7940
8007
|
return void 0;
|
|
7941
8008
|
}
|
|
8009
|
+
function readStatNumber(payload, key) {
|
|
8010
|
+
if (!payload || typeof payload !== "object") return void 0;
|
|
8011
|
+
const direct = payload[key];
|
|
8012
|
+
if (typeof direct === "number") return direct;
|
|
8013
|
+
const nested = payload.data;
|
|
8014
|
+
if (nested && typeof nested === "object") {
|
|
8015
|
+
const nestedValue = nested[key];
|
|
8016
|
+
if (typeof nestedValue === "number") return nestedValue;
|
|
8017
|
+
}
|
|
8018
|
+
return void 0;
|
|
8019
|
+
}
|
|
8020
|
+
function estimateGraphIngestMinutes(stats) {
|
|
8021
|
+
const totalFiles = readStatNumber(stats, "total_files");
|
|
8022
|
+
const totalLines = readStatNumber(stats, "total_lines");
|
|
8023
|
+
if (!totalFiles && !totalLines) return null;
|
|
8024
|
+
const fileScore = totalFiles ? totalFiles / 1e3 : 0;
|
|
8025
|
+
const lineScore = totalLines ? totalLines / 5e4 : 0;
|
|
8026
|
+
const sizeScore = Math.max(fileScore, lineScore);
|
|
8027
|
+
const minMinutes = Math.min(45, Math.max(1, Math.round(1 + sizeScore * 1.5)));
|
|
8028
|
+
const maxMinutes = Math.min(60, Math.max(minMinutes + 1, Math.round(2 + sizeScore * 3)));
|
|
8029
|
+
const basisParts = [];
|
|
8030
|
+
if (totalFiles) basisParts.push(`${totalFiles.toLocaleString()} files`);
|
|
8031
|
+
if (totalLines) basisParts.push(`${totalLines.toLocaleString()} lines`);
|
|
8032
|
+
return {
|
|
8033
|
+
min: minMinutes,
|
|
8034
|
+
max: maxMinutes,
|
|
8035
|
+
basis: basisParts.length > 0 ? basisParts.join(" / ") : void 0
|
|
8036
|
+
};
|
|
8037
|
+
}
|
|
7942
8038
|
function normalizeLessonField(value) {
|
|
7943
8039
|
return value.trim().toLowerCase().replace(/\s+/g, " ");
|
|
7944
8040
|
}
|
|
@@ -8013,6 +8109,9 @@ function registerTools(server, client, sessionManager) {
|
|
|
8013
8109
|
return proTools.has(toolName) ? "pro" : "free";
|
|
8014
8110
|
}
|
|
8015
8111
|
function getToolAccessLabel(toolName) {
|
|
8112
|
+
const graphTier = graphToolTiers.get(toolName);
|
|
8113
|
+
if (graphTier === "lite") return "Pro (Graph-Lite)";
|
|
8114
|
+
if (graphTier === "full") return "Elite/Team (Full Graph)";
|
|
8016
8115
|
return getToolAccessTier(toolName) === "pro" ? "PRO" : "Free";
|
|
8017
8116
|
}
|
|
8018
8117
|
async function gateIfProTool(toolName) {
|
|
@@ -8026,6 +8125,94 @@ function registerTools(server, client, sessionManager) {
|
|
|
8026
8125
|
].join("\n")
|
|
8027
8126
|
);
|
|
8028
8127
|
}
|
|
8128
|
+
const graphToolTiers = /* @__PURE__ */ new Map([
|
|
8129
|
+
["graph_dependencies", "lite"],
|
|
8130
|
+
["graph_impact", "lite"],
|
|
8131
|
+
["graph_related", "full"],
|
|
8132
|
+
["graph_decisions", "full"],
|
|
8133
|
+
["graph_path", "full"],
|
|
8134
|
+
["graph_call_path", "full"],
|
|
8135
|
+
["graph_circular_dependencies", "full"],
|
|
8136
|
+
["graph_unused_code", "full"],
|
|
8137
|
+
["graph_ingest", "full"],
|
|
8138
|
+
["graph_contradictions", "full"]
|
|
8139
|
+
]);
|
|
8140
|
+
const graphLiteMaxDepth = 1;
|
|
8141
|
+
function normalizeGraphTargetType(value) {
|
|
8142
|
+
return String(value ?? "").trim().toLowerCase();
|
|
8143
|
+
}
|
|
8144
|
+
function isModuleTargetType(value) {
|
|
8145
|
+
return value === "module" || value === "file" || value === "path";
|
|
8146
|
+
}
|
|
8147
|
+
function graphLiteConstraintError(toolName, detail) {
|
|
8148
|
+
return errorResult(
|
|
8149
|
+
[
|
|
8150
|
+
`Access denied: \`${toolName}\` is limited to Graph-Lite (module-level, 1-hop queries).`,
|
|
8151
|
+
detail,
|
|
8152
|
+
`Upgrade to Elite or Team for full graph access: ${upgradeUrl}`
|
|
8153
|
+
].join("\n")
|
|
8154
|
+
);
|
|
8155
|
+
}
|
|
8156
|
+
async function gateIfGraphTool(toolName, input) {
|
|
8157
|
+
const requiredTier = graphToolTiers.get(toolName);
|
|
8158
|
+
if (!requiredTier) return null;
|
|
8159
|
+
const graphTier = await client.getGraphTier();
|
|
8160
|
+
if (graphTier === "full") return null;
|
|
8161
|
+
if (graphTier === "lite") {
|
|
8162
|
+
if (requiredTier === "full") {
|
|
8163
|
+
return errorResult(
|
|
8164
|
+
[
|
|
8165
|
+
`Access denied: \`${toolName}\` requires Elite or Team (Full Graph).`,
|
|
8166
|
+
"Pro includes Graph-Lite (module-level dependencies and 1-hop impact only).",
|
|
8167
|
+
`Upgrade: ${upgradeUrl}`
|
|
8168
|
+
].join("\n")
|
|
8169
|
+
);
|
|
8170
|
+
}
|
|
8171
|
+
if (toolName === "graph_dependencies") {
|
|
8172
|
+
const targetType = normalizeGraphTargetType(input?.target?.type);
|
|
8173
|
+
if (!isModuleTargetType(targetType)) {
|
|
8174
|
+
return graphLiteConstraintError(
|
|
8175
|
+
toolName,
|
|
8176
|
+
"Set target.type to module, file, or path."
|
|
8177
|
+
);
|
|
8178
|
+
}
|
|
8179
|
+
if (typeof input?.max_depth === "number" && input.max_depth > graphLiteMaxDepth) {
|
|
8180
|
+
return graphLiteConstraintError(
|
|
8181
|
+
toolName,
|
|
8182
|
+
`Set max_depth to ${graphLiteMaxDepth} or lower.`
|
|
8183
|
+
);
|
|
8184
|
+
}
|
|
8185
|
+
if (input?.include_transitive === true) {
|
|
8186
|
+
return graphLiteConstraintError(
|
|
8187
|
+
toolName,
|
|
8188
|
+
"Set include_transitive to false."
|
|
8189
|
+
);
|
|
8190
|
+
}
|
|
8191
|
+
}
|
|
8192
|
+
if (toolName === "graph_impact") {
|
|
8193
|
+
const targetType = normalizeGraphTargetType(input?.target?.type);
|
|
8194
|
+
if (!isModuleTargetType(targetType)) {
|
|
8195
|
+
return graphLiteConstraintError(
|
|
8196
|
+
toolName,
|
|
8197
|
+
"Set target.type to module, file, or path."
|
|
8198
|
+
);
|
|
8199
|
+
}
|
|
8200
|
+
if (typeof input?.max_depth === "number" && input.max_depth > graphLiteMaxDepth) {
|
|
8201
|
+
return graphLiteConstraintError(
|
|
8202
|
+
toolName,
|
|
8203
|
+
`Set max_depth to ${graphLiteMaxDepth} or lower.`
|
|
8204
|
+
);
|
|
8205
|
+
}
|
|
8206
|
+
}
|
|
8207
|
+
return null;
|
|
8208
|
+
}
|
|
8209
|
+
return errorResult(
|
|
8210
|
+
[
|
|
8211
|
+
`Access denied: \`${toolName}\` requires ContextStream Pro (Graph-Lite) or Elite/Team (Full Graph).`,
|
|
8212
|
+
`Upgrade: ${upgradeUrl}`
|
|
8213
|
+
].join("\n")
|
|
8214
|
+
);
|
|
8215
|
+
}
|
|
8029
8216
|
function wrapWithAutoContext(toolName, handler) {
|
|
8030
8217
|
if (!sessionManager) {
|
|
8031
8218
|
return handler;
|
|
@@ -8058,12 +8245,13 @@ function registerTools(server, client, sessionManager) {
|
|
|
8058
8245
|
return;
|
|
8059
8246
|
}
|
|
8060
8247
|
const accessLabel = getToolAccessLabel(name);
|
|
8248
|
+
const showUpgrade = accessLabel !== "Free";
|
|
8061
8249
|
const labeledConfig = {
|
|
8062
8250
|
...config,
|
|
8063
8251
|
title: `${config.title} (${accessLabel})`,
|
|
8064
8252
|
description: `${config.description}
|
|
8065
8253
|
|
|
8066
|
-
Access: ${accessLabel}${
|
|
8254
|
+
Access: ${accessLabel}${showUpgrade ? ` (upgrade: ${upgradeUrl})` : ""}`
|
|
8067
8255
|
};
|
|
8068
8256
|
const safeHandler = async (input) => {
|
|
8069
8257
|
try {
|
|
@@ -8545,6 +8733,8 @@ Access: Free`,
|
|
|
8545
8733
|
})
|
|
8546
8734
|
},
|
|
8547
8735
|
async (input) => {
|
|
8736
|
+
const gate = await gateIfGraphTool("graph_related", input);
|
|
8737
|
+
if (gate) return gate;
|
|
8548
8738
|
const result = await client.graphRelated(input);
|
|
8549
8739
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8550
8740
|
}
|
|
@@ -8562,6 +8752,8 @@ Access: Free`,
|
|
|
8562
8752
|
})
|
|
8563
8753
|
},
|
|
8564
8754
|
async (input) => {
|
|
8755
|
+
const gate = await gateIfGraphTool("graph_path", input);
|
|
8756
|
+
if (gate) return gate;
|
|
8565
8757
|
const result = await client.graphPath(input);
|
|
8566
8758
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8567
8759
|
}
|
|
@@ -8578,6 +8770,8 @@ Access: Free`,
|
|
|
8578
8770
|
})
|
|
8579
8771
|
},
|
|
8580
8772
|
async (input) => {
|
|
8773
|
+
const gate = await gateIfGraphTool("graph_decisions", input);
|
|
8774
|
+
if (gate) return gate;
|
|
8581
8775
|
const result = await client.graphDecisions(input);
|
|
8582
8776
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8583
8777
|
}
|
|
@@ -8586,9 +8780,7 @@ Access: Free`,
|
|
|
8586
8780
|
"graph_dependencies",
|
|
8587
8781
|
{
|
|
8588
8782
|
title: "Code dependencies",
|
|
8589
|
-
description:
|
|
8590
|
-
|
|
8591
|
-
Access: Free`,
|
|
8783
|
+
description: "Dependency graph query",
|
|
8592
8784
|
inputSchema: external_exports.object({
|
|
8593
8785
|
target: external_exports.object({
|
|
8594
8786
|
type: external_exports.string().describe("Code element type. Accepted values: module (aliases: file, path), function (alias: method), type (aliases: struct, enum, trait, class), variable (aliases: data, const, constant). For knowledge/memory nodes, use graph_path with UUID ids instead."),
|
|
@@ -8599,6 +8791,8 @@ Access: Free`,
|
|
|
8599
8791
|
})
|
|
8600
8792
|
},
|
|
8601
8793
|
async (input) => {
|
|
8794
|
+
const gate = await gateIfGraphTool("graph_dependencies", input);
|
|
8795
|
+
if (gate) return gate;
|
|
8602
8796
|
const result = await client.graphDependencies(input);
|
|
8603
8797
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8604
8798
|
}
|
|
@@ -8607,9 +8801,7 @@ Access: Free`,
|
|
|
8607
8801
|
"graph_call_path",
|
|
8608
8802
|
{
|
|
8609
8803
|
title: "Call path",
|
|
8610
|
-
description:
|
|
8611
|
-
|
|
8612
|
-
Access: Free`,
|
|
8804
|
+
description: "Find call path between two targets",
|
|
8613
8805
|
inputSchema: external_exports.object({
|
|
8614
8806
|
source: external_exports.object({
|
|
8615
8807
|
type: external_exports.string().describe('Must be "function" (alias: method). Only function types are supported for call path analysis. For knowledge/memory nodes, use graph_path with UUID ids instead.'),
|
|
@@ -8623,6 +8815,8 @@ Access: Free`,
|
|
|
8623
8815
|
})
|
|
8624
8816
|
},
|
|
8625
8817
|
async (input) => {
|
|
8818
|
+
const gate = await gateIfGraphTool("graph_call_path", input);
|
|
8819
|
+
if (gate) return gate;
|
|
8626
8820
|
const result = await client.graphCallPath(input);
|
|
8627
8821
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8628
8822
|
}
|
|
@@ -8631,9 +8825,7 @@ Access: Free`,
|
|
|
8631
8825
|
"graph_impact",
|
|
8632
8826
|
{
|
|
8633
8827
|
title: "Impact analysis",
|
|
8634
|
-
description:
|
|
8635
|
-
|
|
8636
|
-
Access: Free`,
|
|
8828
|
+
description: "Analyze impact of a target node",
|
|
8637
8829
|
inputSchema: external_exports.object({
|
|
8638
8830
|
target: external_exports.object({
|
|
8639
8831
|
type: external_exports.string().describe("Code element type. Accepted values: module (aliases: file, path), function (alias: method), type (aliases: struct, enum, trait, class), variable (aliases: data, const, constant). For knowledge/memory nodes, use graph_path with UUID ids instead."),
|
|
@@ -8643,6 +8835,8 @@ Access: Free`,
|
|
|
8643
8835
|
})
|
|
8644
8836
|
},
|
|
8645
8837
|
async (input) => {
|
|
8838
|
+
const gate = await gateIfGraphTool("graph_impact", input);
|
|
8839
|
+
if (gate) return gate;
|
|
8646
8840
|
const result = await client.graphImpact(input);
|
|
8647
8841
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8648
8842
|
}
|
|
@@ -8651,19 +8845,40 @@ Access: Free`,
|
|
|
8651
8845
|
"graph_ingest",
|
|
8652
8846
|
{
|
|
8653
8847
|
title: "Ingest code graph",
|
|
8654
|
-
description: "Build and persist the dependency graph for a project",
|
|
8848
|
+
description: "Build and persist the dependency graph for a project. Runs async by default (wait=false) and can take a few minutes for larger repos.",
|
|
8655
8849
|
inputSchema: external_exports.object({
|
|
8656
8850
|
project_id: external_exports.string().uuid().optional(),
|
|
8657
|
-
wait: external_exports.boolean().optional().describe("If true, wait for ingestion to finish before returning.")
|
|
8851
|
+
wait: external_exports.boolean().optional().describe("If true, wait for ingestion to finish before returning. Defaults to false (async).")
|
|
8658
8852
|
})
|
|
8659
8853
|
},
|
|
8660
8854
|
async (input) => {
|
|
8855
|
+
const gate = await gateIfGraphTool("graph_ingest", input);
|
|
8856
|
+
if (gate) return gate;
|
|
8661
8857
|
const projectId = resolveProjectId(input.project_id);
|
|
8662
8858
|
if (!projectId) {
|
|
8663
8859
|
return errorResult("Error: project_id is required. Please call session_init first or provide project_id explicitly.");
|
|
8664
8860
|
}
|
|
8665
|
-
const
|
|
8666
|
-
|
|
8861
|
+
const wait = input.wait ?? false;
|
|
8862
|
+
let estimate = null;
|
|
8863
|
+
try {
|
|
8864
|
+
const stats = await client.projectStatistics(projectId);
|
|
8865
|
+
estimate = estimateGraphIngestMinutes(stats);
|
|
8866
|
+
} catch (error) {
|
|
8867
|
+
console.error("[ContextStream] Failed to fetch project statistics for graph ingest estimate:", error);
|
|
8868
|
+
}
|
|
8869
|
+
const result = await client.graphIngest({ project_id: projectId, wait });
|
|
8870
|
+
const estimateText = estimate ? `Estimated time: ${estimate.min}-${estimate.max} min${estimate.basis ? ` (based on ${estimate.basis})` : ""}.` : "Estimated time varies with repo size.";
|
|
8871
|
+
const note = `Graph ingestion is running ${wait ? "synchronously" : "asynchronously"} and can take a few minutes. ${estimateText}`;
|
|
8872
|
+
const structured = toStructured(result);
|
|
8873
|
+
const structuredContent = structured && typeof structured === "object" ? { ...structured, wait, note, ...estimate ? { estimate_minutes: { min: estimate.min, max: estimate.max }, estimate_basis: estimate.basis } : {} } : { wait, note, ...estimate ? { estimate_minutes: { min: estimate.min, max: estimate.max }, estimate_basis: estimate.basis } : {} };
|
|
8874
|
+
return {
|
|
8875
|
+
content: [{
|
|
8876
|
+
type: "text",
|
|
8877
|
+
text: `${note}
|
|
8878
|
+
${formatContent(result)}`
|
|
8879
|
+
}],
|
|
8880
|
+
structuredContent
|
|
8881
|
+
};
|
|
8667
8882
|
}
|
|
8668
8883
|
);
|
|
8669
8884
|
registerTool(
|
|
@@ -9114,6 +9329,8 @@ Automatically detects code files and skips ignored directories like node_modules
|
|
|
9114
9329
|
inputSchema: external_exports.object({ project_id: external_exports.string().uuid().optional() })
|
|
9115
9330
|
},
|
|
9116
9331
|
async (input) => {
|
|
9332
|
+
const gate = await gateIfGraphTool("graph_circular_dependencies", input);
|
|
9333
|
+
if (gate) return gate;
|
|
9117
9334
|
const projectId = resolveProjectId(input.project_id);
|
|
9118
9335
|
if (!projectId) {
|
|
9119
9336
|
return errorResult("Error: project_id is required. Please call session_init first or provide project_id explicitly.");
|
|
@@ -9130,6 +9347,8 @@ Automatically detects code files and skips ignored directories like node_modules
|
|
|
9130
9347
|
inputSchema: external_exports.object({ project_id: external_exports.string().uuid().optional() })
|
|
9131
9348
|
},
|
|
9132
9349
|
async (input) => {
|
|
9350
|
+
const gate = await gateIfGraphTool("graph_unused_code", input);
|
|
9351
|
+
if (gate) return gate;
|
|
9133
9352
|
const projectId = resolveProjectId(input.project_id);
|
|
9134
9353
|
if (!projectId) {
|
|
9135
9354
|
return errorResult("Error: project_id is required. Please call session_init first or provide project_id explicitly.");
|
|
@@ -9146,6 +9365,8 @@ Automatically detects code files and skips ignored directories like node_modules
|
|
|
9146
9365
|
inputSchema: external_exports.object({ node_id: external_exports.string().uuid() })
|
|
9147
9366
|
},
|
|
9148
9367
|
async (input) => {
|
|
9368
|
+
const gate = await gateIfGraphTool("graph_contradictions", input);
|
|
9369
|
+
if (gate) return gate;
|
|
9149
9370
|
const result = await client.findContradictions(input.node_id);
|
|
9150
9371
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
9151
9372
|
}
|
|
@@ -10180,8 +10401,10 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
10180
10401
|
const footer = `
|
|
10181
10402
|
---
|
|
10182
10403
|
\u{1F3AF} ${result.sources_used} sources | ~${result.token_estimate} tokens | format: ${result.format}`;
|
|
10404
|
+
const versionNoticeLine = result.version_notice?.behind ? `
|
|
10405
|
+
[VERSION_NOTICE] current=${result.version_notice.current} latest=${result.version_notice.latest} upgrade="${result.version_notice.upgrade_command}"` : "";
|
|
10183
10406
|
return {
|
|
10184
|
-
content: [{ type: "text", text: result.context + footer }],
|
|
10407
|
+
content: [{ type: "text", text: result.context + footer + versionNoticeLine }],
|
|
10185
10408
|
structuredContent: toStructured(result)
|
|
10186
10409
|
};
|
|
10187
10410
|
}
|
package/package.json
CHANGED