@zokizuan/satori-mcp 3.3.0 → 3.5.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/README.md CHANGED
@@ -85,7 +85,7 @@ Unified semantic search with runtime/docs scope control, grouped/raw output mode
85
85
 
86
86
  ### `call_graph`
87
87
 
88
- Traverse the prebuilt TS/Python call graph sidecar for callers/callees/bidirectional symbol relationships.
88
+ Traverse the prebuilt TS/JS/Python call graph sidecar for callers/callees/bidirectional symbol relationships.
89
89
 
90
90
  | Parameter | Type | Required | Default | Description |
91
91
  |---|---|---|---|---|
@@ -3,9 +3,10 @@ import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
4
  import crypto from 'node:crypto';
5
5
  import ignore from 'ignore';
6
- import { AstCodeSplitter } from '@zokizuan/satori-core';
7
- const SUPPORTED_SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py']);
8
- const QUERY_SUPPORTED_EXTENSIONS = new Set(['.ts', '.tsx', '.py']);
6
+ import { AstCodeSplitter, getLanguageIdFromExtension, getSupportedExtensionsForCapability, getSupportedLanguageIdsForCapability, } from '@zokizuan/satori-core';
7
+ const SUPPORTED_SOURCE_EXTENSIONS = new Set(getSupportedExtensionsForCapability('callGraphBuild'));
8
+ const QUERY_SUPPORTED_EXTENSIONS = new Set(getSupportedExtensionsForCapability('callGraphQuery'));
9
+ const QUERY_SUPPORTED_LANGUAGE_IDS = getSupportedLanguageIdsForCapability('callGraphQuery');
9
10
  const DEFAULT_IGNORE_PATTERNS = ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**', '**/coverage/**', '**/.next/**'];
10
11
  const CALL_KEYWORDS = new Set([
11
12
  'if', 'for', 'while', 'switch', 'catch', 'return', 'new', 'typeof', 'function', 'class', 'def', 'await', 'with', 'from', 'import',
@@ -478,11 +479,9 @@ export class CallGraphSidecarManager {
478
479
  }
479
480
  getSplitLanguage(relativePath) {
480
481
  const ext = path.extname(relativePath).toLowerCase();
481
- if (ext === '.py') {
482
- return 'python';
483
- }
484
- if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') {
485
- return ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs' ? 'javascript' : 'typescript';
482
+ const language = getLanguageIdFromExtension(ext, 'unknown');
483
+ if (language === 'python' || language === 'typescript' || language === 'javascript') {
484
+ return language;
486
485
  }
487
486
  return 'unknown';
488
487
  }
@@ -495,8 +494,9 @@ export class CallGraphSidecarManager {
495
494
  supported: false,
496
495
  reason: 'unsupported_language',
497
496
  hints: {
498
- supportedExtensions: ['.ts', '.tsx', '.py'],
499
- message: 'call_graph currently supports TypeScript and Python symbols.',
497
+ supportedExtensions: Array.from(QUERY_SUPPORTED_EXTENSIONS).sort((a, b) => a.localeCompare(b)),
498
+ supportedLanguages: QUERY_SUPPORTED_LANGUAGE_IDS,
499
+ message: 'call_graph currently supports TypeScript, JavaScript, and Python symbols.',
500
500
  },
501
501
  };
502
502
  }
@@ -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;
@@ -199,7 +202,19 @@ export declare class ToolHandlers {
199
202
  }[];
200
203
  isError: boolean;
201
204
  }>;
202
- 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
+ }>;
203
218
  /**
204
219
  * Handle sync request - manually trigger incremental sync for a codebase
205
220
  */
@@ -2,7 +2,7 @@ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import crypto from "node:crypto";
4
4
  import ignore from "ignore";
5
- import { COLLECTION_LIMIT_MESSAGE } from "@zokizuan/satori-core";
5
+ import { COLLECTION_LIMIT_MESSAGE, getSupportedExtensionsForCapability, isLanguageCapabilitySupportedForExtension, isLanguageCapabilitySupportedForLanguage, } from "@zokizuan/satori-core";
6
6
  import { ensureAbsolutePath, truncateContent, trackCodebasePath } from "../utils.js";
7
7
  import { SEARCH_MAX_CANDIDATES, SEARCH_PROXIMITY_WINDOW, SEARCH_RRF_K, SCOPE_PATH_MULTIPLIERS, STALENESS_THRESHOLDS_MS } from "./search-constants.js";
8
8
  import { CallGraphSidecarManager } from "./call-graph.js";
@@ -14,6 +14,7 @@ const COLLECTION_LIMIT_PATTERNS = [
14
14
  ];
15
15
  const SATORI_COLLECTION_PREFIXES = ['code_chunks_', 'hybrid_code_chunks_'];
16
16
  const ZILLIZ_FREE_TIER_COLLECTION_LIMIT = 5;
17
+ const OUTLINE_SUPPORTED_EXTENSIONS = getSupportedExtensionsForCapability('fileOutline');
17
18
  function collectErrorFragments(value, output, visited, depth = 0) {
18
19
  if (value === null || value === undefined || depth > 4 || output.length >= 8) {
19
20
  return;
@@ -119,6 +120,46 @@ export class ToolHandlers {
119
120
  }
120
121
  };
121
122
  }
123
+ summarizeFingerprint(fingerprint) {
124
+ return `${fingerprint.embeddingProvider}/${fingerprint.embeddingModel}/${fingerprint.embeddingDimension}/${fingerprint.vectorStoreProvider}/${fingerprint.schemaVersion}`;
125
+ }
126
+ buildCompatibilityDiagnostics(codebasePath) {
127
+ const info = typeof this.snapshotManager.getCodebaseInfo === 'function'
128
+ ? this.snapshotManager.getCodebaseInfo(codebasePath)
129
+ : undefined;
130
+ const statusAtCheck = info?.status
131
+ || (typeof this.snapshotManager.getCodebaseStatus === 'function'
132
+ ? this.snapshotManager.getCodebaseStatus(codebasePath)
133
+ : 'not_found');
134
+ const diagnostics = {
135
+ runtimeFingerprint: this.runtimeFingerprint,
136
+ statusAtCheck
137
+ };
138
+ if (info?.indexFingerprint) {
139
+ diagnostics.indexedFingerprint = info.indexFingerprint;
140
+ }
141
+ if (info?.fingerprintSource) {
142
+ diagnostics.fingerprintSource = info.fingerprintSource;
143
+ }
144
+ if (info?.reindexReason) {
145
+ diagnostics.reindexReason = info.reindexReason;
146
+ }
147
+ return diagnostics;
148
+ }
149
+ buildCompatibilityStatusLines(codebasePath) {
150
+ const diagnostics = this.buildCompatibilityDiagnostics(codebasePath);
151
+ let lines = `\n🧬 Runtime fingerprint: ${this.summarizeFingerprint(diagnostics.runtimeFingerprint)}`;
152
+ lines += diagnostics.indexedFingerprint
153
+ ? `\n🧬 Indexed fingerprint: ${this.summarizeFingerprint(diagnostics.indexedFingerprint)}`
154
+ : `\n🧬 Indexed fingerprint: unavailable`;
155
+ if (diagnostics.fingerprintSource) {
156
+ lines += `\n🧬 Fingerprint source: ${diagnostics.fingerprintSource}`;
157
+ }
158
+ if (diagnostics.reindexReason) {
159
+ lines += `\n🧬 Reindex reason: ${diagnostics.reindexReason}`;
160
+ }
161
+ return lines;
162
+ }
122
163
  buildRequiresReindexPayload(codebasePath, detail, searchContext) {
123
164
  const detailLine = detail ? `${detail}\n\n` : '';
124
165
  const base = searchContext ? {
@@ -140,7 +181,8 @@ export class ToolHandlers {
140
181
  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
182
  hints: {
142
183
  reindex: this.buildReindexHint(codebasePath)
143
- }
184
+ },
185
+ compatibility: this.buildCompatibilityDiagnostics(codebasePath)
144
186
  };
145
187
  }
146
188
  buildRequiresReindexSearchResponse(codebasePath, detail, searchContext) {
@@ -172,7 +214,8 @@ export class ToolHandlers {
172
214
  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
215
  hints: {
174
216
  reindex: this.buildReindexHint(codebasePath)
175
- }
217
+ },
218
+ compatibility: this.buildCompatibilityDiagnostics(codebasePath)
176
219
  };
177
220
  }
178
221
  getMatchingBlockedRoot(absolutePath) {
@@ -431,13 +474,12 @@ export class ToolHandlers {
431
474
  return `grp_${digest}`;
432
475
  }
433
476
  isCallGraphLanguageSupported(language, file) {
434
- const normalized = String(language || '').toLowerCase();
435
- if (normalized === 'typescript' || normalized === 'ts' || normalized === 'python' || normalized === 'py') {
477
+ if (isLanguageCapabilitySupportedForLanguage(language, 'callGraphQuery')) {
436
478
  return true;
437
479
  }
438
480
  if (typeof file === 'string') {
439
481
  const ext = path.extname(file).toLowerCase();
440
- return ext === '.ts' || ext === '.tsx' || ext === '.py';
482
+ return isLanguageCapabilitySupportedForExtension(ext, 'callGraphQuery');
441
483
  }
442
484
  return false;
443
485
  }
@@ -1763,7 +1805,7 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
1763
1805
  file: normalizedFile,
1764
1806
  outline: null,
1765
1807
  hasMore: false,
1766
- message: `File '${normalizedFile}' is not supported for sidecar outline. Supported extensions: .ts, .tsx, .py.`
1808
+ message: `File '${normalizedFile}' is not supported for sidecar outline. Supported extensions: ${OUTLINE_SUPPORTED_EXTENSIONS.join(', ')}.`
1767
1809
  };
1768
1810
  return {
1769
1811
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
@@ -2131,7 +2173,17 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
2131
2173
  // Check indexing status using new status system
2132
2174
  const statusGate = this.enforceFingerprintGate(absolutePath);
2133
2175
  if (statusGate.blockedResponse) {
2134
- 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
+ };
2135
2187
  }
2136
2188
  const status = this.snapshotManager.getCodebaseStatus(absolutePath);
2137
2189
  const info = this.snapshotManager.getCodebaseInfo(absolutePath);
@@ -2204,10 +2256,11 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
2204
2256
  const pathInfo = codebasePath !== absolutePath
2205
2257
  ? `\nNote: Input path '${codebasePath}' was resolved to absolute path '${absolutePath}'`
2206
2258
  : '';
2259
+ const compatibilityStatus = this.buildCompatibilityStatusLines(absolutePath);
2207
2260
  return {
2208
2261
  content: [{
2209
2262
  type: "text",
2210
- text: statusMessage + pathInfo
2263
+ text: statusMessage + compatibilityStatus + pathInfo
2211
2264
  }]
2212
2265
  };
2213
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";
@@ -18,7 +18,7 @@ const callGraphInputSchema = z.object({
18
18
  });
19
19
  export const callGraphTool = {
20
20
  name: 'call_graph',
21
- description: () => 'Traverse the prebuilt TS/Python call graph sidecar for callers/callees/bidirectional symbol relationships.',
21
+ description: () => 'Traverse the prebuilt TS/JS/Python call graph sidecar for callers/callees/bidirectional symbol relationships.',
22
22
  inputSchemaZod: () => callGraphInputSchema,
23
23
  execute: async (args, ctx) => {
24
24
  const normalizedArgs = (args && typeof args === 'object')
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { z } from "zod";
4
+ import { isLanguageCapabilitySupportedForExtension } from "@zokizuan/satori-core";
4
5
  import { formatZodError } from "./types.js";
5
6
  import { ensureAbsolutePath } from "../utils.js";
6
7
  const readFileInputSchema = z.object({
@@ -27,7 +28,7 @@ function normalizeRelativePath(value) {
27
28
  }
28
29
  function isOutlineSupportedFile(absolutePath) {
29
30
  const ext = path.extname(absolutePath).toLowerCase();
30
- return ext === ".ts" || ext === ".tsx" || ext === ".py";
31
+ return isLanguageCapabilitySupportedForExtension(ext, "fileOutline");
31
32
  }
32
33
  function resolveCodebaseRootForFile(absolutePath, ctx) {
33
34
  const allCodebases = typeof ctx.snapshotManager?.getAllCodebases === "function"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zokizuan/satori-mcp",
3
- "version": "3.3.0",
3
+ "version": "3.5.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",