@zokizuan/satori-mcp 4.11.0 → 4.11.2

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 ham-zax
3
+ Copyright (c) 2026 Hamza (@ham-zax)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -7,8 +7,8 @@ Read-only MCP server for Satori. It gives coding agents six deterministic tools
7
7
  Use the CLI installer for normal setup:
8
8
 
9
9
  ```bash
10
- npx -y @zokizuan/satori-cli@0.4.0 install --client all
11
- npx -y @zokizuan/satori-cli@0.4.0 doctor
10
+ npx -y @zokizuan/satori-cli@0.4.2 install --client all
11
+ npx -y @zokizuan/satori-cli@0.4.2 doctor
12
12
  ```
13
13
 
14
14
  The CLI installer supports `codex`, `claude`, `opencode`, and `all`. It creates the runtime cache, writes the stable launcher, and writes client config for you. Avoid using `npx` as the resident MCP server command; first-run package resolution can exceed normal MCP startup timeouts.
@@ -16,9 +16,11 @@ The CLI installer supports `codex`, `claude`, `opencode`, and `all`. It creates
16
16
  Advanced direct execution is available through the package bin:
17
17
 
18
18
  ```bash
19
- npx -y @zokizuan/satori-mcp@4.11.0 --help
19
+ npx -y @zokizuan/satori-mcp@4.11.2 --help
20
20
  ```
21
21
 
22
+ Use direct package execution for inspection, smoke tests, or unsupported harnesses. For supported clients, prefer `satori-cli install` so startup does not depend on package-manager resolution.
23
+
22
24
  ## Agent Workflow
23
25
 
24
26
  ```text
@@ -42,6 +44,27 @@ Important defaults:
42
44
 
43
45
  Configure an embedding provider and Milvus-compatible backend before indexing. Supported embedding providers are OpenAI, VoyageAI, Gemini, and Ollama. Changing provider, model, dimension, vector store, or schema requires a reindex because those values are part of the index fingerprint.
44
46
 
47
+ MCP startup, `tools/list`, and installer operations are lazy with respect to provider credentials. Missing provider values become `MISSING_PROVIDER_CONFIG` only when a provider-backed tool call needs them.
48
+
49
+ Installer-managed client config starts the resident launcher. Runtime provider settings come from the MCP client's environment and are exposed in native client config:
50
+
51
+ - Codex writes active `env_vars` forwarding plus an optional commented `[mcp_servers.satori.env]` template in `~/.codex/config.toml`.
52
+ - Claude Code writes `mcpServers.satori.env` in `~/.claude.json` with `${VAR:-}` pass-through values.
53
+ - OpenCode writes `mcp.satori.environment` in `~/.config/opencode/opencode.json` with `{env:VAR}` pass-through values.
54
+
55
+ Users who want literal values in a client config can replace the generated pass-through value for that client. In Codex, uncomment or add this table outside the installer-managed launcher block so reinstalls keep edits:
56
+
57
+ ```toml
58
+ [mcp_servers.satori.env]
59
+ EMBEDDING_PROVIDER = "VoyageAI"
60
+ EMBEDDING_MODEL = "voyage-4-large"
61
+ EMBEDDING_OUTPUT_DIMENSION = "1024"
62
+ VOYAGEAI_API_KEY = "pa-..."
63
+ VOYAGEAI_RERANKER_MODEL = "rerank-2.5"
64
+ MILVUS_ADDRESS = "https://your-zilliz-endpoint"
65
+ MILVUS_TOKEN = "your-zilliz-token"
66
+ ```
67
+
45
68
  Cloud-quality setup:
46
69
 
47
70
  ```bash
@@ -7,12 +7,47 @@ import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser";
7
7
  import { CliError } from "./errors.js";
8
8
  const MANAGED_BLOCK_START = "# >>> satori-cli managed satori start >>>";
9
9
  const MANAGED_BLOCK_END = "# <<< satori-cli managed satori end <<<";
10
+ const CODEX_ENV_TEMPLATE_START = "# >>> satori-cli optional satori env template >>>";
11
+ const CODEX_ENV_TEMPLATE_END = "# <<< satori-cli optional satori env template <<<";
10
12
  const INSTRUCTIONS_BLOCK_START = "<!-- satori-mcp:start -->";
11
13
  const INSTRUCTIONS_BLOCK_END = "<!-- satori-mcp:end -->";
12
14
  const OWNED_SKILL_DIRS = ["satori"];
13
15
  const MANAGED_RUNTIME_DIR = "mcp-runtime";
14
16
  const MANAGED_BIN_DIR = "bin";
15
17
  const MANAGED_LAUNCHER_FILE = "satori-mcp.js";
18
+ const SATORI_RUNTIME_ENV_VARS = [
19
+ "EMBEDDING_PROVIDER",
20
+ "EMBEDDING_MODEL",
21
+ "EMBEDDING_OUTPUT_DIMENSION",
22
+ "OPENAI_API_KEY",
23
+ "OPENAI_BASE_URL",
24
+ "VOYAGEAI_API_KEY",
25
+ "VOYAGEAI_RERANKER_MODEL",
26
+ "GEMINI_API_KEY",
27
+ "GEMINI_BASE_URL",
28
+ "OLLAMA_HOST",
29
+ "OLLAMA_MODEL",
30
+ "MILVUS_ADDRESS",
31
+ "MILVUS_TOKEN",
32
+ "READ_FILE_MAX_LINES",
33
+ "MCP_ENABLE_WATCHER",
34
+ "MCP_WATCH_DEBOUNCE_MS",
35
+ ];
36
+ const CODEX_ENV_TEMPLATE_LINES = [
37
+ CODEX_ENV_TEMPLATE_START,
38
+ "# Optional direct Codex env values. Uncomment/fill these if you prefer",
39
+ "# ~/.codex/config.toml to store Satori runtime settings directly.",
40
+ "# This template is outside the launcher block so reinstall keeps edits.",
41
+ "# [mcp_servers.satori.env]",
42
+ "# EMBEDDING_PROVIDER = \"VoyageAI\"",
43
+ "# EMBEDDING_MODEL = \"voyage-4-large\"",
44
+ "# EMBEDDING_OUTPUT_DIMENSION = \"1024\"",
45
+ "# VOYAGEAI_API_KEY = \"pa-...\"",
46
+ "# VOYAGEAI_RERANKER_MODEL = \"rerank-2.5\"",
47
+ "# MILVUS_ADDRESS = \"https://your-zilliz-endpoint\"",
48
+ "# MILVUS_TOKEN = \"your-zilliz-token\"",
49
+ CODEX_ENV_TEMPLATE_END,
50
+ ];
16
51
  const OPENCODE_INSTRUCTIONS = `# Satori MCP
17
52
 
18
53
  This project uses Satori MCP for semantic code search, deterministic navigation, and index lifecycle management.
@@ -60,7 +95,7 @@ function resolveClientTargets(homeDir) {
60
95
  },
61
96
  {
62
97
  client: "claude",
63
- configPath: path.join(homeDir, ".claude", "settings.json"),
98
+ configPath: path.join(homeDir, ".claude.json"),
64
99
  companion: {
65
100
  kind: "skills",
66
101
  path: path.join(homeDir, ".claude", "skills"),
@@ -107,6 +142,21 @@ function toTomlString(value) {
107
142
  function buildTomlArray(values) {
108
143
  return `[${values.map(toTomlString).join(", ")}]`;
109
144
  }
145
+ function runtimeEnvMap(valueForName) {
146
+ return Object.fromEntries(SATORI_RUNTIME_ENV_VARS.map((name) => [name, valueForName(name)]));
147
+ }
148
+ function objectValue(value) {
149
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
150
+ return undefined;
151
+ }
152
+ return value;
153
+ }
154
+ function mergeRuntimeEnv(existing, defaults) {
155
+ return {
156
+ ...defaults,
157
+ ...(objectValue(existing) ?? {}),
158
+ };
159
+ }
110
160
  function packageNameFromSpecifier(packageSpecifier) {
111
161
  if (packageSpecifier.startsWith("@")) {
112
162
  const versionMarker = packageSpecifier.indexOf("@", 1);
@@ -262,10 +312,25 @@ function buildCodexManagedBlock(runtimeCommand) {
262
312
  "[mcp_servers.satori]",
263
313
  `command = ${toTomlString(runtimeCommand.command)}`,
264
314
  `args = ${buildTomlArray(runtimeCommand.args)}`,
315
+ "# Satori reads provider/vector settings from environment at MCP startup.",
316
+ "# env_vars forwards these names from Codex's parent environment when set.",
317
+ `env_vars = ${buildTomlArray([...SATORI_RUNTIME_ENV_VARS])}`,
265
318
  MANAGED_BLOCK_END,
266
319
  "",
267
320
  ].join("\n");
268
321
  }
322
+ function buildCodexEnvTemplateBlock() {
323
+ return `${CODEX_ENV_TEMPLATE_LINES.join("\n")}\n`;
324
+ }
325
+ function codexHasSatoriEnvTable(content) {
326
+ return /^\s*\[mcp_servers\.satori\.env\]\s*$/m.test(content);
327
+ }
328
+ function ensureCodexEnvTemplate(content) {
329
+ if (content.includes(CODEX_ENV_TEMPLATE_START) || codexHasSatoriEnvTable(content)) {
330
+ return content;
331
+ }
332
+ return `${normalizeTrailingNewline(content)}\n${buildCodexEnvTemplateBlock()}`;
333
+ }
269
334
  function codexHasUnmanagedSatoriSection(content) {
270
335
  if (!content.includes("[mcp_servers.satori]")) {
271
336
  return false;
@@ -288,6 +353,7 @@ function prepareCodexInstall(filePath, runtimeCommand) {
288
353
  else {
289
354
  next = `${normalizeTrailingNewline(current)}\n${managedBlock}`;
290
355
  }
356
+ next = ensureCodexEnvTemplate(next);
291
357
  return {
292
358
  changed: next !== current,
293
359
  apply: () => {
@@ -341,10 +407,12 @@ function parseJsonObject(filePath) {
341
407
  }
342
408
  return parsed;
343
409
  }
344
- function buildClaudeServerConfig(runtimeCommand) {
410
+ function buildClaudeServerConfig(runtimeCommand, existing) {
345
411
  return {
412
+ type: "stdio",
346
413
  command: runtimeCommand.command,
347
414
  args: runtimeCommand.args,
415
+ env: mergeRuntimeEnv(existing?.env, runtimeEnvMap((name) => `\${${name}:-}`)),
348
416
  };
349
417
  }
350
418
  function isManagedLauncherPath(value) {
@@ -370,7 +438,8 @@ function isManagedClaudeEntry(value) {
370
438
  function prepareClaudeInstall(filePath, runtimeCommand) {
371
439
  const currentObject = parseJsonObject(filePath);
372
440
  const currentSerialized = JSON.stringify(currentObject);
373
- const desiredServer = buildClaudeServerConfig(runtimeCommand);
441
+ const existingSatori = objectValue(currentObject.mcpServers?.satori);
442
+ const desiredServer = buildClaudeServerConfig(runtimeCommand, existingSatori);
374
443
  const mcpServersValue = currentObject.mcpServers;
375
444
  let mcpServers;
376
445
  if (mcpServersValue === undefined) {
@@ -382,8 +451,7 @@ function prepareClaudeInstall(filePath, runtimeCommand) {
382
451
  else {
383
452
  throw new CliError("E_USAGE", `Expected mcpServers to be an object in ${filePath}.`, 2);
384
453
  }
385
- const existingSatori = mcpServers.satori;
386
- if (existingSatori !== undefined && !isManagedClaudeEntry(existingSatori)) {
454
+ if (mcpServers.satori !== undefined && !isManagedClaudeEntry(mcpServers.satori)) {
387
455
  throw new CliError("E_USAGE", `Refusing to overwrite unmanaged Satori config in ${filePath}. Remove mcpServers.satori manually or align it to the managed Satori form first.`, 2);
388
456
  }
389
457
  mcpServers.satori = {
@@ -443,11 +511,12 @@ function parseJsoncObject(filePath, content) {
443
511
  }
444
512
  return parsed;
445
513
  }
446
- function buildOpenCodeServerConfig(runtimeCommand) {
514
+ function buildOpenCodeServerConfig(runtimeCommand, existing) {
447
515
  return {
448
516
  enabled: true,
449
517
  type: "local",
450
518
  command: [runtimeCommand.command, ...runtimeCommand.args],
519
+ environment: mergeRuntimeEnv(existing?.environment, runtimeEnvMap((name) => `{env:${name}}`)),
451
520
  };
452
521
  }
453
522
  function isManagedOpenCodeEntry(value) {
@@ -492,7 +561,7 @@ function prepareOpenCodeInstall(filePath, runtimeCommand) {
492
561
  if (existingSatori !== undefined && !isManagedOpenCodeEntry(existingSatori)) {
493
562
  throw new CliError("E_USAGE", `Refusing to overwrite unmanaged Satori config in ${filePath}. Remove mcp.satori manually or align it to the managed Satori form first.`, 2);
494
563
  }
495
- return mutateJsonc(filePath, current, ["mcp", "satori"], buildOpenCodeServerConfig(runtimeCommand));
564
+ return mutateJsonc(filePath, current, ["mcp", "satori"], buildOpenCodeServerConfig(runtimeCommand, objectValue(existingSatori)));
496
565
  }
497
566
  function prepareOpenCodeUninstall(filePath) {
498
567
  const current = readTextIfExists(filePath);
package/dist/config.js CHANGED
@@ -209,7 +209,7 @@ Environment Variables:
209
209
 
210
210
  Examples:
211
211
  # Install resident MCP config without package-manager startup on every client launch
212
- npx -y @zokizuan/satori-cli@0.4.0 install --client all
212
+ npx -y @zokizuan/satori-cli@0.4.2 install --client all
213
213
 
214
214
  # Start MCP server with OpenAI and explicit Milvus address
215
215
  OPENAI_API_KEY=sk-xxx MILVUS_ADDRESS=localhost:19530 satori
@@ -116,6 +116,7 @@ export declare class ToolHandlers {
116
116
  private collapseDuplicateDeclarationGroups;
117
117
  private buildFallbackGroupId;
118
118
  private isCallGraphLanguageSupported;
119
+ private loadCallGraphSidecarForSearch;
119
120
  private buildCallGraphHint;
120
121
  private sanitizeIndexedRelativeFilePath;
121
122
  private buildNavigationFallback;
@@ -1905,20 +1905,60 @@ export class ToolHandlers {
1905
1905
  }
1906
1906
  return false;
1907
1907
  }
1908
- buildCallGraphHint(file, span, language, symbolId, symbolLabel) {
1908
+ loadCallGraphSidecarForSearch(codebaseRoot) {
1909
+ const loadSidecar = this.callGraphManager?.loadSidecar;
1910
+ if (typeof loadSidecar !== 'function') {
1911
+ return null;
1912
+ }
1913
+ const sidecar = loadSidecar.call(this.callGraphManager, codebaseRoot);
1914
+ if (!sidecar || !Array.isArray(sidecar.nodes)) {
1915
+ return null;
1916
+ }
1917
+ return {
1918
+ builtAt: typeof sidecar.builtAt === 'string' ? sidecar.builtAt : undefined,
1919
+ nodes: sidecar.nodes,
1920
+ };
1921
+ }
1922
+ buildCallGraphHint(file, language, sidecar, symbolId, symbolLabel) {
1909
1923
  if (!symbolId) {
1910
1924
  return { supported: false, reason: 'missing_symbol' };
1911
1925
  }
1912
1926
  if (!this.isCallGraphLanguageSupported(language, file)) {
1913
1927
  return { supported: false, reason: 'unsupported_language' };
1914
1928
  }
1929
+ if (!sidecar) {
1930
+ return { supported: false, reason: 'missing_sidecar' };
1931
+ }
1932
+ const normalizedFile = this.sanitizeIndexedRelativeFilePath(file);
1933
+ if (!normalizedFile) {
1934
+ return { supported: false, reason: 'stale_symbol_ref' };
1935
+ }
1936
+ const sidecarNode = sidecar.nodes.find((node) => node.symbolId === symbolId);
1937
+ const normalizedSidecarFile = sidecarNode ? this.sanitizeIndexedRelativeFilePath(sidecarNode.file) : undefined;
1938
+ const sidecarSpan = sidecarNode?.span;
1939
+ if (!sidecarNode ||
1940
+ normalizedSidecarFile !== normalizedFile ||
1941
+ !sidecarSpan ||
1942
+ !Number.isFinite(sidecarSpan.startLine) ||
1943
+ !Number.isFinite(sidecarSpan.endLine)) {
1944
+ return { supported: false, reason: 'stale_symbol_ref' };
1945
+ }
1946
+ const safeStartLine = Math.max(1, Number(sidecarSpan.startLine));
1947
+ const safeEndLine = Math.max(safeStartLine, Number(sidecarSpan.endLine));
1948
+ const validatedAt = new Date(this.now()).toISOString();
1915
1949
  return {
1916
1950
  supported: true,
1951
+ validated: true,
1952
+ validatedAt,
1953
+ sidecarBuiltAt: sidecar.builtAt || validatedAt,
1917
1954
  symbolRef: {
1918
- file,
1955
+ file: normalizedSidecarFile,
1919
1956
  symbolId,
1920
- symbolLabel: symbolLabel || undefined,
1921
- span
1957
+ symbolLabel: sidecarNode.symbolLabel || symbolLabel || undefined,
1958
+ span: {
1959
+ startLine: safeStartLine,
1960
+ endLine: safeEndLine
1961
+ }
1922
1962
  }
1923
1963
  };
1924
1964
  }
@@ -1978,12 +2018,13 @@ export class ToolHandlers {
1978
2018
  if (!callGraphHint.supported) {
1979
2019
  return undefined;
1980
2020
  }
1981
- const normalizedFile = this.sanitizeIndexedRelativeFilePath(relativeFilePath);
2021
+ const normalizedFile = this.sanitizeIndexedRelativeFilePath(callGraphHint.symbolRef.file || relativeFilePath);
1982
2022
  if (!normalizedFile) {
1983
2023
  return undefined;
1984
2024
  }
1985
- const safeStartLine = Number.isFinite(span.startLine) ? Math.max(1, Number(span.startLine)) : 1;
1986
- const safeEndLine = Number.isFinite(span.endLine) ? Math.max(safeStartLine, Number(span.endLine)) : safeStartLine;
2025
+ const actionSpan = callGraphHint.symbolRef.span || span;
2026
+ const safeStartLine = Number.isFinite(actionSpan.startLine) ? Math.max(1, Number(actionSpan.startLine)) : 1;
2027
+ const safeEndLine = Number.isFinite(actionSpan.endLine) ? Math.max(safeStartLine, Number(actionSpan.endLine)) : safeStartLine;
1987
2028
  const absolutePath = path.resolve(codebaseRoot, normalizedFile);
1988
2029
  const symbolRef = {
1989
2030
  ...callGraphHint.symbolRef,
@@ -3541,6 +3582,7 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
3541
3582
  rerankerFailurePhase = 'parse_results';
3542
3583
  throw new Error('reranker_parse_failed');
3543
3584
  }
3585
+ let rerankerUpdatedCandidates = 0;
3544
3586
  for (let idx = 0; idx < rerankSlice.length; idx++) {
3545
3587
  const rank = rerankRanks.get(idx);
3546
3588
  if (!rank) {
@@ -3549,9 +3591,10 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
3549
3591
  const rerankRrf = 1 / (SEARCH_RERANK_RRF_K + rank);
3550
3592
  rerankSlice[idx].fusionScore += SEARCH_RERANK_WEIGHT * rerankRrf;
3551
3593
  rerankSlice[idx].finalScore = (rerankSlice[idx].fusionScore + rerankSlice[idx].lexicalScore) * rerankSlice[idx].pathMultiplier * rerankSlice[idx].changedFilesMultiplier;
3594
+ rerankerUpdatedCandidates++;
3552
3595
  }
3553
3596
  exactMatchPinningApplied = this.sortSearchCandidates(scored, rerankDecision.exactMatchPinningEnabled, parsedOperators.must.length > 0) || exactMatchPinningApplied;
3554
- rerankerApplied = true;
3597
+ rerankerApplied = rerankerUpdatedCandidates > 0;
3555
3598
  }
3556
3599
  catch {
3557
3600
  if (!rerankerFailurePhase) {
@@ -3728,6 +3771,7 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
3728
3771
  ? this.snapshotManager.getCodebaseCallGraphSidecar(effectiveRoot)
3729
3772
  : undefined;
3730
3773
  const sidecarReadyForOutline = Boolean(sidecarInfo && sidecarInfo.version === 'v3');
3774
+ const callGraphSidecar = sidecarReadyForOutline ? this.loadCallGraphSidecarForSearch(effectiveRoot) : null;
3731
3775
  const groupedResults = [];
3732
3776
  for (const group of groups.values()) {
3733
3777
  exactMatchPinningApplied = this.sortSearchCandidates(group.chunks, queryPlan.exactMatchPinningEnabled, parsedOperators.must.length > 0) || exactMatchPinningApplied;
@@ -3748,7 +3792,7 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
3748
3792
  const repSymbolId = typeof representative.result.symbolId === 'string' ? representative.result.symbolId : null;
3749
3793
  const repSymbolLabel = typeof representative.result.symbolLabel === 'string' ? representative.result.symbolLabel : null;
3750
3794
  const groupId = repSymbolId || this.buildFallbackGroupId(representative.result.relativePath, span);
3751
- const callGraphHint = this.buildCallGraphHint(representative.result.relativePath, span, representative.result.language || 'unknown', repSymbolId || undefined, repSymbolLabel || undefined);
3795
+ const callGraphHint = this.buildCallGraphHint(representative.result.relativePath, representative.result.language || 'unknown', callGraphSidecar, repSymbolId || undefined, repSymbolLabel || undefined);
3752
3796
  const navigationFallback = this.buildNavigationFallback(effectiveRoot, representative.result.relativePath, span, callGraphHint, sidecarReadyForOutline);
3753
3797
  const nextActions = this.buildSearchNextActions(effectiveRoot, representative.result.relativePath, span, callGraphHint, sidecarReadyForOutline);
3754
3798
  groupedResults.push({
@@ -4061,6 +4105,7 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
4061
4105
  const endsAfterWindowStart = windowStart === undefined || node.span.endLine >= windowStart;
4062
4106
  return startsBeforeWindowEnd && endsAfterWindowStart;
4063
4107
  });
4108
+ const outlineValidatedAt = new Date(this.now()).toISOString();
4064
4109
  const symbols = this.sortFileOutlineSymbols(windowed.map((node) => {
4065
4110
  const symbolLabel = (typeof node.symbolLabel === 'string' && node.symbolLabel.trim().length > 0)
4066
4111
  ? node.symbolLabel
@@ -4074,6 +4119,9 @@ To force rebuild from scratch: call manage_index with {"action":"create","path":
4074
4119
  },
4075
4120
  callGraphHint: {
4076
4121
  supported: true,
4122
+ validated: true,
4123
+ validatedAt: outlineValidatedAt,
4124
+ sidecarBuiltAt: sidecar.builtAt,
4077
4125
  symbolRef: {
4078
4126
  file: normalizedFile,
4079
4127
  symbolId: node.symbolId,
@@ -15,9 +15,12 @@ export interface CallGraphSymbolRef {
15
15
  export type CallGraphHint = {
16
16
  supported: true;
17
17
  symbolRef: CallGraphSymbolRef;
18
+ validated: true;
19
+ validatedAt: string;
20
+ sidecarBuiltAt: string;
18
21
  } | {
19
22
  supported: false;
20
- reason: "missing_symbol" | "unsupported_language";
23
+ reason: "missing_symbol" | "unsupported_language" | "missing_sidecar" | "stale_symbol_ref";
21
24
  };
22
25
  export interface SearchNextActionReadSymbol {
23
26
  tool: "read_file";
@@ -334,10 +337,9 @@ export interface FileOutlineSymbolResult {
334
337
  symbolId: string;
335
338
  symbolLabel: string;
336
339
  span: SearchSpan;
337
- callGraphHint: {
340
+ callGraphHint: Extract<CallGraphHint, {
338
341
  supported: true;
339
- symbolRef: CallGraphSymbolRef;
340
- };
342
+ }>;
341
343
  }
342
344
  export interface FileOutlineResponseEnvelope {
343
345
  status: FileOutlineStatus;
@@ -2,6 +2,7 @@ import { installCliStdoutRedirect, installConsoleToStderrPatch } from "./stdio-s
2
2
  export function installBootstrapStdioSafety(options) {
3
3
  const restoreConsole = installConsoleToStderrPatch({
4
4
  writeToStderr: options.writeToStderr,
5
+ methods: options.runMode === "mcp" ? ["warn", "error"] : undefined,
5
6
  });
6
7
  let restoreStdout = () => { };
7
8
  if (options.guardMode !== "off") {
@@ -1,6 +1,8 @@
1
1
  export type StdoutGuardMode = "drop" | "redirect";
2
+ type ConsoleMethodName = "log" | "info" | "warn" | "error" | "debug";
2
3
  interface ConsolePatchOptions {
3
4
  writeToStderr?: (text: string) => void;
5
+ methods?: ConsoleMethodName[];
4
6
  }
5
7
  interface CliStdoutRedirectOptions {
6
8
  mode?: StdoutGuardMode;
@@ -37,6 +37,7 @@ export function installConsoleToStderrPatch(options = {}) {
37
37
  const writeToStderr = options.writeToStderr || ((text) => {
38
38
  process.stderr.write(text);
39
39
  });
40
+ const methods = options.methods || ["log", "info", "warn", "error", "debug"];
40
41
  const original = {};
41
42
  const patchMethod = (method) => {
42
43
  original[method] = console[method];
@@ -44,11 +45,9 @@ export function installConsoleToStderrPatch(options = {}) {
44
45
  writeToStderr(ensureTrailingNewline(args.map(toLogString).join(" ")));
45
46
  };
46
47
  };
47
- patchMethod("log");
48
- patchMethod("info");
49
- patchMethod("warn");
50
- patchMethod("error");
51
- patchMethod("debug");
48
+ for (const method of methods) {
49
+ patchMethod(method);
50
+ }
52
51
  return () => {
53
52
  for (const method of Object.keys(original)) {
54
53
  const fn = original[method];
@@ -72,10 +71,11 @@ export function installCliStdoutRedirect(options = {}) {
72
71
  writeToStderr(ensureTrailingNewline(`[STDOUT_BLOCKED] ${text}`));
73
72
  return;
74
73
  }
75
- writeToStderr(ensureTrailingNewline(`[STDOUT_BLOCKED] dropped len=${text.length}`));
76
74
  return;
77
75
  }
78
- writeToStderr(ensureTrailingNewline(`[STDOUT_BLOCKED_BINARY len=${chunkLength(chunk)}]`));
76
+ if (mode === "redirect") {
77
+ writeToStderr(ensureTrailingNewline(`[STDOUT_BLOCKED_BINARY len=${chunkLength(chunk)}]`));
78
+ }
79
79
  };
80
80
  const patch = (methodName, replacement) => {
81
81
  const original = stdout[methodName];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zokizuan/satori-mcp",
3
- "version": "4.11.0",
3
+ "version": "4.11.2",
4
4
  "description": "MCP server for Satori with agent-safe semantic search and indexing",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "jsonc-parser": "^3.3.1",
16
16
  "zod": "^3.25.55",
17
17
  "zod-to-json-schema": "^3.25.1",
18
- "@zokizuan/satori-core": "1.6.0"
18
+ "@zokizuan/satori-core": "1.6.2"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/node": "^20.0.0",