@zokizuan/satori-mcp 4.8.0 → 4.9.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 +13 -6
- package/dist/cli/install.js +16 -8
- package/dist/config.js +10 -10
- package/dist/core/capabilities.js +1 -1
- package/dist/core/manage-types.d.ts +2 -1
- package/dist/core/search-types.d.ts +2 -1
- package/dist/index.js +8 -1
- package/dist/server/provider-runtime.d.ts +38 -0
- package/dist/server/provider-runtime.js +211 -0
- package/dist/server/start-server.d.ts +7 -4
- package/dist/server/start-server.js +54 -72
- package/dist/tools/manage_index.js +17 -5
- package/dist/tools/search_codebase.js +35 -2
- package/dist/tools/setup-errors.d.ts +14 -0
- package/dist/tools/setup-errors.js +48 -0
- package/dist/tools/types.d.ts +17 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,7 +157,7 @@ No parameters.
|
|
|
157
157
|
"mcpServers": {
|
|
158
158
|
"satori": {
|
|
159
159
|
"command": "npx",
|
|
160
|
-
"args": ["-y", "@zokizuan/satori-mcp@4.
|
|
160
|
+
"args": ["-y", "@zokizuan/satori-mcp@4.9.0"],
|
|
161
161
|
"timeout": 180000,
|
|
162
162
|
"env": {
|
|
163
163
|
"EMBEDDING_PROVIDER": "VoyageAI",
|
|
@@ -178,11 +178,13 @@ No parameters.
|
|
|
178
178
|
```toml
|
|
179
179
|
[mcp_servers.satori]
|
|
180
180
|
command = "npx"
|
|
181
|
-
args = ["-y", "@zokizuan/satori-mcp@4.
|
|
181
|
+
args = ["-y", "@zokizuan/satori-mcp@4.9.0"]
|
|
182
182
|
startup_timeout_ms = 180000
|
|
183
183
|
env = { EMBEDDING_PROVIDER = "VoyageAI", EMBEDDING_MODEL = "voyage-4-large", EMBEDDING_OUTPUT_DIMENSION = "1024", VOYAGEAI_API_KEY = "your-api-key", VOYAGEAI_RERANKER_MODEL = "rerank-2.5", MILVUS_ADDRESS = "your-milvus-endpoint", MILVUS_TOKEN = "your-milvus-token" }
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
+
`MILVUS_TOKEN` is optional auth for endpoints that require it; local unauthenticated Milvus only needs `MILVUS_ADDRESS`.
|
|
187
|
+
|
|
186
188
|
### Local development (when working on this repo)
|
|
187
189
|
|
|
188
190
|
```json
|
|
@@ -228,10 +230,11 @@ Supported installer targets in Phase 1:
|
|
|
228
230
|
Examples:
|
|
229
231
|
|
|
230
232
|
```bash
|
|
231
|
-
npx -y @zokizuan/satori-cli@0.
|
|
232
|
-
npx -y @zokizuan/satori-cli@0.
|
|
233
|
-
npx -y @zokizuan/satori-cli@0.
|
|
234
|
-
npx -y @zokizuan/satori-cli@0.
|
|
233
|
+
npx -y @zokizuan/satori-cli@0.3.0 install --client codex
|
|
234
|
+
npx -y @zokizuan/satori-cli@0.3.0 install --client claude
|
|
235
|
+
npx -y @zokizuan/satori-cli@0.3.0 install --client all --dry-run
|
|
236
|
+
npx -y @zokizuan/satori-cli@0.3.0 uninstall --client codex
|
|
237
|
+
npx -y @zokizuan/satori-cli@0.3.0 doctor
|
|
235
238
|
```
|
|
236
239
|
|
|
237
240
|
Install and uninstall run before MCP session startup, only touch Satori-managed config, and copy/remove these packaged skills:
|
|
@@ -286,6 +289,10 @@ When spawned by `satori-cli`, server process mode is `SATORI_RUN_MODE=cli`:
|
|
|
286
289
|
|
|
287
290
|
`SATORI_CLI_STDOUT_GUARD=drop|redirect` controls accidental non-protocol stdout handling (`drop` default).
|
|
288
291
|
|
|
292
|
+
### Startup vs Provider Setup
|
|
293
|
+
|
|
294
|
+
MCP startup does not require provider credentials, network access, or a live Milvus backend. The server should complete `initialize` and expose the six tools with an empty provider environment. Provider-backed calls (`manage_index create|reindex|sync|clear` and `search_codebase`) validate their required environment at call time and return `MISSING_PROVIDER_CONFIG` when setup is incomplete.
|
|
295
|
+
|
|
289
296
|
## Development
|
|
290
297
|
|
|
291
298
|
```bash
|
package/dist/cli/install.js
CHANGED
|
@@ -69,7 +69,7 @@ function buildCodexManagedBlock(packageSpecifier) {
|
|
|
69
69
|
MANAGED_BLOCK_START,
|
|
70
70
|
"[mcp_servers.satori]",
|
|
71
71
|
'command = "npx"',
|
|
72
|
-
`args = ["-y", "
|
|
72
|
+
`args = ["-y", "${packageSpecifier}"]`,
|
|
73
73
|
`startup_timeout_ms = ${MANAGED_TIMEOUT_MS}`,
|
|
74
74
|
MANAGED_BLOCK_END,
|
|
75
75
|
"",
|
|
@@ -153,10 +153,13 @@ function parseJsonObject(filePath) {
|
|
|
153
153
|
function buildClaudeServerConfig(packageSpecifier) {
|
|
154
154
|
return {
|
|
155
155
|
command: "npx",
|
|
156
|
-
args: ["-y",
|
|
156
|
+
args: ["-y", packageSpecifier],
|
|
157
157
|
timeout: MANAGED_TIMEOUT_MS,
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
|
+
function isManagedPackageSpecifier(value) {
|
|
161
|
+
return typeof value === "string" && /^@zokizuan\/satori-mcp@.+$/.test(value);
|
|
162
|
+
}
|
|
160
163
|
function isManagedClaudeEntry(value) {
|
|
161
164
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
162
165
|
return false;
|
|
@@ -168,14 +171,19 @@ function isManagedClaudeEntry(value) {
|
|
|
168
171
|
if (entry.timeout !== MANAGED_TIMEOUT_MS) {
|
|
169
172
|
return false;
|
|
170
173
|
}
|
|
171
|
-
if (!Array.isArray(entry.args)
|
|
174
|
+
if (!Array.isArray(entry.args)) {
|
|
172
175
|
return false;
|
|
173
176
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
if (entry.args.length === 2) {
|
|
178
|
+
return entry.args[0] === "-y" && isManagedPackageSpecifier(entry.args[1]);
|
|
179
|
+
}
|
|
180
|
+
if (entry.args.length === 4) {
|
|
181
|
+
return entry.args[0] === "-y"
|
|
182
|
+
&& entry.args[1] === "--package"
|
|
183
|
+
&& isManagedPackageSpecifier(entry.args[2])
|
|
184
|
+
&& entry.args[3] === "satori";
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
179
187
|
}
|
|
180
188
|
function prepareClaudeInstall(filePath, packageSpecifier) {
|
|
181
189
|
const currentObject = parseJsonObject(filePath);
|
package/dist/config.js
CHANGED
|
@@ -119,8 +119,8 @@ export function createMcpConfig() {
|
|
|
119
119
|
// Ollama configuration
|
|
120
120
|
ollamaEncoderModel: envManager.get('OLLAMA_MODEL'),
|
|
121
121
|
ollamaEndpoint: envManager.get('OLLAMA_HOST'),
|
|
122
|
-
// Vector database configuration
|
|
123
|
-
milvusEndpoint: envManager.get('MILVUS_ADDRESS'),
|
|
122
|
+
// Vector database configuration
|
|
123
|
+
milvusEndpoint: envManager.get('MILVUS_ADDRESS'),
|
|
124
124
|
milvusApiToken: envManager.get('MILVUS_TOKEN'),
|
|
125
125
|
// Reranker configuration
|
|
126
126
|
rankerModel,
|
|
@@ -139,7 +139,7 @@ export function logConfigurationSummary(config) {
|
|
|
139
139
|
console.log(`[MCP] Server: ${config.name} v${config.version}`);
|
|
140
140
|
console.log(`[MCP] Embedding Provider: ${config.encoderProvider}`);
|
|
141
141
|
console.log(`[MCP] Embedding Model: ${config.encoderModel}`);
|
|
142
|
-
console.log(`[MCP] Milvus Address: ${config.milvusEndpoint ||
|
|
142
|
+
console.log(`[MCP] Milvus Address: ${config.milvusEndpoint || '[Not configured]'}`);
|
|
143
143
|
console.log(`[MCP] Proactive Watcher: ${config.watchSyncEnabled ? `enabled (${config.watchDebounceMs || DEFAULT_WATCH_DEBOUNCE_MS}ms debounce)` : 'disabled'}`);
|
|
144
144
|
// Log provider-specific configuration without exposing sensitive data
|
|
145
145
|
switch (config.encoderProvider) {
|
|
@@ -169,7 +169,7 @@ export function showHelpMessage() {
|
|
|
169
169
|
console.log(`
|
|
170
170
|
Satori MCP Server
|
|
171
171
|
|
|
172
|
-
Usage: npx -y @zokizuan/satori-mcp@4.
|
|
172
|
+
Usage: npx -y @zokizuan/satori-mcp@4.9.0 [options]
|
|
173
173
|
|
|
174
174
|
Options:
|
|
175
175
|
--help, -h Show this help message
|
|
@@ -194,8 +194,8 @@ Environment Variables:
|
|
|
194
194
|
OLLAMA_MODEL Ollama model name (alternative to EMBEDDING_MODEL for Ollama)
|
|
195
195
|
|
|
196
196
|
Vector Database Configuration:
|
|
197
|
-
MILVUS_ADDRESS Milvus address (
|
|
198
|
-
MILVUS_TOKEN Milvus token (optional, used for
|
|
197
|
+
MILVUS_ADDRESS Milvus address (required for index/search/clear tool calls)
|
|
198
|
+
MILVUS_TOKEN Milvus token (optional, used for authenticated endpoints)
|
|
199
199
|
|
|
200
200
|
Read File Configuration:
|
|
201
201
|
READ_FILE_MAX_LINES Max lines returned by read_file when no explicit range is provided (default: 1000)
|
|
@@ -206,16 +206,16 @@ Environment Variables:
|
|
|
206
206
|
|
|
207
207
|
Examples:
|
|
208
208
|
# Start MCP server with OpenAI and explicit Milvus address
|
|
209
|
-
OPENAI_API_KEY=sk-xxx MILVUS_ADDRESS=localhost:19530 npx -y @zokizuan/satori-mcp@4.
|
|
209
|
+
OPENAI_API_KEY=sk-xxx MILVUS_ADDRESS=localhost:19530 npx -y @zokizuan/satori-mcp@4.9.0
|
|
210
210
|
|
|
211
211
|
# Start MCP server with VoyageAI and specific model
|
|
212
|
-
EMBEDDING_PROVIDER=VoyageAI VOYAGEAI_API_KEY=pa-xxx EMBEDDING_MODEL=voyage-4-large MILVUS_TOKEN=your-token npx -y @zokizuan/satori-mcp@4.
|
|
212
|
+
EMBEDDING_PROVIDER=VoyageAI VOYAGEAI_API_KEY=pa-xxx EMBEDDING_MODEL=voyage-4-large MILVUS_ADDRESS=https://your-zilliz-endpoint MILVUS_TOKEN=your-token npx -y @zokizuan/satori-mcp@4.9.0
|
|
213
213
|
|
|
214
214
|
# Start MCP server with Gemini and specific model
|
|
215
|
-
EMBEDDING_PROVIDER=Gemini GEMINI_API_KEY=xxx EMBEDDING_MODEL=gemini-embedding-001 MILVUS_TOKEN=your-token npx -y @zokizuan/satori-mcp@4.
|
|
215
|
+
EMBEDDING_PROVIDER=Gemini GEMINI_API_KEY=xxx EMBEDDING_MODEL=gemini-embedding-001 MILVUS_ADDRESS=https://your-zilliz-endpoint MILVUS_TOKEN=your-token npx -y @zokizuan/satori-mcp@4.9.0
|
|
216
216
|
|
|
217
217
|
# Start MCP server with Ollama and specific model
|
|
218
|
-
EMBEDDING_PROVIDER=Ollama EMBEDDING_MODEL=nomic-embed-text
|
|
218
|
+
EMBEDDING_PROVIDER=Ollama EMBEDDING_MODEL=nomic-embed-text MILVUS_ADDRESS=localhost:19530 npx -y @zokizuan/satori-mcp@4.9.0
|
|
219
219
|
`);
|
|
220
220
|
}
|
|
221
221
|
//# sourceMappingURL=config.js.map
|
|
@@ -15,7 +15,7 @@ export class CapabilityResolver {
|
|
|
15
15
|
else {
|
|
16
16
|
performanceProfile = 'standard';
|
|
17
17
|
}
|
|
18
|
-
const hasVectorStore = Boolean(this.config.milvusEndpoint
|
|
18
|
+
const hasVectorStore = Boolean(this.config.milvusEndpoint);
|
|
19
19
|
const hasReranker = Boolean(this.config.voyageKey);
|
|
20
20
|
const defaultSearchLimit = performanceProfile === 'fast' ? 50 :
|
|
21
21
|
performanceProfile === 'standard' ? 25 :
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { WarningCode } from "./warnings.js";
|
|
2
2
|
export type ManageIndexAction = "create" | "reindex" | "sync" | "status" | "clear";
|
|
3
3
|
export type ManageIndexStatus = "ok" | "not_ready" | "not_indexed" | "requires_reindex" | "blocked" | "error";
|
|
4
|
-
export type ManageIndexReason = "indexing" | "not_indexed" | "requires_reindex" | "unnecessary_reindex_ignore_only" | "preflight_unknown" | "backend_timeout" | "remote_delete_pending";
|
|
4
|
+
export type ManageIndexReason = "indexing" | "not_indexed" | "requires_reindex" | "unnecessary_reindex_ignore_only" | "preflight_unknown" | "backend_timeout" | "remote_delete_pending" | "missing_provider_config";
|
|
5
5
|
export type ManageReindexPreflightOutcome = "reindex_required" | "reindex_unnecessary_ignore_only" | "unknown" | "probe_failed";
|
|
6
6
|
export interface ManageIndexToolHint {
|
|
7
7
|
tool: "manage_index";
|
|
@@ -14,6 +14,7 @@ export interface ManageIndexResponseEnvelope {
|
|
|
14
14
|
path: string;
|
|
15
15
|
status: ManageIndexStatus;
|
|
16
16
|
reason?: ManageIndexReason;
|
|
17
|
+
code?: "MISSING_PROVIDER_CONFIG";
|
|
17
18
|
message: string;
|
|
18
19
|
humanText: string;
|
|
19
20
|
warnings?: WarningCode[];
|
|
@@ -198,10 +198,11 @@ export interface SearchResponseHints extends Record<string, unknown> {
|
|
|
198
198
|
noiseMitigation?: SearchNoiseMitigationHint;
|
|
199
199
|
debugSearch?: SearchDebugHint;
|
|
200
200
|
}
|
|
201
|
-
export type NonOkReason = "indexing" | "requires_reindex" | "not_indexed";
|
|
201
|
+
export type NonOkReason = "indexing" | "requires_reindex" | "not_indexed" | "missing_provider_config";
|
|
202
202
|
interface SearchBaseResponseEnvelope {
|
|
203
203
|
status: "ok" | "requires_reindex" | "not_indexed" | "not_ready";
|
|
204
204
|
reason?: NonOkReason;
|
|
205
|
+
code?: "MISSING_PROVIDER_CONFIG";
|
|
205
206
|
path: string;
|
|
206
207
|
query: string;
|
|
207
208
|
scope: SearchScope;
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,10 @@ import { Writable } from "node:stream";
|
|
|
3
3
|
let activeServer = null;
|
|
4
4
|
let shuttingDown = false;
|
|
5
5
|
let guardDisabledWarningEmitted = false;
|
|
6
|
+
const bootstrapKeepAlive = setInterval(() => {
|
|
7
|
+
// Node may otherwise exit before top-level async ESM imports finish and stdio transport connects.
|
|
8
|
+
// The server owns steady-state lifetime after startMcpServerFromEnv resolves.
|
|
9
|
+
}, 60 * 60 * 1000);
|
|
6
10
|
function resolveRunMode() {
|
|
7
11
|
return process.env.SATORI_RUN_MODE === "cli" ? "cli" : "mcp";
|
|
8
12
|
}
|
|
@@ -79,7 +83,10 @@ process.on("SIGINT", () => {
|
|
|
79
83
|
process.on("SIGTERM", () => {
|
|
80
84
|
void handleShutdownSignal("SIGTERM");
|
|
81
85
|
});
|
|
82
|
-
main().
|
|
86
|
+
main().then(() => {
|
|
87
|
+
clearInterval(bootstrapKeepAlive);
|
|
88
|
+
}).catch((error) => {
|
|
89
|
+
clearInterval(bootstrapKeepAlive);
|
|
83
90
|
const message = error instanceof Error ? error.message : String(error);
|
|
84
91
|
if (message.includes("E_PROTOCOL_FAILURE")) {
|
|
85
92
|
console.error(`E_PROTOCOL_FAILURE ${message}`);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Context } from "@zokizuan/satori-core";
|
|
2
|
+
import { CapabilityResolver } from "../core/capabilities.js";
|
|
3
|
+
import { CallGraphSidecarManager } from "../core/call-graph.js";
|
|
4
|
+
import { SnapshotManager } from "../core/snapshot.js";
|
|
5
|
+
import { ContextMcpConfig, IndexFingerprint } from "../config.js";
|
|
6
|
+
import { MissingProviderConfigIssue, ProviderBackedOperation, ToolContext } from "../tools/types.js";
|
|
7
|
+
export declare function resolveConfiguredEmbeddingDimension(config: ContextMcpConfig): number;
|
|
8
|
+
export declare function createLocalOnlyContext(config: ContextMcpConfig): Context;
|
|
9
|
+
export declare class ProviderRuntime {
|
|
10
|
+
private readonly config;
|
|
11
|
+
private readonly snapshotManager;
|
|
12
|
+
private readonly runtimeFingerprint;
|
|
13
|
+
private readonly capabilities;
|
|
14
|
+
private readonly readFileMaxLines;
|
|
15
|
+
private readonly watchSyncEnabled;
|
|
16
|
+
private readonly watchDebounceMs;
|
|
17
|
+
private readonly callGraphManager;
|
|
18
|
+
private readonly now;
|
|
19
|
+
private embeddingRuntimePromise;
|
|
20
|
+
private vectorRuntimePromise;
|
|
21
|
+
private activeContexts;
|
|
22
|
+
constructor(args: {
|
|
23
|
+
config: ContextMcpConfig;
|
|
24
|
+
snapshotManager: SnapshotManager;
|
|
25
|
+
runtimeFingerprint: IndexFingerprint;
|
|
26
|
+
capabilities: CapabilityResolver;
|
|
27
|
+
readFileMaxLines: number;
|
|
28
|
+
watchSyncEnabled: boolean;
|
|
29
|
+
watchDebounceMs: number;
|
|
30
|
+
callGraphManager: CallGraphSidecarManager;
|
|
31
|
+
now?: () => number;
|
|
32
|
+
});
|
|
33
|
+
validate(operation: ProviderBackedOperation): MissingProviderConfigIssue | null;
|
|
34
|
+
requireToolContext(operation: ProviderBackedOperation): Promise<ToolContext | MissingProviderConfigIssue>;
|
|
35
|
+
private createRuntime;
|
|
36
|
+
shutdown(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=provider-runtime.d.ts.map
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { Context, Embedding, MilvusVectorDatabase, VoyageAIReranker, } from "@zokizuan/satori-core";
|
|
2
|
+
import { ToolHandlers } from "../core/handlers.js";
|
|
3
|
+
import { SyncManager } from "../core/sync.js";
|
|
4
|
+
import { createEmbeddingInstance, logEmbeddingProviderInfo } from "../embedding.js";
|
|
5
|
+
class MetadataOnlyEmbedding extends Embedding {
|
|
6
|
+
constructor(provider, dimension) {
|
|
7
|
+
super();
|
|
8
|
+
this.maxTokens = 1;
|
|
9
|
+
this.provider = provider;
|
|
10
|
+
this.dimension = dimension;
|
|
11
|
+
}
|
|
12
|
+
async detectDimension() {
|
|
13
|
+
return this.dimension;
|
|
14
|
+
}
|
|
15
|
+
async embed(_text) {
|
|
16
|
+
throw new Error("MISSING_PROVIDER_CONFIG embedding provider is not configured");
|
|
17
|
+
}
|
|
18
|
+
async embedBatch(_texts) {
|
|
19
|
+
throw new Error("MISSING_PROVIDER_CONFIG embedding provider is not configured");
|
|
20
|
+
}
|
|
21
|
+
getDimension() {
|
|
22
|
+
return this.dimension;
|
|
23
|
+
}
|
|
24
|
+
getProvider() {
|
|
25
|
+
return this.provider;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
class UnconfiguredVectorDatabase {
|
|
29
|
+
throwMissing() {
|
|
30
|
+
throw new Error("MISSING_PROVIDER_CONFIG MILVUS_ADDRESS is not configured");
|
|
31
|
+
}
|
|
32
|
+
async createCollection() { this.throwMissing(); }
|
|
33
|
+
async createHybridCollection() { this.throwMissing(); }
|
|
34
|
+
async dropCollection() { this.throwMissing(); }
|
|
35
|
+
async hasCollection() { this.throwMissing(); }
|
|
36
|
+
async listCollections() { this.throwMissing(); }
|
|
37
|
+
async insert() { this.throwMissing(); }
|
|
38
|
+
async insertHybrid() { this.throwMissing(); }
|
|
39
|
+
async search() { this.throwMissing(); }
|
|
40
|
+
async hybridSearch() { this.throwMissing(); }
|
|
41
|
+
async delete() { this.throwMissing(); }
|
|
42
|
+
async query() { this.throwMissing(); }
|
|
43
|
+
async checkCollectionLimit() { this.throwMissing(); }
|
|
44
|
+
}
|
|
45
|
+
// Local-only startup scaffolding: these satisfy Context/ToolHandlers constructor
|
|
46
|
+
// contracts for provider-free tools. They must not perform provider I/O.
|
|
47
|
+
// Provider-backed tools must use ProviderRuntime.requireToolContext instead.
|
|
48
|
+
export function resolveConfiguredEmbeddingDimension(config) {
|
|
49
|
+
switch (config.encoderProvider) {
|
|
50
|
+
case "OpenAI":
|
|
51
|
+
if (config.encoderModel === "text-embedding-3-large")
|
|
52
|
+
return 3072;
|
|
53
|
+
return 1536;
|
|
54
|
+
case "Gemini":
|
|
55
|
+
return 3072;
|
|
56
|
+
case "Ollama":
|
|
57
|
+
return 768;
|
|
58
|
+
case "VoyageAI":
|
|
59
|
+
default:
|
|
60
|
+
return config.encoderOutputDimension || 1024;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function createLocalOnlyContext(config) {
|
|
64
|
+
return new Context({
|
|
65
|
+
embedding: new MetadataOnlyEmbedding(config.encoderProvider, resolveConfiguredEmbeddingDimension(config)),
|
|
66
|
+
vectorDatabase: new UnconfiguredVectorDatabase(),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function createMissingConfigIssue(missingEnv) {
|
|
70
|
+
const uniqueMissing = [...new Set(missingEnv)];
|
|
71
|
+
const message = `Satori provider setup is incomplete. Missing required environment variable(s): ${uniqueMissing.join(", ")}. MCP startup does not require provider credentials, but this tool call does.`;
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
code: "MISSING_PROVIDER_CONFIG",
|
|
75
|
+
missingEnv: uniqueMissing,
|
|
76
|
+
message,
|
|
77
|
+
hints: {
|
|
78
|
+
setup: {
|
|
79
|
+
code: "MISSING_PROVIDER_CONFIG",
|
|
80
|
+
missingEnv: uniqueMissing,
|
|
81
|
+
nextSteps: uniqueMissing.map((name) => `Set ${name}, restart the MCP server, then retry the tool call.`),
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export class ProviderRuntime {
|
|
87
|
+
constructor(args) {
|
|
88
|
+
this.embeddingRuntimePromise = null;
|
|
89
|
+
this.vectorRuntimePromise = null;
|
|
90
|
+
this.activeContexts = [];
|
|
91
|
+
this.config = args.config;
|
|
92
|
+
this.snapshotManager = args.snapshotManager;
|
|
93
|
+
this.runtimeFingerprint = args.runtimeFingerprint;
|
|
94
|
+
this.capabilities = args.capabilities;
|
|
95
|
+
this.readFileMaxLines = args.readFileMaxLines;
|
|
96
|
+
this.watchSyncEnabled = args.watchSyncEnabled;
|
|
97
|
+
this.watchDebounceMs = args.watchDebounceMs;
|
|
98
|
+
this.callGraphManager = args.callGraphManager;
|
|
99
|
+
this.now = args.now || (() => Date.now());
|
|
100
|
+
}
|
|
101
|
+
validate(operation) {
|
|
102
|
+
const missing = [];
|
|
103
|
+
if (operation === "embedding_vector") {
|
|
104
|
+
switch (this.config.encoderProvider) {
|
|
105
|
+
case "OpenAI":
|
|
106
|
+
if (!this.config.openaiKey)
|
|
107
|
+
missing.push("OPENAI_API_KEY");
|
|
108
|
+
break;
|
|
109
|
+
case "VoyageAI":
|
|
110
|
+
if (!this.config.voyageKey)
|
|
111
|
+
missing.push("VOYAGEAI_API_KEY");
|
|
112
|
+
break;
|
|
113
|
+
case "Gemini":
|
|
114
|
+
if (!this.config.geminiKey)
|
|
115
|
+
missing.push("GEMINI_API_KEY");
|
|
116
|
+
break;
|
|
117
|
+
case "Ollama":
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!this.config.milvusEndpoint) {
|
|
122
|
+
missing.push("MILVUS_ADDRESS");
|
|
123
|
+
}
|
|
124
|
+
return missing.length > 0 ? createMissingConfigIssue(missing) : null;
|
|
125
|
+
}
|
|
126
|
+
async requireToolContext(operation) {
|
|
127
|
+
const validation = this.validate(operation);
|
|
128
|
+
if (validation) {
|
|
129
|
+
return validation;
|
|
130
|
+
}
|
|
131
|
+
if (operation === "vector_only") {
|
|
132
|
+
if (!this.vectorRuntimePromise) {
|
|
133
|
+
this.vectorRuntimePromise = this.createRuntime(false).catch((error) => {
|
|
134
|
+
this.vectorRuntimePromise = null;
|
|
135
|
+
throw error;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return this.vectorRuntimePromise;
|
|
139
|
+
}
|
|
140
|
+
if (!this.embeddingRuntimePromise) {
|
|
141
|
+
this.embeddingRuntimePromise = this.createRuntime(true).catch((error) => {
|
|
142
|
+
this.embeddingRuntimePromise = null;
|
|
143
|
+
throw error;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return this.embeddingRuntimePromise;
|
|
147
|
+
}
|
|
148
|
+
async createRuntime(requireEmbedding) {
|
|
149
|
+
const embedding = requireEmbedding
|
|
150
|
+
? createEmbeddingInstance(this.config)
|
|
151
|
+
: new MetadataOnlyEmbedding(this.config.encoderProvider, resolveConfiguredEmbeddingDimension(this.config));
|
|
152
|
+
if (requireEmbedding) {
|
|
153
|
+
logEmbeddingProviderInfo(this.config, embedding);
|
|
154
|
+
}
|
|
155
|
+
const vectorDatabase = new MilvusVectorDatabase({
|
|
156
|
+
address: this.config.milvusEndpoint,
|
|
157
|
+
...(this.config.milvusApiToken && { token: this.config.milvusApiToken }),
|
|
158
|
+
});
|
|
159
|
+
const context = new Context({
|
|
160
|
+
embedding,
|
|
161
|
+
vectorDatabase,
|
|
162
|
+
});
|
|
163
|
+
const syncManager = new SyncManager(context, this.snapshotManager, {
|
|
164
|
+
watchEnabled: this.watchSyncEnabled,
|
|
165
|
+
watchDebounceMs: this.watchDebounceMs,
|
|
166
|
+
onSyncCompleted: async (codebasePath, stats) => {
|
|
167
|
+
try {
|
|
168
|
+
const sidecar = await this.callGraphManager.rebuildIfSupportedDelta(codebasePath, stats.changedFiles, context.getActiveIgnorePatterns(codebasePath));
|
|
169
|
+
if (sidecar) {
|
|
170
|
+
this.snapshotManager.setCodebaseCallGraphSidecar(codebasePath, sidecar);
|
|
171
|
+
this.snapshotManager.saveCodebaseSnapshot();
|
|
172
|
+
console.log(`[CALL-GRAPH] Rebuilt sidecar for '${codebasePath}' from sync lifecycle callback.`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.warn(`[CALL-GRAPH] Sync lifecycle rebuild failed for '${codebasePath}': ${error?.message || error}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
const reranker = requireEmbedding && this.capabilities.hasReranker()
|
|
181
|
+
? new VoyageAIReranker({
|
|
182
|
+
apiKey: this.config.voyageKey,
|
|
183
|
+
model: this.config.rankerModel || "rerank-2.5",
|
|
184
|
+
})
|
|
185
|
+
: null;
|
|
186
|
+
if (reranker) {
|
|
187
|
+
console.log(`[RERANKER] VoyageAI Reranker initialized with model: ${this.config.rankerModel || "rerank-2.5"}`);
|
|
188
|
+
}
|
|
189
|
+
const toolHandlers = new ToolHandlers(context, this.snapshotManager, syncManager, this.runtimeFingerprint, this.capabilities, this.now, this.callGraphManager, reranker);
|
|
190
|
+
const toolContext = {
|
|
191
|
+
context,
|
|
192
|
+
snapshotManager: this.snapshotManager,
|
|
193
|
+
syncManager,
|
|
194
|
+
capabilities: this.capabilities,
|
|
195
|
+
reranker,
|
|
196
|
+
runtimeFingerprint: this.runtimeFingerprint,
|
|
197
|
+
toolHandlers,
|
|
198
|
+
readFileMaxLines: this.readFileMaxLines,
|
|
199
|
+
providerRuntime: this,
|
|
200
|
+
};
|
|
201
|
+
this.activeContexts.push(toolContext);
|
|
202
|
+
return toolContext;
|
|
203
|
+
}
|
|
204
|
+
async shutdown() {
|
|
205
|
+
await Promise.all(this.activeContexts.map(async (toolContext) => {
|
|
206
|
+
toolContext.syncManager.stopBackgroundSync();
|
|
207
|
+
await toolContext.syncManager.stopWatcherMode();
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=provider-runtime.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Writable } from "node:stream";
|
|
1
|
+
import { Readable, Writable } from "node:stream";
|
|
2
2
|
import { ContextMcpConfig } from "../config.js";
|
|
3
3
|
export type ServerRunMode = "mcp" | "cli";
|
|
4
4
|
export interface StartMcpServerOptions {
|
|
5
5
|
runMode?: ServerRunMode;
|
|
6
|
+
protocolStdin?: Readable;
|
|
6
7
|
protocolStdout?: Writable;
|
|
7
8
|
args?: string[];
|
|
8
9
|
}
|
|
@@ -19,20 +20,22 @@ interface StartupLifecycleDependencies {
|
|
|
19
20
|
export declare function runPostConnectStartupLifecycle(runMode: ServerRunMode, dependencies: StartupLifecycleDependencies): Promise<void>;
|
|
20
21
|
declare class ContextMcpServer {
|
|
21
22
|
private server;
|
|
22
|
-
private
|
|
23
|
+
private toolContext;
|
|
23
24
|
private snapshotManager;
|
|
24
25
|
private syncManager;
|
|
25
26
|
private toolHandlers;
|
|
26
|
-
private reranker;
|
|
27
27
|
private capabilities;
|
|
28
28
|
private runtimeFingerprint;
|
|
29
29
|
private readFileMaxLines;
|
|
30
30
|
private watchSyncEnabled;
|
|
31
31
|
private watchDebounceMs;
|
|
32
32
|
private callGraphManager;
|
|
33
|
+
private providerRuntime;
|
|
33
34
|
private runMode;
|
|
35
|
+
private protocolStdin?;
|
|
34
36
|
private protocolStdout?;
|
|
35
|
-
|
|
37
|
+
private keepAliveTimer;
|
|
38
|
+
constructor(config: ContextMcpConfig, runMode: ServerRunMode, protocolStdout?: Writable, protocolStdin?: Readable);
|
|
36
39
|
private getCliTransportStdout;
|
|
37
40
|
private getToolContext;
|
|
38
41
|
/**
|
|
@@ -4,9 +4,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
import { Context, MilvusVectorDatabase, VoyageAIReranker } from "@zokizuan/satori-core";
|
|
8
7
|
import { buildRuntimeIndexFingerprint, createMcpConfig, logConfigurationSummary, showHelpMessage, } from "../config.js";
|
|
9
|
-
import { createEmbeddingInstance, logEmbeddingProviderInfo } from "../embedding.js";
|
|
10
8
|
import { CapabilityResolver } from "../core/capabilities.js";
|
|
11
9
|
import { SnapshotManager } from "../core/snapshot.js";
|
|
12
10
|
import { SyncManager } from "../core/sync.js";
|
|
@@ -14,6 +12,7 @@ import { ToolHandlers } from "../core/handlers.js";
|
|
|
14
12
|
import { CallGraphSidecarManager } from "../core/call-graph.js";
|
|
15
13
|
import { decideInterruptedIndexingRecovery } from "../core/indexing-recovery.js";
|
|
16
14
|
import { getMcpToolList, toolRegistry } from "../tools/registry.js";
|
|
15
|
+
import { createLocalOnlyContext, ProviderRuntime, resolveConfiguredEmbeddingDimension } from "./provider-runtime.js";
|
|
17
16
|
export async function runPostConnectStartupLifecycle(runMode, dependencies) {
|
|
18
17
|
if (runMode === "cli") {
|
|
19
18
|
try {
|
|
@@ -57,9 +56,10 @@ function migrateLegacyStateDir() {
|
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
class ContextMcpServer {
|
|
60
|
-
constructor(config, runMode, protocolStdout) {
|
|
61
|
-
this.
|
|
59
|
+
constructor(config, runMode, protocolStdout, protocolStdin) {
|
|
60
|
+
this.keepAliveTimer = null;
|
|
62
61
|
this.runMode = runMode;
|
|
62
|
+
this.protocolStdin = protocolStdin;
|
|
63
63
|
this.protocolStdout = protocolStdout;
|
|
64
64
|
this.server = new Server({
|
|
65
65
|
name: config.name,
|
|
@@ -69,100 +69,81 @@ class ContextMcpServer {
|
|
|
69
69
|
tools: {},
|
|
70
70
|
},
|
|
71
71
|
});
|
|
72
|
-
console.log(`[EMBEDDING] Initializing embedding provider: ${config.encoderProvider}`);
|
|
73
|
-
console.log(`[EMBEDDING] Using model: ${config.encoderModel}`);
|
|
74
|
-
const embedding = createEmbeddingInstance(config);
|
|
75
|
-
logEmbeddingProviderInfo(config, embedding);
|
|
76
72
|
this.capabilities = new CapabilityResolver(config);
|
|
77
|
-
this.runtimeFingerprint = buildRuntimeIndexFingerprint(config,
|
|
73
|
+
this.runtimeFingerprint = buildRuntimeIndexFingerprint(config, resolveConfiguredEmbeddingDimension(config));
|
|
78
74
|
this.readFileMaxLines = Math.max(1, config.readFileMaxLines ?? 1000);
|
|
79
75
|
this.watchSyncEnabled = config.watchSyncEnabled === true;
|
|
80
76
|
this.watchDebounceMs = Math.max(1, config.watchDebounceMs ?? 5000);
|
|
81
77
|
console.log(`[FINGERPRINT] Runtime index fingerprint: ${JSON.stringify(this.runtimeFingerprint)}`);
|
|
82
|
-
const vectorDatabase = new MilvusVectorDatabase({
|
|
83
|
-
address: config.milvusEndpoint,
|
|
84
|
-
...(config.milvusApiToken && { token: config.milvusApiToken }),
|
|
85
|
-
});
|
|
86
|
-
this.context = new Context({
|
|
87
|
-
embedding,
|
|
88
|
-
vectorDatabase,
|
|
89
|
-
});
|
|
90
78
|
this.snapshotManager = new SnapshotManager(this.runtimeFingerprint);
|
|
91
79
|
this.callGraphManager = new CallGraphSidecarManager(this.runtimeFingerprint);
|
|
92
|
-
|
|
80
|
+
const localContext = createLocalOnlyContext(config);
|
|
81
|
+
this.syncManager = new SyncManager(localContext, this.snapshotManager, {
|
|
93
82
|
watchEnabled: this.watchSyncEnabled,
|
|
94
83
|
watchDebounceMs: this.watchDebounceMs,
|
|
95
|
-
onSyncCompleted: async (codebasePath, stats) => {
|
|
96
|
-
try {
|
|
97
|
-
const sidecar = await this.callGraphManager.rebuildIfSupportedDelta(codebasePath, stats.changedFiles, this.context.getActiveIgnorePatterns(codebasePath));
|
|
98
|
-
if (sidecar) {
|
|
99
|
-
this.snapshotManager.setCodebaseCallGraphSidecar(codebasePath, sidecar);
|
|
100
|
-
this.snapshotManager.saveCodebaseSnapshot();
|
|
101
|
-
console.log(`[CALL-GRAPH] Rebuilt sidecar for '${codebasePath}' from sync lifecycle callback.`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
console.warn(`[CALL-GRAPH] Sync lifecycle rebuild failed for '${codebasePath}': ${error?.message || error}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
84
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
85
|
+
this.toolHandlers = new ToolHandlers(localContext, this.snapshotManager, this.syncManager, this.runtimeFingerprint, this.capabilities, () => Date.now(), this.callGraphManager, null);
|
|
86
|
+
this.providerRuntime = new ProviderRuntime({
|
|
87
|
+
config,
|
|
88
|
+
snapshotManager: this.snapshotManager,
|
|
89
|
+
runtimeFingerprint: this.runtimeFingerprint,
|
|
90
|
+
capabilities: this.capabilities,
|
|
91
|
+
readFileMaxLines: this.readFileMaxLines,
|
|
92
|
+
watchSyncEnabled: this.watchSyncEnabled,
|
|
93
|
+
watchDebounceMs: this.watchDebounceMs,
|
|
94
|
+
callGraphManager: this.callGraphManager,
|
|
95
|
+
});
|
|
96
|
+
this.toolContext = {
|
|
97
|
+
context: localContext,
|
|
98
|
+
snapshotManager: this.snapshotManager,
|
|
99
|
+
syncManager: this.syncManager,
|
|
100
|
+
capabilities: this.capabilities,
|
|
101
|
+
reranker: null,
|
|
102
|
+
runtimeFingerprint: this.runtimeFingerprint,
|
|
103
|
+
toolHandlers: this.toolHandlers,
|
|
104
|
+
readFileMaxLines: this.readFileMaxLines,
|
|
105
|
+
providerRuntime: this.providerRuntime,
|
|
106
|
+
};
|
|
117
107
|
this.snapshotManager.loadCodebaseSnapshot();
|
|
118
108
|
this.setupTools();
|
|
119
109
|
}
|
|
120
110
|
getCliTransportStdout() {
|
|
111
|
+
if (this.protocolStdout) {
|
|
112
|
+
return this.protocolStdout;
|
|
113
|
+
}
|
|
121
114
|
if (this.runMode !== "cli") {
|
|
122
115
|
return process.stdout;
|
|
123
116
|
}
|
|
124
|
-
|
|
125
|
-
throw new Error("E_PROTOCOL_FAILURE Missing protocolStdout for cli mode");
|
|
126
|
-
}
|
|
127
|
-
return this.protocolStdout;
|
|
117
|
+
throw new Error("E_PROTOCOL_FAILURE Missing protocolStdout for cli mode");
|
|
128
118
|
}
|
|
129
119
|
getToolContext() {
|
|
130
|
-
return
|
|
131
|
-
context: this.context,
|
|
132
|
-
snapshotManager: this.snapshotManager,
|
|
133
|
-
syncManager: this.syncManager,
|
|
134
|
-
capabilities: this.capabilities,
|
|
135
|
-
reranker: this.reranker,
|
|
136
|
-
runtimeFingerprint: this.runtimeFingerprint,
|
|
137
|
-
toolHandlers: this.toolHandlers,
|
|
138
|
-
readFileMaxLines: this.readFileMaxLines,
|
|
139
|
-
};
|
|
120
|
+
return this.toolContext;
|
|
140
121
|
}
|
|
141
122
|
/**
|
|
142
123
|
* Verify cloud state and fix interrupted indexing snapshots.
|
|
143
124
|
*/
|
|
144
|
-
async verifyCloudState() {
|
|
125
|
+
async verifyCloudState(toolContext) {
|
|
145
126
|
console.log("[STARTUP] Verifying interrupted indexing state against completion markers...");
|
|
146
|
-
const indexingCodebases =
|
|
127
|
+
const indexingCodebases = toolContext.snapshotManager.getIndexingCodebases();
|
|
147
128
|
let promotedCount = 0;
|
|
148
129
|
let failedCount = 0;
|
|
149
130
|
for (const codebasePath of indexingCodebases) {
|
|
150
|
-
const marker = typeof
|
|
151
|
-
? await
|
|
131
|
+
const marker = typeof toolContext.context.getIndexCompletionMarker === "function"
|
|
132
|
+
? await toolContext.context.getIndexCompletionMarker(codebasePath)
|
|
152
133
|
: null;
|
|
153
134
|
const decision = decideInterruptedIndexingRecovery(marker, this.runtimeFingerprint);
|
|
154
135
|
if (decision.action === "promote_indexed") {
|
|
155
|
-
|
|
136
|
+
toolContext.snapshotManager.setCodebaseIndexed(codebasePath, decision.stats, this.runtimeFingerprint, "verified");
|
|
156
137
|
promotedCount++;
|
|
157
138
|
console.log(`[STARTUP] Recovered interrupted indexing from marker: ${codebasePath} -> indexed`);
|
|
158
139
|
continue;
|
|
159
140
|
}
|
|
160
|
-
|
|
141
|
+
toolContext.snapshotManager.setCodebaseIndexFailed(codebasePath, decision.message);
|
|
161
142
|
failedCount++;
|
|
162
143
|
console.log(`[STARTUP] Marked interrupted indexing as failed: ${codebasePath} (${decision.reason})`);
|
|
163
144
|
}
|
|
164
145
|
if (promotedCount > 0 || failedCount > 0) {
|
|
165
|
-
|
|
146
|
+
toolContext.snapshotManager.saveCodebaseSnapshot();
|
|
166
147
|
console.log(`[STARTUP] Recovery summary: promoted=${promotedCount}, failed=${failedCount}`);
|
|
167
148
|
}
|
|
168
149
|
else {
|
|
@@ -192,25 +173,26 @@ class ContextMcpServer {
|
|
|
192
173
|
}
|
|
193
174
|
async start() {
|
|
194
175
|
console.log("Starting Satori MCP server...");
|
|
195
|
-
const
|
|
196
|
-
|
|
176
|
+
const transportStdin = this.protocolStdin ?? process.stdin;
|
|
177
|
+
const transport = this.runMode === "cli" || this.protocolStdin || this.protocolStdout
|
|
178
|
+
? new StdioServerTransport(transportStdin, this.getCliTransportStdout())
|
|
197
179
|
: new StdioServerTransport();
|
|
198
180
|
await this.server.connect(transport);
|
|
181
|
+
transportStdin.resume();
|
|
182
|
+
this.keepAliveTimer = setInterval(() => {
|
|
183
|
+
// Keep stdio MCP process alive when startup has no background provider lifecycle.
|
|
184
|
+
}, 60 * 60 * 1000);
|
|
199
185
|
console.log("MCP server started and listening on stdio.");
|
|
200
|
-
await runPostConnectStartupLifecycle(this.runMode, {
|
|
201
|
-
watchSyncEnabled: this.watchSyncEnabled,
|
|
202
|
-
verifyCloudState: () => this.verifyCloudState(),
|
|
203
|
-
onVerifyCloudStateError: (error) => {
|
|
204
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
205
|
-
console.error("[STARTUP] Error verifying cloud state:", message);
|
|
206
|
-
},
|
|
207
|
-
syncManager: this.syncManager,
|
|
208
|
-
});
|
|
209
186
|
}
|
|
210
187
|
async shutdown() {
|
|
211
188
|
console.log("Shutting down Satori MCP server...");
|
|
189
|
+
if (this.keepAliveTimer) {
|
|
190
|
+
clearInterval(this.keepAliveTimer);
|
|
191
|
+
this.keepAliveTimer = null;
|
|
192
|
+
}
|
|
212
193
|
this.syncManager.stopBackgroundSync();
|
|
213
194
|
await this.syncManager.stopWatcherMode();
|
|
195
|
+
await this.providerRuntime.shutdown();
|
|
214
196
|
}
|
|
215
197
|
}
|
|
216
198
|
function isHelpRequested(args) {
|
|
@@ -226,7 +208,7 @@ export async function startMcpServerFromEnv(options = {}) {
|
|
|
226
208
|
migrateLegacyStateDir();
|
|
227
209
|
const config = createMcpConfig();
|
|
228
210
|
logConfigurationSummary(config);
|
|
229
|
-
const server = new ContextMcpServer(config, runMode, options.protocolStdout);
|
|
211
|
+
const server = new ContextMcpServer(config, runMode, options.protocolStdout, options.protocolStdin);
|
|
230
212
|
await server.start();
|
|
231
213
|
return server;
|
|
232
214
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatZodError } from "./types.js";
|
|
3
|
+
import { formatManageProviderConfigError, isMissingProviderConfigIssue } from "./setup-errors.js";
|
|
3
4
|
const actionEnum = z.enum(["create", "reindex", "sync", "status", "clear"]);
|
|
4
5
|
const manageIndexInputSchema = z.object({
|
|
5
6
|
action: actionEnum.describe("Required operation to run."),
|
|
@@ -26,17 +27,28 @@ export const manageIndexTool = {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
const input = parsed.data;
|
|
30
|
+
const providerOperation = input.action === "clear"
|
|
31
|
+
? "vector_only"
|
|
32
|
+
: (input.action === "create" || input.action === "reindex" || input.action === "sync")
|
|
33
|
+
? "embedding_vector"
|
|
34
|
+
: null;
|
|
35
|
+
const executionContext = providerOperation && ctx.providerRuntime
|
|
36
|
+
? await ctx.providerRuntime.requireToolContext(providerOperation)
|
|
37
|
+
: ctx;
|
|
38
|
+
if (isMissingProviderConfigIssue(executionContext)) {
|
|
39
|
+
return formatManageProviderConfigError(input.action, input.path, executionContext);
|
|
40
|
+
}
|
|
29
41
|
switch (input.action) {
|
|
30
42
|
case 'create':
|
|
31
|
-
return
|
|
43
|
+
return executionContext.toolHandlers.handleIndexCodebase(input);
|
|
32
44
|
case 'reindex':
|
|
33
|
-
return
|
|
45
|
+
return executionContext.toolHandlers.handleReindexCodebase(input);
|
|
34
46
|
case 'sync':
|
|
35
|
-
return
|
|
47
|
+
return executionContext.toolHandlers.handleSyncCodebase(input);
|
|
36
48
|
case 'status':
|
|
37
|
-
return
|
|
49
|
+
return executionContext.toolHandlers.handleGetIndexingStatus(input);
|
|
38
50
|
case 'clear':
|
|
39
|
-
return
|
|
51
|
+
return executionContext.toolHandlers.handleClearIndex(input);
|
|
40
52
|
default:
|
|
41
53
|
return {
|
|
42
54
|
content: [{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatZodError } from "./types.js";
|
|
3
3
|
import { emitSearchTelemetry } from "../telemetry/search.js";
|
|
4
|
+
import { formatSearchProviderConfigError, isMissingProviderConfigIssue } from "./setup-errors.js";
|
|
4
5
|
function getProfile(ctx) {
|
|
5
6
|
const locality = ctx.capabilities.getEmbeddingLocality();
|
|
6
7
|
const profile = ctx.capabilities.getPerformanceProfile();
|
|
@@ -92,11 +93,43 @@ export const searchCodebaseTool = {
|
|
|
92
93
|
isError: true
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
|
-
const input =
|
|
96
|
+
const input = {
|
|
97
|
+
...parsed.data,
|
|
98
|
+
scope: parsed.data.scope ?? "runtime",
|
|
99
|
+
resultMode: parsed.data.resultMode ?? "grouped",
|
|
100
|
+
groupBy: parsed.data.groupBy ?? "symbol",
|
|
101
|
+
rankingMode: parsed.data.rankingMode ?? "auto_changed_first",
|
|
102
|
+
};
|
|
96
103
|
const startedAt = Date.now();
|
|
97
104
|
const limit = Math.max(1, Math.min(ctx.capabilities.getMaxSearchLimit(), input.limit ?? ctx.capabilities.getDefaultSearchLimit()));
|
|
98
105
|
const profile = getProfile(ctx);
|
|
99
|
-
const
|
|
106
|
+
const executionContext = ctx.providerRuntime
|
|
107
|
+
? await ctx.providerRuntime.requireToolContext("embedding_vector")
|
|
108
|
+
: ctx;
|
|
109
|
+
if (isMissingProviderConfigIssue(executionContext)) {
|
|
110
|
+
const response = formatSearchProviderConfigError({
|
|
111
|
+
...input,
|
|
112
|
+
limit,
|
|
113
|
+
}, executionContext);
|
|
114
|
+
emitSearchTelemetry({
|
|
115
|
+
event: "search_executed",
|
|
116
|
+
tool_name: "search_codebase",
|
|
117
|
+
profile,
|
|
118
|
+
query_length: input.query.length,
|
|
119
|
+
limit_requested: limit,
|
|
120
|
+
results_before_filter: 0,
|
|
121
|
+
results_after_filter: 0,
|
|
122
|
+
results_returned: 0,
|
|
123
|
+
excluded_by_ignore: 0,
|
|
124
|
+
reranker_used: false,
|
|
125
|
+
reranker_attempted: false,
|
|
126
|
+
latency_ms: Date.now() - startedAt,
|
|
127
|
+
parallel_fanout: true,
|
|
128
|
+
error: executionContext.code,
|
|
129
|
+
});
|
|
130
|
+
return response;
|
|
131
|
+
}
|
|
132
|
+
const response = await executionContext.toolHandlers.handleSearchCode({
|
|
100
133
|
...input,
|
|
101
134
|
limit
|
|
102
135
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ManageIndexAction } from "../core/manage-types.js";
|
|
2
|
+
import { SearchGroupBy, SearchResultMode, SearchScope } from "../core/search-constants.js";
|
|
3
|
+
import { MissingProviderConfigIssue, ToolResponse } from "./types.js";
|
|
4
|
+
export declare function isMissingProviderConfigIssue(value: unknown): value is MissingProviderConfigIssue;
|
|
5
|
+
export declare function formatManageProviderConfigError(action: ManageIndexAction, path: string, issue: MissingProviderConfigIssue): ToolResponse;
|
|
6
|
+
export declare function formatSearchProviderConfigError(input: {
|
|
7
|
+
path: string;
|
|
8
|
+
query: string;
|
|
9
|
+
scope: SearchScope;
|
|
10
|
+
groupBy: SearchGroupBy;
|
|
11
|
+
resultMode: SearchResultMode;
|
|
12
|
+
limit: number;
|
|
13
|
+
}, issue: MissingProviderConfigIssue): ToolResponse;
|
|
14
|
+
//# sourceMappingURL=setup-errors.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function isMissingProviderConfigIssue(value) {
|
|
2
|
+
return Boolean(value)
|
|
3
|
+
&& typeof value === "object"
|
|
4
|
+
&& value.ok === false
|
|
5
|
+
&& value.code === "MISSING_PROVIDER_CONFIG";
|
|
6
|
+
}
|
|
7
|
+
export function formatManageProviderConfigError(action, path, issue) {
|
|
8
|
+
return {
|
|
9
|
+
content: [{
|
|
10
|
+
type: "text",
|
|
11
|
+
text: JSON.stringify({
|
|
12
|
+
tool: "manage_index",
|
|
13
|
+
version: 1,
|
|
14
|
+
action,
|
|
15
|
+
path,
|
|
16
|
+
status: "error",
|
|
17
|
+
reason: "missing_provider_config",
|
|
18
|
+
code: issue.code,
|
|
19
|
+
message: issue.message,
|
|
20
|
+
humanText: issue.message,
|
|
21
|
+
hints: issue.hints,
|
|
22
|
+
}, null, 2)
|
|
23
|
+
}]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function formatSearchProviderConfigError(input, issue) {
|
|
27
|
+
return {
|
|
28
|
+
content: [{
|
|
29
|
+
type: "text",
|
|
30
|
+
text: JSON.stringify({
|
|
31
|
+
status: "not_ready",
|
|
32
|
+
reason: "missing_provider_config",
|
|
33
|
+
code: issue.code,
|
|
34
|
+
path: input.path,
|
|
35
|
+
query: input.query,
|
|
36
|
+
scope: input.scope,
|
|
37
|
+
groupBy: input.groupBy,
|
|
38
|
+
resultMode: input.resultMode,
|
|
39
|
+
limit: input.limit,
|
|
40
|
+
freshnessDecision: null,
|
|
41
|
+
message: issue.message,
|
|
42
|
+
hints: issue.hints,
|
|
43
|
+
results: [],
|
|
44
|
+
}, null, 2)
|
|
45
|
+
}]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=setup-errors.js.map
|
package/dist/tools/types.d.ts
CHANGED
|
@@ -5,6 +5,20 @@ import { SnapshotManager } from "../core/snapshot.js";
|
|
|
5
5
|
import { SyncManager } from "../core/sync.js";
|
|
6
6
|
import { IndexFingerprint } from "../config.js";
|
|
7
7
|
import { ToolHandlers } from "../core/handlers.js";
|
|
8
|
+
export type ProviderBackedOperation = "embedding_vector" | "vector_only";
|
|
9
|
+
export interface MissingProviderConfigIssue {
|
|
10
|
+
ok: false;
|
|
11
|
+
code: "MISSING_PROVIDER_CONFIG";
|
|
12
|
+
missingEnv: string[];
|
|
13
|
+
message: string;
|
|
14
|
+
hints: {
|
|
15
|
+
setup: {
|
|
16
|
+
code: "MISSING_PROVIDER_CONFIG";
|
|
17
|
+
missingEnv: string[];
|
|
18
|
+
nextSteps: string[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
8
22
|
export interface ToolResponse {
|
|
9
23
|
content: Array<{
|
|
10
24
|
type: string;
|
|
@@ -22,6 +36,9 @@ export interface ToolContext {
|
|
|
22
36
|
runtimeFingerprint: IndexFingerprint;
|
|
23
37
|
toolHandlers: ToolHandlers;
|
|
24
38
|
readFileMaxLines: number;
|
|
39
|
+
providerRuntime?: {
|
|
40
|
+
requireToolContext(operation: ProviderBackedOperation): Promise<ToolContext | MissingProviderConfigIssue>;
|
|
41
|
+
};
|
|
25
42
|
}
|
|
26
43
|
export interface McpTool<TSchema extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
27
44
|
name: string;
|