@prom.codes/context-mcp 0.2.2 → 0.4.0
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/bin.js +590 -39
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// dist/bin.js
|
|
4
|
+
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
6
|
|
|
6
7
|
// ../indexer/dist/languages.js
|
|
@@ -3275,15 +3276,30 @@ var EMBEDDING_DRIFT_CODE = "EMBEDDING_DRIFT";
|
|
|
3275
3276
|
function hasLazyIdentity(embedder) {
|
|
3276
3277
|
return typeof embedder.resolveIdentity === "function";
|
|
3277
3278
|
}
|
|
3279
|
+
var EMBED_BODY_CHARS = 1500;
|
|
3280
|
+
var EMBED_TEXT_VERSION = 2;
|
|
3278
3281
|
function chunkTextForSymbol(sym) {
|
|
3279
3282
|
const container = sym.container === null ? "" : ` in ${sym.container}`;
|
|
3280
3283
|
const exported = sym.exported ? "exported " : "";
|
|
3281
|
-
|
|
3284
|
+
const descriptor = `${exported}${sym.kind} ${sym.name}${container} (${sym.language})`;
|
|
3285
|
+
const body = sym.body?.trim();
|
|
3286
|
+
if (!body)
|
|
3287
|
+
return descriptor;
|
|
3288
|
+
const slice = body.length > EMBED_BODY_CHARS ? body.slice(0, EMBED_BODY_CHARS) : body;
|
|
3289
|
+
return `${descriptor}
|
|
3290
|
+
${sym.filePath}
|
|
3291
|
+
${slice}`;
|
|
3282
3292
|
}
|
|
3283
3293
|
async function embedWorkspace(storage, embedder, options = {}) {
|
|
3284
3294
|
if (hasLazyIdentity(embedder)) {
|
|
3285
3295
|
await embedder.resolveIdentity(options.signal);
|
|
3286
3296
|
}
|
|
3297
|
+
{
|
|
3298
|
+
const existing = await storage.getEmbeddingMeta();
|
|
3299
|
+
if (existing !== null && (existing.embedTextVersion ?? 1) !== EMBED_TEXT_VERSION && existing.provider === embedder.name && existing.model === embedder.model && existing.dim === embedder.dimension) {
|
|
3300
|
+
await storage.clearEmbeddings();
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3287
3303
|
try {
|
|
3288
3304
|
return await runEmbedPass(storage, embedder, options, false);
|
|
3289
3305
|
} catch (err) {
|
|
@@ -3301,7 +3317,8 @@ async function runEmbedPass(storage, embedder, options, driftRecovered) {
|
|
|
3301
3317
|
provider: embedder.name,
|
|
3302
3318
|
model: embedder.model,
|
|
3303
3319
|
dim: embedder.dimension,
|
|
3304
|
-
region: embedder.region
|
|
3320
|
+
region: embedder.region,
|
|
3321
|
+
embedTextVersion: EMBED_TEXT_VERSION
|
|
3305
3322
|
};
|
|
3306
3323
|
await storage.setEmbeddingMeta(meta);
|
|
3307
3324
|
const rows = [];
|
|
@@ -3391,6 +3408,13 @@ var EDGE_TYPES = [
|
|
|
3391
3408
|
"co-change"
|
|
3392
3409
|
];
|
|
3393
3410
|
|
|
3411
|
+
// ../shared/dist/hyde-prompt.js
|
|
3412
|
+
function renderHydePrompt(query) {
|
|
3413
|
+
return `You are a code-retrieval assistant. Given the user query, emit a single hypothetical code snippet (no prose, no markdown fences) that would directly answer it. Use the most common language for the intent. Keep it \u2264 25 lines.
|
|
3414
|
+
|
|
3415
|
+
Query: ${query}`;
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3394
3418
|
// ../shared/dist/sensitive-paths.js
|
|
3395
3419
|
var SENSITIVE_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
3396
3420
|
"secrets",
|
|
@@ -3463,17 +3487,52 @@ var SENSITIVE_PATH_ERROR = "path matches the sensitive-file deny-list";
|
|
|
3463
3487
|
// ../shared/dist/index.js
|
|
3464
3488
|
var PROMETHEUS_VERSION = "0.1.0";
|
|
3465
3489
|
|
|
3466
|
-
// ../indexer/dist/
|
|
3467
|
-
var
|
|
3468
|
-
/
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3490
|
+
// ../indexer/dist/ignore.js
|
|
3491
|
+
var IGNORED_DIR_NAMES = [
|
|
3492
|
+
// VCS + JS/TS
|
|
3493
|
+
".git",
|
|
3494
|
+
"node_modules",
|
|
3495
|
+
"dist",
|
|
3496
|
+
"build",
|
|
3497
|
+
"out",
|
|
3498
|
+
".turbo",
|
|
3499
|
+
".next",
|
|
3500
|
+
"coverage",
|
|
3501
|
+
"bower_components",
|
|
3502
|
+
".yarn",
|
|
3503
|
+
".pnpm-store",
|
|
3504
|
+
".svelte-kit",
|
|
3505
|
+
".nuxt",
|
|
3506
|
+
".output",
|
|
3507
|
+
".expo",
|
|
3508
|
+
".vercel",
|
|
3509
|
+
".nyc_output",
|
|
3510
|
+
// Python
|
|
3511
|
+
".venv",
|
|
3512
|
+
"venv",
|
|
3513
|
+
"__pycache__",
|
|
3514
|
+
".tox",
|
|
3515
|
+
".mypy_cache",
|
|
3516
|
+
".pytest_cache",
|
|
3517
|
+
".ruff_cache",
|
|
3518
|
+
".ipynb_checkpoints",
|
|
3519
|
+
// Rust / Java / JVM / native deps
|
|
3520
|
+
"target",
|
|
3521
|
+
"vendor",
|
|
3522
|
+
".gradle",
|
|
3523
|
+
"Pods",
|
|
3524
|
+
// Generic caches / IDE / temp
|
|
3525
|
+
".cache",
|
|
3526
|
+
".parcel-cache",
|
|
3527
|
+
".idea",
|
|
3528
|
+
"tmp",
|
|
3529
|
+
".tmp"
|
|
3476
3530
|
];
|
|
3531
|
+
var RELATIVE_IGNORED_PATTERNS = IGNORED_DIR_NAMES.map((name) => new RegExp(`(^|/)${name.replace(/\./g, "\\.")}(/|$)`));
|
|
3532
|
+
var WATCHER_IGNORED_PATTERNS = IGNORED_DIR_NAMES.map((name) => new RegExp(`(^|[/\\\\])${name.replace(/\./g, "\\.")}([/\\\\]|$)`));
|
|
3533
|
+
|
|
3534
|
+
// ../indexer/dist/watcher.js
|
|
3535
|
+
var DEFAULT_IGNORED = WATCHER_IGNORED_PATTERNS;
|
|
3477
3536
|
function toRelative(absoluteRoot, abs) {
|
|
3478
3537
|
const root = absoluteRoot.endsWith(sep) ? absoluteRoot : absoluteRoot + sep;
|
|
3479
3538
|
const rel = abs.startsWith(root) ? abs.slice(root.length) : abs;
|
|
@@ -3743,16 +3802,7 @@ function toRelative2(absoluteRoot, abs) {
|
|
|
3743
3802
|
const rel = abs.startsWith(root) ? abs.slice(root.length) : abs;
|
|
3744
3803
|
return rel.split(sep2).join("/");
|
|
3745
3804
|
}
|
|
3746
|
-
var DEFAULT_IGNORED_PATTERNS =
|
|
3747
|
-
/(^|\/)\.git(\/|$)/,
|
|
3748
|
-
/(^|\/)node_modules(\/|$)/,
|
|
3749
|
-
/(^|\/)dist(\/|$)/,
|
|
3750
|
-
/(^|\/)build(\/|$)/,
|
|
3751
|
-
/(^|\/)out(\/|$)/,
|
|
3752
|
-
/(^|\/)\.turbo(\/|$)/,
|
|
3753
|
-
/(^|\/)\.next(\/|$)/,
|
|
3754
|
-
/(^|\/)coverage(\/|$)/
|
|
3755
|
-
];
|
|
3805
|
+
var DEFAULT_IGNORED_PATTERNS = RELATIVE_IGNORED_PATTERNS;
|
|
3756
3806
|
var WorkspaceIndexer = class {
|
|
3757
3807
|
#root;
|
|
3758
3808
|
#storage;
|
|
@@ -4113,6 +4163,7 @@ function rowToStoredSymbol(row) {
|
|
|
4113
4163
|
language: row.language,
|
|
4114
4164
|
container: row.container,
|
|
4115
4165
|
exported: row.exported === 1,
|
|
4166
|
+
body: row.body ?? void 0,
|
|
4116
4167
|
range: {
|
|
4117
4168
|
start: { row: row.start_row, column: row.start_col },
|
|
4118
4169
|
end: { row: row.end_row, column: row.end_col },
|
|
@@ -7531,6 +7582,344 @@ var OpenAICompatRerankProvider = class {
|
|
|
7531
7582
|
}
|
|
7532
7583
|
};
|
|
7533
7584
|
|
|
7585
|
+
// ../rewriter-gemini/dist/index.js
|
|
7586
|
+
var DEFAULT_MODEL4 = "gemini-2.5-flash";
|
|
7587
|
+
var DEFAULT_BASE_URL2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
7588
|
+
var DEFAULT_RETRIES7 = 6;
|
|
7589
|
+
var DEFAULT_BACKOFF7 = 2e3;
|
|
7590
|
+
var DEFAULT_RETRY_MAX4 = 6e4;
|
|
7591
|
+
var DEFAULT_MAX_TOKENS = 256;
|
|
7592
|
+
function parseRetryAfterMs4(value, now = Date.now()) {
|
|
7593
|
+
if (value === null)
|
|
7594
|
+
return null;
|
|
7595
|
+
const trimmed = value.trim();
|
|
7596
|
+
if (trimmed === "")
|
|
7597
|
+
return null;
|
|
7598
|
+
if (/^[0-9]+(\.[0-9]+)?$/.test(trimmed)) {
|
|
7599
|
+
const secs = Number(trimmed);
|
|
7600
|
+
if (!Number.isFinite(secs) || secs < 0)
|
|
7601
|
+
return null;
|
|
7602
|
+
return Math.round(secs * 1e3);
|
|
7603
|
+
}
|
|
7604
|
+
if (!/[A-Za-z]/.test(trimmed))
|
|
7605
|
+
return null;
|
|
7606
|
+
const ts = Date.parse(trimmed);
|
|
7607
|
+
if (!Number.isFinite(ts))
|
|
7608
|
+
return null;
|
|
7609
|
+
const delta = ts - now;
|
|
7610
|
+
return delta > 0 ? delta : 0;
|
|
7611
|
+
}
|
|
7612
|
+
function sleep7(ms, signal) {
|
|
7613
|
+
return new Promise((resolve6, reject) => {
|
|
7614
|
+
if (signal?.aborted === true) {
|
|
7615
|
+
reject(new Error("aborted"));
|
|
7616
|
+
return;
|
|
7617
|
+
}
|
|
7618
|
+
const timer = setTimeout(() => {
|
|
7619
|
+
signal?.removeEventListener("abort", onAbort);
|
|
7620
|
+
resolve6();
|
|
7621
|
+
}, ms);
|
|
7622
|
+
const onAbort = () => {
|
|
7623
|
+
clearTimeout(timer);
|
|
7624
|
+
reject(new Error("aborted"));
|
|
7625
|
+
};
|
|
7626
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7627
|
+
});
|
|
7628
|
+
}
|
|
7629
|
+
function nonRetryable7(message) {
|
|
7630
|
+
const err = new Error(message);
|
|
7631
|
+
err.nonRetryable = true;
|
|
7632
|
+
return err;
|
|
7633
|
+
}
|
|
7634
|
+
var GeminiQueryRewriter = class {
|
|
7635
|
+
name;
|
|
7636
|
+
model;
|
|
7637
|
+
region;
|
|
7638
|
+
#baseUrl;
|
|
7639
|
+
#apiKey;
|
|
7640
|
+
#maxRetries;
|
|
7641
|
+
#retryBaseMs;
|
|
7642
|
+
#retryMaxMs;
|
|
7643
|
+
#fetch;
|
|
7644
|
+
constructor(opts) {
|
|
7645
|
+
if (typeof opts.apiKey !== "string" || opts.apiKey === "") {
|
|
7646
|
+
throw new Error("GeminiQueryRewriter: apiKey is required");
|
|
7647
|
+
}
|
|
7648
|
+
this.model = opts.model ?? DEFAULT_MODEL4;
|
|
7649
|
+
this.name = opts.name ?? `gemini:${this.model}`;
|
|
7650
|
+
this.region = opts.region ?? "us";
|
|
7651
|
+
this.#baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
|
|
7652
|
+
this.#apiKey = opts.apiKey;
|
|
7653
|
+
this.#maxRetries = opts.maxRetries ?? DEFAULT_RETRIES7;
|
|
7654
|
+
this.#retryBaseMs = opts.retryBaseMs ?? DEFAULT_BACKOFF7;
|
|
7655
|
+
this.#retryMaxMs = opts.retryMaxMs ?? DEFAULT_RETRY_MAX4;
|
|
7656
|
+
this.#fetch = opts.fetch ?? fetch;
|
|
7657
|
+
}
|
|
7658
|
+
async rewrite(query, opts) {
|
|
7659
|
+
const trimmed = query.trim();
|
|
7660
|
+
if (trimmed === "")
|
|
7661
|
+
return [];
|
|
7662
|
+
const n = opts?.n ?? 1;
|
|
7663
|
+
if (!Number.isInteger(n) || n < 1)
|
|
7664
|
+
return [];
|
|
7665
|
+
const maxTokens = opts?.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
7666
|
+
const temperature = opts?.temperature ?? 0;
|
|
7667
|
+
const signal = opts?.signal;
|
|
7668
|
+
const body = {
|
|
7669
|
+
contents: [
|
|
7670
|
+
{
|
|
7671
|
+
role: "user",
|
|
7672
|
+
parts: [{ text: renderHydePrompt(trimmed) }]
|
|
7673
|
+
}
|
|
7674
|
+
],
|
|
7675
|
+
generationConfig: {
|
|
7676
|
+
temperature,
|
|
7677
|
+
maxOutputTokens: maxTokens,
|
|
7678
|
+
candidateCount: n
|
|
7679
|
+
}
|
|
7680
|
+
};
|
|
7681
|
+
try {
|
|
7682
|
+
const payload = await this.#postWithRetry(body, signal);
|
|
7683
|
+
return this.#decode(payload, n);
|
|
7684
|
+
} catch {
|
|
7685
|
+
return [];
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7688
|
+
async #postWithRetry(body, signal) {
|
|
7689
|
+
const url = `${this.#baseUrl}/models/${encodeURIComponent(this.model)}:generateContent`;
|
|
7690
|
+
const init = {
|
|
7691
|
+
method: "POST",
|
|
7692
|
+
headers: {
|
|
7693
|
+
"content-type": "application/json",
|
|
7694
|
+
"x-goog-api-key": this.#apiKey
|
|
7695
|
+
},
|
|
7696
|
+
body: JSON.stringify(body)
|
|
7697
|
+
};
|
|
7698
|
+
if (signal !== void 0)
|
|
7699
|
+
init.signal = signal;
|
|
7700
|
+
let attempt = 0;
|
|
7701
|
+
let lastError = null;
|
|
7702
|
+
while (attempt <= this.#maxRetries) {
|
|
7703
|
+
try {
|
|
7704
|
+
const res = await this.#fetch(url, init);
|
|
7705
|
+
if (res.status === 429 || res.status >= 500 && res.status < 600) {
|
|
7706
|
+
lastError = new Error(`${this.name}: HTTP ${res.status}`);
|
|
7707
|
+
attempt += 1;
|
|
7708
|
+
if (attempt > this.#maxRetries)
|
|
7709
|
+
break;
|
|
7710
|
+
const backoff = this.#computeBackoff(attempt, res.headers.get("retry-after"));
|
|
7711
|
+
await sleep7(backoff, signal);
|
|
7712
|
+
continue;
|
|
7713
|
+
}
|
|
7714
|
+
if (!res.ok) {
|
|
7715
|
+
const text = await res.text().catch(() => "");
|
|
7716
|
+
throw nonRetryable7(`${this.name}: HTTP ${res.status} ${res.statusText}` + (text === "" ? "" : ` \u2014 ${text}`));
|
|
7717
|
+
}
|
|
7718
|
+
return await res.json();
|
|
7719
|
+
} catch (err) {
|
|
7720
|
+
if (err?.name === "AbortError")
|
|
7721
|
+
throw err;
|
|
7722
|
+
if (err?.nonRetryable === true) {
|
|
7723
|
+
throw err;
|
|
7724
|
+
}
|
|
7725
|
+
if (attempt >= this.#maxRetries)
|
|
7726
|
+
throw err;
|
|
7727
|
+
lastError = err;
|
|
7728
|
+
attempt += 1;
|
|
7729
|
+
await sleep7(this.#computeBackoff(attempt, null), signal);
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
throw lastError instanceof Error ? lastError : new Error(`${this.name}: exhausted ${this.#maxRetries} retries`);
|
|
7733
|
+
}
|
|
7734
|
+
#computeBackoff(attempt, retryAfterHeader) {
|
|
7735
|
+
const exp = this.#retryBaseMs * 2 ** Math.max(0, attempt - 1);
|
|
7736
|
+
const advised = parseRetryAfterMs4(retryAfterHeader);
|
|
7737
|
+
const lower = advised === null ? exp : Math.max(exp, advised);
|
|
7738
|
+
return Math.min(lower, this.#retryMaxMs);
|
|
7739
|
+
}
|
|
7740
|
+
#decode(payload, requested) {
|
|
7741
|
+
const candidates = payload.candidates ?? [];
|
|
7742
|
+
const out = [];
|
|
7743
|
+
for (const cand of candidates) {
|
|
7744
|
+
const parts = cand.content?.parts ?? [];
|
|
7745
|
+
const text = parts.map((p) => typeof p.text === "string" ? p.text : "").join("");
|
|
7746
|
+
const trimmed = text.trim();
|
|
7747
|
+
if (trimmed !== "")
|
|
7748
|
+
out.push(trimmed);
|
|
7749
|
+
if (out.length >= requested)
|
|
7750
|
+
break;
|
|
7751
|
+
}
|
|
7752
|
+
return out;
|
|
7753
|
+
}
|
|
7754
|
+
};
|
|
7755
|
+
|
|
7756
|
+
// ../rewriter-mistral/dist/index.js
|
|
7757
|
+
var DEFAULT_MODEL5 = "mistral-small-latest";
|
|
7758
|
+
var DEFAULT_BASE_URL3 = "https://api.mistral.ai/v1";
|
|
7759
|
+
var DEFAULT_RETRIES8 = 6;
|
|
7760
|
+
var DEFAULT_BACKOFF8 = 2e3;
|
|
7761
|
+
var DEFAULT_RETRY_MAX5 = 6e4;
|
|
7762
|
+
var DEFAULT_MAX_TOKENS2 = 256;
|
|
7763
|
+
function parseRetryAfterMs5(value, now = Date.now()) {
|
|
7764
|
+
if (value === null)
|
|
7765
|
+
return null;
|
|
7766
|
+
const trimmed = value.trim();
|
|
7767
|
+
if (trimmed === "")
|
|
7768
|
+
return null;
|
|
7769
|
+
if (/^[0-9]+(\.[0-9]+)?$/.test(trimmed)) {
|
|
7770
|
+
const secs = Number(trimmed);
|
|
7771
|
+
if (!Number.isFinite(secs) || secs < 0)
|
|
7772
|
+
return null;
|
|
7773
|
+
return Math.round(secs * 1e3);
|
|
7774
|
+
}
|
|
7775
|
+
if (!/[A-Za-z]/.test(trimmed))
|
|
7776
|
+
return null;
|
|
7777
|
+
const ts = Date.parse(trimmed);
|
|
7778
|
+
if (!Number.isFinite(ts))
|
|
7779
|
+
return null;
|
|
7780
|
+
const delta = ts - now;
|
|
7781
|
+
return delta > 0 ? delta : 0;
|
|
7782
|
+
}
|
|
7783
|
+
function sleep8(ms, signal) {
|
|
7784
|
+
return new Promise((resolve6, reject) => {
|
|
7785
|
+
if (signal?.aborted === true) {
|
|
7786
|
+
reject(new Error("aborted"));
|
|
7787
|
+
return;
|
|
7788
|
+
}
|
|
7789
|
+
const timer = setTimeout(() => {
|
|
7790
|
+
signal?.removeEventListener("abort", onAbort);
|
|
7791
|
+
resolve6();
|
|
7792
|
+
}, ms);
|
|
7793
|
+
const onAbort = () => {
|
|
7794
|
+
clearTimeout(timer);
|
|
7795
|
+
reject(new Error("aborted"));
|
|
7796
|
+
};
|
|
7797
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7798
|
+
});
|
|
7799
|
+
}
|
|
7800
|
+
function nonRetryable8(message) {
|
|
7801
|
+
const err = new Error(message);
|
|
7802
|
+
err.nonRetryable = true;
|
|
7803
|
+
return err;
|
|
7804
|
+
}
|
|
7805
|
+
var MistralQueryRewriter = class {
|
|
7806
|
+
name;
|
|
7807
|
+
model;
|
|
7808
|
+
region;
|
|
7809
|
+
#baseUrl;
|
|
7810
|
+
#apiKey;
|
|
7811
|
+
#maxRetries;
|
|
7812
|
+
#retryBaseMs;
|
|
7813
|
+
#retryMaxMs;
|
|
7814
|
+
#fetch;
|
|
7815
|
+
constructor(opts) {
|
|
7816
|
+
if (typeof opts.apiKey !== "string" || opts.apiKey === "") {
|
|
7817
|
+
throw new Error("MistralQueryRewriter: apiKey is required");
|
|
7818
|
+
}
|
|
7819
|
+
this.model = opts.model ?? DEFAULT_MODEL5;
|
|
7820
|
+
this.name = opts.name ?? `mistral:${this.model}`;
|
|
7821
|
+
this.region = opts.region ?? "eu";
|
|
7822
|
+
this.#baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL3).replace(/\/+$/, "");
|
|
7823
|
+
this.#apiKey = opts.apiKey;
|
|
7824
|
+
this.#maxRetries = opts.maxRetries ?? DEFAULT_RETRIES8;
|
|
7825
|
+
this.#retryBaseMs = opts.retryBaseMs ?? DEFAULT_BACKOFF8;
|
|
7826
|
+
this.#retryMaxMs = opts.retryMaxMs ?? DEFAULT_RETRY_MAX5;
|
|
7827
|
+
this.#fetch = opts.fetch ?? fetch;
|
|
7828
|
+
}
|
|
7829
|
+
async rewrite(query, opts) {
|
|
7830
|
+
const trimmed = query.trim();
|
|
7831
|
+
if (trimmed === "")
|
|
7832
|
+
return [];
|
|
7833
|
+
const n = opts?.n ?? 1;
|
|
7834
|
+
if (!Number.isInteger(n) || n < 1)
|
|
7835
|
+
return [];
|
|
7836
|
+
const maxTokens = opts?.maxTokens ?? DEFAULT_MAX_TOKENS2;
|
|
7837
|
+
const temperature = opts?.temperature ?? 0;
|
|
7838
|
+
const signal = opts?.signal;
|
|
7839
|
+
const body = {
|
|
7840
|
+
model: this.model,
|
|
7841
|
+
messages: [
|
|
7842
|
+
{ role: "user", content: renderHydePrompt(trimmed) }
|
|
7843
|
+
],
|
|
7844
|
+
temperature,
|
|
7845
|
+
max_tokens: maxTokens,
|
|
7846
|
+
n
|
|
7847
|
+
};
|
|
7848
|
+
try {
|
|
7849
|
+
const payload = await this.#postWithRetry(body, signal);
|
|
7850
|
+
return this.#decode(payload, n);
|
|
7851
|
+
} catch {
|
|
7852
|
+
return [];
|
|
7853
|
+
}
|
|
7854
|
+
}
|
|
7855
|
+
async #postWithRetry(body, signal) {
|
|
7856
|
+
const url = `${this.#baseUrl}/chat/completions`;
|
|
7857
|
+
const init = {
|
|
7858
|
+
method: "POST",
|
|
7859
|
+
headers: {
|
|
7860
|
+
"content-type": "application/json",
|
|
7861
|
+
"accept": "application/json",
|
|
7862
|
+
"authorization": `Bearer ${this.#apiKey}`
|
|
7863
|
+
},
|
|
7864
|
+
body: JSON.stringify(body)
|
|
7865
|
+
};
|
|
7866
|
+
if (signal !== void 0)
|
|
7867
|
+
init.signal = signal;
|
|
7868
|
+
let attempt = 0;
|
|
7869
|
+
let lastError = null;
|
|
7870
|
+
while (attempt <= this.#maxRetries) {
|
|
7871
|
+
try {
|
|
7872
|
+
const res = await this.#fetch(url, init);
|
|
7873
|
+
if (res.status === 429 || res.status >= 500 && res.status < 600) {
|
|
7874
|
+
lastError = new Error(`${this.name}: HTTP ${res.status}`);
|
|
7875
|
+
attempt += 1;
|
|
7876
|
+
if (attempt > this.#maxRetries)
|
|
7877
|
+
break;
|
|
7878
|
+
const backoff = this.#computeBackoff(attempt, res.headers.get("retry-after"));
|
|
7879
|
+
await sleep8(backoff, signal);
|
|
7880
|
+
continue;
|
|
7881
|
+
}
|
|
7882
|
+
if (!res.ok) {
|
|
7883
|
+
const text = await res.text().catch(() => "");
|
|
7884
|
+
throw nonRetryable8(`${this.name}: HTTP ${res.status} ${res.statusText}` + (text === "" ? "" : ` \u2014 ${text}`));
|
|
7885
|
+
}
|
|
7886
|
+
return await res.json();
|
|
7887
|
+
} catch (err) {
|
|
7888
|
+
if (err?.name === "AbortError")
|
|
7889
|
+
throw err;
|
|
7890
|
+
if (err?.nonRetryable === true) {
|
|
7891
|
+
throw err;
|
|
7892
|
+
}
|
|
7893
|
+
if (attempt >= this.#maxRetries)
|
|
7894
|
+
throw err;
|
|
7895
|
+
lastError = err;
|
|
7896
|
+
attempt += 1;
|
|
7897
|
+
await sleep8(this.#computeBackoff(attempt, null), signal);
|
|
7898
|
+
}
|
|
7899
|
+
}
|
|
7900
|
+
throw lastError instanceof Error ? lastError : new Error(`${this.name}: exhausted ${this.#maxRetries} retries`);
|
|
7901
|
+
}
|
|
7902
|
+
#computeBackoff(attempt, retryAfterHeader) {
|
|
7903
|
+
const exp = this.#retryBaseMs * 2 ** Math.max(0, attempt - 1);
|
|
7904
|
+
const advised = parseRetryAfterMs5(retryAfterHeader);
|
|
7905
|
+
const lower = advised === null ? exp : Math.max(exp, advised);
|
|
7906
|
+
return Math.min(lower, this.#retryMaxMs);
|
|
7907
|
+
}
|
|
7908
|
+
#decode(payload, requested) {
|
|
7909
|
+
const choices = payload.choices ?? [];
|
|
7910
|
+
const out = [];
|
|
7911
|
+
for (const c of choices) {
|
|
7912
|
+
const text = c.message?.content ?? "";
|
|
7913
|
+
const trimmed = text.trim();
|
|
7914
|
+
if (trimmed !== "")
|
|
7915
|
+
out.push(trimmed);
|
|
7916
|
+
if (out.length >= requested)
|
|
7917
|
+
break;
|
|
7918
|
+
}
|
|
7919
|
+
return out;
|
|
7920
|
+
}
|
|
7921
|
+
};
|
|
7922
|
+
|
|
7534
7923
|
// dist/composition.js
|
|
7535
7924
|
var RegionModeViolation = class extends Error {
|
|
7536
7925
|
mode;
|
|
@@ -7816,6 +8205,55 @@ function discoverRerankProvider(env, fetchImpl) {
|
|
|
7816
8205
|
}
|
|
7817
8206
|
throw new NoProviderError(`unknown PROMETHEUS_RERANK_PROVIDER="${forced}" (expected "none", "voyage", or "bge")`);
|
|
7818
8207
|
}
|
|
8208
|
+
function discoverQueryRewriter(env, fetchImpl) {
|
|
8209
|
+
const regionMode = parseRegionMode(env.PROMETHEUS_REGION_MODE);
|
|
8210
|
+
const forced = env.PROMETHEUS_REWRITER_PROVIDER?.toLowerCase() ?? "none";
|
|
8211
|
+
if (forced === "" || forced === "none")
|
|
8212
|
+
return { id: "none", provider: null };
|
|
8213
|
+
if (forced === "gemini") {
|
|
8214
|
+
const apiKey = env.GEMINI_API_KEY;
|
|
8215
|
+
if (apiKey === void 0 || apiKey === "") {
|
|
8216
|
+
throw new NoProviderError(`rewriter provider "gemini" requested but GEMINI_API_KEY is missing`);
|
|
8217
|
+
}
|
|
8218
|
+
const region = "us";
|
|
8219
|
+
if (regionMode !== "default") {
|
|
8220
|
+
throw new RegionModeViolation(regionMode, "gemini", region, regionMode === "eu-strict" ? ["mistral"] : []);
|
|
8221
|
+
}
|
|
8222
|
+
const model = env.GEMINI_REWRITER_MODEL ?? "gemini-2.5-flash";
|
|
8223
|
+
const provider = new GeminiQueryRewriter({
|
|
8224
|
+
name: `gemini:${model}`,
|
|
8225
|
+
apiKey,
|
|
8226
|
+
model,
|
|
8227
|
+
region,
|
|
8228
|
+
maxRetries: intEnv(env, "GEMINI_REWRITER_MAX_RETRIES", 6),
|
|
8229
|
+
retryBaseMs: intEnv(env, "GEMINI_REWRITER_RETRY_BASE_MS", 2e3),
|
|
8230
|
+
...fetchOpt(fetchImpl)
|
|
8231
|
+
});
|
|
8232
|
+
return { id: "gemini", provider };
|
|
8233
|
+
}
|
|
8234
|
+
if (forced === "mistral") {
|
|
8235
|
+
const apiKey = env.MISTRAL_API_KEY;
|
|
8236
|
+
if (apiKey === void 0 || apiKey === "") {
|
|
8237
|
+
throw new NoProviderError(`rewriter provider "mistral" requested but MISTRAL_API_KEY is missing`);
|
|
8238
|
+
}
|
|
8239
|
+
const region = "eu";
|
|
8240
|
+
if (regionMode === "oss-only") {
|
|
8241
|
+
throw new RegionModeViolation(regionMode, "mistral", region, []);
|
|
8242
|
+
}
|
|
8243
|
+
const model = env.MISTRAL_REWRITER_MODEL ?? "mistral-small-latest";
|
|
8244
|
+
const provider = new MistralQueryRewriter({
|
|
8245
|
+
name: `mistral:${model}`,
|
|
8246
|
+
apiKey,
|
|
8247
|
+
model,
|
|
8248
|
+
region,
|
|
8249
|
+
maxRetries: intEnv(env, "MISTRAL_REWRITER_MAX_RETRIES", 6),
|
|
8250
|
+
retryBaseMs: intEnv(env, "MISTRAL_REWRITER_RETRY_BASE_MS", 2e3),
|
|
8251
|
+
...fetchOpt(fetchImpl)
|
|
8252
|
+
});
|
|
8253
|
+
return { id: "mistral", provider };
|
|
8254
|
+
}
|
|
8255
|
+
throw new NoProviderError(`unknown PROMETHEUS_REWRITER_PROVIDER="${forced}" (expected "none", "gemini", or "mistral")`);
|
|
8256
|
+
}
|
|
7819
8257
|
function getStableDbPath(workspaceRoot) {
|
|
7820
8258
|
const abs = resolve4(workspaceRoot);
|
|
7821
8259
|
const hash = createHash3("sha256").update(abs).digest("hex").slice(0, 16);
|
|
@@ -7890,7 +8328,9 @@ function discoverStorageBackend(env, regionMode, options = {}) {
|
|
|
7890
8328
|
}
|
|
7891
8329
|
async function composeFromEnv(opts) {
|
|
7892
8330
|
const env = opts.env;
|
|
7893
|
-
const
|
|
8331
|
+
const override = (opts.workspaceRootOverride ?? "").trim();
|
|
8332
|
+
const envRoot = (env.PROMETHEUS_WORKSPACE_ROOT ?? "").trim();
|
|
8333
|
+
const workspaceRoot = override !== "" ? override : envRoot !== "" ? envRoot : process.cwd();
|
|
7894
8334
|
const workspaceId = (env.PROMETHEUS_WORKSPACE_ID ?? "") !== "" ? env.PROMETHEUS_WORKSPACE_ID : workspaceRoot;
|
|
7895
8335
|
const workspaceName = (env.PROMETHEUS_WORKSPACE_NAME ?? "") !== "" ? env.PROMETHEUS_WORKSPACE_NAME : basename5(workspaceRoot) || workspaceRoot;
|
|
7896
8336
|
const { id: providerId, provider: embedder, regionMode } = discoverEmbeddingProvider(env, opts.fetch);
|
|
@@ -7901,6 +8341,7 @@ async function composeFromEnv(opts) {
|
|
|
7901
8341
|
const retriever = new HybridRetriever({ storage, embedder });
|
|
7902
8342
|
const { id: rerankId, provider: reranker } = discoverRerankProvider(env, opts.fetch);
|
|
7903
8343
|
const rerankTopN = intEnv(env, "PROMETHEUS_RERANK_TOP_N", 100);
|
|
8344
|
+
const { id: queryRewriterId, provider: queryRewriter } = discoverQueryRewriter(env, opts.fetch);
|
|
7904
8345
|
const managed = apiKeyPresent && storageBackend === "sqlite";
|
|
7905
8346
|
let closed = false;
|
|
7906
8347
|
return {
|
|
@@ -7918,6 +8359,8 @@ async function composeFromEnv(opts) {
|
|
|
7918
8359
|
reranker,
|
|
7919
8360
|
rerankId,
|
|
7920
8361
|
rerankTopN,
|
|
8362
|
+
queryRewriter,
|
|
8363
|
+
queryRewriterId,
|
|
7921
8364
|
async close() {
|
|
7922
8365
|
if (closed)
|
|
7923
8366
|
return;
|
|
@@ -7927,6 +8370,36 @@ async function composeFromEnv(opts) {
|
|
|
7927
8370
|
};
|
|
7928
8371
|
}
|
|
7929
8372
|
|
|
8373
|
+
// dist/roots.js
|
|
8374
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8375
|
+
async function rootFromClient(server, timeoutMs = 2500) {
|
|
8376
|
+
let supportsRoots = false;
|
|
8377
|
+
try {
|
|
8378
|
+
supportsRoots = server.getClientCapabilities()?.roots != null;
|
|
8379
|
+
} catch {
|
|
8380
|
+
return null;
|
|
8381
|
+
}
|
|
8382
|
+
if (!supportsRoots)
|
|
8383
|
+
return null;
|
|
8384
|
+
let res;
|
|
8385
|
+
try {
|
|
8386
|
+
res = await server.listRoots(void 0, { timeout: timeoutMs });
|
|
8387
|
+
} catch {
|
|
8388
|
+
return null;
|
|
8389
|
+
}
|
|
8390
|
+
const roots = res?.roots ?? [];
|
|
8391
|
+
for (const r of roots) {
|
|
8392
|
+
const uri = typeof r?.uri === "string" ? r.uri : "";
|
|
8393
|
+
if (uri.startsWith("file://")) {
|
|
8394
|
+
try {
|
|
8395
|
+
return fileURLToPath2(uri);
|
|
8396
|
+
} catch {
|
|
8397
|
+
}
|
|
8398
|
+
}
|
|
8399
|
+
}
|
|
8400
|
+
return null;
|
|
8401
|
+
}
|
|
8402
|
+
|
|
7930
8403
|
// dist/server.js
|
|
7931
8404
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7932
8405
|
|
|
@@ -8072,6 +8545,34 @@ var DEFAULT_K2 = 10;
|
|
|
8072
8545
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
8073
8546
|
var MAX_SNIPPET_BYTES = 1500;
|
|
8074
8547
|
var RERANK_DOC_BYTES = 4096;
|
|
8548
|
+
var DEMOTE_NONSOURCE = process.env.PROMETHEUS_DEMOTE_NONSOURCE !== "off";
|
|
8549
|
+
function isNonSourcePath(filePath) {
|
|
8550
|
+
const p = filePath.replace(/\\/g, "/").toLowerCase();
|
|
8551
|
+
if (/(^|\/)(tests?|testing|doc|docs|examples?|fixtures?|\.github|benchmarks?|samples?)\//.test(p))
|
|
8552
|
+
return true;
|
|
8553
|
+
if (/(^|\/)(test_[^/]*|conftest)\.py$|(_test|\.test|\.spec)\.[a-z]+$/.test(p))
|
|
8554
|
+
return true;
|
|
8555
|
+
if (/\.(txt|md|rst|cfg|toml|ini|lock|json|ya?ml)$/.test(p))
|
|
8556
|
+
return true;
|
|
8557
|
+
return false;
|
|
8558
|
+
}
|
|
8559
|
+
var NONSOURCE_INTENT = /\b(tests?|testing|specs?|fixtures?|docs?|documentation|examples?|benchmarks?|samples?)\b/i;
|
|
8560
|
+
function queryWantsNonSource(query) {
|
|
8561
|
+
const words = query.trim().split(/\s+/);
|
|
8562
|
+
if (words.length > 12)
|
|
8563
|
+
return false;
|
|
8564
|
+
return NONSOURCE_INTENT.test(query);
|
|
8565
|
+
}
|
|
8566
|
+
function demoteNonSource(ordered, query) {
|
|
8567
|
+
if (!DEMOTE_NONSOURCE || queryWantsNonSource(query))
|
|
8568
|
+
return [...ordered];
|
|
8569
|
+
const source = [];
|
|
8570
|
+
const nonSource = [];
|
|
8571
|
+
for (const r of ordered) {
|
|
8572
|
+
(isNonSourcePath(r.symbol.filePath) ? nonSource : source).push(r);
|
|
8573
|
+
}
|
|
8574
|
+
return source.concat(nonSource);
|
|
8575
|
+
}
|
|
8075
8576
|
function symbolToJson(s) {
|
|
8076
8577
|
return {
|
|
8077
8578
|
name: s.name,
|
|
@@ -8228,17 +8729,34 @@ var changedSinceInput = {
|
|
|
8228
8729
|
};
|
|
8229
8730
|
var emptyInput = {};
|
|
8230
8731
|
function registerTools(server, deps) {
|
|
8231
|
-
const { storage, retriever, workspaceRoot, workspaceId, workspaceName, regionMode, providerId, storageBackend, reranker, rerankTopN } = deps;
|
|
8732
|
+
const { storage, retriever, workspaceRoot, workspaceId, workspaceName, regionMode, providerId, storageBackend, reranker, rerankTopN, queryRewriter } = deps;
|
|
8232
8733
|
server.registerTool("search_code", {
|
|
8233
8734
|
title: "Hybrid code search",
|
|
8234
|
-
description:
|
|
8735
|
+
description: (
|
|
8736
|
+
// Adoption lever (B3, 2026-06-22): assertive + HONEST positioning so the
|
|
8737
|
+
// agent reaches for this instead of a grep-then-read loop on the queries
|
|
8738
|
+
// where semantic search genuinely wins. Claims here are true by design.
|
|
8739
|
+
'PRIMARY code search for this workspace \u2014 the fast path to WHERE code lives. Hybrid retrieval (lexical FTS + dense vector over the actual source body + symbol-graph, RRF-fused, cross-encoder reranked) returns the top symbols with provenance AND an inline source snippet per hit \u2014 ONE call that replaces a manual Grep\u2192Read loop, so prefer it for any code-location question. USE THIS FIRST when: you need a concept or behaviour ("where is auth handled?"), the exact symbol name is unknown or may have been renamed, the term is generic/high-frequency (grep would flood), or the repo is large or cross-language. Plain Grep is fine for an exact, known literal string in a known file \u2014 this tool wins on everything else, and returns the code inline so you usually don\'t need a follow-up read. Set `includeSnippet: false` for symbols only.'
|
|
8740
|
+
),
|
|
8235
8741
|
inputSchema: searchInput
|
|
8236
8742
|
}, async (args) => {
|
|
8237
8743
|
const k = clampK(args.k);
|
|
8238
8744
|
const includeSnippet = args.includeSnippet ?? true;
|
|
8239
8745
|
const cache = /* @__PURE__ */ new Map();
|
|
8240
8746
|
const poolK = reranker ? Math.max(k, rerankTopN ?? 100) : k;
|
|
8241
|
-
|
|
8747
|
+
let searchQuery = args.query;
|
|
8748
|
+
if (queryRewriter) {
|
|
8749
|
+
try {
|
|
8750
|
+
const docs = await queryRewriter.rewrite(args.query, { n: 1, maxTokens: 512 });
|
|
8751
|
+
const hyp = docs[0]?.trim();
|
|
8752
|
+
if (hyp)
|
|
8753
|
+
searchQuery = `${args.query}
|
|
8754
|
+
|
|
8755
|
+
${hyp}`;
|
|
8756
|
+
} catch {
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
const pool = await retriever.search(searchQuery, { k: poolK });
|
|
8242
8760
|
let ordered = pool;
|
|
8243
8761
|
let reranked = false;
|
|
8244
8762
|
if (reranker && pool.length > 0) {
|
|
@@ -8250,6 +8768,7 @@ function registerTools(server, deps) {
|
|
|
8250
8768
|
reranked = true;
|
|
8251
8769
|
}
|
|
8252
8770
|
}
|
|
8771
|
+
ordered = demoteNonSource(ordered, args.query);
|
|
8253
8772
|
const results = ordered.slice(0, k);
|
|
8254
8773
|
const mapped = await Promise.all(results.map(async (r) => {
|
|
8255
8774
|
const base = {
|
|
@@ -8426,13 +8945,6 @@ var SERVER_IDENTITY = {
|
|
|
8426
8945
|
version: PROMETHEUS_VERSION,
|
|
8427
8946
|
title: "prom.codes Context"
|
|
8428
8947
|
};
|
|
8429
|
-
function createServer(deps, options = {}) {
|
|
8430
|
-
const identity = { ...SERVER_IDENTITY, ...options.identity ?? {} };
|
|
8431
|
-
const capabilities = options.capabilities ?? { tools: {} };
|
|
8432
|
-
const server = new McpServer(identity, { capabilities });
|
|
8433
|
-
registerTools(server, deps);
|
|
8434
|
-
return server;
|
|
8435
|
-
}
|
|
8436
8948
|
|
|
8437
8949
|
// dist/bin.js
|
|
8438
8950
|
function errMessage(err) {
|
|
@@ -8470,12 +8982,12 @@ function startManagedIndexing(composed) {
|
|
|
8470
8982
|
return indexer;
|
|
8471
8983
|
}
|
|
8472
8984
|
async function main() {
|
|
8473
|
-
const
|
|
8474
|
-
|
|
8475
|
-
`);
|
|
8476
|
-
const server = createServer(composed);
|
|
8985
|
+
const env = process.env;
|
|
8986
|
+
const explicitRoot = (env.PROMETHEUS_WORKSPACE_ROOT ?? "").trim();
|
|
8477
8987
|
const transport = new StdioServerTransport();
|
|
8478
|
-
const
|
|
8988
|
+
const server = new McpServer2(SERVER_IDENTITY, { capabilities: { tools: {} } });
|
|
8989
|
+
let composed = null;
|
|
8990
|
+
let managedIndexer = null;
|
|
8479
8991
|
const shutdown = async (signal) => {
|
|
8480
8992
|
process.stderr.write(`prometheus-context-mcp: received ${signal}, shutting down
|
|
8481
8993
|
`);
|
|
@@ -8488,13 +9000,52 @@ async function main() {
|
|
|
8488
9000
|
} catch {
|
|
8489
9001
|
}
|
|
8490
9002
|
}
|
|
8491
|
-
|
|
9003
|
+
if (composed !== null)
|
|
9004
|
+
await composed.close();
|
|
8492
9005
|
process.exit(0);
|
|
8493
9006
|
}
|
|
8494
9007
|
};
|
|
8495
9008
|
process.once("SIGINT", shutdown);
|
|
8496
9009
|
process.once("SIGTERM", shutdown);
|
|
9010
|
+
const boot = async (override, via) => {
|
|
9011
|
+
composed = await composeFromEnv({
|
|
9012
|
+
env,
|
|
9013
|
+
...override !== void 0 && override !== "" ? { workspaceRootOverride: override } : {}
|
|
9014
|
+
});
|
|
9015
|
+
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}` : ""}
|
|
9016
|
+
`);
|
|
9017
|
+
registerTools(server, composed);
|
|
9018
|
+
if (composed.managed)
|
|
9019
|
+
managedIndexer = startManagedIndexing(composed);
|
|
9020
|
+
};
|
|
9021
|
+
if (explicitRoot !== "") {
|
|
9022
|
+
await boot(void 0, "PROMETHEUS_WORKSPACE_ROOT");
|
|
9023
|
+
await server.connect(transport);
|
|
9024
|
+
return;
|
|
9025
|
+
}
|
|
9026
|
+
let booted = false;
|
|
9027
|
+
const resolveAndBoot = async () => {
|
|
9028
|
+
if (booted)
|
|
9029
|
+
return;
|
|
9030
|
+
booted = true;
|
|
9031
|
+
try {
|
|
9032
|
+
const fromRoots = await rootFromClient(server.server);
|
|
9033
|
+
if (fromRoots !== null)
|
|
9034
|
+
await boot(fromRoots, "MCP roots");
|
|
9035
|
+
else
|
|
9036
|
+
await boot(process.cwd(), "cwd fallback (client advertised no roots)");
|
|
9037
|
+
} catch (err) {
|
|
9038
|
+
process.stderr.write(`prometheus-context-mcp: fatal during boot: ${errMessage(err)}
|
|
9039
|
+
`);
|
|
9040
|
+
process.exit(1);
|
|
9041
|
+
}
|
|
9042
|
+
};
|
|
9043
|
+
server.server.oninitialized = () => {
|
|
9044
|
+
void resolveAndBoot();
|
|
9045
|
+
};
|
|
8497
9046
|
await server.connect(transport);
|
|
9047
|
+
const t = setTimeout(() => void resolveAndBoot(), 5e3);
|
|
9048
|
+
t.unref?.();
|
|
8498
9049
|
}
|
|
8499
9050
|
main().catch((err) => {
|
|
8500
9051
|
const message = err instanceof Error ? err.message : String(err);
|