lmgrep 0.1.10 → 0.1.11
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 +62 -0
- package/dist/lib/search-tool.d.ts +47 -0
- package/dist/lib/search-tool.js +220 -0
- package/dist/lib/search-tool.js.map +1 -0
- package/dist/mcp.js +25 -208
- package/dist/mcp.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -133,6 +133,68 @@ lmgrep includes an MCP server for use with AI coding assistants. When launched w
|
|
|
133
133
|
|
|
134
134
|
You can also start it explicitly with `lmgrep mcp`.
|
|
135
135
|
|
|
136
|
+
### Claude Code
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
# If lmgrep is installed globally
|
|
140
|
+
claude mcp add lmgrep -s user -- lmgrep mcp
|
|
141
|
+
|
|
142
|
+
# Or without a global install
|
|
143
|
+
claude mcp add lmgrep -s user -- npx -y lmgrep mcp
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Codex CLI
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
# If lmgrep is installed globally
|
|
150
|
+
codex mcp add lmgrep -- lmgrep mcp
|
|
151
|
+
|
|
152
|
+
# Or without a global install
|
|
153
|
+
codex mcp add lmgrep -- npx -y lmgrep mcp
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Gemini CLI
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
# If lmgrep is installed globally
|
|
160
|
+
gemini mcp add lmgrep -- lmgrep mcp
|
|
161
|
+
|
|
162
|
+
# Or without a global install
|
|
163
|
+
gemini mcp add lmgrep -- npx -y lmgrep mcp
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Pi coding agent
|
|
167
|
+
|
|
168
|
+
Pi doesn't speak MCP — it uses TypeScript extensions instead. lmgrep ships one at [`pi-extension/`](./pi-extension) that registers two tools: `lmgrep_search` and `lmgrep_list_other_indexed_projects`. It imports lmgrep directly, runs an in-process file watcher to keep the index fresh, and gates tool visibility on embedder health — if lmgrep isn't configured, or the embedding provider is unreachable, the tools stay hidden so you get a clean tool surface instead of a broken one. Configure lmgrep first (`lmgrep init`) before relying on it inside Pi.
|
|
169
|
+
|
|
170
|
+
Install via Pi's package manager:
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
pi install git:github.com/Aetherall/lmgrep
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Update with `pi update`, remove with `pi remove git:github.com/Aetherall/lmgrep`, and list installed extensions with `pi list`.
|
|
177
|
+
|
|
178
|
+
### OpenCode
|
|
179
|
+
|
|
180
|
+
OpenCode has no one-shot install flag — add an entry to `~/.config/opencode/opencode.json` (or project-level `opencode.json`):
|
|
181
|
+
|
|
182
|
+
```jsonc
|
|
183
|
+
{
|
|
184
|
+
"$schema": "https://opencode.ai/config.json",
|
|
185
|
+
"mcp": {
|
|
186
|
+
"lmgrep": {
|
|
187
|
+
"type": "local",
|
|
188
|
+
// If lmgrep is installed globally
|
|
189
|
+
"command": ["lmgrep", "mcp"],
|
|
190
|
+
// Or without a global install
|
|
191
|
+
// "command": ["npx", "-y", "lmgrep", "mcp"],
|
|
192
|
+
"enabled": true
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
136
198
|
The MCP server exposes a `search` tool and a `list_other_indexed_projects` tool. It automatically watches for file changes and keeps the index up to date.
|
|
137
199
|
|
|
138
200
|
## Configuration
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type HealthReason = "ok" | "not_indexed" | "embedding_failed" | "search_empty";
|
|
2
|
+
export interface HealthState {
|
|
3
|
+
healthy: boolean;
|
|
4
|
+
reason: HealthReason;
|
|
5
|
+
}
|
|
6
|
+
export interface SearchArgs {
|
|
7
|
+
query: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
filePrefix?: string;
|
|
10
|
+
type?: string[];
|
|
11
|
+
language?: string[];
|
|
12
|
+
project?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ToolResult {
|
|
15
|
+
text: string;
|
|
16
|
+
isError?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface ParamSpec {
|
|
19
|
+
description: string;
|
|
20
|
+
}
|
|
21
|
+
export interface SearchParamSpecs {
|
|
22
|
+
query: ParamSpec;
|
|
23
|
+
limit: ParamSpec & {
|
|
24
|
+
default: number;
|
|
25
|
+
};
|
|
26
|
+
filePrefix: ParamSpec;
|
|
27
|
+
type: ParamSpec;
|
|
28
|
+
language: ParamSpec;
|
|
29
|
+
project: ParamSpec;
|
|
30
|
+
}
|
|
31
|
+
export declare const searchParamSpecs: SearchParamSpecs;
|
|
32
|
+
export declare const listProjectsDescription: string;
|
|
33
|
+
export interface LmgrepCore {
|
|
34
|
+
readonly cwd: string;
|
|
35
|
+
readonly searchParams: SearchParamSpecs;
|
|
36
|
+
readonly listProjectsDescription: string;
|
|
37
|
+
buildSearchDescription(): string;
|
|
38
|
+
currentHealth(): HealthState;
|
|
39
|
+
onHealthChange(cb: (state: HealthState) => void): () => void;
|
|
40
|
+
startHealthLoop(): void;
|
|
41
|
+
executeSearch(args: SearchArgs): Promise<ToolResult>;
|
|
42
|
+
executeListProjects(): Promise<ToolResult>;
|
|
43
|
+
dispose(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
export declare function createLmgrepCore(opts: {
|
|
46
|
+
cwd: string;
|
|
47
|
+
}): Promise<LmgrepCore>;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { createIndex } from "../index.js";
|
|
3
|
+
import { TreeSitterChunker } from "./chunker/index.js";
|
|
4
|
+
import { loadConfig } from "./config.js";
|
|
5
|
+
import { AISDKEmbedder } from "./embedder.js";
|
|
6
|
+
import { startWatcher } from "./serve.js";
|
|
7
|
+
import { discoverIndexedProjects, findIndexedAncestor, getDbPath, Store, } from "./store.js";
|
|
8
|
+
import { silentLogger } from "./types.js";
|
|
9
|
+
export const searchParamSpecs = {
|
|
10
|
+
query: {
|
|
11
|
+
description: 'Natural-language description of what you\'re looking for — phrase it as a question or intent, not keywords. Good: "how are webhooks authenticated", "where is user deletion handled", "what happens when a record is created". Bad: "webhook auth", "deleteUser", "createRecord".',
|
|
12
|
+
},
|
|
13
|
+
limit: {
|
|
14
|
+
description: "Maximum number of results",
|
|
15
|
+
default: 10,
|
|
16
|
+
},
|
|
17
|
+
filePrefix: {
|
|
18
|
+
description: "Restrict to files under this path (e.g. 'src/lib')",
|
|
19
|
+
},
|
|
20
|
+
type: {
|
|
21
|
+
description: "AST node types to filter by (e.g. ['function_declaration', 'class_declaration'])",
|
|
22
|
+
},
|
|
23
|
+
language: {
|
|
24
|
+
description: "File extensions to filter by (e.g. ['.ts', '.py'])",
|
|
25
|
+
},
|
|
26
|
+
project: {
|
|
27
|
+
description: "Search a different indexed project by its root path instead of the current one",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export const listProjectsDescription = "List all indexed projects other than the current one. " +
|
|
31
|
+
"Use this to discover what projects are available for cross-project search " +
|
|
32
|
+
"via the `project` parameter on the search tool.";
|
|
33
|
+
export async function createLmgrepCore(opts) {
|
|
34
|
+
const cwd = opts.cwd;
|
|
35
|
+
const index = await createIndex({ cwd });
|
|
36
|
+
function isCurrentProjectIndexed() {
|
|
37
|
+
if (findIndexedAncestor(cwd))
|
|
38
|
+
return true;
|
|
39
|
+
return existsSync(getDbPath(cwd));
|
|
40
|
+
}
|
|
41
|
+
function getOtherProjects() {
|
|
42
|
+
const currentDb = getDbPath(cwd);
|
|
43
|
+
return discoverIndexedProjects()
|
|
44
|
+
.filter((p) => getDbPath(p.metadata.root) !== currentDb)
|
|
45
|
+
.map((p) => ({ root: p.metadata.root, remote: p.metadata.remote }));
|
|
46
|
+
}
|
|
47
|
+
async function checkHealth() {
|
|
48
|
+
if (!isCurrentProjectIndexed()) {
|
|
49
|
+
return { healthy: false, reason: "not_indexed" };
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const info = await index.status();
|
|
53
|
+
if (info.fileCount === 0) {
|
|
54
|
+
return { healthy: false, reason: "not_indexed" };
|
|
55
|
+
}
|
|
56
|
+
if (!info.embeddingOk) {
|
|
57
|
+
return { healthy: false, reason: "embedding_failed" };
|
|
58
|
+
}
|
|
59
|
+
if (!info.searchOk) {
|
|
60
|
+
return { healthy: false, reason: "search_empty" };
|
|
61
|
+
}
|
|
62
|
+
return { healthy: true, reason: "ok" };
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { healthy: false, reason: "embedding_failed" };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
let state = isCurrentProjectIndexed()
|
|
69
|
+
? { healthy: true, reason: "ok" }
|
|
70
|
+
: { healthy: false, reason: "not_indexed" };
|
|
71
|
+
const listeners = new Set();
|
|
72
|
+
let stopWatcher;
|
|
73
|
+
let pollTimer;
|
|
74
|
+
let disposed = false;
|
|
75
|
+
function tryStartWatcher() {
|
|
76
|
+
if (stopWatcher)
|
|
77
|
+
return;
|
|
78
|
+
if (!isCurrentProjectIndexed())
|
|
79
|
+
return;
|
|
80
|
+
const config = loadConfig(cwd);
|
|
81
|
+
const store = Store.forProject(cwd);
|
|
82
|
+
const embedder = new AISDKEmbedder(config);
|
|
83
|
+
const chunker = new TreeSitterChunker();
|
|
84
|
+
stopWatcher = startWatcher(cwd, store, config, embedder, chunker, silentLogger);
|
|
85
|
+
}
|
|
86
|
+
function buildSearchDescription() {
|
|
87
|
+
if (state.healthy) {
|
|
88
|
+
return [
|
|
89
|
+
"**lmgrep — primary search tool for this codebase.** Semantic code search powered by a local embedding model; lmgrep understands intent, not string patterns. Prefer lmgrep over Grep/Glob/find/ripgrep for almost all exploration and lookup tasks.",
|
|
90
|
+
"",
|
|
91
|
+
'**Use lmgrep for:** finding where something is handled, how something works, locating relevant code, discovering related files, understanding unfamiliar code, tracing side effects, finding usage patterns, answering "where is X?" or "how does Y work?". One good lmgrep query is usually enough to understand how to proceed.',
|
|
92
|
+
"",
|
|
93
|
+
"**Query lmgrep as natural questions or intent descriptions**, not keyword dumps:",
|
|
94
|
+
'- "how are webhooks authenticated" → finds middleware, token validation, auth checks',
|
|
95
|
+
'- "where is user deletion handled" → finds the handler and related cleanup logic',
|
|
96
|
+
'- "what happens when a record is created" → finds controllers, event emitters, side effects',
|
|
97
|
+
'- "config loading and validation"',
|
|
98
|
+
'- "how to run the playwright tests" → finds config, scripts, prerequisites',
|
|
99
|
+
"",
|
|
100
|
+
"**lmgrep results include** file paths, line numbers, AST node types, and surrounding context (scope, leading comments, role) — often enough to act on directly without re-reading the file. Trust lmgrep results; don't follow up with Glob/Read on files already surfaced by lmgrep unless you genuinely need content that wasn't returned.",
|
|
101
|
+
"",
|
|
102
|
+
"**Fall back to Grep only** when you need exact string or regex matches (specific identifiers, literal constants, error messages, TODO markers). Don't use Grep/Glob/find for conceptual or intent-based search — lmgrep will do better.",
|
|
103
|
+
].join("\n");
|
|
104
|
+
}
|
|
105
|
+
const others = getOtherProjects();
|
|
106
|
+
const suffix = others.length > 0
|
|
107
|
+
? " You can still search other indexed projects via the `project` parameter — call `list_other_indexed_projects` to see what's available."
|
|
108
|
+
: "";
|
|
109
|
+
switch (state.reason) {
|
|
110
|
+
case "not_indexed":
|
|
111
|
+
return ("This project is not indexed. Semantic search is not available for the current directory." +
|
|
112
|
+
suffix);
|
|
113
|
+
case "embedding_failed":
|
|
114
|
+
return ("The embedding provider is unreachable. Semantic search is temporarily unavailable for the current directory." +
|
|
115
|
+
suffix);
|
|
116
|
+
case "search_empty":
|
|
117
|
+
return ("The index for the current branch appears empty or stale — a smoke query returned no results. Re-run `lmgrep index` to rebuild." +
|
|
118
|
+
suffix);
|
|
119
|
+
default:
|
|
120
|
+
return "Semantic search is unavailable." + suffix;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function refreshHealth() {
|
|
124
|
+
if (disposed)
|
|
125
|
+
return;
|
|
126
|
+
const next = await checkHealth();
|
|
127
|
+
if (next.healthy !== state.healthy || next.reason !== state.reason) {
|
|
128
|
+
state = next;
|
|
129
|
+
for (const cb of listeners)
|
|
130
|
+
cb(next);
|
|
131
|
+
}
|
|
132
|
+
if (next.healthy)
|
|
133
|
+
tryStartWatcher();
|
|
134
|
+
}
|
|
135
|
+
function startHealthLoop() {
|
|
136
|
+
if (pollTimer)
|
|
137
|
+
return;
|
|
138
|
+
tryStartWatcher();
|
|
139
|
+
// Non-local providers may bill per request — poll less often.
|
|
140
|
+
const intervalMs = index.config.local ? 10_000 : 60_000;
|
|
141
|
+
refreshHealth();
|
|
142
|
+
pollTimer = setInterval(refreshHealth, intervalMs);
|
|
143
|
+
}
|
|
144
|
+
async function executeSearch(args) {
|
|
145
|
+
if (state.reason === "embedding_failed") {
|
|
146
|
+
return {
|
|
147
|
+
text: "lmgrep is unavailable: the embedding provider is unreachable. Ask the user to check their lmgrep configuration (`lmgrep status`) before retrying.",
|
|
148
|
+
isError: true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const results = await index.search(args.query, {
|
|
153
|
+
limit: args.limit ?? searchParamSpecs.limit.default,
|
|
154
|
+
filePrefix: args.filePrefix,
|
|
155
|
+
type: args.type,
|
|
156
|
+
language: args.language,
|
|
157
|
+
project: args.project,
|
|
158
|
+
});
|
|
159
|
+
if (results.length === 0) {
|
|
160
|
+
return { text: "No results found." };
|
|
161
|
+
}
|
|
162
|
+
const text = results
|
|
163
|
+
.map((r) => {
|
|
164
|
+
const loc = `${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
165
|
+
const header = `${loc} [${r.type}] ${r.name} (score: ${r.score.toFixed(3)})`;
|
|
166
|
+
const parts = [header];
|
|
167
|
+
if (r.context)
|
|
168
|
+
parts.push(r.context);
|
|
169
|
+
parts.push(r.content);
|
|
170
|
+
return parts.join("\n");
|
|
171
|
+
})
|
|
172
|
+
.join("\n\n---\n\n");
|
|
173
|
+
return { text };
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
177
|
+
return { text: `Error: ${msg}`, isError: true };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function executeListProjects() {
|
|
181
|
+
const others = getOtherProjects();
|
|
182
|
+
if (others.length === 0) {
|
|
183
|
+
return { text: "No other indexed projects found." };
|
|
184
|
+
}
|
|
185
|
+
const lines = others.map((p) => {
|
|
186
|
+
const parts = [p.root];
|
|
187
|
+
if (p.remote)
|
|
188
|
+
parts.push(`(${p.remote})`);
|
|
189
|
+
return `- ${parts.join(" ")}`;
|
|
190
|
+
});
|
|
191
|
+
return { text: `Indexed projects:\n${lines.join("\n")}` };
|
|
192
|
+
}
|
|
193
|
+
async function dispose() {
|
|
194
|
+
disposed = true;
|
|
195
|
+
if (pollTimer) {
|
|
196
|
+
clearInterval(pollTimer);
|
|
197
|
+
pollTimer = undefined;
|
|
198
|
+
}
|
|
199
|
+
stopWatcher?.();
|
|
200
|
+
stopWatcher = undefined;
|
|
201
|
+
listeners.clear();
|
|
202
|
+
await index.close();
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
cwd,
|
|
206
|
+
searchParams: searchParamSpecs,
|
|
207
|
+
listProjectsDescription,
|
|
208
|
+
buildSearchDescription,
|
|
209
|
+
currentHealth: () => state,
|
|
210
|
+
onHealthChange(cb) {
|
|
211
|
+
listeners.add(cb);
|
|
212
|
+
return () => listeners.delete(cb);
|
|
213
|
+
},
|
|
214
|
+
startHealthLoop,
|
|
215
|
+
executeSearch,
|
|
216
|
+
executeListProjects,
|
|
217
|
+
dispose,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=search-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-tool.js","sourceRoot":"","sources":["../../src/lib/search-tool.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.js
CHANGED
|
@@ -3,242 +3,59 @@ process.title = "lmgrep-mcp";
|
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import { startWatcher } from "./lib/serve.js";
|
|
9
|
-
import { loadConfig } from "./lib/config.js";
|
|
10
|
-
import { AISDKEmbedder } from "./lib/embedder.js";
|
|
11
|
-
import { TreeSitterChunker } from "./lib/chunker/index.js";
|
|
12
|
-
import { Store } from "./lib/store.js";
|
|
13
|
-
import { existsSync } from "node:fs";
|
|
14
|
-
import { silentLogger } from "./lib/types.js";
|
|
15
|
-
const cwd = process.cwd();
|
|
16
|
-
function isCurrentProjectIndexed() {
|
|
17
|
-
const ancestor = findIndexedAncestor(cwd);
|
|
18
|
-
if (ancestor)
|
|
19
|
-
return true;
|
|
20
|
-
return existsSync(getDbPath(cwd));
|
|
21
|
-
}
|
|
22
|
-
async function checkHealth() {
|
|
23
|
-
if (!isCurrentProjectIndexed()) {
|
|
24
|
-
return { healthy: false, reason: "not_indexed" };
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
const info = await index.status();
|
|
28
|
-
if (info.fileCount === 0) {
|
|
29
|
-
return { healthy: false, reason: "not_indexed" };
|
|
30
|
-
}
|
|
31
|
-
if (!info.embeddingOk) {
|
|
32
|
-
return { healthy: false, reason: "embedding_failed" };
|
|
33
|
-
}
|
|
34
|
-
if (!info.searchOk) {
|
|
35
|
-
return { healthy: false, reason: "search_empty" };
|
|
36
|
-
}
|
|
37
|
-
return { healthy: true, reason: "ok" };
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return { healthy: false, reason: "embedding_failed" };
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function getOtherProjects() {
|
|
44
|
-
const currentDb = getDbPath(cwd);
|
|
45
|
-
return discoverIndexedProjects()
|
|
46
|
-
.filter((p) => {
|
|
47
|
-
const pDb = getDbPath(p.metadata.root);
|
|
48
|
-
return pDb !== currentDb;
|
|
49
|
-
})
|
|
50
|
-
.map((p) => ({
|
|
51
|
-
root: p.metadata.root,
|
|
52
|
-
remote: p.metadata.remote,
|
|
53
|
-
}));
|
|
54
|
-
}
|
|
55
|
-
function buildSearchDescription(state) {
|
|
56
|
-
if (state.healthy) {
|
|
57
|
-
return [
|
|
58
|
-
"**lmgrep — primary search tool for this codebase.** Semantic code search powered by a local embedding model; lmgrep understands intent, not string patterns. Prefer lmgrep over Grep/Glob/find/ripgrep for almost all exploration and lookup tasks.",
|
|
59
|
-
"",
|
|
60
|
-
"**Use lmgrep for:** finding where something is handled, how something works, locating relevant code, discovering related files, understanding unfamiliar code, tracing side effects, finding usage patterns, answering \"where is X?\" or \"how does Y work?\". One good lmgrep query is usually enough to understand how to proceed.",
|
|
61
|
-
"",
|
|
62
|
-
"**Query lmgrep as natural questions or intent descriptions**, not keyword dumps:",
|
|
63
|
-
'- "how are webhooks authenticated" → finds middleware, token validation, auth checks',
|
|
64
|
-
'- "where is user deletion handled" → finds the handler and related cleanup logic',
|
|
65
|
-
'- "what happens when a record is created" → finds controllers, event emitters, side effects',
|
|
66
|
-
'- "config loading and validation"',
|
|
67
|
-
'- "how to run the playwright tests" → finds config, scripts, prerequisites',
|
|
68
|
-
"",
|
|
69
|
-
"**lmgrep results include** file paths, line numbers, AST node types, and surrounding context (scope, leading comments, role) — often enough to act on directly without re-reading the file. Trust lmgrep results; don't follow up with Glob/Read on files already surfaced by lmgrep unless you genuinely need content that wasn't returned.",
|
|
70
|
-
"",
|
|
71
|
-
"**Fall back to Grep only** when you need exact string or regex matches (specific identifiers, literal constants, error messages, TODO markers). Don't use Grep/Glob/find for conceptual or intent-based search — lmgrep will do better.",
|
|
72
|
-
].join("\n");
|
|
73
|
-
}
|
|
74
|
-
const others = getOtherProjects();
|
|
75
|
-
const suffix = others.length > 0
|
|
76
|
-
? " You can still search other indexed projects via the `project` parameter — call `list_other_indexed_projects` to see what's available."
|
|
77
|
-
: "";
|
|
78
|
-
switch (state.reason) {
|
|
79
|
-
case "not_indexed":
|
|
80
|
-
return ("This project is not indexed. Semantic search is not available for the current directory." +
|
|
81
|
-
suffix);
|
|
82
|
-
case "embedding_failed":
|
|
83
|
-
return ("The embedding provider is unreachable. Semantic search is temporarily unavailable for the current directory." +
|
|
84
|
-
suffix);
|
|
85
|
-
case "search_empty":
|
|
86
|
-
return ("The index for the current branch appears empty or stale — a smoke query returned no results. Re-run `lmgrep index` to rebuild." +
|
|
87
|
-
suffix);
|
|
88
|
-
default:
|
|
89
|
-
return "Semantic search is unavailable." + suffix;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
// --- In-process watcher ---
|
|
93
|
-
let stopWatcher;
|
|
94
|
-
function tryStartWatcher() {
|
|
95
|
-
if (stopWatcher)
|
|
96
|
-
return; // already watching
|
|
97
|
-
if (!isCurrentProjectIndexed())
|
|
98
|
-
return;
|
|
99
|
-
const config = loadConfig(cwd);
|
|
100
|
-
const store = Store.forProject(cwd);
|
|
101
|
-
const embedder = new AISDKEmbedder(config);
|
|
102
|
-
const chunker = new TreeSitterChunker();
|
|
103
|
-
stopWatcher = startWatcher(cwd, store, config, embedder, chunker, silentLogger);
|
|
104
|
-
// undefined means lock was held by another process — that's fine
|
|
105
|
-
}
|
|
106
|
-
// --- MCP server ---
|
|
6
|
+
import { createLmgrepCore } from "./lib/search-tool.js";
|
|
7
|
+
const core = await createLmgrepCore({ cwd: process.cwd() });
|
|
107
8
|
const server = new McpServer({
|
|
108
9
|
name: "lmgrep",
|
|
109
10
|
version: "0.1.0",
|
|
110
11
|
});
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
// The first async health check below will refine this and push an update if needed.
|
|
114
|
-
const initialState = isCurrentProjectIndexed()
|
|
115
|
-
? { healthy: true, reason: "ok" }
|
|
116
|
-
: { healthy: false, reason: "not_indexed" };
|
|
117
|
-
const searchTool = server.tool("search", buildSearchDescription(initialState), {
|
|
118
|
-
query: z
|
|
119
|
-
.string()
|
|
120
|
-
.describe('Natural-language description of what you\'re looking for — phrase it as a question or intent, not keywords. Good: "how are webhooks authenticated", "where is user deletion handled", "what happens when a record is created". Bad: "webhook auth", "deleteUser", "createRecord".'),
|
|
12
|
+
const searchTool = server.tool("search", core.buildSearchDescription(), {
|
|
13
|
+
query: z.string().describe(core.searchParams.query.description),
|
|
121
14
|
limit: z
|
|
122
15
|
.number()
|
|
123
16
|
.optional()
|
|
124
|
-
.default(
|
|
125
|
-
.describe(
|
|
17
|
+
.default(core.searchParams.limit.default)
|
|
18
|
+
.describe(core.searchParams.limit.description),
|
|
126
19
|
filePrefix: z
|
|
127
20
|
.string()
|
|
128
21
|
.optional()
|
|
129
|
-
.describe(
|
|
22
|
+
.describe(core.searchParams.filePrefix.description),
|
|
130
23
|
type: z
|
|
131
24
|
.array(z.string())
|
|
132
25
|
.optional()
|
|
133
|
-
.describe(
|
|
26
|
+
.describe(core.searchParams.type.description),
|
|
134
27
|
language: z
|
|
135
28
|
.array(z.string())
|
|
136
29
|
.optional()
|
|
137
|
-
.describe(
|
|
30
|
+
.describe(core.searchParams.language.description),
|
|
138
31
|
project: z
|
|
139
32
|
.string()
|
|
140
33
|
.optional()
|
|
141
|
-
.describe(
|
|
142
|
-
}, async (
|
|
143
|
-
|
|
144
|
-
const results = await index.search(query, {
|
|
145
|
-
limit,
|
|
146
|
-
filePrefix,
|
|
147
|
-
type,
|
|
148
|
-
language,
|
|
149
|
-
project,
|
|
150
|
-
});
|
|
151
|
-
if (results.length === 0) {
|
|
152
|
-
return {
|
|
153
|
-
content: [{ type: "text", text: "No results found." }],
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
const text = results
|
|
157
|
-
.map((r) => {
|
|
158
|
-
const loc = `${r.filePath}:${r.startLine}-${r.endLine}`;
|
|
159
|
-
const header = `${loc} [${r.type}] ${r.name} (score: ${r.score.toFixed(3)})`;
|
|
160
|
-
const parts = [header];
|
|
161
|
-
if (r.context)
|
|
162
|
-
parts.push(r.context);
|
|
163
|
-
parts.push(r.content);
|
|
164
|
-
return parts.join("\n");
|
|
165
|
-
})
|
|
166
|
-
.join("\n\n---\n\n");
|
|
167
|
-
return {
|
|
168
|
-
content: [{ type: "text", text }],
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
catch (err) {
|
|
172
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
173
|
-
return {
|
|
174
|
-
content: [{ type: "text", text: `Error: ${msg}` }],
|
|
175
|
-
isError: true,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
server.tool("list_other_indexed_projects", "List all indexed projects other than the current one. " +
|
|
180
|
-
"Use this to discover what projects are available for cross-project search " +
|
|
181
|
-
"via the `project` parameter on the search tool.", {}, async () => {
|
|
182
|
-
const others = getOtherProjects();
|
|
183
|
-
if (others.length === 0) {
|
|
184
|
-
return {
|
|
185
|
-
content: [
|
|
186
|
-
{
|
|
187
|
-
type: "text",
|
|
188
|
-
text: "No other indexed projects found.",
|
|
189
|
-
},
|
|
190
|
-
],
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
const lines = others.map((p) => {
|
|
194
|
-
const parts = [p.root];
|
|
195
|
-
if (p.remote)
|
|
196
|
-
parts.push(`(${p.remote})`);
|
|
197
|
-
return `- ${parts.join(" ")}`;
|
|
198
|
-
});
|
|
34
|
+
.describe(core.searchParams.project.description),
|
|
35
|
+
}, async (args) => {
|
|
36
|
+
const result = await core.executeSearch(args);
|
|
199
37
|
return {
|
|
200
|
-
content: [
|
|
201
|
-
|
|
202
|
-
type: "text",
|
|
203
|
-
text: `Indexed projects:\n${lines.join("\n")}`,
|
|
204
|
-
},
|
|
205
|
-
],
|
|
38
|
+
content: [{ type: "text", text: result.text }],
|
|
39
|
+
...(result.isError ? { isError: true } : {}),
|
|
206
40
|
};
|
|
207
41
|
});
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
searchTool.update({ description: buildSearchDescription(next) });
|
|
217
|
-
}
|
|
218
|
-
if (next.healthy) {
|
|
219
|
-
tryStartWatcher();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
// Non-local providers may bill per request — poll less often to avoid a drip.
|
|
223
|
-
// Local providers (ollama, lmstudio, etc.) can be checked aggressively.
|
|
224
|
-
const healthPollIntervalMs = index.config.local ? 10_000 : 60_000;
|
|
225
|
-
// Kick off an immediate refresh so the initial description reflects the full
|
|
226
|
-
// health check (embedding + smoke search), not just the file-system probe.
|
|
227
|
-
refreshHealth();
|
|
228
|
-
setInterval(refreshHealth, healthPollIntervalMs);
|
|
229
|
-
// --- Cleanup on exit ---
|
|
42
|
+
server.tool("list_other_indexed_projects", core.listProjectsDescription, {}, async () => {
|
|
43
|
+
const result = await core.executeListProjects();
|
|
44
|
+
return { content: [{ type: "text", text: result.text }] };
|
|
45
|
+
});
|
|
46
|
+
core.onHealthChange(() => {
|
|
47
|
+
searchTool.update({ description: core.buildSearchDescription() });
|
|
48
|
+
});
|
|
49
|
+
core.startHealthLoop();
|
|
230
50
|
process.on("exit", () => {
|
|
231
|
-
|
|
51
|
+
core.dispose().catch(() => { });
|
|
232
52
|
});
|
|
233
53
|
process.on("SIGINT", () => {
|
|
234
|
-
|
|
235
|
-
process.exit(0);
|
|
54
|
+
core.dispose().finally(() => process.exit(0));
|
|
236
55
|
});
|
|
237
56
|
process.on("SIGTERM", () => {
|
|
238
|
-
|
|
239
|
-
process.exit(0);
|
|
57
|
+
core.dispose().finally(() => process.exit(0));
|
|
240
58
|
});
|
|
241
|
-
// --- Start ---
|
|
242
59
|
async function main() {
|
|
243
60
|
const transport = new StdioServerTransport();
|
|
244
61
|
await server.connect(transport);
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":";AACA,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lmgrep",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Semantic code search with any AI embedding provider",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"lmgrep": "./dist/cli.js",
|
|
8
8
|
"lmgrep-mcp": "./dist/mcp.js"
|
|
9
9
|
},
|
|
10
|
+
"pi": {
|
|
11
|
+
"extensions": [
|
|
12
|
+
"./pi-extension/index.ts"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
10
15
|
"scripts": {
|
|
11
16
|
"build": "tsc",
|
|
12
17
|
"dev": "tsc --watch",
|