@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 +10 -1
- package/dist/index.cjs +96 -17
- package/dist/index.cjs.map +3 -3
- package/package.json +1 -1
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 \
|
|
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\
|
|
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 \
|
|
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 \
|
|
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 \
|
|
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\
|
|
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
|
-
|
|
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 \
|
|
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(" \
|
|
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.
|
|
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,
|