@zokizuan/satori-mcp 4.11.13 → 4.11.16
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 +26 -21
- package/dist/cli/args.d.ts +3 -12
- package/dist/cli/args.js +6 -47
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/index.js +2 -21
- package/dist/config.d.ts +2 -0
- package/dist/config.js +21 -3
- package/dist/core/call-graph.d.ts +2 -0
- package/dist/core/handlers.d.ts +1 -1
- package/dist/core/handlers.js +30 -8
- package/dist/core/indexing-recovery.d.ts +2 -1
- package/dist/core/indexing-recovery.js +6 -2
- package/dist/core/manage-indexing-handlers.d.ts +4 -3
- package/dist/core/manage-indexing-handlers.js +30 -9
- package/dist/core/manage-maintenance-handlers.d.ts +7 -4
- package/dist/core/manage-maintenance-handlers.js +57 -7
- package/dist/core/manage-types.d.ts +6 -1
- package/dist/core/manage-types.js +9 -1
- package/dist/core/navigation-handlers.d.ts +6 -0
- package/dist/core/navigation-handlers.js +79 -12
- package/dist/core/registry-file-outline.d.ts +1 -1
- package/dist/core/registry-file-outline.js +3 -2
- package/dist/core/relationship-backed-call-graph.d.ts +18 -0
- package/dist/core/relationship-backed-call-graph.js +151 -6
- package/dist/core/runtime-owner.d.ts +43 -1
- package/dist/core/runtime-owner.js +128 -3
- package/dist/core/search-exact-fast-path.js +2 -0
- package/dist/core/search-execution.d.ts +10 -0
- package/dist/core/search-execution.js +36 -1
- package/dist/core/search-frontdoor.js +11 -2
- package/dist/core/search-group-ordering.d.ts +3 -0
- package/dist/core/search-group-ordering.js +83 -7
- package/dist/core/search-group-results.js +27 -1
- package/dist/core/search-ranking-policy.js +2 -1
- package/dist/core/search-response-helpers.d.ts +27 -1
- package/dist/core/search-response-helpers.js +116 -11
- package/dist/core/search-result-finalization.js +3 -2
- package/dist/core/search-types.d.ts +17 -0
- package/dist/core/tool-response-builders.d.ts +2 -0
- package/dist/core/tool-response-builders.js +3 -0
- package/dist/core/tracked-root-readiness.js +2 -1
- package/dist/server/provider-runtime.js +1 -0
- package/dist/tools/call_graph.js +19 -7
- package/dist/tools/file_outline.js +18 -6
- package/dist/tools/list_codebases.js +82 -33
- package/dist/tools/manage_index.d.ts +3 -0
- package/dist/tools/manage_index.js +77 -5
- package/dist/tools/read_file.js +151 -35
- package/dist/tools/search_codebase.js +15 -3
- package/dist/tools/types.d.ts +7 -0
- package/dist/tools/types.js +28 -0
- package/dist/utils.d.ts +36 -1
- package/dist/utils.js +83 -7
- package/package.json +3 -3
- package/dist/cli/install.d.ts +0 -46
- package/dist/cli/install.js +0 -928
- package/dist/cli/package-installability.d.ts +0 -14
- package/dist/cli/package-installability.js +0 -124
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Read-only MCP server for Satori. It gives coding agents six deterministic tools
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
Use the CLI installer for normal setup:
|
|
7
|
+
Installer ownership is **`@zokizuan/satori-cli` only** (this package serves MCP tools; it does not install client configs). Use the CLI installer for normal setup:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx -y @zokizuan/satori-cli@latest install --client all
|
|
@@ -45,7 +45,7 @@ For Codex, add `--install-guidance-hook` only when you want an installer-managed
|
|
|
45
45
|
Advanced direct execution is available through the package bin:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npx -y @zokizuan/satori-mcp@4.11.
|
|
48
|
+
npx -y @zokizuan/satori-mcp@4.11.16 --help
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
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.
|
|
@@ -63,16 +63,21 @@ read_file path="/absolute/path/to/repo/src/auth.ts" start_line=1 end_line=160
|
|
|
63
63
|
|
|
64
64
|
Important defaults:
|
|
65
65
|
|
|
66
|
-
-
|
|
67
|
-
- `search_codebase`
|
|
66
|
+
- Public paths for tools are **absolute filesystem paths**. Relative paths are rejected (not CWD-resolved).
|
|
67
|
+
- `search_codebase` starts with `scope=runtime`, `resultMode=grouped`, `groupBy=symbol`, `rankingMode=auto_changed_first`.
|
|
68
|
+
- `search_codebase` is the sync-on-read freshness tool; other tools may gate compatibility but do not replace it.
|
|
68
69
|
- Grouped search is symbol-owned: chunks are supporting evidence for an owner symbol, not the final navigation unit.
|
|
69
70
|
- Exact symbol navigation uses `symbolInstanceId`. `symbolKey` is stable-ish candidate lookup only, not exact identity.
|
|
71
|
+
- Ready/`indexed` means searchable-readable, not automatically symbol-rich. Check `manage_index status` `symbolQuality` or Ready-line `symbolQuality=…` before treating outline/graph as rich navigation evidence (`symbol_rich` \| `mixed` \| `symbol_sparse` \| `search_only` \| `unknown` — observed registry evidence, not a parser diagnosis).
|
|
70
72
|
- Index profiles still honor `.satoriignore`, `.gitignore`, `satori.toml`, and the hard denylist for secrets, lockfiles, generated output, dependencies, binaries, bundles, logs, and database dumps.
|
|
71
|
-
- `read_file` is
|
|
72
|
-
- `
|
|
73
|
-
- `
|
|
74
|
-
-
|
|
75
|
-
-
|
|
73
|
+
- `read_file` only reads under tracked searchable roots (`indexed` / `sync_completed`); it is not a general host filesystem reader. Relative paths and root escapes are denied.
|
|
74
|
+
- Prefer `recommendedNextAction` when present; if `callGraphHint.supported=false`, follow `navigationFallback` args rather than inventing spans.
|
|
75
|
+
- `requires_reindex` means reindex first, then retry the original call (do not substitute `sync`).
|
|
76
|
+
- `manage_index action="clear"` is destructive and should be explicit. `repair` only rebuilds local readiness when vector payload and trusted fingerprint proof match; otherwise use create/reindex.
|
|
77
|
+
- After changing `EMBEDDING_PROVIDER`, `EMBEDDING_MODEL`, embedding dimension, `HYBRID_MODE`, vector backend settings, or the Satori runtime version, restart **all** Satori MCP clients before running `manage_index create`, `reindex`, `sync`, `clear`, or `repair`.
|
|
78
|
+
- Satori records live runtime owners in `~/.satori/runtime/owners.json` and blocks those index mutations with `status="blocked"` / `reason="runtime_owner_conflict"` if another live Satori MCP runtime has a different fingerprint, package version, or config identity.
|
|
79
|
+
- On `runtime_owner_conflict`, the manage envelope lists conflicting **pids**, **versions**, and conflict reasons, plus a concrete `hints.nextStep`. MCP tools never kill other processes. Stop the listed host clients (or only orphaned Satori MCP node PIDs), leave a single package version/config running, then retry. `manage_index status` and `list_codebases` also show a compact **Runtime owners** line (live pids/versions); multi-version means mutations may block. `satori-cli doctor` reports multi-version live owners.
|
|
80
|
+
- Grouped `search_codebase` results with supported call graph attach **`inboundRecovery`**: a ready-to-run `must:` search to verify callers before blast-radius edits (`call_graph` inbound stays advisory/low).
|
|
76
81
|
|
|
77
82
|
## Navigation Sidecars
|
|
78
83
|
|
|
@@ -81,7 +86,7 @@ Completed full indexes write a derived symbol registry and relationship sidecar.
|
|
|
81
86
|
- The symbol registry stores candidate owner keys, exact symbol instances, file-owner fallback symbols, and outline records used by grouped search, `file_outline`, and exact reads.
|
|
82
87
|
- The relationship sidecar stores conservative `CALLS v0` edges plus TypeScript/JavaScript `IMPORTS`/`EXPORTS v0` edges with manifest compatibility gates.
|
|
83
88
|
- Runtime navigation still serves canonical JSON sidecars by default. When the default shared runtime store is created at process startup, `SATORI_NAVIGATION_BACKEND=sqlite` can opt that shared store into SQLite-backed reads only after SQLite proves parity with the canonical JSON symbol registry and relationship sidecars. If canonical JSON is missing or incompatible, SQLite is not served as truth; if SQLite is missing, stale, incompatible, or parity-mismatched while JSON is compatible, runtime falls back to JSON with a warning. When the default shared runtime store is created with `SATORI_NAVIGATION_DUAL_READ=1`, JSON remains the serving backend and the runtime emits once-per-root parity mismatch warnings without changing the served result.
|
|
84
|
-
- `CALLS v0` is heuristic/name-based. Same-file unique targets can be high confidence
|
|
89
|
+
- `CALLS v0` is heuristic/name-based. Same-file unique targets can be high confidence; cross-file name-only targets start low and are upgraded when IMPORTS/EXPORTS evidence supports them, or when the imported file has a unique same-name target (class methods without top-level EXPORTS). Generic names like `push`/`get` stay suppressed without EXPORTS. Ambiguous same-name targets are skipped.
|
|
85
90
|
- `IMPORTS`/`EXPORTS v0` records only resolvable relative module edges and unambiguous local export declarations. Package imports, unresolved paths, ambiguous exports, and multiline module syntax are skipped.
|
|
86
91
|
- `call_graph` uses compatible relationship sidecars as the canonical source for symbol-owned traversal.
|
|
87
92
|
- Successful incremental sync reuses changed-file symbol output, preserves unchanged registry state, and recomputes relationships against the merged registry without re-splitting unchanged files. If changed-file indexing stops early, navigation state is cleared instead of publishing a mixed generation.
|
|
@@ -103,7 +108,7 @@ Users who want literal values in a client config can replace the generated pass-
|
|
|
103
108
|
```toml
|
|
104
109
|
[mcp_servers.satori.env]
|
|
105
110
|
EMBEDDING_PROVIDER = "VoyageAI"
|
|
106
|
-
EMBEDDING_MODEL = "voyage-
|
|
111
|
+
EMBEDDING_MODEL = "voyage-code-3"
|
|
107
112
|
EMBEDDING_OUTPUT_DIMENSION = "1024"
|
|
108
113
|
VOYAGEAI_API_KEY = "pa-..."
|
|
109
114
|
VOYAGEAI_RERANKER_MODEL = "rerank-2.5"
|
|
@@ -115,7 +120,7 @@ Cloud-quality setup:
|
|
|
115
120
|
|
|
116
121
|
```bash
|
|
117
122
|
EMBEDDING_PROVIDER=VoyageAI
|
|
118
|
-
EMBEDDING_MODEL=voyage-
|
|
123
|
+
EMBEDDING_MODEL=voyage-code-3
|
|
119
124
|
EMBEDDING_OUTPUT_DIMENSION=1024
|
|
120
125
|
VOYAGEAI_API_KEY=your-api-key
|
|
121
126
|
VOYAGEAI_RERANKER_MODEL=rerank-2.5
|
|
@@ -136,7 +141,7 @@ Manage index lifecycle operations (create/reindex/sync/status/clear/repair) for
|
|
|
136
141
|
| Parameter | Type | Required | Default | Description |
|
|
137
142
|
|---|---|---|---|---|
|
|
138
143
|
| `action` | enum("create", "reindex", "sync", "status", "clear", "repair") | yes | | Required operation to run. |
|
|
139
|
-
| `path` | string | yes | | ABSOLUTE path to the target codebase. |
|
|
144
|
+
| `path` | string | yes | | ABSOLUTE filesystem path to the target codebase (relative paths are rejected). |
|
|
140
145
|
| `force` | boolean | no | | Only for action='create'. Force rebuild from scratch. |
|
|
141
146
|
| `allowUnnecessaryReindex` | boolean | no | | Only for action='reindex'. Override preflight block when reindex is detected as unnecessary ignore-only churn. |
|
|
142
147
|
| `customExtensions` | array<string> | no | | Only for action='create'. Additional file extensions to include. |
|
|
@@ -149,9 +154,9 @@ Unified semantic search with runtime-first defaults (start with scope="runtime")
|
|
|
149
154
|
|
|
150
155
|
| Parameter | Type | Required | Default | Description |
|
|
151
156
|
|---|---|---|---|---|
|
|
152
|
-
| `path` | string | yes | | ABSOLUTE path to an indexed codebase or subdirectory. |
|
|
157
|
+
| `path` | string | yes | | ABSOLUTE filesystem path to an indexed codebase or subdirectory (relative paths are rejected). |
|
|
153
158
|
| `query` | string | yes | | Natural-language query. |
|
|
154
|
-
| `scope` | enum("runtime", "mixed", "docs") | no | `"runtime"` | Search scope policy. runtime includes source/runtime code and tests while excluding docs/generated/artifacts/landing/fixtures; docs returns
|
|
159
|
+
| `scope` | enum("runtime", "mixed", "docs") | no | `"runtime"` | Search scope policy. runtime includes source/runtime code and tests while excluding docs/generated/artifacts/landing/fixtures; docs returns documentation paths only (not tests); mixed includes all. Docs scope skips reranker by policy in the current tool surface. |
|
|
155
160
|
| `resultMode` | enum("grouped", "raw") | no | `"grouped"` | Output mode. grouped returns merged search groups, raw returns chunk hits. |
|
|
156
161
|
| `groupBy` | enum("symbol", "file") | no | `"symbol"` | Grouping strategy in grouped mode. |
|
|
157
162
|
| `rankingMode` | enum("default", "auto_changed_first") | no | `"auto_changed_first"` | Ranking policy. auto_changed_first boosts files changed in the current git working tree when available. |
|
|
@@ -160,11 +165,11 @@ Unified semantic search with runtime-first defaults (start with scope="runtime")
|
|
|
160
165
|
|
|
161
166
|
### `call_graph`
|
|
162
167
|
|
|
163
|
-
Traverse registry-resolved caller/callee relationships for indexed TS/JS/Python code.
|
|
168
|
+
Traverse registry-resolved caller/callee relationships for indexed TS/JS/Python code. Relationship-backed CALLS v0 is heuristic and name-based (not a compiler-grade call graph): unique same-file name matches are high confidence; cross-file edges stay low unless IMPORTS/EXPORTS evidence upgrades them, or an imported module has a unique same-name target (e.g. class methods without a top-level EXPORTS record; generic names like push/get stay suppressed). Traversal is bounded (depth/limit) and incomplete by design—empty or short edge lists are not proof of “no callers.” Output is advisory navigation context, not authoritative blast-radius proof; confirm impact with search_codebase, read_file, tests, and references before editing. In successful traversal responses, sidecar.nodeCount and sidecar.edgeCount report counts returned in that response, not whole-sidecar totals.
|
|
164
169
|
|
|
165
170
|
| Parameter | Type | Required | Default | Description |
|
|
166
171
|
|---|---|---|---|---|
|
|
167
|
-
| `path` | string | yes | | ABSOLUTE path to the indexed codebase root
|
|
172
|
+
| `path` | string | yes | | ABSOLUTE filesystem path to the indexed codebase root or subdirectory (relative paths are rejected). |
|
|
168
173
|
| `symbolRef` | object | yes | | Symbol reference from a grouped search result callGraphHint. |
|
|
169
174
|
| `direction` | enum("callers", "callees", "both") | no | `"both"` | Traversal direction from the starting symbol. |
|
|
170
175
|
| `depth` | integer | no | `1` | Traversal depth (max 3). |
|
|
@@ -176,8 +181,8 @@ Return a sidecar-backed symbol outline for one file, including call_graph jump h
|
|
|
176
181
|
|
|
177
182
|
| Parameter | Type | Required | Default | Description |
|
|
178
183
|
|---|---|---|---|---|
|
|
179
|
-
| `path` | string | yes | | ABSOLUTE path to the indexed codebase root. |
|
|
180
|
-
| `file` | string | yes | |
|
|
184
|
+
| `path` | string | yes | | ABSOLUTE filesystem path to the indexed codebase root (relative paths are rejected). |
|
|
185
|
+
| `file` | string | yes | | Repo-relative file path inside the codebase root (not absolute; resolved only against that root). |
|
|
181
186
|
| `start_line` | integer | no | | Optional start line filter (1-based, inclusive). |
|
|
182
187
|
| `end_line` | integer | no | | Optional end line filter (1-based, inclusive). |
|
|
183
188
|
| `limitSymbols` | integer | no | `500` | Maximum number of returned symbols after line filtering. |
|
|
@@ -187,11 +192,11 @@ Return a sidecar-backed symbol outline for one file, including call_graph jump h
|
|
|
187
192
|
|
|
188
193
|
### `read_file`
|
|
189
194
|
|
|
190
|
-
Read file content
|
|
195
|
+
Read file content under an indexed/searchable Satori codebase root only (not a general host filesystem reader). Requires an absolute path whose canonical real path is inside a tracked root with status indexed or sync_completed. Supports optional 1-based inclusive line ranges and safe truncation.
|
|
191
196
|
|
|
192
197
|
| Parameter | Type | Required | Default | Description |
|
|
193
198
|
|---|---|---|---|---|
|
|
194
|
-
| `path` | string | yes | | ABSOLUTE path to the file. |
|
|
199
|
+
| `path` | string | yes | | ABSOLUTE path to the file under an indexed/searchable codebase root (relative paths are rejected). |
|
|
195
200
|
| `start_line` | integer | no | | Optional start line (1-based, inclusive). |
|
|
196
201
|
| `end_line` | integer | no | | Optional end line (1-based, inclusive). |
|
|
197
202
|
| `mode` | enum("plain", "annotated") | no | `"plain"` | Output mode. plain returns text only; annotated returns content plus sidecar-backed outline metadata. |
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -19,16 +19,6 @@ export type ParsedCommand = {
|
|
|
19
19
|
kind: "help";
|
|
20
20
|
} | {
|
|
21
21
|
kind: "version";
|
|
22
|
-
} | {
|
|
23
|
-
kind: "install";
|
|
24
|
-
client: InstallClient;
|
|
25
|
-
dryRun: boolean;
|
|
26
|
-
installGuidanceHook: boolean;
|
|
27
|
-
profile?: InstallProfile;
|
|
28
|
-
} | {
|
|
29
|
-
kind: "uninstall";
|
|
30
|
-
client: InstallClient;
|
|
31
|
-
dryRun: boolean;
|
|
32
22
|
} | {
|
|
33
23
|
kind: "tools-list";
|
|
34
24
|
} | {
|
|
@@ -49,8 +39,9 @@ export interface ResolveRawArgsOptions {
|
|
|
49
39
|
stdin?: NodeJS.ReadStream;
|
|
50
40
|
stdinTimeoutMs: number;
|
|
51
41
|
}
|
|
52
|
-
|
|
53
|
-
export
|
|
42
|
+
/** Hard-deprecation messages only — no installer work, no config writes. */
|
|
43
|
+
export declare const MCP_INSTALL_USE_SATORI_CLI_MESSAGE = "Install is owned by @zokizuan/satori-cli. Use: npx -y @zokizuan/satori-cli@latest install --client <client>";
|
|
44
|
+
export declare const MCP_UNINSTALL_USE_SATORI_CLI_MESSAGE = "Uninstall is owned by @zokizuan/satori-cli. Use: npx -y @zokizuan/satori-cli@latest uninstall --client <client>";
|
|
54
45
|
export declare function parseCliArgs(argv: string[]): ParsedCliInput;
|
|
55
46
|
export declare function resolveRawArguments(rawArgsMode: RawArgsMode, options: ResolveRawArgsOptions): Promise<Record<string, unknown>>;
|
|
56
47
|
export declare function parseWrapperArgumentsFromSchema(toolName: string, inputSchema: unknown, wrapperArgs: string[]): Record<string, unknown>;
|
package/dist/cli/args.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { CliError } from "./errors.js";
|
|
3
|
+
/** Installer SSOT is packages/cli (satori-cli). MCP CLI keeps these reserved so they never become tool wrappers. */
|
|
3
4
|
const RESERVED_SUBCOMMANDS = new Set(["tools", "tool", "help", "version", "install", "uninstall"]);
|
|
5
|
+
/** Hard-deprecation messages only — no installer work, no config writes. */
|
|
6
|
+
export const MCP_INSTALL_USE_SATORI_CLI_MESSAGE = "Install is owned by @zokizuan/satori-cli. Use: npx -y @zokizuan/satori-cli@latest install --client <client>";
|
|
7
|
+
export const MCP_UNINSTALL_USE_SATORI_CLI_MESSAGE = "Uninstall is owned by @zokizuan/satori-cli. Use: npx -y @zokizuan/satori-cli@latest uninstall --client <client>";
|
|
4
8
|
const PRIMITIVE_TYPES = new Set(["string", "number", "integer", "boolean"]);
|
|
5
9
|
function isWrapperJsonSchema(value) {
|
|
6
10
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -112,45 +116,6 @@ function parseRawArgsMode(args) {
|
|
|
112
116
|
}
|
|
113
117
|
return { rawArgsMode, remaining };
|
|
114
118
|
}
|
|
115
|
-
function parseInstallCommand(kind, args) {
|
|
116
|
-
let client = "all";
|
|
117
|
-
let dryRun = false;
|
|
118
|
-
let installGuidanceHook = false;
|
|
119
|
-
let profile;
|
|
120
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
121
|
-
const token = args[i];
|
|
122
|
-
if (token === "--client") {
|
|
123
|
-
const next = args[i + 1];
|
|
124
|
-
if (next !== "all" && next !== "claude" && next !== "codex" && next !== "opencode") {
|
|
125
|
-
throw new CliError("E_USAGE", "--client must be one of: all, claude, codex, opencode.", 2);
|
|
126
|
-
}
|
|
127
|
-
client = next;
|
|
128
|
-
i += 1;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (token === "--dry-run") {
|
|
132
|
-
dryRun = true;
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
if (kind === "install" && token === "--install-guidance-hook") {
|
|
136
|
-
installGuidanceHook = true;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
if (kind === "install" && token === "--profile") {
|
|
140
|
-
const next = args[i + 1];
|
|
141
|
-
if (next !== "default" && next !== "minimal" && next !== "all-text") {
|
|
142
|
-
throw new CliError("E_USAGE", "--profile must be one of: default, minimal, all-text.", 2);
|
|
143
|
-
}
|
|
144
|
-
profile = next;
|
|
145
|
-
i += 1;
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
throw new CliError("E_USAGE", `Unknown arguments for ${kind}: ${args.slice(i).join(" ")}`, 2);
|
|
149
|
-
}
|
|
150
|
-
return kind === "install"
|
|
151
|
-
? { kind, client, dryRun, installGuidanceHook, profile }
|
|
152
|
-
: { kind, client, dryRun };
|
|
153
|
-
}
|
|
154
119
|
export function parseCliArgs(argv) {
|
|
155
120
|
const { globals, rest } = parseGlobalOptions(argv);
|
|
156
121
|
if (rest.length === 0 || rest[0] === "help" || rest.includes("--help") || rest.includes("-h")) {
|
|
@@ -166,16 +131,10 @@ export function parseCliArgs(argv) {
|
|
|
166
131
|
};
|
|
167
132
|
}
|
|
168
133
|
if (rest[0] === "install") {
|
|
169
|
-
|
|
170
|
-
globals,
|
|
171
|
-
command: parseInstallCommand("install", rest.slice(1))
|
|
172
|
-
};
|
|
134
|
+
throw new CliError("E_USAGE", MCP_INSTALL_USE_SATORI_CLI_MESSAGE, 2);
|
|
173
135
|
}
|
|
174
136
|
if (rest[0] === "uninstall") {
|
|
175
|
-
|
|
176
|
-
globals,
|
|
177
|
-
command: parseInstallCommand("uninstall", rest.slice(1))
|
|
178
|
-
};
|
|
137
|
+
throw new CliError("E_USAGE", MCP_UNINSTALL_USE_SATORI_CLI_MESSAGE, 2);
|
|
179
138
|
}
|
|
180
139
|
if (rest[0] === "tools") {
|
|
181
140
|
if (rest.length === 2 && rest[1] === "list") {
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { type CallToolResult, type ListToolsResult } from "./client.js";
|
|
3
|
-
import { type ManagedRuntimeCommand } from "./install.js";
|
|
4
3
|
interface RunCliOptions {
|
|
5
4
|
writeStdout?: (text: string) => void;
|
|
6
5
|
writeStderr?: (text: string) => void;
|
|
@@ -12,8 +11,6 @@ interface RunCliOptions {
|
|
|
12
11
|
startupTimeoutMs?: number;
|
|
13
12
|
callTimeoutMs?: number;
|
|
14
13
|
cwd?: string;
|
|
15
|
-
installabilityVerifier?: () => string | Promise<string>;
|
|
16
|
-
installRuntimeCommand?: ManagedRuntimeCommand;
|
|
17
14
|
connectSession?: (options: {
|
|
18
15
|
command: string;
|
|
19
16
|
args: string[];
|
package/dist/cli/index.js
CHANGED
|
@@ -6,8 +6,6 @@ import { parseCliArgs, parseWrapperArgumentsFromSchema, resolveRawArguments } fr
|
|
|
6
6
|
import { connectCliMcpSession } from "./client.js";
|
|
7
7
|
import { asCliError, CliError } from "./errors.js";
|
|
8
8
|
import { emitError, emitJson, parseStructuredEnvelope } from "./format.js";
|
|
9
|
-
import { executeInstallCommand } from "./install.js";
|
|
10
|
-
import { verifyManagedPackageInstallability } from "./package-installability.js";
|
|
11
9
|
import { resolveServerEntryPath } from "./resolve-server-entry.js";
|
|
12
10
|
function isRecord(value) {
|
|
13
11
|
return typeof value === "object" && value !== null;
|
|
@@ -44,15 +42,14 @@ function resolveDefaultServerArgs() {
|
|
|
44
42
|
}
|
|
45
43
|
function buildHelpPayload() {
|
|
46
44
|
return {
|
|
47
|
-
usage: "satori
|
|
45
|
+
usage: "satori <command> (tool shell; install via satori-cli)",
|
|
48
46
|
commands: [
|
|
49
|
-
"install [--client all|codex|claude|opencode] [--profile default|minimal|all-text] [--dry-run] [--install-guidance-hook]",
|
|
50
|
-
"uninstall [--client all|codex|claude|opencode] [--dry-run]",
|
|
51
47
|
"tools list",
|
|
52
48
|
"tool call <toolName> --args-json '<json>'",
|
|
53
49
|
"tool call <toolName> --args-file <path>",
|
|
54
50
|
"<toolName> [schema-driven flags]"
|
|
55
51
|
],
|
|
52
|
+
install: "npx -y @zokizuan/satori-cli@latest install --client all",
|
|
56
53
|
globalFlags: [
|
|
57
54
|
"--startup-timeout-ms <n>",
|
|
58
55
|
"--call-timeout-ms <n>",
|
|
@@ -155,22 +152,6 @@ export async function runCli(argv, options = {}) {
|
|
|
155
152
|
}
|
|
156
153
|
return 0;
|
|
157
154
|
}
|
|
158
|
-
if (parsed.command.kind === "install" || parsed.command.kind === "uninstall") {
|
|
159
|
-
let packageSpecifier;
|
|
160
|
-
if (parsed.command.kind === "install") {
|
|
161
|
-
packageSpecifier = await (options.installabilityVerifier || verifyManagedPackageInstallability)();
|
|
162
|
-
}
|
|
163
|
-
const result = executeInstallCommand(parsed.command, {
|
|
164
|
-
homeDir: effectiveEnv.HOME,
|
|
165
|
-
packageSpecifier,
|
|
166
|
-
runtimeCommand: options.installRuntimeCommand,
|
|
167
|
-
});
|
|
168
|
-
emitJson(writers, result);
|
|
169
|
-
if (parsed.globals.format === "text") {
|
|
170
|
-
writers.writeStderr(`satori-cli ${parsed.command.kind} completed for ${parsed.command.client}.\n`);
|
|
171
|
-
}
|
|
172
|
-
return 0;
|
|
173
|
-
}
|
|
174
155
|
const session = await (options.connectSession || connectCliMcpSession)({
|
|
175
156
|
command: options.serverCommand || process.execPath,
|
|
176
157
|
args: options.serverArgs || resolveDefaultServerArgs(),
|
package/dist/config.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export type VectorStoreProvider = 'Milvus';
|
|
|
3
3
|
export type FingerprintSource = 'verified' | 'assumed_v2';
|
|
4
4
|
export declare const DEFAULT_WATCH_DEBOUNCE_MS = 5000;
|
|
5
5
|
export declare const DEFAULT_MANAGE_RETRY_AFTER_MS = 2000;
|
|
6
|
+
/** Package version from packages/mcp/package.json (not the stale historical default 1.0.0). */
|
|
7
|
+
export declare function resolveMcpPackageVersion(): string;
|
|
6
8
|
export interface IndexFingerprint {
|
|
7
9
|
embeddingProvider: EmbeddingProvider;
|
|
8
10
|
embeddingModel: string;
|
package/dist/config.js
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
1
4
|
import { envManager } from "@zokizuan/satori-core";
|
|
2
5
|
export const DEFAULT_WATCH_DEBOUNCE_MS = 5000;
|
|
3
6
|
export const DEFAULT_MANAGE_RETRY_AFTER_MS = 2000;
|
|
7
|
+
/** Package version from packages/mcp/package.json (not the stale historical default 1.0.0). */
|
|
8
|
+
export function resolveMcpPackageVersion() {
|
|
9
|
+
try {
|
|
10
|
+
const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
11
|
+
const raw = fs.readFileSync(packageJsonPath, "utf8");
|
|
12
|
+
const parsed = JSON.parse(raw);
|
|
13
|
+
if (typeof parsed.version === "string" && parsed.version.trim().length > 0) {
|
|
14
|
+
return parsed.version.trim();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// fall through
|
|
19
|
+
}
|
|
20
|
+
return "0.0.0";
|
|
21
|
+
}
|
|
4
22
|
// Helper function to get default model for each provider
|
|
5
23
|
export function getDefaultModelForProvider(provider) {
|
|
6
24
|
switch (provider) {
|
|
7
25
|
case 'OpenAI':
|
|
8
26
|
return 'text-embedding-3-small';
|
|
9
27
|
case 'VoyageAI':
|
|
10
|
-
return 'voyage-
|
|
28
|
+
return 'voyage-code-3';
|
|
11
29
|
case 'Gemini':
|
|
12
30
|
return 'gemini-embedding-001';
|
|
13
31
|
case 'Ollama':
|
|
@@ -106,7 +124,7 @@ export function createMcpConfig() {
|
|
|
106
124
|
}
|
|
107
125
|
const config = {
|
|
108
126
|
name: envManager.get('MCP_SERVER_NAME') || "Satori MCP Server",
|
|
109
|
-
version: envManager.get('MCP_SERVER_VERSION') ||
|
|
127
|
+
version: envManager.get('MCP_SERVER_VERSION') || resolveMcpPackageVersion(),
|
|
110
128
|
// Embedding provider configuration
|
|
111
129
|
encoderProvider: defaultProvider,
|
|
112
130
|
encoderModel: getEmbeddingModelForProvider(defaultProvider),
|
|
@@ -215,7 +233,7 @@ Examples:
|
|
|
215
233
|
OPENAI_API_KEY=sk-xxx MILVUS_ADDRESS=localhost:19530 satori
|
|
216
234
|
|
|
217
235
|
# Start MCP server with VoyageAI and specific model
|
|
218
|
-
EMBEDDING_PROVIDER=VoyageAI VOYAGEAI_API_KEY=pa-xxx EMBEDDING_MODEL=voyage-
|
|
236
|
+
EMBEDDING_PROVIDER=VoyageAI VOYAGEAI_API_KEY=pa-xxx EMBEDDING_MODEL=voyage-code-3 MILVUS_ADDRESS=https://your-zilliz-endpoint MILVUS_TOKEN=your-token satori
|
|
219
237
|
|
|
220
238
|
# Start MCP server with Gemini and specific model
|
|
221
239
|
EMBEDDING_PROVIDER=Gemini GEMINI_API_KEY=xxx EMBEDDING_MODEL=gemini-embedding-001 MILVUS_ADDRESS=https://your-zilliz-endpoint MILVUS_TOKEN=your-token satori
|
|
@@ -84,6 +84,8 @@ export interface CallGraphResponseSupported {
|
|
|
84
84
|
nodeCount: number;
|
|
85
85
|
edgeCount: number;
|
|
86
86
|
};
|
|
87
|
+
/** Executable recovery steps when graph evidence is incomplete (e.g. notes-only inbound). */
|
|
88
|
+
hints?: Record<string, unknown>;
|
|
87
89
|
}
|
|
88
90
|
export type CallGraphQueryResponse = CallGraphResponseSupported | CallGraphResponseUnsupported;
|
|
89
91
|
export interface CallGraphDeltaPolicy {
|
package/dist/core/handlers.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { FileOutlineInput } from "./search-types.js";
|
|
|
7
7
|
import { CallGraphSidecarManager } from "./call-graph.js";
|
|
8
8
|
import { type ReindexPreflightResult } from "./working-tree-state.js";
|
|
9
9
|
import { type SearchDiagnostics } from "./search-execution.js";
|
|
10
|
-
import type
|
|
10
|
+
import { type RuntimeOwnerMutationGate } from "./runtime-owner.js";
|
|
11
11
|
type IndexCodebaseArgs = {
|
|
12
12
|
path: string;
|
|
13
13
|
force?: boolean;
|
package/dist/core/handlers.js
CHANGED
|
@@ -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 { COLLECTION_LIMIT_MESSAGE, createRuntimeNavigationStore, getSupportedExtensionsForCapability, isLanguageCapabilitySupportedForExtension, isLanguageCapabilitySupportedForFilename, isLanguageCapabilitySupportedForLanguage, } from "@zokizuan/satori-core";
|
|
5
|
-
import {
|
|
5
|
+
import { absolutePathOrRaw, requireAbsoluteFilesystemPath } from "../utils.js";
|
|
6
6
|
import { DEFAULT_MANAGE_RETRY_AFTER_MS, DEFAULT_WATCH_DEBOUNCE_MS } from "../config.js";
|
|
7
7
|
import { SEARCH_CHANGED_FILES_CACHE_TTL_MS, SEARCH_CHANGED_FIRST_MAX_CHANGED_FILES, SEARCH_MAX_CANDIDATES, SEARCH_MUST_RETRY_ROUNDS, SEARCH_GITIGNORE_FORCE_RELOAD_EVERY_N } from "./search-constants.js";
|
|
8
8
|
import { CallGraphSidecarManager, } from "./call-graph.js";
|
|
@@ -29,6 +29,7 @@ import { classifyVectorBackendError, } from "./backend-diagnostics.js";
|
|
|
29
29
|
import { runSearchExecution, } from "./search-execution.js";
|
|
30
30
|
import { runExactRegistryFastPath } from "./search-exact-fast-path.js";
|
|
31
31
|
import { finalizeSearchResults } from "./search-result-finalization.js";
|
|
32
|
+
import { formatRuntimeOwnerConflictMessage, formatRuntimeOwnerConflictNextStep, } from "./runtime-owner.js";
|
|
32
33
|
const SEARCH_PARTIAL_INDEX_LIMIT_REACHED_WARNING = 'SEARCH_PARTIAL_INDEX:limit_reached';
|
|
33
34
|
const SEARCH_PARTIAL_INDEX_NAVIGATION_UNAVAILABLE_WARNING = 'SEARCH_PARTIAL_INDEX_NAVIGATION_UNAVAILABLE';
|
|
34
35
|
const SEARCH_GROUP_PREVIEW_MAX_CHARS = 800;
|
|
@@ -272,6 +273,12 @@ export class ToolHandlers {
|
|
|
272
273
|
touchWatchedCodebase: this.touchWatchedCodebase.bind(this),
|
|
273
274
|
manageVectorBackendResponse: this.toolResponseBuilders.manageVectorBackendResponse.bind(this.toolResponseBuilders),
|
|
274
275
|
canSyncStaleLocal: this.canSyncStaleLocal.bind(this),
|
|
276
|
+
getLiveOwnersSummary: async () => {
|
|
277
|
+
if (!this.runtimeOwnerGate || typeof this.runtimeOwnerGate.getLiveOwnersSummary !== "function") {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
return this.runtimeOwnerGate.getLiveOwnersSummary();
|
|
281
|
+
},
|
|
275
282
|
};
|
|
276
283
|
this.manageMaintenanceHandlers = new ManageMaintenanceHandlers(manageMaintenanceHandlersHost);
|
|
277
284
|
const getManageIndexingContext = () => this.context;
|
|
@@ -617,13 +624,21 @@ export class ToolHandlers {
|
|
|
617
624
|
return this.buildRuntimeOwnerConflictResponse(action, codebasePath, result);
|
|
618
625
|
}
|
|
619
626
|
buildRuntimeOwnerConflictResponse(action, codebasePath, result) {
|
|
627
|
+
const conflictingOwners = result.conflictingOwners || [];
|
|
620
628
|
const message = result.message
|
|
621
|
-
||
|
|
629
|
+
|| formatRuntimeOwnerConflictMessage({
|
|
630
|
+
conflictingOwners,
|
|
631
|
+
});
|
|
622
632
|
return this.toolResponseBuilders.manageResponse(action, codebasePath, "blocked", message, {
|
|
623
633
|
reason: "runtime_owner_conflict",
|
|
624
634
|
hints: {
|
|
625
|
-
runtimeOwners:
|
|
626
|
-
nextStep:
|
|
635
|
+
runtimeOwners: conflictingOwners,
|
|
636
|
+
nextStep: formatRuntimeOwnerConflictNextStep(conflictingOwners),
|
|
637
|
+
nextSteps: [
|
|
638
|
+
formatRuntimeOwnerConflictNextStep(conflictingOwners),
|
|
639
|
+
"Do not loop create/reindex/sync while runtime_owner_conflict is returned.",
|
|
640
|
+
"Search may still work with degraded freshness; mutations stay blocked until a single runtime identity remains.",
|
|
641
|
+
],
|
|
627
642
|
}
|
|
628
643
|
});
|
|
629
644
|
}
|
|
@@ -1701,7 +1716,7 @@ export class ToolHandlers {
|
|
|
1701
1716
|
const vectorBackendDiagnostic = classifyVectorBackendError(error);
|
|
1702
1717
|
if (vectorBackendDiagnostic) {
|
|
1703
1718
|
const payload = this.buildVectorBackendSearchPayload(vectorBackendDiagnostic, {
|
|
1704
|
-
path:
|
|
1719
|
+
path: absolutePathOrRaw(input.path),
|
|
1705
1720
|
query: input.query,
|
|
1706
1721
|
scope: input.scope,
|
|
1707
1722
|
groupBy: input.groupBy,
|
|
@@ -1721,7 +1736,7 @@ export class ToolHandlers {
|
|
|
1721
1736
|
const errorMessage = typeof error === 'string' ? error : (error instanceof Error ? error.message : String(error));
|
|
1722
1737
|
if (errorMessage === COLLECTION_LIMIT_MESSAGE || errorMessage.includes(COLLECTION_LIMIT_MESSAGE)) {
|
|
1723
1738
|
const payload = this.buildInvalidSearchRequestPayload({
|
|
1724
|
-
path: typeof input.path === 'string' ?
|
|
1739
|
+
path: typeof input.path === 'string' ? absolutePathOrRaw(input.path) : '',
|
|
1725
1740
|
query: typeof input.query === 'string' ? input.query : '',
|
|
1726
1741
|
scope: input.scope,
|
|
1727
1742
|
groupBy: input.groupBy,
|
|
@@ -1746,7 +1761,7 @@ export class ToolHandlers {
|
|
|
1746
1761
|
};
|
|
1747
1762
|
}
|
|
1748
1763
|
const payload = this.buildInvalidSearchRequestPayload({
|
|
1749
|
-
path: typeof input.path === 'string' ?
|
|
1764
|
+
path: typeof input.path === 'string' ? absolutePathOrRaw(input.path) : '',
|
|
1750
1765
|
query: typeof input.query === 'string' ? input.query : '',
|
|
1751
1766
|
scope: input.scope,
|
|
1752
1767
|
groupBy: input.groupBy,
|
|
@@ -1780,7 +1795,14 @@ export class ToolHandlers {
|
|
|
1780
1795
|
async handleReadCode(args) {
|
|
1781
1796
|
const filePath = typeof args.path === 'string' ? args.path : '';
|
|
1782
1797
|
try {
|
|
1783
|
-
const
|
|
1798
|
+
const absolutePathResult = requireAbsoluteFilesystemPath(filePath, "path");
|
|
1799
|
+
if (!absolutePathResult.ok) {
|
|
1800
|
+
return {
|
|
1801
|
+
content: [{ type: "text", text: absolutePathResult.message }],
|
|
1802
|
+
isError: true
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
const absolutePath = absolutePathResult.absolutePath;
|
|
1784
1806
|
if (!fs.existsSync(absolutePath)) {
|
|
1785
1807
|
return {
|
|
1786
1808
|
content: [{ type: "text", text: `Error: File '${absolutePath}' not found.` }],
|
|
@@ -6,7 +6,8 @@ export type InterruptedIndexingRecoveryDecision = {
|
|
|
6
6
|
stats: {
|
|
7
7
|
indexedFiles: number;
|
|
8
8
|
totalChunks: number;
|
|
9
|
-
|
|
9
|
+
/** Preserved from marker.indexStatus; legacy markers without the field are completed. */
|
|
10
|
+
status: 'completed' | 'limit_reached';
|
|
10
11
|
};
|
|
11
12
|
indexFingerprint: IndexFingerprint;
|
|
12
13
|
} | {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
function resolveMarkerIndexStatus(marker) {
|
|
2
|
+
return marker.indexStatus === 'limit_reached' ? 'limit_reached' : 'completed';
|
|
3
|
+
}
|
|
1
4
|
function fingerprintsMatch(a, b) {
|
|
2
5
|
return a.embeddingProvider === b.embeddingProvider
|
|
3
6
|
&& a.embeddingModel === b.embeddingModel
|
|
@@ -42,6 +45,7 @@ export function decideInterruptedIndexingRecovery(marker, runtimeFingerprint) {
|
|
|
42
45
|
message: 'Interrupted indexing detected but completion marker payload is invalid.'
|
|
43
46
|
};
|
|
44
47
|
}
|
|
48
|
+
const indexStatus = resolveMarkerIndexStatus(marker);
|
|
45
49
|
if (!fingerprintsMatch(marker.fingerprint, runtimeFingerprint)) {
|
|
46
50
|
return {
|
|
47
51
|
action: 'promote_indexed',
|
|
@@ -50,7 +54,7 @@ export function decideInterruptedIndexingRecovery(marker, runtimeFingerprint) {
|
|
|
50
54
|
stats: {
|
|
51
55
|
indexedFiles: Number(marker.indexedFiles),
|
|
52
56
|
totalChunks: Number(marker.totalChunks),
|
|
53
|
-
status:
|
|
57
|
+
status: indexStatus,
|
|
54
58
|
}
|
|
55
59
|
};
|
|
56
60
|
}
|
|
@@ -61,7 +65,7 @@ export function decideInterruptedIndexingRecovery(marker, runtimeFingerprint) {
|
|
|
61
65
|
stats: {
|
|
62
66
|
indexedFiles: Number(marker.indexedFiles),
|
|
63
67
|
totalChunks: Number(marker.totalChunks),
|
|
64
|
-
status:
|
|
68
|
+
status: indexStatus,
|
|
65
69
|
}
|
|
66
70
|
};
|
|
67
71
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Context } from "@zokizuan/satori-core";
|
|
2
2
|
import type { SnapshotManager } from "./snapshot.js";
|
|
3
3
|
import type { SyncManager } from "./sync.js";
|
|
4
|
-
import { ManageIndexAction } from "./manage-types.js";
|
|
4
|
+
import type { ManageIndexAction } from "./manage-types.js";
|
|
5
5
|
import type { CompletionProofValidationResult } from "./completion-proof.js";
|
|
6
6
|
import { type VectorBackendDiagnostic } from "./backend-diagnostics.js";
|
|
7
7
|
import type { IndexFingerprint } from "../config.js";
|
|
8
8
|
import type { ReindexPreflightResult } from "./working-tree-state.js";
|
|
9
|
+
import type { RuntimeOwnerMutationAction } from "./runtime-owner.js";
|
|
9
10
|
type ToolTextResponse = {
|
|
10
11
|
content: Array<{
|
|
11
12
|
type: "text";
|
|
@@ -39,12 +40,12 @@ type ManageIndexingHandlersHost = {
|
|
|
39
40
|
runtimeFingerprint: IndexFingerprint;
|
|
40
41
|
startBackgroundIndexing?: (codebasePath: string, forceReindex: boolean, writeCollectionName?: string) => Promise<void> | void;
|
|
41
42
|
manageResponse(action: ManageIndexAction | "reindex", path: string, status: string, message: string, options?: Record<string, unknown>): ToolTextResponse;
|
|
42
|
-
buildRuntimeOwnerConflictResponseIfBlocked(action:
|
|
43
|
+
buildRuntimeOwnerConflictResponseIfBlocked(action: RuntimeOwnerMutationAction, codebasePath: string): Promise<ToolTextResponse | null>;
|
|
43
44
|
recoverStaleIndexingStateIfNeeded(codebasePath: string): Promise<void>;
|
|
44
45
|
getSnapshotIndexingCodebases(): string[];
|
|
45
46
|
getSnapshotCodebaseInfo(codebasePath: string): Record<string, unknown> | undefined;
|
|
46
47
|
getSnapshotIndexedCodebases(): string[];
|
|
47
|
-
buildManageActionBlockedMessage(codebasePath: string, action: "create" | "reindex" | "repair"): string;
|
|
48
|
+
buildManageActionBlockedMessage(codebasePath: string, action: Extract<RuntimeOwnerMutationAction, "create" | "reindex" | "repair">): string;
|
|
48
49
|
buildCreateHint(codebasePath: string): Record<string, unknown>;
|
|
49
50
|
buildStatusHint(codebasePath: string): Record<string, unknown>;
|
|
50
51
|
getManageRetryAfterMs(): number;
|