@zokizuan/satori-mcp 3.2.0 → 3.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.
@@ -16,6 +16,9 @@ export declare class ToolHandlers {
16
16
  constructor(context: Context, snapshotManager: SnapshotManager, syncManager: SyncManager, runtimeFingerprint: IndexFingerprint, now?: () => number, callGraphManager?: CallGraphSidecarManager);
17
17
  private buildReindexInstruction;
18
18
  private buildReindexHint;
19
+ private summarizeFingerprint;
20
+ private buildCompatibilityDiagnostics;
21
+ private buildCompatibilityStatusLines;
19
22
  private buildRequiresReindexPayload;
20
23
  private buildRequiresReindexSearchResponse;
21
24
  private buildRequiresReindexCallGraphPayload;
@@ -46,6 +49,9 @@ export declare class ToolHandlers {
46
49
  private getOutlineStatusForLanguage;
47
50
  private sortFileOutlineSymbols;
48
51
  private buildSearchPassWarning;
52
+ private isSearchPassFaultInjectionEnabled;
53
+ private getForcedFailedSearchPassId;
54
+ private shouldForceSearchPassFailure;
49
55
  private mapCallGraphStatus;
50
56
  private getContextIgnorePatterns;
51
57
  private rebuildCallGraphForIndex;
@@ -196,7 +202,19 @@ export declare class ToolHandlers {
196
202
  }[];
197
203
  isError: boolean;
198
204
  }>;
199
- handleGetIndexingStatus(args: any): Promise<any>;
205
+ handleGetIndexingStatus(args: any): Promise<{
206
+ content: {
207
+ type: string;
208
+ text: string;
209
+ }[];
210
+ isError: boolean;
211
+ } | {
212
+ content: {
213
+ type: string;
214
+ text: string;
215
+ }[];
216
+ isError?: undefined;
217
+ }>;
200
218
  /**
201
219
  * Handle sync request - manually trigger incremental sync for a codebase
202
220
  */
@@ -119,6 +119,46 @@ export class ToolHandlers {
119
119
  }
120
120
  };
121
121
  }
122
+ summarizeFingerprint(fingerprint) {
123
+ return `${fingerprint.embeddingProvider}/${fingerprint.embeddingModel}/${fingerprint.embeddingDimension}/${fingerprint.vectorStoreProvider}/${fingerprint.schemaVersion}`;
124
+ }
125
+ buildCompatibilityDiagnostics(codebasePath) {
126
+ const info = typeof this.snapshotManager.getCodebaseInfo === 'function'
127
+ ? this.snapshotManager.getCodebaseInfo(codebasePath)
128
+ : undefined;
129
+ const statusAtCheck = info?.status
130
+ || (typeof this.snapshotManager.getCodebaseStatus === 'function'
131
+ ? this.snapshotManager.getCodebaseStatus(codebasePath)
132
+ : 'not_found');
133
+ const diagnostics = {
134
+ runtimeFingerprint: this.runtimeFingerprint,
135
+ statusAtCheck
136
+ };
137
+ if (info?.indexFingerprint) {
138
+ diagnostics.indexedFingerprint = info.indexFingerprint;
139
+ }
140
+ if (info?.fingerprintSource) {
141
+ diagnostics.fingerprintSource = info.fingerprintSource;
142
+ }
143
+ if (info?.reindexReason) {
144
+ diagnostics.reindexReason = info.reindexReason;
145
+ }
146
+ return diagnostics;
147
+ }
148
+ buildCompatibilityStatusLines(codebasePath) {
149
+ const diagnostics = this.buildCompatibilityDiagnostics(codebasePath);
150
+ let lines = `\n🧬 Runtime fingerprint: ${this.summarizeFingerprint(diagnostics.runtimeFingerprint)}`;
151
+ lines += diagnostics.indexedFingerprint
152
+ ? `\n🧬 Indexed fingerprint: ${this.summarizeFingerprint(diagnostics.indexedFingerprint)}`
153
+ : `\n🧬 Indexed fingerprint: unavailable`;
154
+ if (diagnostics.fingerprintSource) {
155
+ lines += `\n🧬 Fingerprint source: ${diagnostics.fingerprintSource}`;
156
+ }
157
+ if (diagnostics.reindexReason) {
158
+ lines += `\n🧬 Reindex reason: ${diagnostics.reindexReason}`;
159
+ }
160
+ return lines;
161
+ }
122
162
  buildRequiresReindexPayload(codebasePath, detail, searchContext) {
123
163
  const detailLine = detail ? `${detail}\n\n` : '';
124
164
  const base = searchContext ? {
@@ -140,7 +180,8 @@ export class ToolHandlers {
140
180
  message: `${detailLine}The index at '${codebasePath}' is incompatible with the current runtime and must be rebuilt. Please run manage_index with {\"action\":\"reindex\",\"path\":\"${codebasePath}\"}.`,
141
181
  hints: {
142
182
  reindex: this.buildReindexHint(codebasePath)
143
- }
183
+ },
184
+ compatibility: this.buildCompatibilityDiagnostics(codebasePath)
144
185
  };
145
186
  }
146
187
  buildRequiresReindexSearchResponse(codebasePath, detail, searchContext) {
@@ -172,7 +213,8 @@ export class ToolHandlers {
172
213
  message: `${detailLine}The index at '${codebasePath}' is incompatible with the current runtime and must be rebuilt. Please run manage_index with {"action":"reindex","path":"${codebasePath}"}.`,
173
214
  hints: {
174
215
  reindex: this.buildReindexHint(codebasePath)
175
- }
216
+ },
217
+ compatibility: this.buildCompatibilityDiagnostics(codebasePath)
176
218
  };
177
219
  }
178
220
  getMatchingBlockedRoot(absolutePath) {
@@ -498,6 +540,28 @@ export class ToolHandlers {
498
540
  buildSearchPassWarning(passId) {
499
541
  return `SEARCH_PASS_FAILED:${passId} - ${passId} semantic search pass failed; results may be degraded.`;
500
542
  }
543
+ isSearchPassFaultInjectionEnabled() {
544
+ return process.env.NODE_ENV === 'test';
545
+ }
546
+ getForcedFailedSearchPassId() {
547
+ if (!this.isSearchPassFaultInjectionEnabled()) {
548
+ return undefined;
549
+ }
550
+ const raw = typeof process.env.SATORI_TEST_FAIL_SEARCH_PASS === 'string'
551
+ ? process.env.SATORI_TEST_FAIL_SEARCH_PASS.trim().toLowerCase()
552
+ : '';
553
+ if (raw === 'primary' || raw === 'expanded' || raw === 'both') {
554
+ return raw;
555
+ }
556
+ return undefined;
557
+ }
558
+ shouldForceSearchPassFailure(passId) {
559
+ const forced = this.getForcedFailedSearchPassId();
560
+ if (!forced) {
561
+ return false;
562
+ }
563
+ return forced === 'both' || forced === passId;
564
+ }
501
565
  mapCallGraphStatus(graph) {
502
566
  if (graph.supported) {
503
567
  return 'ok';
@@ -1386,7 +1450,11 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
1386
1450
  { id: 'expanded', query: expandedQuery },
1387
1451
  ];
1388
1452
  searchDiagnostics.searchPassCount = passDescriptors.length;
1389
- const passSettled = await Promise.allSettled(passDescriptors.map((pass) => {
1453
+ const passSettled = await Promise.allSettled(passDescriptors.map(async (pass) => {
1454
+ const passId = pass.id;
1455
+ if (this.shouldForceSearchPassFailure(passId)) {
1456
+ throw new Error(`FORCED_TEST_SEARCH_PASS_FAILURE:${passId}`);
1457
+ }
1390
1458
  return this.context.semanticSearch(effectiveRoot, pass.query, candidateLimit, 0.3);
1391
1459
  }));
1392
1460
  const searchWarnings = [];
@@ -2105,7 +2173,17 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
2105
2173
  // Check indexing status using new status system
2106
2174
  const statusGate = this.enforceFingerprintGate(absolutePath);
2107
2175
  if (statusGate.blockedResponse) {
2108
- return statusGate.blockedResponse;
2176
+ const statusMessage = this.buildReindexInstruction(absolutePath, statusGate.message);
2177
+ const compatibilityStatus = this.buildCompatibilityStatusLines(absolutePath);
2178
+ const pathInfo = codebasePath !== absolutePath
2179
+ ? `\nNote: Input path '${codebasePath}' was resolved to absolute path '${absolutePath}'`
2180
+ : '';
2181
+ return {
2182
+ content: [{
2183
+ type: "text",
2184
+ text: statusMessage + compatibilityStatus + pathInfo
2185
+ }]
2186
+ };
2109
2187
  }
2110
2188
  const status = this.snapshotManager.getCodebaseStatus(absolutePath);
2111
2189
  const info = this.snapshotManager.getCodebaseInfo(absolutePath);
@@ -2178,10 +2256,11 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
2178
2256
  const pathInfo = codebasePath !== absolutePath
2179
2257
  ? `\nNote: Input path '${codebasePath}' was resolved to absolute path '${absolutePath}'`
2180
2258
  : '';
2259
+ const compatibilityStatus = this.buildCompatibilityStatusLines(absolutePath);
2181
2260
  return {
2182
2261
  content: [{
2183
2262
  type: "text",
2184
- text: statusMessage + pathInfo
2263
+ text: statusMessage + compatibilityStatus + pathInfo
2185
2264
  }]
2186
2265
  };
2187
2266
  }
@@ -1,5 +1,6 @@
1
1
  import { FreshnessDecision } from "./sync.js";
2
2
  import { SearchGroupBy, SearchResultMode, SearchScope } from "./search-constants.js";
3
+ import { FingerprintSource, IndexFingerprint } from "../config.js";
3
4
  export type StalenessBucket = "fresh" | "aging" | "stale" | "unknown";
4
5
  export interface SearchSpan {
5
6
  startLine: number;
@@ -57,6 +58,13 @@ export interface SearchGroupResult {
57
58
  topChunkScore: number;
58
59
  };
59
60
  }
61
+ export interface FingerprintCompatibilityDiagnostics {
62
+ runtimeFingerprint: IndexFingerprint;
63
+ indexedFingerprint?: IndexFingerprint;
64
+ fingerprintSource?: FingerprintSource;
65
+ reindexReason?: "legacy_unverified_fingerprint" | "fingerprint_mismatch" | "missing_fingerprint";
66
+ statusAtCheck?: "indexed" | "indexing" | "indexfailed" | "sync_completed" | "requires_reindex" | "not_found";
67
+ }
60
68
  interface SearchBaseResponseEnvelope {
61
69
  status: "ok" | "requires_reindex" | "not_indexed";
62
70
  path: string;
@@ -70,6 +78,7 @@ interface SearchBaseResponseEnvelope {
70
78
  warnings?: string[];
71
79
  message?: string;
72
80
  hints?: Record<string, unknown>;
81
+ compatibility?: FingerprintCompatibilityDiagnostics;
73
82
  }
74
83
  export interface SearchGroupedResponseEnvelope extends SearchBaseResponseEnvelope {
75
84
  resultMode: "grouped";
@@ -21,7 +21,15 @@ export const callGraphTool = {
21
21
  description: () => 'Traverse the prebuilt TS/Python call graph sidecar for callers/callees/bidirectional symbol relationships.',
22
22
  inputSchemaZod: () => callGraphInputSchema,
23
23
  execute: async (args, ctx) => {
24
- const parsed = callGraphInputSchema.safeParse(args || {});
24
+ const normalizedArgs = (args && typeof args === 'object')
25
+ ? { ...args }
26
+ : (args || {});
27
+ if (normalizedArgs
28
+ && typeof normalizedArgs === 'object'
29
+ && normalizedArgs.direction === 'bidirectional') {
30
+ normalizedArgs.direction = 'both';
31
+ }
32
+ const parsed = callGraphInputSchema.safeParse(normalizedArgs);
25
33
  if (!parsed.success) {
26
34
  return {
27
35
  content: [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zokizuan/satori-mcp",
3
- "version": "3.2.0",
3
+ "version": "3.4.0",
4
4
  "description": "MCP server for Satori with agent-safe semantic search and indexing",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",