@prom.codes/context-mcp 0.4.4 → 0.4.6

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 (3) hide show
  1. package/README.md +6 -1
  2. package/dist/bin.js +99 -32
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -25,10 +25,15 @@ MCP hosts (Cursor, VS Code) use the same command/args in their own config.
25
25
  the MCP `roots` capability. Set it only to point at a different folder.
26
26
  - The index is a local SQLite DB at `~/.prometheus/<hash>.db` (one per project).
27
27
  Your code never leaves your machine — only embedding *text* transits to the proxy.
28
+ - **Won't crawl your home folder.** If a fresh window opens with no project, the
29
+ workspace falls back to the host cwd (often home); the server **refuses to
30
+ auto-index the home directory or a filesystem root** and leaves the index empty
31
+ until you open a real project. Call `index_status` to see what's going on.
28
32
 
29
33
  Tools: `search_code`, `get_symbol`, `find_references`, `find_callers`,
30
34
  `find_callees`, `expand_context`, `get_file`, `list_changed_since`,
31
- `list_workspaces`, `framework_overview`.
35
+ `list_workspaces`, `framework_overview`, `index_status` (health check:
36
+ which folder, how much is indexed, does the key work?).
32
37
 
33
38
  ## Native modules
34
39
 
package/dist/bin.js CHANGED
@@ -3369,7 +3369,7 @@ async function runEmbedPass(storage, embedder, options, driftRecovered) {
3369
3369
 
3370
3370
  // ../indexer/dist/watcher.js
3371
3371
  import chokidar from "chokidar";
3372
- import { resolve, sep } from "node:path";
3372
+ import { resolve as resolve2, sep } from "node:path";
3373
3373
  import { EventEmitter } from "node:events";
3374
3374
 
3375
3375
  // ../shared/dist/types.js
@@ -3631,6 +3631,20 @@ function notify(log, name, current, latest) {
3631
3631
  `);
3632
3632
  }
3633
3633
 
3634
+ // ../shared/dist/workspace-root.js
3635
+ import { homedir as homedir2 } from "node:os";
3636
+ import { dirname, resolve } from "node:path";
3637
+ function isHomeOrFilesystemRoot(root) {
3638
+ const abs = resolve(root);
3639
+ if (abs === "")
3640
+ return true;
3641
+ if (abs === resolve(homedir2()))
3642
+ return true;
3643
+ if (dirname(abs) === abs)
3644
+ return true;
3645
+ return false;
3646
+ }
3647
+
3634
3648
  // ../shared/dist/index.js
3635
3649
  var PROMETHEUS_VERSION = "0.1.0";
3636
3650
 
@@ -3695,7 +3709,7 @@ var WorkspaceWatcher = class extends EventEmitter {
3695
3709
  #pending = /* @__PURE__ */ new Map();
3696
3710
  constructor(options) {
3697
3711
  super();
3698
- this.#root = resolve(options.root);
3712
+ this.#root = resolve2(options.root);
3699
3713
  this.#ignored = options.ignored ?? DEFAULT_IGNORED;
3700
3714
  this.#debounceMs = options.debounceMs ?? 50;
3701
3715
  const envPolling = process.env.CHOKIDAR_USEPOLLING === "true" || process.env.CHOKIDAR_USEPOLLING === "1";
@@ -3773,7 +3787,7 @@ var WorkspaceWatcher = class extends EventEmitter {
3773
3787
 
3774
3788
  // ../indexer/dist/workspace-indexer.js
3775
3789
  import { readFile as readFile3, readdir, stat } from "node:fs/promises";
3776
- import { join as join2, resolve as resolve2, sep as sep2 } from "node:path";
3790
+ import { join as join2, resolve as resolve3, sep as sep2 } from "node:path";
3777
3791
 
3778
3792
  // ../indexer/dist/co-change.js
3779
3793
  import { spawn } from "node:child_process";
@@ -3978,7 +3992,7 @@ var WorkspaceIndexer = class {
3978
3992
  #watchTuning;
3979
3993
  #watcher = null;
3980
3994
  constructor(options) {
3981
- this.#root = resolve2(options.root);
3995
+ this.#root = resolve3(options.root);
3982
3996
  this.#storage = options.storage;
3983
3997
  this.#indexer = options.indexer ?? new Indexer();
3984
3998
  this.#ownsIndexer = options.indexer === void 0;
@@ -4171,8 +4185,8 @@ var WorkspaceIndexer = class {
4171
4185
  // dist/composition.js
4172
4186
  import { createHash as createHash3 } from "node:crypto";
4173
4187
  import { mkdirSync } from "node:fs";
4174
- import { homedir as homedir2 } from "node:os";
4175
- import { basename as basename5, dirname as dirname2, join as join4, resolve as resolve4 } from "node:path";
4188
+ import { homedir as homedir3 } from "node:os";
4189
+ import { basename as basename5, dirname as dirname3, join as join4, resolve as resolve5 } from "node:path";
4176
4190
 
4177
4191
  // ../storage-sqlite/dist/adapter.js
4178
4192
  import Database from "better-sqlite3";
@@ -4897,7 +4911,7 @@ var DEFAULT_VECTOR_DIMENSION = 1024;
4897
4911
  var DEFAULT_SCHEMA = "public";
4898
4912
 
4899
4913
  // ../storage-supabase/dist/migrations.js
4900
- import { dirname, join as join3, resolve as resolve3 } from "node:path";
4914
+ import { dirname as dirname2, join as join3, resolve as resolve4 } from "node:path";
4901
4915
  import { fileURLToPath as fileURLToPath2 } from "node:url";
4902
4916
 
4903
4917
  // ../storage-supabase/dist/migrations-data.generated.js
@@ -4989,8 +5003,8 @@ COMMIT;
4989
5003
  };
4990
5004
 
4991
5005
  // ../storage-supabase/dist/migrations.js
4992
- var HERE = dirname(fileURLToPath2(import.meta.url));
4993
- var REPO_ROOT = resolve3(HERE, "..", "..", "..");
5006
+ var HERE = dirname2(fileURLToPath2(import.meta.url));
5007
+ var REPO_ROOT = resolve4(HERE, "..", "..", "..");
4994
5008
  var MIGRATIONS_DIR = join3(REPO_ROOT, "infra", "supabase", "migrations");
4995
5009
  var MIGRATIONS = Object.keys(EMBEDDED_MIGRATIONS).sort().map((filename, idx) => ({
4996
5010
  id: idx + 1,
@@ -6574,14 +6588,14 @@ function parseRetryAfterMs(value, now = Date.now()) {
6574
6588
  return delta > 0 ? delta : 0;
6575
6589
  }
6576
6590
  function sleep(ms, signal) {
6577
- return new Promise((resolve6, reject) => {
6591
+ return new Promise((resolve7, reject) => {
6578
6592
  if (signal?.aborted === true) {
6579
6593
  reject(new Error("aborted"));
6580
6594
  return;
6581
6595
  }
6582
6596
  const timer = setTimeout(() => {
6583
6597
  signal?.removeEventListener("abort", onAbort);
6584
- resolve6();
6598
+ resolve7();
6585
6599
  }, ms);
6586
6600
  const onAbort = () => {
6587
6601
  clearTimeout(timer);
@@ -6809,14 +6823,14 @@ function taskTypeFor(inputType) {
6809
6823
  return inputType === "query" ? "RETRIEVAL_QUERY" : "RETRIEVAL_DOCUMENT";
6810
6824
  }
6811
6825
  function sleep2(ms, signal) {
6812
- return new Promise((resolve6, reject) => {
6826
+ return new Promise((resolve7, reject) => {
6813
6827
  if (signal?.aborted === true) {
6814
6828
  reject(new Error("aborted"));
6815
6829
  return;
6816
6830
  }
6817
6831
  const timer = setTimeout(() => {
6818
6832
  signal?.removeEventListener("abort", onAbort);
6819
- resolve6();
6833
+ resolve7();
6820
6834
  }, ms);
6821
6835
  const onAbort = () => {
6822
6836
  clearTimeout(timer);
@@ -6946,14 +6960,14 @@ var DEFAULT_BATCH3 = 96;
6946
6960
  var DEFAULT_RETRIES3 = 4;
6947
6961
  var DEFAULT_BACKOFF3 = 250;
6948
6962
  function sleep3(ms, signal) {
6949
- return new Promise((resolve6, reject) => {
6963
+ return new Promise((resolve7, reject) => {
6950
6964
  if (signal?.aborted === true) {
6951
6965
  reject(new Error("aborted"));
6952
6966
  return;
6953
6967
  }
6954
6968
  const timer = setTimeout(() => {
6955
6969
  signal?.removeEventListener("abort", onAbort);
6956
- resolve6();
6970
+ resolve7();
6957
6971
  }, ms);
6958
6972
  const onAbort = () => {
6959
6973
  clearTimeout(timer);
@@ -7096,14 +7110,14 @@ var DEFAULT_BATCH_CHARS = 4e5;
7096
7110
  var DEFAULT_RETRIES4 = 4;
7097
7111
  var DEFAULT_BACKOFF4 = 250;
7098
7112
  function sleep4(ms, signal) {
7099
- return new Promise((resolve6, reject) => {
7113
+ return new Promise((resolve7, reject) => {
7100
7114
  if (signal?.aborted === true) {
7101
7115
  reject(new Error("aborted"));
7102
7116
  return;
7103
7117
  }
7104
7118
  const timer = setTimeout(() => {
7105
7119
  signal?.removeEventListener("abort", onAbort);
7106
- resolve6();
7120
+ resolve7();
7107
7121
  }, ms);
7108
7122
  const onAbort = () => {
7109
7123
  clearTimeout(timer);
@@ -7418,14 +7432,14 @@ function parseRetryAfterMs2(value, now = Date.now()) {
7418
7432
  return delta > 0 ? delta : 0;
7419
7433
  }
7420
7434
  function sleep5(ms, signal) {
7421
- return new Promise((resolve6, reject) => {
7435
+ return new Promise((resolve7, reject) => {
7422
7436
  if (signal?.aborted === true) {
7423
7437
  reject(new Error("aborted"));
7424
7438
  return;
7425
7439
  }
7426
7440
  const timer = setTimeout(() => {
7427
7441
  signal?.removeEventListener("abort", onAbort);
7428
- resolve6();
7442
+ resolve7();
7429
7443
  }, ms);
7430
7444
  const onAbort = () => {
7431
7445
  clearTimeout(timer);
@@ -7590,14 +7604,14 @@ function parseRetryAfterMs3(value, now = Date.now()) {
7590
7604
  return delta > 0 ? delta : 0;
7591
7605
  }
7592
7606
  function sleep6(ms, signal) {
7593
- return new Promise((resolve6, reject) => {
7607
+ return new Promise((resolve7, reject) => {
7594
7608
  if (signal?.aborted === true) {
7595
7609
  reject(new Error("aborted"));
7596
7610
  return;
7597
7611
  }
7598
7612
  const timer = setTimeout(() => {
7599
7613
  signal?.removeEventListener("abort", onAbort);
7600
- resolve6();
7614
+ resolve7();
7601
7615
  }, ms);
7602
7616
  const onAbort = () => {
7603
7617
  clearTimeout(timer);
@@ -7789,14 +7803,14 @@ function parseRetryAfterMs4(value, now = Date.now()) {
7789
7803
  return delta > 0 ? delta : 0;
7790
7804
  }
7791
7805
  function sleep7(ms, signal) {
7792
- return new Promise((resolve6, reject) => {
7806
+ return new Promise((resolve7, reject) => {
7793
7807
  if (signal?.aborted === true) {
7794
7808
  reject(new Error("aborted"));
7795
7809
  return;
7796
7810
  }
7797
7811
  const timer = setTimeout(() => {
7798
7812
  signal?.removeEventListener("abort", onAbort);
7799
- resolve6();
7813
+ resolve7();
7800
7814
  }, ms);
7801
7815
  const onAbort = () => {
7802
7816
  clearTimeout(timer);
@@ -7960,14 +7974,14 @@ function parseRetryAfterMs5(value, now = Date.now()) {
7960
7974
  return delta > 0 ? delta : 0;
7961
7975
  }
7962
7976
  function sleep8(ms, signal) {
7963
- return new Promise((resolve6, reject) => {
7977
+ return new Promise((resolve7, reject) => {
7964
7978
  if (signal?.aborted === true) {
7965
7979
  reject(new Error("aborted"));
7966
7980
  return;
7967
7981
  }
7968
7982
  const timer = setTimeout(() => {
7969
7983
  signal?.removeEventListener("abort", onAbort);
7970
- resolve6();
7984
+ resolve7();
7971
7985
  }, ms);
7972
7986
  const onAbort = () => {
7973
7987
  clearTimeout(timer);
@@ -8434,9 +8448,12 @@ function discoverQueryRewriter(env, fetchImpl) {
8434
8448
  throw new NoProviderError(`unknown PROMETHEUS_REWRITER_PROVIDER="${forced}" (expected "none", "gemini", or "mistral")`);
8435
8449
  }
8436
8450
  function getStableDbPath(workspaceRoot) {
8437
- const abs = resolve4(workspaceRoot);
8451
+ const abs = resolve5(workspaceRoot);
8438
8452
  const hash = createHash3("sha256").update(abs).digest("hex").slice(0, 16);
8439
- return join4(homedir2(), ".prometheus", `${hash}.db`);
8453
+ return join4(homedir3(), ".prometheus", `${hash}.db`);
8454
+ }
8455
+ function isAutoIndexableRoot(workspaceRoot) {
8456
+ return !isHomeOrFilesystemRoot(workspaceRoot);
8440
8457
  }
8441
8458
  var StorageConfigError = class extends Error {
8442
8459
  constructor(reason) {
@@ -8476,7 +8493,7 @@ function discoverStorageBackend(env, regionMode, options = {}) {
8476
8493
  }
8477
8494
  const writable = options.writable === true;
8478
8495
  if (writable && dbPath !== ":memory:") {
8479
- mkdirSync(dirname2(dbPath), { recursive: true });
8496
+ mkdirSync(dirname3(dbPath), { recursive: true });
8480
8497
  }
8481
8498
  return {
8482
8499
  id: "sqlite",
@@ -8526,6 +8543,7 @@ async function composeFromEnv(opts) {
8526
8543
  const rerankTopN = intEnv(env, "PROMETHEUS_RERANK_TOP_N", 100);
8527
8544
  const { id: queryRewriterId, provider: queryRewriter } = discoverQueryRewriter(env, opts.fetch);
8528
8545
  const managed = apiKeyPresent && storageBackend === "sqlite";
8546
+ const autoIndexable = isAutoIndexableRoot(workspaceRoot);
8529
8547
  let closed = false;
8530
8548
  return {
8531
8549
  storage,
@@ -8538,6 +8556,7 @@ async function composeFromEnv(opts) {
8538
8556
  providerId,
8539
8557
  storageBackend,
8540
8558
  managed,
8559
+ autoIndexable,
8541
8560
  dbPath,
8542
8561
  reranker,
8543
8562
  rerankId,
@@ -8588,7 +8607,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8588
8607
 
8589
8608
  // dist/tools.js
8590
8609
  import { readFile as readFile4 } from "node:fs/promises";
8591
- import { isAbsolute, join as join5, normalize, relative, resolve as resolve5, sep as sep3 } from "node:path";
8610
+ import { isAbsolute, join as join5, normalize, relative, resolve as resolve6, sep as sep3 } from "node:path";
8592
8611
  import { z } from "zod";
8593
8612
 
8594
8613
  // dist/frameworks.js
@@ -8847,7 +8866,7 @@ function resolveInWorkspace(workspaceRoot, input) {
8847
8866
  throw new Error(`path "${input}" must be relative to the workspace root.`);
8848
8867
  }
8849
8868
  const normalised = normalize(input);
8850
- const abs = resolve5(workspaceRoot, normalised);
8869
+ const abs = resolve6(workspaceRoot, normalised);
8851
8870
  const rel = relative(workspaceRoot, abs);
8852
8871
  if (rel === "" || rel.startsWith("..") || sep3 !== "/" && rel.startsWith(`..${sep3}`)) {
8853
8872
  throw new Error(`path "${input}" escapes the workspace root.`);
@@ -8912,7 +8931,7 @@ var changedSinceInput = {
8912
8931
  };
8913
8932
  var emptyInput = {};
8914
8933
  function registerTools(server, deps) {
8915
- const { storage, retriever, workspaceRoot, workspaceId, workspaceName, regionMode, providerId, storageBackend, reranker, rerankTopN, queryRewriter } = deps;
8934
+ const { storage, embedder, retriever, workspaceRoot, workspaceId, workspaceName, regionMode, providerId, storageBackend, managed, autoIndexable, dbPath, reranker, rerankTopN, queryRewriter } = deps;
8916
8935
  server.registerTool("search_code", {
8917
8936
  title: "Hybrid code search",
8918
8937
  description: (
@@ -9140,6 +9159,50 @@ ${hyp}`;
9140
9159
  languages
9141
9160
  });
9142
9161
  });
9162
+ server.registerTool("index_status", {
9163
+ title: "Index status / health check",
9164
+ description: "Health check for this workspace's code index. Reports the resolved workspace root, the on-disk index path, whether auto-indexing is enabled AND allowed for this root (it is refused for the home directory / filesystem root), how much is indexed (files / symbols / references + per-language counts), the embedding provider, and \u2014 best-effort, zero-cost \u2014 whether embeddings are reachable with the configured key. CALL THIS FIRST when `search_code` returns nothing, when you're unsure whether the right folder is indexed, or to confirm the API key works.",
9165
+ inputSchema: emptyInput
9166
+ }, async () => {
9167
+ const [stats, languages] = await Promise.all([
9168
+ storage.stats(),
9169
+ languageDistribution(storage)
9170
+ ]);
9171
+ const indexed = stats.fileCount > 0;
9172
+ let embeddingsReachable = null;
9173
+ let embeddingsError = null;
9174
+ const resolveIdentity = embedder.resolveIdentity;
9175
+ if (typeof resolveIdentity === "function") {
9176
+ try {
9177
+ await resolveIdentity.call(embedder);
9178
+ embeddingsReachable = true;
9179
+ } catch (err) {
9180
+ embeddingsReachable = false;
9181
+ embeddingsError = err instanceof Error ? err.message : String(err);
9182
+ }
9183
+ }
9184
+ const autoIndexReason = !managed ? "disabled \u2014 no PROMETHEUS_API_KEY (keyword + symbol-graph only)" : autoIndexable ? "enabled \u2014 indexing this workspace" : "SKIPPED \u2014 workspace is your home directory or a filesystem root; open a project folder or set PROMETHEUS_WORKSPACE_ROOT, then restart";
9185
+ const summary = indexed ? `Indexed ${stats.fileCount} files / ${stats.symbolCount} symbols at ${workspaceRoot}.` : managed && !autoIndexable ? `Empty index: auto-indexing was refused because the workspace resolved to ${workspaceRoot} (home/root). Open your project folder and restart.` : managed ? `Empty index: indexing may still be running, or this folder has no supported source files. Workspace: ${workspaceRoot}.` : `Empty index: no API key, so nothing was embedded/indexed. Semantic search is off; lexical + structural tools still work. Workspace: ${workspaceRoot}.`;
9186
+ return textResult({
9187
+ installed: true,
9188
+ workspace: { id: workspaceId, name: workspaceName, root: workspaceRoot },
9189
+ storage: { backend: storageBackend, dbPath, regionMode },
9190
+ autoIndex: { managed, allowedForThisRoot: autoIndexable, reason: autoIndexReason },
9191
+ index: {
9192
+ indexed,
9193
+ files: stats.fileCount,
9194
+ symbols: stats.symbolCount,
9195
+ references: stats.referenceCount,
9196
+ languages
9197
+ },
9198
+ embeddings: {
9199
+ provider: providerId,
9200
+ reachable: embeddingsReachable,
9201
+ ...embeddingsError !== null ? { error: embeddingsError } : {}
9202
+ },
9203
+ summary
9204
+ });
9205
+ });
9143
9206
  }
9144
9207
 
9145
9208
  // dist/server.js
@@ -9225,8 +9288,12 @@ async function main() {
9225
9288
  process.stderr.write(`prometheus-context-mcp: storage=${composed.storageBackend} provider=${composed.providerId} region-mode=${composed.regionMode} managed=${composed.managed} workspace=${composed.workspaceRoot} (via ${via})${composed.dbPath !== null ? ` db=${composed.dbPath}` : ""}
9226
9289
  `);
9227
9290
  registerTools(server, composed);
9228
- if (composed.managed)
9291
+ if (composed.managed && composed.autoIndexable) {
9229
9292
  managedIndexer = startManagedIndexing(composed);
9293
+ } else if (composed.managed && !composed.autoIndexable) {
9294
+ process.stderr.write(`prometheus-context-mcp: workspace resolved to ${composed.workspaceRoot} (your home directory or a filesystem root) \u2014 NOT auto-indexing it. Open a project folder (Claude Code passes it via CLAUDE_PROJECT_DIR) or set PROMETHEUS_WORKSPACE_ROOT to your repo, then restart. Call index_status for details.
9295
+ `);
9296
+ }
9230
9297
  };
9231
9298
  if (eagerVia !== null) {
9232
9299
  await boot(void 0, eagerVia);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prom.codes/context-mcp",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
4
4
  "description": "prom.codes Context — local-first codebase indexing & retrieval as an MCP server.",
5
5
  "type": "module",
6
6
  "bin": {