@getmikk/mcp-server 1.9.0 → 1.9.1

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/bin/mikk-mcp.js CHANGED
@@ -18,4 +18,13 @@ if (idx !== -1 && process.argv[idx + 1]) {
18
18
  process.env.MIKK_PROJECT_ROOT = projectRoot
19
19
 
20
20
  // Load the CJS bundle (auto-starts stdio server via src/index.ts)
21
- require('../dist/index.cjs')
21
+ const mod = require('../dist/index.cjs')
22
+ if (mod.startStdioServer) {
23
+ mod.startStdioServer().catch(err => {
24
+ console.error('Failed to start MCP server:', err)
25
+ process.exit(1)
26
+ })
27
+ } else {
28
+ console.error('MCP server bundle is missing startStdioServer export.')
29
+ process.exit(1)
30
+ }
package/dist/index.cjs CHANGED
@@ -238659,6 +238659,38 @@ var IntentSchema = external_exports.object({
238659
238659
  // src/tools.ts
238660
238660
  var projectCache = /* @__PURE__ */ new Map();
238661
238661
  var CACHE_TTL_MS = 3e4;
238662
+ var _CPT = 4;
238663
+ var _ALC = 42;
238664
+ var _tallies = /* @__PURE__ */ new Map();
238665
+ function _tally(r) {
238666
+ let t = _tallies.get(r);
238667
+ if (!t) {
238668
+ t = { calls: 0, used: 0, raw: 0, saved: 0, start: Date.now() };
238669
+ _tallies.set(r, t);
238670
+ }
238671
+ return t;
238672
+ }
238673
+ function _tok(o) {
238674
+ return Math.max(1, Math.round(JSON.stringify(o).length / _CPT));
238675
+ }
238676
+ function _fileTok(lock, fp) {
238677
+ const fs22 = Object.values(lock.functions).filter((f) => f.file === fp);
238678
+ const ln = fs22.length > 0 ? Math.max(...fs22.map((f) => f.endLine)) : 80;
238679
+ return Math.round(ln * _ALC / _CPT);
238680
+ }
238681
+ function _filesTok(lock, fps) {
238682
+ return fps.reduce((s, f) => s + _fileTok(lock, f), 0);
238683
+ }
238684
+ function _track(root, raw, resp) {
238685
+ const used = _tok(resp);
238686
+ const saved = Math.max(0, raw - used);
238687
+ const t = _tally(root);
238688
+ t.calls++;
238689
+ t.used += used;
238690
+ t.raw += raw;
238691
+ t.saved += saved;
238692
+ return { used, raw, saved, sessionSaved: t.saved, sessionCalls: t.calls };
238693
+ }
238662
238694
  var semanticSearchers = /* @__PURE__ */ new Map();
238663
238695
  function getSemanticSearcher(projectRoot) {
238664
238696
  let s = semanticSearchers.get(projectRoot);
@@ -238706,12 +238738,14 @@ function registerTools(server2, projectRoot) {
238706
238738
  warning: staleness,
238707
238739
  hint: "Next: Use mikk_query_context with your task description, or mikk_list_modules to explore the architecture."
238708
238740
  };
238741
+ const _rawOverview = Math.min(15, Object.keys(lock.files).length) * Math.round(80 * _ALC / _CPT);
238742
+ overview.tokens = _track(projectRoot, _rawOverview, overview);
238709
238743
  return { content: [{ type: "text", text: JSON.stringify(overview, null, 2) }] };
238710
238744
  }
238711
238745
  );
238712
238746
  server2.tool(
238713
238747
  "mikk_query_context",
238714
- "Ask an architecture question \u201D returns graph-traced context with relevant functions, files, and call chains. Use this to understand how code flows through the project.",
238748
+ "Ask an architecture question \xC3\xA2\xE2\u201A\xAC\xC2\x9D returns graph-traced context with relevant functions, files, and call chains. Use this to understand how code flows through the project.",
238715
238749
  {
238716
238750
  question: external_exports.string().describe("The architecture question or task description"),
238717
238751
  maxHops: external_exports.number().optional().default(4).describe("Graph traversal depth (default: 4)"),
@@ -238748,8 +238782,14 @@ function registerTools(server2, projectRoot) {
238748
238782
  const warning = staleness ? `
238749
238783
 
238750
238784
  ${staleness}` : "";
238785
+ const _rawQC = (tokenBudget ?? 6e3) * 3;
238786
+ const _tokQC = _track(projectRoot, _rawQC, output);
238787
+ const tokLine = `
238788
+
238789
+ ---
238790
+ // tokens: ${JSON.stringify(_tokQC)}`;
238751
238791
  return {
238752
- content: [{ type: "text", text: output + warning + "\n\n---\nHint: Use mikk_before_edit on any files you plan to modify, then mikk_impact_analysis to see the full blast radius." }]
238792
+ content: [{ type: "text", text: output + warning + "\n\n---\nHint: Use mikk_before_edit on any files you plan to modify, then mikk_impact_analysis to see the full blast radius." + tokLine }]
238753
238793
  };
238754
238794
  }
238755
238795
  );
@@ -238802,6 +238842,8 @@ ${staleness}` : "";
238802
238842
  warning: staleness,
238803
238843
  hint: "Next: Use mikk_get_function_detail on critical/high items to review them. Then mikk_before_edit to validate your planned changes."
238804
238844
  };
238845
+ const _rawIA = _fileTok(lock, normalizedFile) + result.impacted.length * Math.round(40 * _ALC / _CPT);
238846
+ response.tokens = _track(projectRoot, _rawIA, response);
238805
238847
  return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
238806
238848
  }
238807
238849
  );
@@ -238911,8 +238953,10 @@ ${staleness}` : "";
238911
238953
  constraintStatus: totalViolations === 0 ? "pass" : "fail",
238912
238954
  files: fileReports,
238913
238955
  warning: staleness,
238914
- hint: totalViolations > 0 ? "\x8F Constraint violations detected! Review the violations before proceeding. Use mikk_get_constraints for full rule context." : "All constraints satisfied. If safe, proceed with your edits."
238956
+ hint: totalViolations > 0 ? "\xC3\u201A\xC2\x8F Constraint violations detected! Review the violations before proceeding. Use mikk_get_constraints for full rule context." : "All constraints satisfied. If safe, proceed with your edits."
238915
238957
  };
238958
+ const _rawBE = _filesTok(lock, filesToEdit) * 4;
238959
+ response.tokens = _track(projectRoot, _rawBE, response);
238916
238960
  return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
238917
238961
  }
238918
238962
  );
@@ -239043,7 +239087,7 @@ ${staleness}` : "";
239043
239087
  content: [{
239044
239088
  type: "text",
239045
239089
  text: [
239046
- "\x9D\u0152 Semantic search requires @xenova/transformers.",
239090
+ "\xC3\u201A\xC2\x9D\xC3\u2026\xE2\u20AC\u2122 Semantic search requires @xenova/transformers.",
239047
239091
  "",
239048
239092
  "Install it in your project root:",
239049
239093
  " npm install @xenova/transformers",
@@ -239172,7 +239216,7 @@ ${content}`
239172
239216
  );
239173
239217
  server2.tool(
239174
239218
  "mikk_dead_code",
239175
- "Detect dead code \u201D functions with zero callers after exempting exports, entry points, route handlers, tests, and constructors. Use this before refactoring or cleanup.",
239219
+ "Detect dead code \xC3\xA2\xE2\u201A\xAC\xC2\x9D functions with zero callers after exempting exports, entry points, route handlers, tests, and constructors. Use this before refactoring or cleanup.",
239176
239220
  {
239177
239221
  moduleId: external_exports.string().optional().describe("Filter results to a specific module ID")
239178
239222
  },
@@ -239197,7 +239241,7 @@ ${content}`
239197
239241
  );
239198
239242
  server2.tool(
239199
239243
  "mikk_manage_adr",
239200
- "CRUD for Architectural Decision Records (ADRs) in mikk.json. Actions: list, get, add, update, remove. WHEN TO USE: When making architectural changes \u2014 document WHY so future AI agents understand. AFTER THIS: ADRs automatically surface in mikk_query_context responses. Required for add: id, title, reason.",
239244
+ "CRUD for Architectural Decision Records (ADRs) in mikk.json. Actions: list, get, add, update, remove. WHEN TO USE: When making architectural changes \xC3\xA2\xE2\u201A\xAC\xE2\u20AC\x9D document WHY so future AI agents understand. AFTER THIS: ADRs automatically surface in mikk_query_context responses. Required for add: id, title, reason.",
239201
239245
  {
239202
239246
  action: external_exports.enum(["list", "get", "add", "update", "remove"]).describe("The CRUD action to perform"),
239203
239247
  id: external_exports.string().optional().describe("ADR id (required for get, update, remove)"),
@@ -239300,12 +239344,14 @@ ${content}`
239300
239344
  warning: staleness,
239301
239345
  hint: modified.length + added.length > 0 ? "Run `mikk analyze` to update the lock file with these changes." : "Codebase is in sync with the lock file."
239302
239346
  };
239347
+ const _rawGC = Math.min(50, Object.keys(lock.files).length) * Math.round(60 * _ALC / _CPT);
239348
+ response.tokens = _track(projectRoot, _rawGC, response);
239303
239349
  return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
239304
239350
  }
239305
239351
  );
239306
239352
  server2.tool(
239307
239353
  "mikk_read_file",
239308
- "Read file scoped to specific functions. Returns bodies with metadata headers (params, calls, calledBy). WHEN TO USE: When you know which functions you need \u2014 saves tokens vs mikk_get_file. AFTER THIS: Use mikk_before_edit before making changes. TIP: This is the preferred way to read code \u2014 always specify function names when possible.",
239354
+ "Read file scoped to specific functions. Returns bodies with metadata headers (params, calls, calledBy). WHEN TO USE: When you know which functions you need \xC3\xA2\xE2\u201A\xAC\xE2\u20AC\x9D saves tokens vs mikk_get_file. AFTER THIS: Use mikk_before_edit before making changes. TIP: This is the preferred way to read code \xC3\xA2\xE2\u201A\xAC\xE2\u20AC\x9D always specify function names when possible.",
239309
239355
  {
239310
239356
  file: external_exports.string().describe("File path relative to project root"),
239311
239357
  functions: external_exports.array(external_exports.string()).optional().describe("Function names to extract. If omitted, returns the whole file.")
@@ -239345,7 +239391,7 @@ ${content}` }]
239345
239391
  (f) => (f.name === fnName || f.name.endsWith(`.${fnName}`)) && (f.file === normalizedFile || f.file.endsWith("/" + normalizedFile))
239346
239392
  );
239347
239393
  if (!fn) {
239348
- sections.push(`// \x9D\u0152 Function "${fnName}" not found in ${file}`);
239394
+ sections.push(`// \xC3\u201A\xC2\x9D\xC3\u2026\xE2\u20AC\u2122 Function "${fnName}" not found in ${file}`);
239349
239395
  continue;
239350
239396
  }
239351
239397
  const header = [
@@ -239368,7 +239414,10 @@ ${body}`);
239368
239414
  const warningText = staleness ? `
239369
239415
 
239370
239416
  ${staleness}` : "";
239371
- return { content: [{ type: "text", text: output + warningText }] };
239417
+ const _rawRF = _fileTok(lock, file.replace(/\\/g, "/"));
239418
+ const _tokRF = _track(projectRoot, _rawRF, output);
239419
+ return { content: [{ type: "text", text: output + warningText + `
239420
+ // tokens: ${JSON.stringify(_tokRF)}` }] };
239372
239421
  }
239373
239422
  );
239374
239423
  server2.tool(
@@ -239432,6 +239481,8 @@ ${staleness}` : "";
239432
239481
  warning: staleness,
239433
239482
  hint: changedCount > 0 ? `${changedCount} file(s) may have changed. Run \`mikk analyze\` for accurate results, or use mikk_get_changes for details.` : "Codebase is in sync. Use mikk_query_context with your task description to get started."
239434
239483
  };
239484
+ const _rawSC = Math.min(20, Object.keys(lock.files).length) * Math.round(100 * _ALC / _CPT);
239485
+ response.tokens = _track(projectRoot, _rawSC, response);
239435
239486
  return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
239436
239487
  }
239437
239488
  );
@@ -239480,7 +239531,7 @@ ${staleness}` : "";
239480
239531
  );
239481
239532
  server2.tool(
239482
239533
  "mikk_rename",
239483
- "Plan a coordinated multi-file rename. Finds all call sites and import locations for a function and provides a step-by-step edit plan. WHEN TO USE: Before renaming any function \u2014 ensures you update ALL call sites. AFTER THIS: Execute the edit plan, then run mikk analyze.",
239534
+ "Plan a coordinated multi-file rename. Finds all call sites and import locations for a function and provides a step-by-step edit plan. WHEN TO USE: Before renaming any function \xC3\xA2\xE2\u201A\xAC\xE2\u20AC\x9D ensures you update ALL call sites. AFTER THIS: Execute the edit plan, then run mikk analyze.",
239484
239535
  {
239485
239536
  functionName: external_exports.string().describe("The current function name to rename"),
239486
239537
  newName: external_exports.string().describe("The desired new name")
@@ -239536,6 +239587,37 @@ ${staleness}` : "";
239536
239587
  };
239537
239588
  }
239538
239589
  );
239590
+ server2.tool(
239591
+ "mikk_token_stats",
239592
+ "Show token savings for this session \u2014 how many tokens Mikk saved vs. the agent reading raw source files. WHEN TO USE: Any time. Useful at end of session to see cumulative efficiency. Returns per-session totals and cost estimates.",
239593
+ {},
239594
+ async () => {
239595
+ const t = _tally(projectRoot);
239596
+ const { lock } = await loadContractAndLock(projectRoot);
239597
+ const totalFileLine = Object.values(lock.functions).reduce((s, f) => s + (f.endLine - f.startLine + 1), 0);
239598
+ const fullCodebaseTok = Math.round(totalFileLine * _ALC / _CPT);
239599
+ const elapsedMin = Math.round((Date.now() - t.start) / 6e4);
239600
+ const response = {
239601
+ session: {
239602
+ calls: t.calls,
239603
+ elapsedMinutes: elapsedMin
239604
+ },
239605
+ tokens: {
239606
+ used: t.used,
239607
+ rawWouldHaveCost: t.raw,
239608
+ saved: t.saved,
239609
+ savingsPercent: t.raw > 0 ? Math.round(t.saved / t.raw * 100) : 0
239610
+ },
239611
+ context: {
239612
+ fullCodebaseTokens: fullCodebaseTok,
239613
+ percentOfCodebaseRead: t.raw > 0 ? Math.round(t.used / fullCodebaseTok * 100) : 0,
239614
+ note: "Full codebase = if agent read every tracked source line once"
239615
+ },
239616
+ interpretation: t.saved > 0 ? `Mikk saved ~${t.saved.toLocaleString()} tokens this session (${Math.round(t.saved / t.raw * 100)}% reduction). Roughly ${Math.round(t.saved / 1e3)}k tokens = ~${(t.saved * 3e-6).toFixed(3)} USD at GPT-4o rates.` : "No tools called yet this session."
239617
+ };
239618
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
239619
+ }
239620
+ );
239539
239621
  }
239540
239622
  async function loadContractAndLock(projectRoot) {
239541
239623
  const cached2 = projectCache.get(projectRoot);
@@ -239549,7 +239631,7 @@ async function loadContractAndLock(projectRoot) {
239549
239631
  const syncStatus = lock.syncState?.status ?? "unknown";
239550
239632
  let staleness = null;
239551
239633
  if (syncStatus === "drifted" || syncStatus === "conflict") {
239552
- staleness = `\x8F Lock file is ${syncStatus}. Run \`mikk analyze\` for accurate results.`;
239634
+ staleness = `\xC3\u201A\xC2\x8F Lock file is ${syncStatus}. Run \`mikk analyze\` for accurate results.`;
239553
239635
  }
239554
239636
  if (!staleness) {
239555
239637
  const fileEntries = Object.entries(lock.files);
@@ -239572,7 +239654,7 @@ async function loadContractAndLock(projectRoot) {
239572
239654
  }
239573
239655
  }
239574
239656
  if (mismatched > 0) {
239575
- staleness = `\x8F STALE: ${mismatched} file(s) changed since last analysis (${mismatchedFiles.slice(0, 3).join(", ")}${mismatched > 3 ? "..." : ""}). Run \`mikk analyze\`.`;
239657
+ staleness = `\xC3\u201A\xC2\x8F STALE: ${mismatched} file(s) changed since last analysis (${mismatchedFiles.slice(0, 3).join(", ")}${mismatched > 3 ? "..." : ""}). Run \`mikk analyze\`.`;
239576
239658
  }
239577
239659
  }
239578
239660
  const graph = buildGraphFromLock(lock);
@@ -239644,7 +239726,7 @@ function detectCircularDeps(fns, lock) {
239644
239726
  const cycleStart = cyclePath.indexOf(id);
239645
239727
  const cycle = cyclePath.slice(cycleStart).map((cid) => lock.functions[cid]?.name ?? cid);
239646
239728
  cycle.push(lock.functions[id]?.name ?? id);
239647
- warnings.push(`\x8F Circular: ${cycle.join(" \u2020\u2019 ")}`);
239729
+ warnings.push(`\xC3\u201A\xC2\x8F Circular: ${cycle.join(" \xC3\xA2\xE2\u201A\xAC\xC2\xA0\xC3\xA2\xE2\u201A\xAC\xE2\u201E\xA2 ")}`);
239648
239730
  return true;
239649
239731
  }
239650
239732
  if (visited.has(id)) return false;
@@ -239779,7 +239861,7 @@ async function safeRead(filePath) {
239779
239861
  }
239780
239862
 
239781
239863
  // src/server.ts
239782
- var VERSION = true ? "1.9.0" : "0.0.0-dev";
239864
+ var VERSION = true ? "1.9.1" : "0.0.0-dev";
239783
239865
  function createMikkMcpServer(projectRoot) {
239784
239866
  const server2 = new McpServer({
239785
239867
  name: "mikk",
@@ -239889,9 +239971,6 @@ async function startStdioServer() {
239889
239971
  const transport = new StdioServerTransport();
239890
239972
  await server2.connect(transport);
239891
239973
  }
239892
-
239893
- // src/index.ts
239894
- startStdioServer();
239895
239974
  // Annotate the CommonJS export names for ESM import in node:
239896
239975
  0 && (module.exports = {
239897
239976
  createMikkMcpServer,