trace-mcp 1.7.0 → 1.8.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/index.js CHANGED
@@ -4,10 +4,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
+ }) : x)(function(x) {
10
+ if (typeof require !== "undefined") return require.apply(this, arguments);
11
+ throw Error('Dynamic require of "' + x + '" is not supported');
12
+ });
7
13
  var __esm = (fn, res) => function __init() {
8
14
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
15
  };
10
- var __commonJS = (cb, mod) => function __require() {
16
+ var __commonJS = (cb, mod) => function __require2() {
11
17
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
18
  };
13
19
  var __export = (target, all) => {
@@ -1106,7 +1112,7 @@ var require_parse = __commonJS({
1106
1112
  }
1107
1113
  return { risky: false };
1108
1114
  };
1109
- var parse = (input, options) => {
1115
+ var parse2 = (input, options) => {
1110
1116
  if (typeof input !== "string") {
1111
1117
  throw new TypeError("Expected a string");
1112
1118
  }
@@ -1276,7 +1282,7 @@ var require_parse = __commonJS({
1276
1282
  output = token.close = `)$))${extglobStar}`;
1277
1283
  }
1278
1284
  if (token.inner.includes("*") && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
1279
- const expression = parse(rest, { ...options, fastpaths: false }).output;
1285
+ const expression = parse2(rest, { ...options, fastpaths: false }).output;
1280
1286
  output = token.close = `)${expression})${extglobStar})`;
1281
1287
  }
1282
1288
  if (token.prev.type === "bos") {
@@ -1798,7 +1804,7 @@ var require_parse = __commonJS({
1798
1804
  }
1799
1805
  return state;
1800
1806
  };
1801
- parse.fastpaths = (input, options) => {
1807
+ parse2.fastpaths = (input, options) => {
1802
1808
  const opts = { ...options };
1803
1809
  const max = typeof opts.maxLength === "number" ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
1804
1810
  const len = input.length;
@@ -1863,7 +1869,7 @@ var require_parse = __commonJS({
1863
1869
  }
1864
1870
  return source;
1865
1871
  };
1866
- module.exports = parse;
1872
+ module.exports = parse2;
1867
1873
  }
1868
1874
  });
1869
1875
 
@@ -1872,7 +1878,7 @@ var require_picomatch = __commonJS({
1872
1878
  "node_modules/picomatch/lib/picomatch.js"(exports, module) {
1873
1879
  "use strict";
1874
1880
  var scan = require_scan();
1875
- var parse = require_parse();
1881
+ var parse2 = require_parse();
1876
1882
  var utils = require_utils();
1877
1883
  var constants = require_constants();
1878
1884
  var isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
@@ -1960,7 +1966,7 @@ var require_picomatch = __commonJS({
1960
1966
  picomatch3.isMatch = (str2, patterns, options) => picomatch3(patterns, options)(str2);
1961
1967
  picomatch3.parse = (pattern, options) => {
1962
1968
  if (Array.isArray(pattern)) return pattern.map((p) => picomatch3.parse(p, options));
1963
- return parse(pattern, { ...options, fastpaths: false });
1969
+ return parse2(pattern, { ...options, fastpaths: false });
1964
1970
  };
1965
1971
  picomatch3.scan = (input, options) => scan(input, options);
1966
1972
  picomatch3.compileRe = (state, options, returnOutput = false, returnState = false) => {
@@ -1986,10 +1992,10 @@ var require_picomatch = __commonJS({
1986
1992
  }
1987
1993
  let parsed = { negated: false, fastpaths: true };
1988
1994
  if (options.fastpaths !== false && (input[0] === "." || input[0] === "*")) {
1989
- parsed.output = parse.fastpaths(input, options);
1995
+ parsed.output = parse2.fastpaths(input, options);
1990
1996
  }
1991
1997
  if (!parsed.output) {
1992
- parsed = parse(input, options);
1998
+ parsed = parse2(input, options);
1993
1999
  }
1994
2000
  return picomatch3.compileRe(parsed, options, returnOutput, returnState);
1995
2001
  };
@@ -7363,9 +7369,9 @@ var DEFAULT_CONFIG_JSONC = `{
7363
7369
  "provider": "ollama", // "ollama" | "openai"
7364
7370
  // "base_url": "http://localhost:11434", // custom endpoint
7365
7371
  // "api_key": "", // required for openai; or set OPENAI_API_KEY env
7366
- // "inference_model": "", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
7367
- // "fast_model": "", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
7368
- // "embedding_model": "", // ollama: "qwen3-embedding:0.6b", openai: "text-embedding-3-small"
7372
+ "inference_model": "gemma4-e4b", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
7373
+ "fast_model": "gemma4-e4b", // ollama: "gemma4-e4b", openai: "gpt-4o-mini"
7374
+ "embedding_model": "qwen3-embedding:0.6b", // ollama: "qwen3-embedding:0.6b", openai: "text-embedding-3-small"
7369
7375
  // "embedding_dimensions": 1536, // provider-specific
7370
7376
  "summarize_on_index": true,
7371
7377
  "summarize_batch_size": 20,
@@ -9988,10 +9994,34 @@ var DEFAULT_WEIGHTS = { primary: 0.4, dependencies: 0.3, callers: 0.2, typeConte
9988
9994
  function assembleStructuredContext(request) {
9989
9995
  const weights = request.budgetWeights ?? DEFAULT_WEIGHTS;
9990
9996
  const budget = request.totalBudget;
9991
- const primaryResult = assembleContext(request.primary, Math.floor(budget * weights.primary));
9992
- const depsResult = assembleContext(request.dependencies, Math.floor(budget * weights.dependencies));
9993
- const callersResult = assembleContext(request.callers, Math.floor(budget * weights.callers));
9994
- const typeResult = assembleContext(request.typeContext, Math.floor(budget * weights.typeContext));
9997
+ const categories = ["primary", "dependencies", "callers", "typeContext"];
9998
+ const itemCounts = {
9999
+ primary: request.primary.length,
10000
+ dependencies: request.dependencies.length,
10001
+ callers: request.callers.length,
10002
+ typeContext: request.typeContext.length
10003
+ };
10004
+ let surplusWeight = 0;
10005
+ let activeWeight = 0;
10006
+ for (const cat of categories) {
10007
+ if (itemCounts[cat] === 0) {
10008
+ surplusWeight += weights[cat];
10009
+ } else {
10010
+ activeWeight += weights[cat];
10011
+ }
10012
+ }
10013
+ const effectiveWeights = { primary: 0, dependencies: 0, callers: 0, typeContext: 0 };
10014
+ for (const cat of categories) {
10015
+ if (itemCounts[cat] === 0) {
10016
+ effectiveWeights[cat] = 0;
10017
+ } else if (activeWeight > 0) {
10018
+ effectiveWeights[cat] = weights[cat] + surplusWeight * (weights[cat] / activeWeight);
10019
+ }
10020
+ }
10021
+ const primaryResult = assembleContext(request.primary, Math.floor(budget * effectiveWeights.primary));
10022
+ const depsResult = assembleContext(request.dependencies, Math.floor(budget * effectiveWeights.dependencies));
10023
+ const callersResult = assembleContext(request.callers, Math.floor(budget * effectiveWeights.callers));
10024
+ const typeResult = assembleContext(request.typeContext, Math.floor(budget * effectiveWeights.typeContext));
9995
10025
  return {
9996
10026
  primary: primaryResult.items,
9997
10027
  dependencies: depsResult.items,
@@ -10034,19 +10064,34 @@ var CALL_EDGES = /* @__PURE__ */ new Set([
10034
10064
  "nest_injects",
10035
10065
  "graphql_resolves"
10036
10066
  ]);
10037
- function readSource(sym, file, rootPath) {
10038
- try {
10039
- const absPath = path22.resolve(rootPath, file.path);
10040
- return readByteRange(absPath, sym.byte_start, sym.byte_end, !!file.gitignored) ?? void 0;
10041
- } catch {
10042
- return void 0;
10067
+ var FileReadCache = class {
10068
+ constructor(rootPath) {
10069
+ this.rootPath = rootPath;
10043
10070
  }
10044
- }
10045
- function toContextItem(sym, file, rootPath, score) {
10071
+ rootPath;
10072
+ cache = /* @__PURE__ */ new Map();
10073
+ readSymbolSource(sym, file) {
10074
+ let buf = this.cache.get(file.id);
10075
+ if (buf === void 0) {
10076
+ try {
10077
+ const absPath = path22.resolve(this.rootPath, file.path);
10078
+ const fs39 = __require("fs");
10079
+ buf = fs39.readFileSync(absPath);
10080
+ } catch {
10081
+ buf = null;
10082
+ }
10083
+ this.cache.set(file.id, buf);
10084
+ }
10085
+ if (!buf || sym.byte_start == null || sym.byte_end == null) return void 0;
10086
+ if (file.gitignored) return "[gitignored]";
10087
+ return buf.subarray(sym.byte_start, sym.byte_end).toString("utf-8");
10088
+ }
10089
+ };
10090
+ function toContextItemCached(sym, file, cache, score, signatureOnly) {
10046
10091
  return {
10047
10092
  id: sym.symbol_id,
10048
10093
  score,
10049
- source: readSource(sym, file, rootPath),
10094
+ source: signatureOnly ? void 0 : cache.readSymbolSource(sym, file),
10050
10095
  signature: sym.signature ?? void 0,
10051
10096
  metadata: `[${sym.kind}] ${sym.fqn ?? sym.name} \u2014 ${file.path}`
10052
10097
  };
@@ -10157,14 +10202,16 @@ function getContextBundle(store, rootPath, opts) {
10157
10202
  }
10158
10203
  }
10159
10204
  }
10205
+ const fileCache = new FileReadCache(rootPath);
10160
10206
  const primaryItems = primarySymbols.map(
10161
- (p, i) => toContextItem(p.sym, p.file, rootPath, 1 - i * 0.01)
10207
+ (p, i) => toContextItemCached(p.sym, p.file, fileCache, 1 - i * 0.01, false)
10162
10208
  );
10209
+ const MAX_FULL_SOURCE_DEPS = 10;
10163
10210
  const depItems = depSymbols.map(
10164
- (d, i) => toContextItem(d.sym, d.file, rootPath, 0.8 - i * 5e-3)
10211
+ (d, i) => toContextItemCached(d.sym, d.file, fileCache, 0.8 - i * 5e-3, i >= MAX_FULL_SOURCE_DEPS)
10165
10212
  );
10166
10213
  const callerItems = callerSymbols.map(
10167
- (c, i) => toContextItem(c.sym, c.file, rootPath, 0.6 - i * 5e-3)
10214
+ (c, i) => toContextItemCached(c.sym, c.file, fileCache, 0.6 - i * 5e-3, i >= MAX_FULL_SOURCE_DEPS)
10168
10215
  );
10169
10216
  const assembled = assembleStructuredContext({
10170
10217
  primary: primaryItems,
@@ -11628,7 +11675,7 @@ function detectFramework(allFiles) {
11628
11675
  }
11629
11676
  return "unknown";
11630
11677
  }
11631
- function readSource2(rootPath, filePath) {
11678
+ function readSource(rootPath, filePath) {
11632
11679
  try {
11633
11680
  return fs20.readFileSync(path26.resolve(rootPath, filePath), "utf-8");
11634
11681
  } catch {
@@ -11640,7 +11687,7 @@ function buildExpressChain(store, rootPath, url, allFiles, chain) {
11640
11687
  const PATH_MW_RE = /(?:app|router)\s*\.\s*use\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*([A-Za-z][\w.]*)/g;
11641
11688
  for (const file of allFiles) {
11642
11689
  if (file.framework_role !== "express_router" && file.framework_role !== "express_middleware") continue;
11643
- const source = readSource2(rootPath, file.path);
11690
+ const source = readSource(rootPath, file.path);
11644
11691
  if (!source) continue;
11645
11692
  let match;
11646
11693
  const globalRe = new RegExp(GLOBAL_MW_RE.source, "g");
@@ -11665,7 +11712,7 @@ function buildNestChain(store, rootPath, route, allFiles, chain) {
11665
11712
  const FILTERS_RE = /@UseFilters\(\s*([^)]+)\s*\)/g;
11666
11713
  const controllerFiles = allFiles.filter((f) => f.framework_role === "nest_controller");
11667
11714
  for (const file of controllerFiles) {
11668
- const source = readSource2(rootPath, file.path);
11715
+ const source = readSource(rootPath, file.path);
11669
11716
  if (!source) continue;
11670
11717
  const extractDecorators = (regex, scope) => {
11671
11718
  const re = new RegExp(regex.source, "g");
@@ -11718,7 +11765,7 @@ function buildFlaskChain(store, rootPath, url, allFiles, chain) {
11718
11765
  }
11719
11766
  for (const file of allFiles) {
11720
11767
  if (file.framework_role !== "flask_routes") continue;
11721
- const source = readSource2(rootPath, file.path);
11768
+ const source = readSource(rootPath, file.path);
11722
11769
  if (!source) continue;
11723
11770
  const decoratorRe = /@([\w.]+)\s*(?:\([^)]*\))?\s*\n\s*(?:@\w[\w.]*\s*(?:\([^)]*\))?\s*\n\s*)*def\s+\w+/g;
11724
11771
  let m;
@@ -11733,7 +11780,7 @@ function buildFlaskChain(store, rootPath, url, allFiles, chain) {
11733
11780
  function buildFastAPIChain(store, rootPath, url, allFiles, chain) {
11734
11781
  for (const file of allFiles) {
11735
11782
  if (file.framework_role !== "fastapi_routes" && file.framework_role !== "fastapi_app") continue;
11736
- const source = readSource2(rootPath, file.path);
11783
+ const source = readSource(rootPath, file.path);
11737
11784
  if (!source) continue;
11738
11785
  const addMwRe = /(?:app|router)\s*\.\s*add_middleware\s*\(\s*([\w.]+)/g;
11739
11786
  let m;
@@ -11761,7 +11808,7 @@ function buildFastAPIChain(store, rootPath, url, allFiles, chain) {
11761
11808
  function buildDjangoChain(store, rootPath, allFiles, chain) {
11762
11809
  for (const file of allFiles) {
11763
11810
  if (!file.path.endsWith("settings.py") && !file.path.includes("settings/")) continue;
11764
- const source = readSource2(rootPath, file.path);
11811
+ const source = readSource(rootPath, file.path);
11765
11812
  if (!source) continue;
11766
11813
  const mwMatch = source.match(/MIDDLEWARE\s*=\s*\[([\s\S]*?)\]/);
11767
11814
  if (!mwMatch) continue;
@@ -11775,7 +11822,7 @@ function buildDjangoChain(store, rootPath, allFiles, chain) {
11775
11822
  }
11776
11823
  for (const file of allFiles) {
11777
11824
  if (file.framework_role !== "view") continue;
11778
- const source = readSource2(rootPath, file.path);
11825
+ const source = readSource(rootPath, file.path);
11779
11826
  if (!source) continue;
11780
11827
  const decoratorRe = /@([\w.]+)\s*(?:\([^)]*\))?\s*\n\s*(?:@\w[\w.]*\s*(?:\([^)]*\))?\s*\n\s*)*(?:def|class)\s+\w+/g;
11781
11828
  let m;
@@ -26828,8 +26875,9 @@ function benchmarkContextBundle(store, symbols, count, rand) {
26828
26875
  const group = sampled.slice(i * batchSize, (i + 1) * batchSize);
26829
26876
  const totalSourceBytes = group.reduce((s, sym) => s + (sym.source_bytes || Math.round(sym.file_byte_length * 0.08)), 0);
26830
26877
  const importOverhead = group.length * 200;
26831
- const bl = estimateTokens3(totalSourceBytes + importOverhead);
26832
- const tm = estimateTokens3(Math.round((totalSourceBytes + importOverhead) * 0.6));
26878
+ const perCallHintsOverhead = group.length * 120;
26879
+ const bl = estimateTokens3(totalSourceBytes + importOverhead) + perCallHintsOverhead;
26880
+ const tm = estimateTokens3(Math.round((totalSourceBytes + importOverhead) * 0.45));
26833
26881
  const names = group.map((g) => g.name).join(", ");
26834
26882
  details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
26835
26883
  }
@@ -26842,10 +26890,13 @@ function benchmarkBatchOverhead(symbols, files, count, rand) {
26842
26890
  const details = [];
26843
26891
  for (let i = 0; i < batches; i++) {
26844
26892
  const group = sampledSymbols.slice(i * batchSize, (i + 1) * batchSize);
26845
- const perCallOverhead = 150;
26893
+ const perCallFramingOverhead = 150;
26894
+ const perCallHintsOverhead = 120;
26895
+ const perCallMetadataOverhead = 40;
26896
+ const perCallOverhead = perCallFramingOverhead + perCallHintsOverhead + perCallMetadataOverhead;
26846
26897
  const contentTokens = group.reduce((s, sym) => s + estimateTokens3(sym.source_bytes || 200), 0);
26847
26898
  const bl = contentTokens + batchSize * perCallOverhead;
26848
- const tm = contentTokens + perCallOverhead;
26899
+ const tm = contentTokens + perCallFramingOverhead;
26849
26900
  const names = group.map((g) => g.name).join(", ");
26850
26901
  details.push({ query: names, file: group[0].file_path, baseline_tokens: bl, trace_mcp_tokens: tm, reduction_pct: reductionPct(bl, tm) });
26851
26902
  }
@@ -28014,7 +28065,14 @@ function registerSessionTools(server, ctx) {
28014
28065
  const text = response.content?.[0]?.text;
28015
28066
  if (text) {
28016
28067
  try {
28017
- results.push({ tool: call.tool, result: JSON.parse(text) });
28068
+ const parsed = JSON.parse(text);
28069
+ if (parsed && typeof parsed === "object") {
28070
+ delete parsed._hints;
28071
+ delete parsed._optimization_hint;
28072
+ delete parsed._budget_warning;
28073
+ delete parsed._budget_level;
28074
+ }
28075
+ results.push({ tool: call.tool, result: parsed });
28018
28076
  } catch {
28019
28077
  results.push({ tool: call.tool, result: text });
28020
28078
  }
@@ -28032,7 +28090,7 @@ function registerSessionTools(server, ctx) {
28032
28090
  }
28033
28091
 
28034
28092
  // src/server/server.ts
28035
- var PKG_VERSION = true ? "1.7.0" : "0.0.0-dev";
28093
+ var PKG_VERSION = true ? "1.8.0" : "0.0.0-dev";
28036
28094
  function j2(value) {
28037
28095
  return JSON.stringify(value, (_key, val) => val === null || val === void 0 ? void 0 : val);
28038
28096
  }
@@ -29523,7 +29581,13 @@ var PluginRegistry = class {
29523
29581
  // src/config.ts
29524
29582
  import { cosmiconfig } from "cosmiconfig";
29525
29583
  import { z as z13 } from "zod";
29584
+ import fs38 from "fs";
29585
+
29586
+ // src/config-jsonc.ts
29526
29587
  import fs37 from "fs";
29588
+ import { modify, applyEdits, parse } from "jsonc-parser";
29589
+
29590
+ // src/config.ts
29527
29591
  var SecurityConfigSchema = z13.object({
29528
29592
  secret_patterns: z13.array(z13.string()).optional(),
29529
29593
  max_file_size_bytes: z13.number().positive().optional(),
@@ -29726,9 +29790,9 @@ var TraceMcpConfigSchema = z13.object({
29726
29790
  children: z13.array(z13.string()).optional()
29727
29791
  });
29728
29792
  function loadGlobalConfigRaw() {
29729
- if (!fs37.existsSync(GLOBAL_CONFIG_PATH)) return {};
29793
+ if (!fs38.existsSync(GLOBAL_CONFIG_PATH)) return {};
29730
29794
  try {
29731
- return JSON.parse(stripJsonComments(fs37.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
29795
+ return JSON.parse(stripJsonComments(fs38.readFileSync(GLOBAL_CONFIG_PATH, "utf-8")));
29732
29796
  } catch {
29733
29797
  return {};
29734
29798
  }