syntax-map-mcp 0.1.0 → 0.1.1
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 +6 -4
- package/dist/analysis/index.js +99 -0
- package/dist/tools.js +16 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ npm run typecheck
|
|
|
19
19
|
## 실행
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
|
|
22
|
+
npx -y syntax-map-mcp --workspace-root /path/to/workspace
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
`workspaceRoot` 결정 순서:
|
|
@@ -38,6 +38,7 @@ node dist/cli.js --workspace-root /path/to/workspace
|
|
|
38
38
|
- `build_context`: 여러 파일 요약을 markdown 컨텍스트로 구성
|
|
39
39
|
- `index_workspace`: 지원 소스 파일을 파싱해 SQLite 심볼 인덱스 생성 또는 갱신
|
|
40
40
|
- `search_symbols`: SQLite 인덱스에서 심볼 이름 검색
|
|
41
|
+
- `find_indexed_definition`: SQLite 인덱스에서 정확한 심볼 정의 검색 및 snippet 반환
|
|
41
42
|
- `get_index_status`: 인덱스 경로, 인덱싱된 파일 수, 심볼 수, stale 파일 수 반환
|
|
42
43
|
- `clear_index`: SQLite 인덱스 파일 삭제
|
|
43
44
|
|
|
@@ -45,7 +46,7 @@ node dist/cli.js --workspace-root /path/to/workspace
|
|
|
45
46
|
|
|
46
47
|
`index_workspace`는 `workspaceRoot` 아래의 `.syntax-map-mcp/index.sqlite`에 인덱스를 저장합니다. 인덱싱 대상은 `.js`, `.jsx`, `.ts`, `.tsx`, `.py` 파일이며, `.git`, `.syntax-map-mcp`, `dist`, `node_modules` 디렉터리는 제외합니다.
|
|
47
48
|
|
|
48
|
-
파일 변경 여부는 `mtimeMs`와 `size`로 판단합니다. 다시 `index_workspace`를 호출하면 변경된 파일만 재파싱하고, 삭제된 파일은 인덱스에서 제거합니다. 자동 watch 모드는 아직 포함하지 않았습니다.
|
|
49
|
+
파일 변경 여부는 `mtimeMs`와 `size`로 판단합니다. 다시 `index_workspace`를 호출하면 변경된 파일만 재파싱하고, 삭제된 파일은 인덱스에서 제거합니다. `find_indexed_definition`은 인덱스에 저장된 정의 위치를 조회한 뒤 현재 파일에서 해당 줄 snippet을 읽어 반환합니다. 자동 watch 모드는 아직 포함하지 않았습니다.
|
|
49
50
|
|
|
50
51
|
## MCP 설정 예시
|
|
51
52
|
|
|
@@ -53,9 +54,10 @@ node dist/cli.js --workspace-root /path/to/workspace
|
|
|
53
54
|
{
|
|
54
55
|
"mcpServers": {
|
|
55
56
|
"syntax-map-mcp": {
|
|
56
|
-
"command": "
|
|
57
|
+
"command": "npx",
|
|
57
58
|
"args": [
|
|
58
|
-
"
|
|
59
|
+
"-y",
|
|
60
|
+
"syntax-map-mcp",
|
|
59
61
|
"--workspace-root",
|
|
60
62
|
"/path/to/workspace"
|
|
61
63
|
]
|
package/dist/analysis/index.js
CHANGED
|
@@ -163,6 +163,9 @@ function sqlLikePattern(query) {
|
|
|
163
163
|
function rowValue(row, key) {
|
|
164
164
|
return row[key];
|
|
165
165
|
}
|
|
166
|
+
function lineAt(text, row) {
|
|
167
|
+
return text.split(/\r?\n/)[row] ?? '';
|
|
168
|
+
}
|
|
166
169
|
export async function indexWorkspace(workspace) {
|
|
167
170
|
const indexPath = indexPathForWorkspace(workspace);
|
|
168
171
|
const database = await openDatabase(indexPath);
|
|
@@ -242,6 +245,102 @@ export async function indexWorkspace(workspace) {
|
|
|
242
245
|
database.close();
|
|
243
246
|
}
|
|
244
247
|
}
|
|
248
|
+
export async function findIndexedDefinitions(workspace, input) {
|
|
249
|
+
const indexPath = indexPathForWorkspace(workspace);
|
|
250
|
+
const database = await openDatabase(indexPath);
|
|
251
|
+
try {
|
|
252
|
+
initSchema(database);
|
|
253
|
+
const where = ['name = ?'];
|
|
254
|
+
const params = [input.name];
|
|
255
|
+
if (input.kinds && input.kinds.length > 0) {
|
|
256
|
+
where.push(`kind IN (${input.kinds.map(() => '?').join(', ')})`);
|
|
257
|
+
params.push(...input.kinds);
|
|
258
|
+
}
|
|
259
|
+
const limit = input.limit ?? 50;
|
|
260
|
+
params.push(limit);
|
|
261
|
+
const statement = database.prepare(`
|
|
262
|
+
SELECT
|
|
263
|
+
file_path,
|
|
264
|
+
language,
|
|
265
|
+
name,
|
|
266
|
+
kind,
|
|
267
|
+
parent_name,
|
|
268
|
+
start_row,
|
|
269
|
+
start_column,
|
|
270
|
+
end_row,
|
|
271
|
+
end_column,
|
|
272
|
+
selection_start_row,
|
|
273
|
+
selection_start_column,
|
|
274
|
+
selection_end_row,
|
|
275
|
+
selection_end_column
|
|
276
|
+
FROM symbols
|
|
277
|
+
WHERE ${where.join(' AND ')}
|
|
278
|
+
ORDER BY file_path ASC, start_row ASC, name ASC
|
|
279
|
+
LIMIT ?
|
|
280
|
+
`, params);
|
|
281
|
+
const definitions = [];
|
|
282
|
+
try {
|
|
283
|
+
while (statement.step()) {
|
|
284
|
+
const row = statement.getAsObject();
|
|
285
|
+
const selectionStartRow = rowValue(row, 'selection_start_row');
|
|
286
|
+
const selectionStartColumn = rowValue(row, 'selection_start_column');
|
|
287
|
+
const selectionEndRow = rowValue(row, 'selection_end_row');
|
|
288
|
+
const selectionEndColumn = rowValue(row, 'selection_end_column');
|
|
289
|
+
const filePath = String(rowValue(row, 'file_path'));
|
|
290
|
+
const startRow = Number(rowValue(row, 'start_row'));
|
|
291
|
+
const file = await workspace.readSourceFile(filePath);
|
|
292
|
+
definitions.push({
|
|
293
|
+
path: filePath,
|
|
294
|
+
language: rowValue(row, 'language'),
|
|
295
|
+
name: String(rowValue(row, 'name')),
|
|
296
|
+
kind: rowValue(row, 'kind'),
|
|
297
|
+
parentName: rowValue(row, 'parent_name') === null ? undefined : String(rowValue(row, 'parent_name')),
|
|
298
|
+
range: {
|
|
299
|
+
start: {
|
|
300
|
+
row: startRow,
|
|
301
|
+
column: Number(rowValue(row, 'start_column'))
|
|
302
|
+
},
|
|
303
|
+
end: {
|
|
304
|
+
row: Number(rowValue(row, 'end_row')),
|
|
305
|
+
column: Number(rowValue(row, 'end_column'))
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
selectionRange: selectionStartRow === null ||
|
|
309
|
+
selectionStartColumn === null ||
|
|
310
|
+
selectionEndRow === null ||
|
|
311
|
+
selectionEndColumn === null
|
|
312
|
+
? undefined
|
|
313
|
+
: {
|
|
314
|
+
start: {
|
|
315
|
+
row: Number(selectionStartRow),
|
|
316
|
+
column: Number(selectionStartColumn)
|
|
317
|
+
},
|
|
318
|
+
end: {
|
|
319
|
+
row: Number(selectionEndRow),
|
|
320
|
+
column: Number(selectionEndColumn)
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
snippet: file.ok ? lineAt(file.text, startRow) : ''
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
statement.free();
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
ok: true,
|
|
332
|
+
indexPath,
|
|
333
|
+
total: definitions.length,
|
|
334
|
+
definitions
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
339
|
+
}
|
|
340
|
+
finally {
|
|
341
|
+
database.close();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
245
344
|
export async function searchSymbols(workspace, input) {
|
|
246
345
|
const indexPath = indexPathForWorkspace(workspace);
|
|
247
346
|
const database = await openDatabase(indexPath);
|
package/dist/tools.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { buildContext as buildContextAnalysis } from './analysis/context.js';
|
|
3
3
|
import { findDefinitions } from './analysis/definitions.js';
|
|
4
|
-
import { clearIndex as clearWorkspaceIndex, getIndexStatus as getWorkspaceIndexStatus, indexWorkspace as indexWorkspaceAnalysis, searchSymbols as searchIndexedSymbols } from './analysis/index.js';
|
|
4
|
+
import { clearIndex as clearWorkspaceIndex, findIndexedDefinitions, getIndexStatus as getWorkspaceIndexStatus, indexWorkspace as indexWorkspaceAnalysis, searchSymbols as searchIndexedSymbols } from './analysis/index.js';
|
|
5
5
|
import { runTreeSitterQuery } from './analysis/query.js';
|
|
6
6
|
import { findReferences as findReferencesAnalysis } from './analysis/references.js';
|
|
7
7
|
import { summarizeFile as summarizeFileAnalysis } from './analysis/summary.js';
|
|
@@ -74,6 +74,12 @@ export function createToolHandlers(workspace) {
|
|
|
74
74
|
return toolFailure(result.error.code, result.error.message);
|
|
75
75
|
return jsonResult(result);
|
|
76
76
|
},
|
|
77
|
+
async findIndexedDefinition(input) {
|
|
78
|
+
const result = await findIndexedDefinitions(workspace, input);
|
|
79
|
+
if (!result.ok)
|
|
80
|
+
return toolFailure(result.error.code, result.error.message);
|
|
81
|
+
return jsonResult(result);
|
|
82
|
+
},
|
|
77
83
|
async getIndexStatus(_input) {
|
|
78
84
|
const result = await getWorkspaceIndexStatus(workspace);
|
|
79
85
|
if (!result.ok)
|
|
@@ -151,6 +157,15 @@ export function registerTools(server, workspace) {
|
|
|
151
157
|
limit: z.number().int().positive().max(500).optional()
|
|
152
158
|
}
|
|
153
159
|
}, handlers.searchSymbols);
|
|
160
|
+
server.registerTool('find_indexed_definition', {
|
|
161
|
+
title: 'Find indexed definition',
|
|
162
|
+
description: 'Find exact symbol definitions from the SQLite workspace index with snippets.',
|
|
163
|
+
inputSchema: {
|
|
164
|
+
name: z.string(),
|
|
165
|
+
kinds: z.array(symbolKindSchema).optional(),
|
|
166
|
+
limit: z.number().int().positive().max(500).optional()
|
|
167
|
+
}
|
|
168
|
+
}, handlers.findIndexedDefinition);
|
|
154
169
|
server.registerTool('get_index_status', {
|
|
155
170
|
title: 'Get index status',
|
|
156
171
|
description: 'Return SQLite index path, indexed file count, symbol count, and stale file count.',
|
package/package.json
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "syntax-map-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Tree-sitter based code analysis MCP server",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"bin": {
|
|
7
|
-
"syntax-map-mcp": "
|
|
8
|
+
"syntax-map-mcp": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/kht6163/syntax-map-mcp.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/kht6163/syntax-map-mcp#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/kht6163/syntax-map-mcp/issues"
|
|
8
17
|
},
|
|
9
18
|
"files": [
|
|
10
19
|
"dist",
|