mcp-probe-kit 3.0.13 → 3.0.15

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/README.md CHANGED
@@ -347,6 +347,12 @@ Recommended on Windows:
347
347
  2. Prefer stable local/global CLI usage for GitNexus when your MCP client supports `env`.
348
348
  3. Increase GitNexus connect/call timeouts on slower or first-run environments.
349
349
 
350
+ Quick install command (Windows):
351
+
352
+ ```powershell
353
+ winget install Microsoft.VisualStudio.2022.BuildTools
354
+ ```
355
+
350
356
  Example config using a preinstalled `gitnexus` CLI:
351
357
 
352
358
  ```json
@@ -3,7 +3,7 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import spawn from "cross-spawn";
5
5
  import { afterEach, describe, expect, test } from "vitest";
6
- import { prepareBridgeWorkspace, resolveExecutableCommand, resolveGitNexusBridgeCommand, resolveSpawnCommand, rerankQueryStructuredContent, } from "../gitnexus-bridge.js";
6
+ import { extractResolvedSymbolIdFromContext, prepareBridgeWorkspace, resolveExecutableCommand, resolveGitNexusBridgeCommand, resolveSpawnCommand, rerankQueryStructuredContent, } from "../gitnexus-bridge.js";
7
7
  const tempRoots = [];
8
8
  afterEach(() => {
9
9
  while (tempRoots.length > 0) {
@@ -87,6 +87,14 @@ describe("gitnexus-bridge workspace preparation", () => {
87
87
  expect(reranked.structuredContent.processes[0].heuristicLabel).toBe("Login -> GenerateToken");
88
88
  expect(reranked.note).toMatch(/Top matches/i);
89
89
  });
90
+ test("impact 可复用 context 解析出的 symbol id", () => {
91
+ expect(extractResolvedSymbolIdFromContext({
92
+ target: { id: "Method:sysAuthController.js:login:18", name: "login" },
93
+ })).toBe("Method:sysAuthController.js:login:18");
94
+ expect(extractResolvedSymbolIdFromContext({
95
+ symbol: { uid: "Function:src/auth.ts:login" },
96
+ })).toBe("Function:src/auth.ts:login");
97
+ });
90
98
  test.runIf(process.platform === "win32")("Windows 下带空格路径的 cmd 可执行文件可以真实启动", async () => {
91
99
  const root = makeTempDir("gitnexus-space-");
92
100
  const executable = path.join(root, "Program Files", "tool.cmd");
@@ -77,6 +77,7 @@ export declare function resolveSpawnCommand(command: string, args: string[], pla
77
77
  command: string;
78
78
  args: string[];
79
79
  };
80
+ export declare function extractResolvedSymbolIdFromContext(value: unknown): string | undefined;
80
81
  export declare function rerankQueryStructuredContent(structuredContent: unknown, hints: {
81
82
  query?: string;
82
83
  goal?: string;
@@ -333,6 +333,83 @@ function buildAmbiguities(executions) {
333
333
  };
334
334
  });
335
335
  }
336
+ function getLastExecutionByTool(executions, tool) {
337
+ for (let index = executions.length - 1; index >= 0; index -= 1) {
338
+ if (executions[index]?.tool === tool) {
339
+ return executions[index];
340
+ }
341
+ }
342
+ return undefined;
343
+ }
344
+ function inferSymbolKind(value) {
345
+ const record = toRecord(value);
346
+ if (!record) {
347
+ return undefined;
348
+ }
349
+ for (const key of ["kind", "type", "symbolType"]) {
350
+ const candidate = record[key];
351
+ if (typeof candidate === "string" && candidate.trim()) {
352
+ return candidate.trim();
353
+ }
354
+ }
355
+ const id = record.id;
356
+ if (typeof id === "string" && id.includes(":")) {
357
+ return id.split(":")[0];
358
+ }
359
+ return undefined;
360
+ }
361
+ function isNonCallableSymbolKind(kind) {
362
+ if (!kind) {
363
+ return false;
364
+ }
365
+ const normalized = kind.toLowerCase();
366
+ return normalized.includes("folder")
367
+ || normalized.includes("file")
368
+ || normalized.includes("module")
369
+ || normalized.includes("community")
370
+ || normalized.includes("process")
371
+ || normalized.includes("package");
372
+ }
373
+ export function extractResolvedSymbolIdFromContext(value) {
374
+ const record = toRecord(value);
375
+ if (!record) {
376
+ return undefined;
377
+ }
378
+ const direct = [record.uid, record.id, record.targetUid, record.targetId]
379
+ .find((item) => typeof item === "string" && item.trim());
380
+ if (typeof direct === "string") {
381
+ return direct.trim();
382
+ }
383
+ for (const key of ["target", "symbol", "node", "element"]) {
384
+ const nested = toRecord(record[key]);
385
+ if (!nested) {
386
+ continue;
387
+ }
388
+ const candidate = [nested.uid, nested.id]
389
+ .find((item) => typeof item === "string" && item.trim());
390
+ if (typeof candidate === "string") {
391
+ return candidate.trim();
392
+ }
393
+ }
394
+ return undefined;
395
+ }
396
+ function normalizeImpactTargetByContext(contextExecution, originalTarget) {
397
+ const target = toRecord(contextExecution.structuredContent)?.target;
398
+ const kind = inferSymbolKind(target);
399
+ if (!isNonCallableSymbolKind(kind)) {
400
+ return contextExecution;
401
+ }
402
+ const message = `impact 目标 "${originalTarget}" 被解析为 ${kind},请改用函数/方法 uid 或补充 file_path 再重试。`;
403
+ contextExecution.status = "ambiguous";
404
+ contextExecution.text = message;
405
+ contextExecution.structuredContent = {
406
+ status: "ambiguous",
407
+ message,
408
+ suggestion: "For impact analysis, prefer Method/Function over Folder/File.",
409
+ candidates: target ? [target] : [],
410
+ };
411
+ return contextExecution;
412
+ }
336
413
  const QUERY_STOP_WORDS = new Set([
337
414
  "the", "and", "for", "with", "from", "into", "that", "this", "user", "flow",
338
415
  "sign", "code", "project", "module",
@@ -383,23 +460,38 @@ function scoreQueryCandidate(candidate, terms) {
383
460
  if (fields.length === 0 || terms.length === 0) {
384
461
  return 0;
385
462
  }
386
- let score = 0;
463
+ let score = -30;
387
464
  let matchedTerms = 0;
388
465
  for (const term of terms) {
389
466
  let matched = false;
390
467
  for (const field of fields) {
391
- if (!field.value.includes(term)) {
468
+ const value = field.value;
469
+ const key = field.key;
470
+ const exact = value === term;
471
+ const wholeWord = new RegExp(`\\b${term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "i").test(value);
472
+ const partial = value.includes(term);
473
+ if (!partial) {
392
474
  continue;
393
475
  }
394
476
  matched = true;
395
- if (field.key.includes("name") || field.key.includes("title") || field.key.includes("label")) {
396
- score += field.value === term ? 24 : 16;
477
+ const inName = key.includes("name") || key.includes("title") || key.includes("label") || key.includes("symbol");
478
+ const inPath = key.includes("path") || key.includes("file") || key.includes("module");
479
+ const inDocs = key.includes("comment") || key.includes("doc") || key.includes("description") || key.includes("summary");
480
+ const inCode = key.includes("content") || key.includes("source") || key.includes("body") || key.includes("code");
481
+ if (inName) {
482
+ score += exact ? 120 : wholeWord ? 90 : 55;
483
+ }
484
+ else if (inPath) {
485
+ score += wholeWord ? 70 : 45;
486
+ }
487
+ else if (inDocs) {
488
+ score += wholeWord ? 26 : 16;
397
489
  }
398
- else if (field.key.includes("path") || field.key.includes("file") || field.key.includes("module")) {
399
- score += 10;
490
+ else if (inCode) {
491
+ score += wholeWord ? 18 : 10;
400
492
  }
401
493
  else {
402
- score += 4;
494
+ score += wholeWord ? 24 : 12;
403
495
  }
404
496
  }
405
497
  if (matched) {
@@ -407,15 +499,18 @@ function scoreQueryCandidate(candidate, terms) {
407
499
  }
408
500
  }
409
501
  if (matchedTerms > 0) {
410
- score += matchedTerms * 5;
502
+ score += matchedTerms * 14;
503
+ }
504
+ else {
505
+ score -= 60;
411
506
  }
412
507
  if (matchedTerms === terms.length) {
413
- score += 12;
508
+ score += 35;
414
509
  }
415
510
  const candidateRecord = toRecord(candidate);
416
511
  const priority = candidateRecord?.priority;
417
512
  if (typeof priority === "number" && Number.isFinite(priority)) {
418
- score += priority;
513
+ score += matchedTerms > 0 ? (priority * 5) : (priority * 0.5);
419
514
  }
420
515
  return score;
421
516
  }
@@ -957,8 +1052,35 @@ export async function runCodeInsightBridge(request) {
957
1052
  warnings.push("缺少 target/uid 参数,已跳过 impact");
958
1053
  return;
959
1054
  }
1055
+ let impactTarget = request.uid || request.target;
1056
+ if (!request.uid && request.target) {
1057
+ let contextExecution = getLastExecutionByTool(executions, "context");
1058
+ if (!contextExecution) {
1059
+ contextExecution = await callBridgeTool(client, "context", {
1060
+ name: request.target,
1061
+ ...(request.filePath ? { file_path: request.filePath } : {}),
1062
+ ...(effectiveRepo ? { repo: effectiveRepo } : {}),
1063
+ }, request.signal, workspace);
1064
+ executions.push(contextExecution);
1065
+ }
1066
+ if (contextExecution.ok && contextExecution.status === "ambiguous") {
1067
+ warnings.push("impact_target_ambiguous");
1068
+ return;
1069
+ }
1070
+ if (contextExecution.ok) {
1071
+ normalizeImpactTargetByContext(contextExecution, request.target);
1072
+ if (contextExecution.status === "ambiguous") {
1073
+ warnings.push("impact_target_non_callable");
1074
+ return;
1075
+ }
1076
+ const resolvedTargetId = extractResolvedSymbolIdFromContext(contextExecution.structuredContent);
1077
+ if (resolvedTargetId) {
1078
+ impactTarget = resolvedTargetId;
1079
+ }
1080
+ }
1081
+ }
960
1082
  executions.push(await callBridgeTool(client, "impact", {
961
- target: request.uid || request.target,
1083
+ target: impactTarget,
962
1084
  direction: request.direction || "upstream",
963
1085
  ...(request.maxDepth ? { maxDepth: request.maxDepth } : {}),
964
1086
  ...(typeof request.includeTests === "boolean"
@@ -101,6 +101,7 @@ describe("code_insight 单元测试", () => {
101
101
  });
102
102
  expect(status).toBe("ambiguous");
103
103
  expect(plan?.kind).toBe("ambiguity");
104
+ expect(plan?.steps).toHaveLength(2);
104
105
  expect(plan?.steps[1].action).toMatch(/uid 或 file_path/);
105
106
  });
106
107
  test("未显式要求保存时不生成 docs delegated plan", async () => {
@@ -113,7 +114,8 @@ describe("code_insight 单元测试", () => {
113
114
  expect(result.isError).toBe(false);
114
115
  const text = String(result.content?.[0]?.text || "");
115
116
  const structured = result.structuredContent;
116
- expect(text).not.toMatch(/delegated plan/);
117
+ expect(text).not.toMatch(/## delegated plan/);
118
+ expect(text).toMatch(/使用场景指南/);
117
119
  expect(structured.plan).toBeUndefined();
118
120
  expect(structured.projectDocs).toBeUndefined();
119
121
  }
@@ -141,15 +143,16 @@ describe("code_insight 单元测试", () => {
141
143
  expect(text).toMatch(/delegated plan/);
142
144
  expect(text).toMatch(/不要只口头总结而不写文件/);
143
145
  expect(text).toMatch(/docs\/graph-insights\/latest\.md/);
144
- expect(text).toMatch(/docs\/project-context\.md/);
146
+ expect(text).toMatch(/使用场景指南/);
145
147
  expect(structured.projectDocs.latestMarkdownFilePath).toContain("/docs/graph-insights/latest.md");
146
148
  expect(structured.projectDocs.archiveMarkdownFilePath).toContain("/docs/graph-insights/");
147
149
  expect(structured.projectDocs.projectContextFilePath).toContain("/docs/project-context.md");
148
150
  expect(structured.projectDocs.navigationSnippet).toMatch(/代码图谱洞察/);
149
151
  expect(structured.plan.mode).toBe("delegated");
150
- expect(structured.plan.steps[0].outputs[0]).toContain("/docs/project-context.md");
151
- const updateIndexStep = structured.plan.steps.find((step) => step.id === "update-project-context-index");
152
- expect(updateIndexStep.note).toMatch(/代码图谱洞察/);
152
+ expect(structured.plan.steps).toHaveLength(2);
153
+ expect(structured.plan.steps[0].id).toBe("consume-result");
154
+ expect(structured.plan.steps[1].id).toBe("optional-save");
155
+ expect(structured.plan.steps[1].outputs[0]).toContain("/docs/graph-insights/latest.md");
153
156
  expect(fs.existsSync(path.join(projectRoot, "docs", "graph-insights", "latest.md"))).toBe(false);
154
157
  }
155
158
  finally {
@@ -151,17 +151,12 @@ export function buildCodeInsightDelegatedPlan(input) {
151
151
  kind: "ambiguity",
152
152
  steps: [
153
153
  {
154
- id: "inspect-candidates",
155
- action: "阅读本次返回的 candidates,确认目标符号对应的 uid file_path",
156
- note: "若存在同名符号,优先使用 uid;需要限定文件时使用 file_path",
154
+ id: "consume-candidates",
155
+ action: "消费本次 candidates 列表,确认唯一目标符号(优先 uid,其次 file_path",
157
156
  },
158
157
  {
159
158
  id: "rerun-with-disambiguate",
160
- action: "重新调用 code_insight,并显式传入 uid 或 file_path 完成消歧",
161
- },
162
- {
163
- id: "resume-analysis",
164
- action: "消歧后再继续 context/impact 分析,必要时再决定是否保存到 docs/graph-insights",
159
+ action: "重新调用 code_insight,并传入 uid 或 file_path 后继续 context/impact 分析",
165
160
  },
166
161
  ],
167
162
  };
@@ -175,45 +170,32 @@ export function buildCodeInsightDelegatedPlan(input) {
175
170
  kind: "docs",
176
171
  steps: [
177
172
  {
178
- id: "ensure-project-context",
179
- action: projectContextExists
180
- ? `确认 ${input.projectDocs.projectContextFilePath} 已存在并可更新`
181
- : `检查 ${input.projectDocs.projectContextFilePath} 是否存在;若不存在,先调用 init_project_context 生成项目上下文索引`,
182
- outputs: [input.projectDocs.projectContextFilePath],
183
- note: projectContextExists
184
- ? "已有项目上下文,可直接补充图谱入口"
185
- : "只有 project-context.md 存在,后续图谱文档入口才可持续复用",
186
- },
187
- {
188
- id: "save-latest-md",
189
- action: `将本次 code_insight 的文本分析结果写入 ${input.projectDocs.latestMarkdownFilePath}`,
190
- outputs: [input.projectDocs.latestMarkdownFilePath],
191
- },
192
- {
193
- id: "save-archive-md",
194
- action: `将本次 code_insight 的文本分析结果归档到 ${input.projectDocs.archiveMarkdownFilePath}`,
195
- outputs: [input.projectDocs.archiveMarkdownFilePath],
196
- },
197
- {
198
- id: "save-latest-json",
199
- action: `将本次 code_insight 的 structuredContent 写入 ${input.projectDocs.latestJsonFilePath}`,
200
- outputs: [input.projectDocs.latestJsonFilePath],
201
- note: "建议保留完整结构化结果,便于后续 AI 继续读取",
202
- },
203
- {
204
- id: "save-archive-json",
205
- action: `将本次 code_insight 的 structuredContent 归档到 ${input.projectDocs.archiveJsonFilePath}`,
206
- outputs: [input.projectDocs.archiveJsonFilePath],
173
+ id: "consume-result",
174
+ action: "先消费本次分析结果(processes/symbols/impact),确认是否满足当前问题",
207
175
  },
208
176
  {
209
- id: "update-project-context-index",
210
- action: `更新 ${input.projectDocs.projectContextFilePath},在“## 📚 文档导航”加入图谱文档入口,并在“## 💡 开发时查看对应文档”加入代码图谱洞察链接`,
211
- outputs: [input.projectDocs.projectContextFilePath],
212
- note: `建议插入内容:\n${input.projectDocs.navigationSnippet}\n${input.projectDocs.devGuideSnippet}`,
177
+ id: "optional-save",
178
+ action: `如需保存,再写入 ${input.projectDocs.latestMarkdownFilePath}(文本)和 ${input.projectDocs.latestJsonFilePath}(结构化)`,
179
+ outputs: [input.projectDocs.latestMarkdownFilePath, input.projectDocs.latestJsonFilePath],
180
+ note: projectContextExists
181
+ ? `可选同步更新 ${input.projectDocs.projectContextFilePath} 的图谱入口`
182
+ : "若后续要持续沉淀,建议先补 init_project_context",
213
183
  },
214
184
  ],
215
185
  };
216
186
  }
187
+ function renderUsageGuide() {
188
+ return `## 使用场景指南
189
+ - 探索调用链: \`{ mode: "query", query: "login", goal: "理解登录认证流程" }\`
190
+ - 深入函数上下文: \`{ mode: "context", target: "login", file_path: "src/auth/login.ts" }\`
191
+ - 评估影响范围: \`{ mode: "impact", target: "login", direction: "upstream", file_path: "..." }\`
192
+ - 查看代码内容: \`{ mode: "context", target: "login", include_content: true }\`
193
+
194
+ ## 下一步建议
195
+ - 查询不精确: 增加 \`goal\`(例如“理解登录认证流程”)
196
+ - 出现歧义: 传入 \`uid\` 或 \`file_path\` 重新执行
197
+ - 需要落盘: 传 \`save_to_docs: true\`,再按 delegated plan 写入 docs/graph-insights`;
198
+ }
217
199
  export function resolveCodeInsightQuery(input) {
218
200
  const finalTarget = input.target || ((input.mode === "context" || input.mode === "impact") ? input.input : "");
219
201
  const finalQuery = input.query
@@ -325,6 +307,7 @@ ${executionSummary}
325
307
 
326
308
  ${ambiguityText ? `歧义候选:\n${ambiguityText}\n\n` : ""}\
327
309
  ${result.warnings.length > 0 ? `警告: ${result.warnings.join(", ")}` : ""}`.trim();
310
+ const usageGuide = renderUsageGuide();
328
311
  const structured = {
329
312
  status,
330
313
  provider: result.provider,
@@ -397,13 +380,15 @@ ${result.warnings.length > 0 ? `警告: ${result.warnings.join(", ")}` : ""}`.tr
397
380
  ${renderPlanSteps(delegatedPlan.steps)}
398
381
 
399
382
  ${delegatedPlan.kind === "docs" && projectDocs ? `后续操作:
400
- - 请先确保 ${projectDocs.projectContextFilePath} 可用,并把图谱入口挂到该索引中
401
- - 请将本次分析保存到 ${projectDocs.latestMarkdownFilePath}
402
- - 如需归档,请额外保存到 ${projectDocs.archiveMarkdownFilePath}
403
- - 如需结构化副本,请保存 JSON 到 ${projectDocs.latestJsonFilePath} 或 ${projectDocs.archiveJsonFilePath}` : `后续操作:
383
+ - 如需落盘,写入 ${projectDocs.latestMarkdownFilePath} 与 ${projectDocs.latestJsonFilePath}
384
+ - 如需长期沉淀,可再补充 ${projectDocs.projectContextFilePath} 的图谱入口` : `后续操作:
404
385
  - 请先从 candidates 中选定唯一符号
405
- - 重新传入 uid 或 file_path 后再继续 context / impact 分析`}`
406
- : message, structured);
386
+ - 重新传入 uid 或 file_path 后再继续 context / impact 分析`}
387
+
388
+ ${usageGuide}`
389
+ : `${message}
390
+
391
+ ${usageGuide}`, structured);
407
392
  }
408
393
  catch (error) {
409
394
  const errorMessage = error instanceof Error ? error.message : String(error);
package/docs/i18n/en.json CHANGED
@@ -93,6 +93,8 @@
93
93
  "item1": "The first cold start may take 20+ seconds while npx checks or downloads dependencies.",
94
94
  "item2": "Some GitNexus dependencies use tree-sitter native modules and may require Visual Studio Build Tools.",
95
95
  "item3": "If your MCP client supports env, prefer a preinstalled gitnexus CLI and raise GitNexus timeouts.",
96
+ "item4": "Quick install Build Tools on Windows: winget install Microsoft.VisualStudio.2022.BuildTools",
97
+ "quickInstallTitle": "Quick install command:",
96
98
  "exampleTitle": "Example using a preinstalled gitnexus CLI:"
97
99
  },
98
100
  "source": {
package/docs/i18n/ja.json CHANGED
@@ -93,6 +93,8 @@
93
93
  "item1": "初回のコールドスタートでは、npx が依存関係を確認またはダウンロードするため 20 秒以上かかることがあります。",
94
94
  "item2": "GitNexus の一部依存関係は tree-sitter ネイティブモジュールを使用しており、Windows では Visual Studio Build Tools が必要になる場合があります。",
95
95
  "item3": "MCP クライアントが env をサポートしている場合は、プリインストール済みの gitnexus CLI を優先し、GitNexus のタイムアウトを引き上げてください。",
96
+ "item4": "Windows では winget で Build Tools をすばやくインストールできます:winget install Microsoft.VisualStudio.2022.BuildTools",
97
+ "quickInstallTitle": "クイックインストールコマンド:",
96
98
  "exampleTitle": "プリインストール済み gitnexus CLI を使う設定例:"
97
99
  },
98
100
  "source": {
package/docs/i18n/ko.json CHANGED
@@ -93,6 +93,8 @@
93
93
  "item1": "첫 콜드 스타트에서는 npx 가 의존성을 확인하거나 다운로드하므로 20초 이상 걸릴 수 있습니다.",
94
94
  "item2": "GitNexus 의 일부 의존성은 tree-sitter 네이티브 모듈을 사용하며 Windows 에서는 Visual Studio Build Tools 가 필요할 수 있습니다.",
95
95
  "item3": "MCP 클라이언트가 env 를 지원하면, 미리 설치한 gitnexus CLI 를 우선 사용하고 GitNexus 타임아웃을 늘리세요.",
96
+ "item4": "Windows 에서 Build Tools 빠른 설치: winget install Microsoft.VisualStudio.2022.BuildTools",
97
+ "quickInstallTitle": "빠른 설치 명령:",
96
98
  "exampleTitle": "미리 설치한 gitnexus CLI 를 사용하는 구성 예시:"
97
99
  },
98
100
  "source": {
@@ -93,6 +93,8 @@
93
93
  "item1": "首次冷启动时,npx 可能检查或下载依赖,耗时可能超过 20 秒。",
94
94
  "item2": "GitNexus 的部分依赖使用 tree-sitter 原生模块,在 Windows 上可能需要 Visual Studio Build Tools。",
95
95
  "item3": "如果 MCP 客户端支持 env,优先使用预装的 gitnexus CLI,并适当增大 GitNexus 超时。",
96
+ "item4": "Windows 下可用 winget 快速安装 Build Tools:winget install Microsoft.VisualStudio.2022.BuildTools",
97
+ "quickInstallTitle": "快速安装命令:",
96
98
  "exampleTitle": "使用预装 gitnexus CLI 的配置示例:"
97
99
  },
98
100
  "source": {
@@ -317,7 +317,10 @@ npm run build</code></pre>
317
317
  <li data-i18n="gettingStarted.step1.windowsGraph.item1">The first cold start may take 20+ seconds while npx checks or downloads dependencies.</li>
318
318
  <li data-i18n="gettingStarted.step1.windowsGraph.item2">Some GitNexus dependencies use tree-sitter native modules and may require Visual Studio Build Tools.</li>
319
319
  <li data-i18n="gettingStarted.step1.windowsGraph.item3">If your MCP client supports env, prefer a preinstalled gitnexus CLI and raise GitNexus timeouts.</li>
320
+ <li data-i18n="gettingStarted.step1.windowsGraph.item4">Quick install Build Tools on Windows: winget install Microsoft.VisualStudio.2022.BuildTools</li>
320
321
  </ul>
322
+ <p class="mb-2" data-i18n="gettingStarted.step1.windowsGraph.quickInstallTitle">Quick install command:</p>
323
+ <pre class="code-block mb-3 p-3 text-sm text-slate-200 bg-slate-900 rounded overflow-x-auto"><code>winget install Microsoft.VisualStudio.2022.BuildTools</code></pre>
321
324
  <p class="mb-2" data-i18n="gettingStarted.step1.windowsGraph.exampleTitle">Example using a preinstalled gitnexus CLI:</p>
322
325
  <div class="bg-slate-900 rounded-lg overflow-hidden">
323
326
  <div class="flex items-center justify-between px-3 py-2 bg-slate-800">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-probe-kit",
3
- "version": "3.0.13",
3
+ "version": "3.0.15",
4
4
  "description": "AI-Powered Development Toolkit - MCP Server with 22 tools covering code quality, development efficiency, project management, and UI/UX design. Features: Structured Output, Workflow Orchestration, UI/UX Pro Max, and Requirements Interview.",
5
5
  "mcpName": "io.github.mybolide/mcp-probe-kit",
6
6
  "type": "module",