@contextstream/mcp-server 0.4.62 → 0.4.63
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 +231 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17229,6 +17229,170 @@ var TASK_HINTS = {
|
|
|
17229
17229
|
linked_to_plan: "Task linked to plan. Progress will be tracked."
|
|
17230
17230
|
};
|
|
17231
17231
|
|
|
17232
|
+
// src/project-index-utils.ts
|
|
17233
|
+
var INDEX_FRESH_HOURS = 1;
|
|
17234
|
+
var INDEX_RECENT_HOURS = 24;
|
|
17235
|
+
var INDEX_STALE_HOURS = 24 * 7;
|
|
17236
|
+
function asRecord(value) {
|
|
17237
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
17238
|
+
}
|
|
17239
|
+
function candidateObjects(result) {
|
|
17240
|
+
const root = asRecord(result);
|
|
17241
|
+
const data = asRecord(root?.data);
|
|
17242
|
+
if (data && root) return [data, root];
|
|
17243
|
+
if (data) return [data];
|
|
17244
|
+
if (root) return [root];
|
|
17245
|
+
return [];
|
|
17246
|
+
}
|
|
17247
|
+
function readBoolean(candidates, key) {
|
|
17248
|
+
for (const candidate of candidates) {
|
|
17249
|
+
const value = candidate[key];
|
|
17250
|
+
if (typeof value === "boolean") {
|
|
17251
|
+
return value;
|
|
17252
|
+
}
|
|
17253
|
+
}
|
|
17254
|
+
return void 0;
|
|
17255
|
+
}
|
|
17256
|
+
function readNumber(candidates, keys) {
|
|
17257
|
+
for (const candidate of candidates) {
|
|
17258
|
+
for (const key of keys) {
|
|
17259
|
+
const value = candidate[key];
|
|
17260
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
17261
|
+
return value;
|
|
17262
|
+
}
|
|
17263
|
+
if (typeof value === "string" && value.trim()) {
|
|
17264
|
+
const parsed = Number(value);
|
|
17265
|
+
if (Number.isFinite(parsed)) {
|
|
17266
|
+
return parsed;
|
|
17267
|
+
}
|
|
17268
|
+
}
|
|
17269
|
+
}
|
|
17270
|
+
}
|
|
17271
|
+
return void 0;
|
|
17272
|
+
}
|
|
17273
|
+
function readString(candidates, key) {
|
|
17274
|
+
for (const candidate of candidates) {
|
|
17275
|
+
const value = candidate[key];
|
|
17276
|
+
if (typeof value === "string" && value.trim()) {
|
|
17277
|
+
return value.trim();
|
|
17278
|
+
}
|
|
17279
|
+
}
|
|
17280
|
+
return void 0;
|
|
17281
|
+
}
|
|
17282
|
+
function extractIndexTimestamp(result) {
|
|
17283
|
+
const candidates = candidateObjects(result);
|
|
17284
|
+
for (const key of ["last_updated", "indexed_at", "last_indexed"]) {
|
|
17285
|
+
const raw = readString(candidates, key);
|
|
17286
|
+
if (!raw) continue;
|
|
17287
|
+
const parsed = new Date(raw);
|
|
17288
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
17289
|
+
return parsed;
|
|
17290
|
+
}
|
|
17291
|
+
}
|
|
17292
|
+
return void 0;
|
|
17293
|
+
}
|
|
17294
|
+
function apiResultReportsIndexed(result) {
|
|
17295
|
+
const candidates = candidateObjects(result);
|
|
17296
|
+
const indexed = readBoolean(candidates, "indexed");
|
|
17297
|
+
if (indexed !== void 0) {
|
|
17298
|
+
return indexed;
|
|
17299
|
+
}
|
|
17300
|
+
const indexedFiles = readNumber(candidates, ["indexed_files", "indexed_file_count"]) ?? 0;
|
|
17301
|
+
if (indexedFiles > 0) {
|
|
17302
|
+
return true;
|
|
17303
|
+
}
|
|
17304
|
+
const totalFiles = readNumber(candidates, ["total_files"]) ?? 0;
|
|
17305
|
+
if (totalFiles > 0) {
|
|
17306
|
+
const status = readString(candidates, "status")?.toLowerCase();
|
|
17307
|
+
if (status === "completed" || status === "ready") {
|
|
17308
|
+
return true;
|
|
17309
|
+
}
|
|
17310
|
+
}
|
|
17311
|
+
return false;
|
|
17312
|
+
}
|
|
17313
|
+
function apiResultIsIndexing(result) {
|
|
17314
|
+
const candidates = candidateObjects(result);
|
|
17315
|
+
const projectIndexState = readString(candidates, "project_index_state")?.toLowerCase();
|
|
17316
|
+
if (projectIndexState === "indexing" || projectIndexState === "committing") {
|
|
17317
|
+
return true;
|
|
17318
|
+
}
|
|
17319
|
+
const status = readString(candidates, "status")?.toLowerCase();
|
|
17320
|
+
if (status === "indexing" || status === "processing") {
|
|
17321
|
+
return true;
|
|
17322
|
+
}
|
|
17323
|
+
const pendingFiles = readNumber(candidates, ["pending_files"]) ?? 0;
|
|
17324
|
+
return pendingFiles > 0;
|
|
17325
|
+
}
|
|
17326
|
+
function countFromObject(value) {
|
|
17327
|
+
const obj = asRecord(value);
|
|
17328
|
+
if (!obj) return void 0;
|
|
17329
|
+
if (Array.isArray(obj.entries)) {
|
|
17330
|
+
return obj.entries.length;
|
|
17331
|
+
}
|
|
17332
|
+
if (Array.isArray(obj.history)) {
|
|
17333
|
+
return obj.history.length;
|
|
17334
|
+
}
|
|
17335
|
+
return void 0;
|
|
17336
|
+
}
|
|
17337
|
+
function indexHistoryEntryCount(result) {
|
|
17338
|
+
const rootCount = countFromObject(result);
|
|
17339
|
+
if (typeof rootCount === "number") {
|
|
17340
|
+
return rootCount;
|
|
17341
|
+
}
|
|
17342
|
+
const root = asRecord(result);
|
|
17343
|
+
const dataCount = countFromObject(root?.data);
|
|
17344
|
+
if (typeof dataCount === "number") {
|
|
17345
|
+
return dataCount;
|
|
17346
|
+
}
|
|
17347
|
+
if (Array.isArray(result)) {
|
|
17348
|
+
return result.length;
|
|
17349
|
+
}
|
|
17350
|
+
if (Array.isArray(root?.data)) {
|
|
17351
|
+
return root.data.length;
|
|
17352
|
+
}
|
|
17353
|
+
return 0;
|
|
17354
|
+
}
|
|
17355
|
+
function classifyIndexFreshness(indexed, ageHours) {
|
|
17356
|
+
if (!indexed) {
|
|
17357
|
+
return "missing";
|
|
17358
|
+
}
|
|
17359
|
+
if (typeof ageHours !== "number" || Number.isNaN(ageHours)) {
|
|
17360
|
+
return "unknown";
|
|
17361
|
+
}
|
|
17362
|
+
if (ageHours <= INDEX_FRESH_HOURS) {
|
|
17363
|
+
return "fresh";
|
|
17364
|
+
}
|
|
17365
|
+
if (ageHours <= INDEX_RECENT_HOURS) {
|
|
17366
|
+
return "recent";
|
|
17367
|
+
}
|
|
17368
|
+
if (ageHours <= INDEX_STALE_HOURS) {
|
|
17369
|
+
return "aging";
|
|
17370
|
+
}
|
|
17371
|
+
return "stale";
|
|
17372
|
+
}
|
|
17373
|
+
function classifyIndexConfidence(indexed, apiIndexed, locallyIndexed, freshness) {
|
|
17374
|
+
if (!indexed) {
|
|
17375
|
+
return {
|
|
17376
|
+
confidence: "low",
|
|
17377
|
+
reason: "Neither API status nor local index metadata currently indicates a usable index."
|
|
17378
|
+
};
|
|
17379
|
+
}
|
|
17380
|
+
if (apiIndexed && locallyIndexed) {
|
|
17381
|
+
const reason = freshness === "stale" ? "API and local metadata agree, but index age indicates stale coverage." : "API and local metadata agree for this project scope.";
|
|
17382
|
+
return { confidence: "high", reason };
|
|
17383
|
+
}
|
|
17384
|
+
if (apiIndexed || locallyIndexed) {
|
|
17385
|
+
return {
|
|
17386
|
+
confidence: "medium",
|
|
17387
|
+
reason: "Only one source reports index readiness (API vs local metadata)."
|
|
17388
|
+
};
|
|
17389
|
+
}
|
|
17390
|
+
return {
|
|
17391
|
+
confidence: "low",
|
|
17392
|
+
reason: "Index state is inferred but lacks corroborating API/local metadata."
|
|
17393
|
+
};
|
|
17394
|
+
}
|
|
17395
|
+
|
|
17232
17396
|
// src/tools.ts
|
|
17233
17397
|
function parseBoolEnvDefault(raw, fallback) {
|
|
17234
17398
|
if (raw === void 0) return fallback;
|
|
@@ -25575,6 +25739,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
25575
25739
|
plan_step_id: external_exports.string().optional().describe("Which plan step this task implements"),
|
|
25576
25740
|
description: external_exports.string().optional().describe("Description for task"),
|
|
25577
25741
|
task_status: external_exports.enum(["pending", "in_progress", "completed", "blocked", "cancelled"]).optional().describe("Task status"),
|
|
25742
|
+
status: external_exports.enum(["pending", "in_progress", "completed", "blocked", "cancelled"]).optional().describe("Backward-compatible alias for task_status in task actions"),
|
|
25578
25743
|
priority: external_exports.enum(["low", "medium", "high", "urgent"]).optional().describe("Task priority"),
|
|
25579
25744
|
order: external_exports.number().optional().describe("Task order within plan"),
|
|
25580
25745
|
task_ids: external_exports.array(external_exports.string().uuid()).optional().describe("Task IDs for reorder_tasks"),
|
|
@@ -25877,6 +26042,7 @@ ${formatContent(result)}`
|
|
|
25877
26042
|
if (!workspaceId) {
|
|
25878
26043
|
return errorResult("create_task requires workspace_id. Call session_init first.");
|
|
25879
26044
|
}
|
|
26045
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
25880
26046
|
const result = await client.createTask({
|
|
25881
26047
|
workspace_id: workspaceId,
|
|
25882
26048
|
project_id: projectId,
|
|
@@ -25885,7 +26051,7 @@ ${formatContent(result)}`
|
|
|
25885
26051
|
description: input.description,
|
|
25886
26052
|
plan_id: input.plan_id ?? void 0,
|
|
25887
26053
|
plan_step_id: input.plan_step_id,
|
|
25888
|
-
status:
|
|
26054
|
+
status: requestedTaskStatus,
|
|
25889
26055
|
priority: input.priority,
|
|
25890
26056
|
order: input.order,
|
|
25891
26057
|
code_refs: input.code_refs,
|
|
@@ -25913,12 +26079,13 @@ ${formatContent(result)}`
|
|
|
25913
26079
|
if (!input.task_id) {
|
|
25914
26080
|
return errorResult("update_task requires: task_id");
|
|
25915
26081
|
}
|
|
26082
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
25916
26083
|
const result = await client.updateTask({
|
|
25917
26084
|
task_id: input.task_id,
|
|
25918
26085
|
title: input.title,
|
|
25919
26086
|
content: input.content,
|
|
25920
26087
|
description: input.description,
|
|
25921
|
-
status:
|
|
26088
|
+
status: requestedTaskStatus,
|
|
25922
26089
|
priority: input.priority,
|
|
25923
26090
|
order: input.order,
|
|
25924
26091
|
plan_id: input.plan_id,
|
|
@@ -25928,11 +26095,11 @@ ${formatContent(result)}`
|
|
|
25928
26095
|
blocked_reason: input.blocked_reason
|
|
25929
26096
|
});
|
|
25930
26097
|
let taskUpdateHint = "Task updated.";
|
|
25931
|
-
if (
|
|
26098
|
+
if (requestedTaskStatus === "completed") {
|
|
25932
26099
|
taskUpdateHint = TASK_HINTS.completed;
|
|
25933
|
-
} else if (
|
|
26100
|
+
} else if (requestedTaskStatus === "blocked") {
|
|
25934
26101
|
taskUpdateHint = TASK_HINTS.blocked;
|
|
25935
|
-
} else if (
|
|
26102
|
+
} else if (requestedTaskStatus === "cancelled") {
|
|
25936
26103
|
taskUpdateHint = TASK_HINTS.cancelled;
|
|
25937
26104
|
}
|
|
25938
26105
|
const resultWithHint = { ...result, hint: taskUpdateHint };
|
|
@@ -25955,11 +26122,12 @@ ${formatContent(result)}`
|
|
|
25955
26122
|
if (!workspaceId) {
|
|
25956
26123
|
return errorResult("list_tasks requires workspace_id. Call session_init first.");
|
|
25957
26124
|
}
|
|
26125
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
25958
26126
|
const result = await client.listTasks({
|
|
25959
26127
|
workspace_id: workspaceId,
|
|
25960
26128
|
project_id: projectId,
|
|
25961
26129
|
plan_id: input.plan_id ?? void 0,
|
|
25962
|
-
status:
|
|
26130
|
+
status: requestedTaskStatus,
|
|
25963
26131
|
priority: input.priority,
|
|
25964
26132
|
limit: input.limit,
|
|
25965
26133
|
is_personal: input.is_personal
|
|
@@ -26246,11 +26414,12 @@ ${formatContent(result)}`
|
|
|
26246
26414
|
const teamWorkspacesForTasks = await client.listTeamWorkspaces({ page_size: 100 });
|
|
26247
26415
|
const workspacesForTasks = teamWorkspacesForTasks?.items || teamWorkspacesForTasks?.data?.items || [];
|
|
26248
26416
|
const allTasks = [];
|
|
26417
|
+
const requestedTaskStatus = input.task_status ?? input.status;
|
|
26249
26418
|
for (const ws of workspacesForTasks.slice(0, 10)) {
|
|
26250
26419
|
try {
|
|
26251
26420
|
const tasks = await client.listTasks({
|
|
26252
26421
|
workspace_id: ws.id,
|
|
26253
|
-
status:
|
|
26422
|
+
status: requestedTaskStatus,
|
|
26254
26423
|
limit: input.limit ? Math.ceil(input.limit / workspacesForTasks.length) : 10
|
|
26255
26424
|
});
|
|
26256
26425
|
const items = tasks?.data?.tasks || tasks?.tasks || tasks?.data?.items || tasks?.items || [];
|
|
@@ -26866,8 +27035,7 @@ ${formatContent(result)}`
|
|
|
26866
27035
|
for (const [index, candidateId] of candidateIds.entries()) {
|
|
26867
27036
|
try {
|
|
26868
27037
|
const statusResult = await client.projectIndexStatus(candidateId);
|
|
26869
|
-
const
|
|
26870
|
-
const apiIndexed = Boolean(data?.indexed);
|
|
27038
|
+
const apiIndexed = apiResultReportsIndexed(statusResult);
|
|
26871
27039
|
if (apiIndexed) {
|
|
26872
27040
|
selected = { index, projectId: candidateId, result: statusResult, apiIndexed };
|
|
26873
27041
|
break;
|
|
@@ -26897,7 +27065,17 @@ ${formatContent(result)}`
|
|
|
26897
27065
|
});
|
|
26898
27066
|
}
|
|
26899
27067
|
const locallyIndexed = localIndexProjectId !== void 0 ? localIndexProjectId === selected.projectId : Boolean(folderPath && await indexedProjectIdForFolder(folderPath));
|
|
27068
|
+
const apiIndexing = apiResultIsIndexing(selected.result);
|
|
26900
27069
|
const indexed = selected.apiIndexed || locallyIndexed;
|
|
27070
|
+
const indexedAt = extractIndexTimestamp(selected.result);
|
|
27071
|
+
const ageHours = indexedAt !== void 0 ? Math.floor((Date.now() - indexedAt.getTime()) / (1e3 * 60 * 60)) : void 0;
|
|
27072
|
+
const freshness = classifyIndexFreshness(indexed, ageHours);
|
|
27073
|
+
const { confidence: confidenceLevel, reason: confidenceReason } = classifyIndexConfidence(
|
|
27074
|
+
indexed,
|
|
27075
|
+
selected.apiIndexed,
|
|
27076
|
+
locallyIndexed,
|
|
27077
|
+
freshness
|
|
27078
|
+
);
|
|
26901
27079
|
const response = selected.result && typeof selected.result === "object" ? { ...selected.result } : {};
|
|
26902
27080
|
const responseData = response.data && typeof response.data === "object" ? { ...response.data } : {};
|
|
26903
27081
|
responseData.indexed = indexed;
|
|
@@ -26909,8 +27087,30 @@ ${formatContent(result)}`
|
|
|
26909
27087
|
}
|
|
26910
27088
|
responseData.resolved_project_id = selected.projectId;
|
|
26911
27089
|
responseData.resolution_rank = selected.index;
|
|
27090
|
+
responseData.index_freshness = freshness;
|
|
27091
|
+
responseData.index_age_hours = ageHours ?? null;
|
|
27092
|
+
responseData.index_confidence = confidenceLevel;
|
|
27093
|
+
responseData.index_confidence_reason = confidenceReason;
|
|
27094
|
+
responseData.index_timestamp = indexedAt ? indexedAt.toISOString() : null;
|
|
27095
|
+
responseData.index_in_progress = apiIndexing;
|
|
26912
27096
|
response.data = responseData;
|
|
26913
|
-
|
|
27097
|
+
let text = "";
|
|
27098
|
+
if (apiIndexing) {
|
|
27099
|
+
text = indexed ? "Project indexing is in progress. Search is using the latest committed generation." : "Project indexing is in progress. Semantic search will be available after the first commit.";
|
|
27100
|
+
} else if (indexed) {
|
|
27101
|
+
text = locallyIndexed && !selected.apiIndexed ? "Project index is ready (local state). Semantic search is available." : "Project index is ready. Semantic search is available.";
|
|
27102
|
+
} else {
|
|
27103
|
+
text = 'Project index not found. Run project(action="ingest_local", path="<folder>") to start indexing.';
|
|
27104
|
+
}
|
|
27105
|
+
const ageDisplay = typeof ageHours === "number" ? `${ageHours}h` : "unknown";
|
|
27106
|
+
text += ` Freshness: ${freshness} (${ageDisplay}). Confidence: ${confidenceLevel}.`;
|
|
27107
|
+
if (confidenceLevel !== "high") {
|
|
27108
|
+
text += ` ${confidenceReason}`;
|
|
27109
|
+
}
|
|
27110
|
+
if (freshness === "stale" || freshness === "missing") {
|
|
27111
|
+
const ingestPath = folderPath || "<folder>";
|
|
27112
|
+
text += ` Refresh with project(action="ingest_local", path="${ingestPath}").`;
|
|
27113
|
+
}
|
|
26914
27114
|
return {
|
|
26915
27115
|
content: [{ type: "text", text: `${text}
|
|
26916
27116
|
|
|
@@ -26933,8 +27133,28 @@ ${formatContent(response)}` }],
|
|
|
26933
27133
|
page: input.page,
|
|
26934
27134
|
limit: input.page_size
|
|
26935
27135
|
});
|
|
27136
|
+
const count = indexHistoryEntryCount(result);
|
|
27137
|
+
const response = result && typeof result === "object" ? Array.isArray(result) ? [...result] : { ...result } : result;
|
|
27138
|
+
if (response && typeof response === "object" && !Array.isArray(response)) {
|
|
27139
|
+
response.entries_count = count;
|
|
27140
|
+
if (response.data && typeof response.data === "object") {
|
|
27141
|
+
response.data = {
|
|
27142
|
+
...response.data,
|
|
27143
|
+
entries_count: count
|
|
27144
|
+
};
|
|
27145
|
+
}
|
|
27146
|
+
}
|
|
27147
|
+
const structuredHistory = response && typeof response === "object" && !Array.isArray(response) ? response : void 0;
|
|
26936
27148
|
return {
|
|
26937
|
-
content: [
|
|
27149
|
+
content: [
|
|
27150
|
+
{
|
|
27151
|
+
type: "text",
|
|
27152
|
+
text: `Found ${count} index history entries.
|
|
27153
|
+
|
|
27154
|
+
${formatContent(response)}`
|
|
27155
|
+
}
|
|
27156
|
+
],
|
|
27157
|
+
...structuredHistory ? { structuredContent: structuredHistory } : {}
|
|
26938
27158
|
};
|
|
26939
27159
|
}
|
|
26940
27160
|
case "ingest_local": {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contextstream/mcp-server",
|
|
3
3
|
"mcpName": "io.github.contextstreamio/mcp-server",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.63",
|
|
5
5
|
"description": "ContextStream MCP server - v0.4.x with consolidated domain tools (~11 tools, ~75% token reduction). Code context, memory, search, and AI tools.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|