@contextstream/mcp-server 0.4.56 → 0.4.57

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.
Files changed (2) hide show
  1. package/dist/index.js +264 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -312,8 +312,8 @@ function detectUpdateMethod() {
312
312
  if (execPath.includes("node_modules") || execPath.includes("npm")) {
313
313
  return "npm";
314
314
  }
315
- const os = platform();
316
- if (os === "win32") {
315
+ const os2 = platform();
316
+ if (os2 === "win32") {
317
317
  return "powershell";
318
318
  }
319
319
  return "curl";
@@ -10373,6 +10373,10 @@ function rewriteNotFoundMessage(input) {
10373
10373
  // src/files.ts
10374
10374
  import * as fs2 from "fs";
10375
10375
  import * as path2 from "path";
10376
+ import { exec } from "child_process";
10377
+ import { promisify } from "util";
10378
+ import * as os from "os";
10379
+ import * as crypto from "crypto";
10376
10380
 
10377
10381
  // src/ignore.ts
10378
10382
  var import_ignore = __toESM(require_ignore(), 1);
@@ -10446,6 +10450,7 @@ async function loadIgnorePatterns(projectRoot) {
10446
10450
  }
10447
10451
 
10448
10452
  // src/files.ts
10453
+ var execAsync = promisify(exec);
10449
10454
  var CODE_EXTENSIONS = /* @__PURE__ */ new Set([
10450
10455
  // Rust
10451
10456
  "rs",
@@ -10553,12 +10558,116 @@ var MAX_FILE_SIZE = 1024 * 1024;
10553
10558
  var MAX_BATCH_BYTES = 10 * 1024 * 1024;
10554
10559
  var LARGE_FILE_THRESHOLD = 2 * 1024 * 1024;
10555
10560
  var MAX_FILES_PER_BATCH = 200;
10561
+ var gitContextCache = /* @__PURE__ */ new Map();
10562
+ function getMachineId() {
10563
+ const hostname2 = os.hostname();
10564
+ const hash = crypto.createHash("sha256").update(hostname2).digest("hex");
10565
+ return hash.substring(0, 12);
10566
+ }
10567
+ async function isGitRepo(rootPath) {
10568
+ try {
10569
+ await execAsync("git rev-parse --is-inside-work-tree", {
10570
+ cwd: rootPath,
10571
+ timeout: 5e3
10572
+ });
10573
+ return true;
10574
+ } catch {
10575
+ return false;
10576
+ }
10577
+ }
10578
+ async function getGitBranch(rootPath) {
10579
+ try {
10580
+ const { stdout: stdout2 } = await execAsync("git branch --show-current", {
10581
+ cwd: rootPath,
10582
+ timeout: 5e3
10583
+ });
10584
+ const branch = stdout2.trim();
10585
+ return branch || void 0;
10586
+ } catch {
10587
+ return void 0;
10588
+ }
10589
+ }
10590
+ async function getGitDefaultBranch(rootPath) {
10591
+ try {
10592
+ const { stdout: stdout2 } = await execAsync(
10593
+ "git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null",
10594
+ { cwd: rootPath, timeout: 5e3 }
10595
+ );
10596
+ const match = stdout2.trim().match(/refs\/remotes\/origin\/(.+)/);
10597
+ if (match) return match[1];
10598
+ } catch {
10599
+ }
10600
+ try {
10601
+ const { stdout: stdout2 } = await execAsync(
10602
+ "git config --get init.defaultBranch 2>/dev/null",
10603
+ { cwd: rootPath, timeout: 5e3 }
10604
+ );
10605
+ const branch = stdout2.trim();
10606
+ if (branch) return branch;
10607
+ } catch {
10608
+ }
10609
+ try {
10610
+ const { stdout: stdout2 } = await execAsync("git branch --list main master", {
10611
+ cwd: rootPath,
10612
+ timeout: 5e3
10613
+ });
10614
+ const branches = stdout2.trim().split("\n").map((b) => b.replace(/^\*?\s*/, "").trim()).filter(Boolean);
10615
+ if (branches.includes("main")) return "main";
10616
+ if (branches.includes("master")) return "master";
10617
+ } catch {
10618
+ }
10619
+ return void 0;
10620
+ }
10621
+ async function getFileGitInfo(rootPath, relativePath) {
10622
+ try {
10623
+ const { stdout: stdout2 } = await execAsync(
10624
+ `git log -1 --format="%H %ct" -- "${relativePath}"`,
10625
+ { cwd: rootPath, timeout: 5e3 }
10626
+ );
10627
+ const parts = stdout2.trim().split(" ");
10628
+ if (parts.length >= 2) {
10629
+ const sha = parts[0];
10630
+ const unixTimestamp = parseInt(parts[1], 10);
10631
+ if (sha && !isNaN(unixTimestamp)) {
10632
+ const timestamp = new Date(unixTimestamp * 1e3).toISOString();
10633
+ return { sha, timestamp };
10634
+ }
10635
+ }
10636
+ } catch {
10637
+ }
10638
+ return void 0;
10639
+ }
10640
+ async function getGitContext(rootPath) {
10641
+ const cached = gitContextCache.get(rootPath);
10642
+ if (cached) return cached;
10643
+ const machineId = getMachineId();
10644
+ const isRepo = await isGitRepo(rootPath);
10645
+ if (!isRepo) {
10646
+ const context2 = { isGitRepo: false, machineId };
10647
+ gitContextCache.set(rootPath, context2);
10648
+ return context2;
10649
+ }
10650
+ const [branch, defaultBranch] = await Promise.all([
10651
+ getGitBranch(rootPath),
10652
+ getGitDefaultBranch(rootPath)
10653
+ ]);
10654
+ const context = {
10655
+ isGitRepo: true,
10656
+ branch,
10657
+ defaultBranch,
10658
+ isDefaultBranch: branch !== void 0 && branch === defaultBranch,
10659
+ machineId
10660
+ };
10661
+ gitContextCache.set(rootPath, context);
10662
+ return context;
10663
+ }
10556
10664
  async function* readAllFilesInBatches(rootPath, options = {}) {
10557
10665
  const maxBatchBytes = options.maxBatchBytes ?? MAX_BATCH_BYTES;
10558
10666
  const largeFileThreshold = options.largeFileThreshold ?? LARGE_FILE_THRESHOLD;
10559
10667
  const maxFilesPerBatch = options.maxFilesPerBatch ?? options.batchSize ?? MAX_FILES_PER_BATCH;
10560
10668
  const maxFileSize = options.maxFileSize ?? MAX_FILE_SIZE;
10561
10669
  const ig = options.ignoreInstance ?? await loadIgnorePatterns(rootPath);
10670
+ const gitCtx = await getGitContext(rootPath);
10562
10671
  let batch = [];
10563
10672
  let currentBatchBytes = 0;
10564
10673
  async function* walkDir(dir, relativePath = "") {
@@ -10584,7 +10693,25 @@ async function* readAllFilesInBatches(rootPath, options = {}) {
10584
10693
  const stat2 = await fs2.promises.stat(fullPath);
10585
10694
  if (stat2.size > maxFileSize) continue;
10586
10695
  const content = await fs2.promises.readFile(fullPath, "utf-8");
10587
- yield { path: relPath, content, sizeBytes: stat2.size };
10696
+ const file = {
10697
+ path: relPath,
10698
+ content,
10699
+ sizeBytes: stat2.size,
10700
+ // Always include machine_id and source_modified_at
10701
+ machine_id: gitCtx.machineId,
10702
+ source_modified_at: stat2.mtime.toISOString()
10703
+ };
10704
+ if (gitCtx.isGitRepo) {
10705
+ file.git_branch = gitCtx.branch;
10706
+ file.git_default_branch = gitCtx.defaultBranch;
10707
+ file.is_default_branch = gitCtx.isDefaultBranch;
10708
+ const gitInfo = await getFileGitInfo(rootPath, relPath);
10709
+ if (gitInfo) {
10710
+ file.git_commit_sha = gitInfo.sha;
10711
+ file.git_commit_timestamp = gitInfo.timestamp;
10712
+ }
10713
+ }
10714
+ yield file;
10588
10715
  } catch {
10589
10716
  }
10590
10717
  }
@@ -10593,11 +10720,12 @@ async function* readAllFilesInBatches(rootPath, options = {}) {
10593
10720
  for await (const file of walkDir(rootPath)) {
10594
10721
  if (file.sizeBytes > largeFileThreshold) {
10595
10722
  if (batch.length > 0) {
10596
- yield batch.map(({ sizeBytes, ...rest }) => rest);
10723
+ yield batch.map(({ sizeBytes: sizeBytes2, ...rest }) => rest);
10597
10724
  batch = [];
10598
10725
  currentBatchBytes = 0;
10599
10726
  }
10600
- yield [{ path: file.path, content: file.content }];
10727
+ const { sizeBytes, ...fileData } = file;
10728
+ yield [fileData];
10601
10729
  continue;
10602
10730
  }
10603
10731
  const wouldExceedBytes = currentBatchBytes + file.sizeBytes > maxBatchBytes;
@@ -10623,6 +10751,7 @@ async function* readChangedFilesInBatches(rootPath, sinceTimestamp, options = {}
10623
10751
  const maxFileSize = options.maxFileSize ?? MAX_FILE_SIZE;
10624
10752
  const sinceMs = sinceTimestamp.getTime();
10625
10753
  const ig = options.ignoreInstance ?? await loadIgnorePatterns(rootPath);
10754
+ const gitCtx = await getGitContext(rootPath);
10626
10755
  let batch = [];
10627
10756
  let currentBatchBytes = 0;
10628
10757
  let filesScanned = 0;
@@ -10653,7 +10782,25 @@ async function* readChangedFilesInBatches(rootPath, sinceTimestamp, options = {}
10653
10782
  if (stat2.size > maxFileSize) continue;
10654
10783
  const content = await fs2.promises.readFile(fullPath, "utf-8");
10655
10784
  filesChanged++;
10656
- yield { path: relPath, content, sizeBytes: stat2.size };
10785
+ const file = {
10786
+ path: relPath,
10787
+ content,
10788
+ sizeBytes: stat2.size,
10789
+ // Always include machine_id and source_modified_at
10790
+ machine_id: gitCtx.machineId,
10791
+ source_modified_at: stat2.mtime.toISOString()
10792
+ };
10793
+ if (gitCtx.isGitRepo) {
10794
+ file.git_branch = gitCtx.branch;
10795
+ file.git_default_branch = gitCtx.defaultBranch;
10796
+ file.is_default_branch = gitCtx.isDefaultBranch;
10797
+ const gitInfo = await getFileGitInfo(rootPath, relPath);
10798
+ if (gitInfo) {
10799
+ file.git_commit_sha = gitInfo.sha;
10800
+ file.git_commit_timestamp = gitInfo.timestamp;
10801
+ }
10802
+ }
10803
+ yield file;
10657
10804
  } catch {
10658
10805
  }
10659
10806
  }
@@ -10662,11 +10809,12 @@ async function* readChangedFilesInBatches(rootPath, sinceTimestamp, options = {}
10662
10809
  for await (const file of walkDir(rootPath)) {
10663
10810
  if (file.sizeBytes > largeFileThreshold) {
10664
10811
  if (batch.length > 0) {
10665
- yield batch.map(({ sizeBytes, ...rest }) => rest);
10812
+ yield batch.map(({ sizeBytes: sizeBytes2, ...rest }) => rest);
10666
10813
  batch = [];
10667
10814
  currentBatchBytes = 0;
10668
10815
  }
10669
- yield [{ path: file.path, content: file.content }];
10816
+ const { sizeBytes, ...fileData } = file;
10817
+ yield [fileData];
10670
10818
  continue;
10671
10819
  }
10672
10820
  const wouldExceedBytes = currentBatchBytes + file.sizeBytes > maxBatchBytes;
@@ -11763,6 +11911,26 @@ var ContextStreamClient = class {
11763
11911
  uuidSchema.parse(projectId);
11764
11912
  return request(this.config, `/projects/${projectId}/index/status`, { method: "GET" });
11765
11913
  }
11914
+ /**
11915
+ * Get index history for audit trail
11916
+ * Shows which files were indexed, when, by which machine, and from which branch
11917
+ */
11918
+ projectIndexHistory(projectId, params) {
11919
+ uuidSchema.parse(projectId);
11920
+ const queryParams = new URLSearchParams();
11921
+ if (params?.machine_id) queryParams.set("machine_id", params.machine_id);
11922
+ if (params?.branch) queryParams.set("branch", params.branch);
11923
+ if (params?.since) queryParams.set("since", params.since);
11924
+ if (params?.until) queryParams.set("until", params.until);
11925
+ if (params?.path_pattern) queryParams.set("path_pattern", params.path_pattern);
11926
+ if (params?.sort_by) queryParams.set("sort_by", params.sort_by);
11927
+ if (params?.sort_order) queryParams.set("sort_order", params.sort_order);
11928
+ if (params?.page) queryParams.set("page", params.page.toString());
11929
+ if (params?.limit) queryParams.set("limit", params.limit.toString());
11930
+ const queryString = queryParams.toString();
11931
+ const url = `/projects/${projectId}/index/history${queryString ? `?${queryString}` : ""}`;
11932
+ return request(this.config, url, { method: "GET" });
11933
+ }
11766
11934
  /**
11767
11935
  * Check if project ingestion is recommended and return a recommendation.
11768
11936
  * This is used by session_init to inform the AI about the project's index status.
@@ -11837,10 +12005,11 @@ var ContextStreamClient = class {
11837
12005
  * Ingest files for indexing
11838
12006
  * This uploads files to the API for indexing
11839
12007
  * @param projectId - Project UUID
11840
- * @param files - Array of files to ingest
12008
+ * @param files - Array of files to ingest (with optional version metadata)
11841
12009
  * @param options - Optional ingest options
11842
12010
  * @param options.write_to_disk - When true, write files to disk under QA_FILE_WRITE_ROOT before indexing
11843
12011
  * @param options.overwrite - Allow overwriting existing files when write_to_disk is enabled
12012
+ * @param options.force - When true, bypass version checking and force re-index all files
11844
12013
  */
11845
12014
  ingestFiles(projectId, files, options) {
11846
12015
  uuidSchema.parse(projectId);
@@ -11848,7 +12017,8 @@ var ContextStreamClient = class {
11848
12017
  body: {
11849
12018
  files,
11850
12019
  ...options?.write_to_disk !== void 0 && { write_to_disk: options.write_to_disk },
11851
- ...options?.overwrite !== void 0 && { overwrite: options.overwrite }
12020
+ ...options?.overwrite !== void 0 && { overwrite: options.overwrite },
12021
+ ...options?.force !== void 0 && { force: options.force }
11852
12022
  }
11853
12023
  });
11854
12024
  }
@@ -11946,6 +12116,23 @@ var ContextStreamClient = class {
11946
12116
  uuidSchema.parse(workspaceId);
11947
12117
  return request(this.config, `/workspaces/${workspaceId}/content`, { method: "GET" });
11948
12118
  }
12119
+ /**
12120
+ * Get workspace index settings for multi-machine sync configuration
12121
+ */
12122
+ getWorkspaceIndexSettings(workspaceId) {
12123
+ uuidSchema.parse(workspaceId);
12124
+ return request(this.config, `/workspaces/${workspaceId}/index-settings`, { method: "GET" });
12125
+ }
12126
+ /**
12127
+ * Update workspace index settings (admin only)
12128
+ */
12129
+ updateWorkspaceIndexSettings(workspaceId, settings) {
12130
+ uuidSchema.parse(workspaceId);
12131
+ return request(this.config, `/workspaces/${workspaceId}/index-settings`, {
12132
+ method: "PUT",
12133
+ body: settings
12134
+ });
12135
+ }
11949
12136
  // Memory extended operations
11950
12137
  getMemoryEvent(eventId) {
11951
12138
  uuidSchema.parse(eventId);
@@ -24991,7 +25178,7 @@ ${formatContent(result)}`
24991
25178
  "project",
24992
25179
  {
24993
25180
  title: "Project",
24994
- description: `Project management. Actions: list, get, create, update, index (trigger indexing), overview, statistics, files, index_status, ingest_local (index local folder), team_projects (list all team projects - team plans only).`,
25181
+ description: `Project management. Actions: list, get, create, update, index (trigger indexing), overview, statistics, files, index_status, index_history (audit trail of indexed files), ingest_local (index local folder), team_projects (list all team projects - team plans only).`,
24995
25182
  inputSchema: external_exports.object({
24996
25183
  action: external_exports.enum([
24997
25184
  "list",
@@ -25003,6 +25190,7 @@ ${formatContent(result)}`
25003
25190
  "statistics",
25004
25191
  "files",
25005
25192
  "index_status",
25193
+ "index_history",
25006
25194
  "ingest_local",
25007
25195
  "team_projects"
25008
25196
  ]).describe("Action to perform"),
@@ -25017,6 +25205,15 @@ ${formatContent(result)}`
25017
25205
  path: external_exports.string().optional().describe("Local path to ingest"),
25018
25206
  overwrite: external_exports.boolean().optional(),
25019
25207
  write_to_disk: external_exports.boolean().optional(),
25208
+ force: external_exports.boolean().optional().describe("Force re-index all files, bypassing version check logic"),
25209
+ // Index history filters
25210
+ machine_id: external_exports.string().optional().describe("Filter by machine ID that indexed the files"),
25211
+ branch: external_exports.string().optional().describe("Filter by git branch"),
25212
+ since: external_exports.string().optional().describe("Filter files indexed after this timestamp (ISO 8601)"),
25213
+ until: external_exports.string().optional().describe("Filter files indexed before this timestamp (ISO 8601)"),
25214
+ path_pattern: external_exports.string().optional().describe("Filter by file path pattern (partial match)"),
25215
+ sort_by: external_exports.enum(["path", "indexed", "size"]).optional().describe("Sort field (default: indexed)"),
25216
+ sort_order: external_exports.enum(["asc", "desc"]).optional().describe("Sort order (default: desc)"),
25020
25217
  // Pagination
25021
25218
  page: external_exports.number().optional(),
25022
25219
  page_size: external_exports.number().optional()
@@ -25115,6 +25312,25 @@ ${formatContent(result)}`
25115
25312
  content: [{ type: "text", text: formatContent(result) }]
25116
25313
  };
25117
25314
  }
25315
+ case "index_history": {
25316
+ if (!projectId) {
25317
+ return errorResult("index_history requires: project_id");
25318
+ }
25319
+ const result = await client.projectIndexHistory(projectId, {
25320
+ machine_id: input.machine_id,
25321
+ branch: input.branch,
25322
+ since: input.since,
25323
+ until: input.until,
25324
+ path_pattern: input.path_pattern,
25325
+ sort_by: input.sort_by,
25326
+ sort_order: input.sort_order,
25327
+ page: input.page,
25328
+ limit: input.page_size
25329
+ });
25330
+ return {
25331
+ content: [{ type: "text", text: formatContent(result) }]
25332
+ };
25333
+ }
25118
25334
  case "ingest_local": {
25119
25335
  if (!input.path) {
25120
25336
  return errorResult("ingest_local requires: path");
@@ -25128,23 +25344,26 @@ ${formatContent(result)}`
25128
25344
  }
25129
25345
  const ingestOptions = {
25130
25346
  ...input.write_to_disk !== void 0 && { write_to_disk: input.write_to_disk },
25131
- ...input.overwrite !== void 0 && { overwrite: input.overwrite }
25347
+ ...input.overwrite !== void 0 && { overwrite: input.overwrite },
25348
+ ...input.force !== void 0 && { force: input.force }
25132
25349
  };
25133
25350
  startBackgroundIngest(projectId, validPath.resolvedPath, ingestOptions);
25351
+ const forceNote = input.force ? " (force mode - version checks bypassed)" : "";
25134
25352
  const result = {
25135
25353
  status: "started",
25136
- message: "Ingestion running in background",
25354
+ message: `Ingestion running in background${forceNote}`,
25137
25355
  project_id: projectId,
25138
25356
  path: validPath.resolvedPath,
25139
25357
  ...input.write_to_disk !== void 0 && { write_to_disk: input.write_to_disk },
25140
25358
  ...input.overwrite !== void 0 && { overwrite: input.overwrite },
25359
+ ...input.force !== void 0 && { force: input.force },
25141
25360
  note: "Use 'project' with action 'index_status' to monitor progress."
25142
25361
  };
25143
25362
  return {
25144
25363
  content: [
25145
25364
  {
25146
25365
  type: "text",
25147
- text: `Ingestion started in background for directory: ${validPath.resolvedPath}. Use 'project' with action 'index_status' to monitor progress.`
25366
+ text: `Ingestion started in background${forceNote} for directory: ${validPath.resolvedPath}. Use 'project' with action 'index_status' to monitor progress.`
25148
25367
  }
25149
25368
  ]
25150
25369
  };
@@ -25178,9 +25397,9 @@ ${formatContent(result)}`
25178
25397
  "workspace",
25179
25398
  {
25180
25399
  title: "Workspace",
25181
- description: `Workspace management. Actions: list, get, associate (link folder to workspace), bootstrap (create workspace and initialize), team_members (list members with access - team plans only).`,
25400
+ description: `Workspace management. Actions: list, get, associate (link folder to workspace), bootstrap (create workspace and initialize), team_members (list members with access - team plans only), index_settings (get/update multi-machine sync settings - admin only).`,
25182
25401
  inputSchema: external_exports.object({
25183
- action: external_exports.enum(["list", "get", "associate", "bootstrap", "team_members"]).describe("Action to perform"),
25402
+ action: external_exports.enum(["list", "get", "associate", "bootstrap", "team_members", "index_settings"]).describe("Action to perform"),
25184
25403
  workspace_id: external_exports.string().uuid().optional(),
25185
25404
  // Associate/bootstrap params
25186
25405
  folder_path: external_exports.string().optional(),
@@ -25192,6 +25411,12 @@ ${formatContent(result)}`
25192
25411
  visibility: external_exports.enum(["private", "public"]).optional(),
25193
25412
  auto_index: external_exports.boolean().optional(),
25194
25413
  context_hint: external_exports.string().optional(),
25414
+ // Index settings params (for update)
25415
+ branch_policy: external_exports.enum(["default_branch_wins", "newest_wins", "feature_branch_wins"]).optional().describe("Which branch takes priority: default_branch_wins (default), newest_wins, feature_branch_wins"),
25416
+ conflict_resolution: external_exports.enum(["newest_timestamp", "default_branch", "manual"]).optional().describe("How to resolve conflicts: newest_timestamp (default), default_branch, manual"),
25417
+ allowed_machines: external_exports.array(external_exports.string()).optional().describe("List of allowed machine IDs (empty = all allowed)"),
25418
+ auto_sync_enabled: external_exports.boolean().optional().describe("Whether to auto-sync from all machines (default: true)"),
25419
+ max_machines: external_exports.number().optional().describe("Maximum machines allowed to index (0 = unlimited)"),
25195
25420
  // Pagination
25196
25421
  page: external_exports.number().optional(),
25197
25422
  page_size: external_exports.number().optional()
@@ -25295,6 +25520,29 @@ ${formatContent(result)}`
25295
25520
  content: [{ type: "text", text: formatContent(teamMembers) }]
25296
25521
  };
25297
25522
  }
25523
+ case "index_settings": {
25524
+ if (!input.workspace_id) {
25525
+ return errorResult("index_settings requires: workspace_id");
25526
+ }
25527
+ const hasUpdateParams = input.branch_policy !== void 0 || input.conflict_resolution !== void 0 || input.allowed_machines !== void 0 || input.auto_sync_enabled !== void 0 || input.max_machines !== void 0;
25528
+ if (hasUpdateParams) {
25529
+ const result = await client.updateWorkspaceIndexSettings(input.workspace_id, {
25530
+ branch_policy: input.branch_policy,
25531
+ conflict_resolution: input.conflict_resolution,
25532
+ allowed_machines: input.allowed_machines,
25533
+ auto_sync_enabled: input.auto_sync_enabled,
25534
+ max_machines: input.max_machines
25535
+ });
25536
+ return {
25537
+ content: [{ type: "text", text: formatContent(result) }]
25538
+ };
25539
+ } else {
25540
+ const result = await client.getWorkspaceIndexSettings(input.workspace_id);
25541
+ return {
25542
+ content: [{ type: "text", text: formatContent(result) }]
25543
+ };
25544
+ }
25545
+ }
25298
25546
  default:
25299
25547
  return errorResult(`Unknown action: ${input.action}`);
25300
25548
  }
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.56",
4
+ "version": "0.4.57",
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",