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 CHANGED
@@ -19,7 +19,7 @@ npm run typecheck
19
19
  ## 실행
20
20
 
21
21
  ```bash
22
- node dist/cli.js --workspace-root /path/to/workspace
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": "node",
57
+ "command": "npx",
57
58
  "args": [
58
- "/Users/hantaekim/my-project/tree-sitter/dist/cli.js",
59
+ "-y",
60
+ "syntax-map-mcp",
59
61
  "--workspace-root",
60
62
  "/path/to/workspace"
61
63
  ]
@@ -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.0",
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": "./dist/cli.js"
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",