syntax-map-mcp 0.1.3 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.4 - 2026-05-04
6
+
7
+ - `build_context` 인덱스 검색 metadata에 `summarizedFiles`와 `omittedFiles`를 추가했습니다.
8
+ - `build_context`에 `maxFiles` 옵션을 추가해 인덱스 검색 기반 파일 요약 수를 제한할 수 있게 했습니다.
9
+ - 인덱스 기반 심볼/정의/참조 검색 결과를 소스 위치 기준으로 안정 정렬하도록 했습니다.
10
+ - `build_context`의 인덱스 검색 응답에 `metadata`를 추가해 stale 상태와 refresh 여부를 확인할 수 있게 했습니다.
11
+ - `build_context.indexedSearch`가 symbols 모드와 references 모드를 모두 지원하도록 확장했습니다.
12
+ - README의 도구별 입력/응답 예시를 `docs/tools.md`로 분리했습니다.
13
+ - npm 패키지에 `docs/tools.md`를 포함해 README의 도구 레퍼런스 링크가 패키지 안에서도 유지되도록 했습니다.
14
+
5
15
  ## 0.1.3 - 2026-05-04
6
16
 
7
17
  - README에 `summarize_file`, 인덱스 검색 도구, `get_index_status`의 입력/응답 예시를 추가했습니다.
package/README.md CHANGED
@@ -53,185 +53,9 @@ npx -y syntax-map-mcp --workspace-root /path/to/workspace
53
53
 
54
54
  Python은 JavaScript/TypeScript처럼 명시적인 `export` 문법이 없으므로, top-level `__all__`에 선언된 문자열 이름을 `exports`로 반환합니다. `__all__`이 없으면 `exports`는 빈 배열입니다.
55
55
 
56
- ## 도구 사용 예시
56
+ ## 도구 레퍼런스
57
57
 
58
- ### summarize_file
59
-
60
- 입력:
61
-
62
- ```json
63
- {
64
- "path": "src/index.ts"
65
- }
66
- ```
67
-
68
- 응답 일부:
69
-
70
- ```json
71
- {
72
- "ok": true,
73
- "path": "src/index.ts",
74
- "language": "typescript",
75
- "imports": ["import { createServer } from './server.js';"],
76
- "exports": ["export async function main() {"],
77
- "sources": {
78
- "symbols": "ast",
79
- "imports": "ast",
80
- "exports": "ast"
81
- }
82
- }
83
- ```
84
-
85
- ### search_symbols
86
-
87
- 입력:
88
-
89
- ```json
90
- {
91
- "query": "UserService",
92
- "kinds": ["class"],
93
- "refreshIfStale": true,
94
- "contextBefore": 2,
95
- "contextAfter": 2,
96
- "includePreview": true
97
- }
98
- ```
99
-
100
- 응답 일부:
101
-
102
- ```json
103
- {
104
- "ok": true,
105
- "isStale": false,
106
- "refreshed": true,
107
- "symbols": [
108
- {
109
- "path": "src/users.ts",
110
- "name": "UserService",
111
- "kind": "class",
112
- "snippet": "export class UserService {",
113
- "context": {
114
- "before": ["export type UserId = User['id'];", ""],
115
- "after": [" constructor(private readonly users: User[]) {}", ""]
116
- },
117
- "previewMarkdown": "src/users.ts:8\n\n```typescript\nexport type UserId = User['id'];\n\nexport class UserService {\n constructor(private readonly users: User[]) {}\n\n```"
118
- }
119
- ]
120
- }
121
- ```
122
-
123
- ### find_indexed_definition
124
-
125
- 입력:
126
-
127
- ```json
128
- {
129
- "name": "UserService",
130
- "refreshIfStale": true,
131
- "contextBefore": 1,
132
- "contextAfter": 1
133
- }
134
- ```
135
-
136
- 응답 일부:
137
-
138
- ```json
139
- {
140
- "ok": true,
141
- "total": 1,
142
- "definitions": [
143
- {
144
- "path": "src/users.ts",
145
- "name": "UserService",
146
- "kind": "class",
147
- "snippet": "export class UserService {"
148
- }
149
- ]
150
- }
151
- ```
152
-
153
- ### find_indexed_references
154
-
155
- 입력:
156
-
157
- ```json
158
- {
159
- "name": "formatUser",
160
- "limit": 20,
161
- "refreshIfStale": true
162
- }
163
- ```
164
-
165
- 응답 일부:
166
-
167
- ```json
168
- {
169
- "ok": true,
170
- "references": [
171
- {
172
- "path": "src/users.ts",
173
- "name": "formatUser",
174
- "nodeType": "identifier",
175
- "snippet": "formatUser(defaultUser);"
176
- }
177
- ]
178
- }
179
- ```
180
-
181
- ### build_context
182
-
183
- 파일 경로 기반 입력:
184
-
185
- ```json
186
- {
187
- "paths": ["src/users.ts", "src/index.ts"],
188
- "detail": "compact"
189
- }
190
- ```
191
-
192
- 인덱스 검색 기반 입력:
193
-
194
- ```json
195
- {
196
- "detail": "compact",
197
- "indexedSearch": {
198
- "query": "UserService",
199
- "kinds": ["class"],
200
- "refreshIfStale": true,
201
- "contextBefore": 1,
202
- "contextAfter": 1
203
- }
204
- }
205
- ```
206
-
207
- 응답 일부:
208
-
209
- ```json
210
- {
211
- "ok": true,
212
- "markdown": "# Code Context\n\n## Indexed Search Results\n\n### UserService\n\nsrc/users.ts:8\n\n```typescript\nexport class UserService {\n```"
213
- }
214
- ```
215
-
216
- ### get_index_status
217
-
218
- 입력:
219
-
220
- ```json
221
- {}
222
- ```
223
-
224
- 응답 일부:
225
-
226
- ```json
227
- {
228
- "ok": true,
229
- "indexedFiles": 12,
230
- "symbols": 84,
231
- "references": 231,
232
- "staleFiles": 0
233
- }
234
- ```
58
+ 도구별 입력과 응답 예시는 [docs/tools.md](./docs/tools.md)를 참고하세요.
235
59
 
236
60
  ## 변경 이력
237
61
 
@@ -1,9 +1,10 @@
1
- import { searchSymbols } from './index.js';
1
+ import { findIndexedReferences, searchSymbols } from './index.js';
2
2
  import { summarizeFile } from './summary.js';
3
3
  export async function buildContext(workspace, input) {
4
4
  if (input.indexedSearch) {
5
5
  return buildIndexedSearchContext(workspace, {
6
6
  detail: input.detail,
7
+ maxFiles: input.maxFiles,
7
8
  indexedSearch: input.indexedSearch
8
9
  });
9
10
  }
@@ -23,13 +24,49 @@ export async function buildContext(workspace, input) {
23
24
  };
24
25
  }
25
26
  async function buildIndexedSearchContext(workspace, input) {
27
+ const { detail, indexedSearch } = input;
28
+ if (indexedSearch.mode === 'references') {
29
+ return buildIndexedReferenceContext(workspace, { detail, maxFiles: input.maxFiles, indexedSearch });
30
+ }
26
31
  const search = await searchSymbols(workspace, {
32
+ ...indexedSearch,
33
+ includePreview: true
34
+ });
35
+ if (!search.ok)
36
+ return search;
37
+ const allPaths = [...new Set(search.symbols.map(symbol => symbol.path))];
38
+ const paths = limitPaths(allPaths, input.maxFiles);
39
+ const summaries = [];
40
+ for (const filePath of paths) {
41
+ const summary = await summarizeFile(workspace, filePath);
42
+ if (!summary.ok)
43
+ return summary;
44
+ summaries.push(summary);
45
+ }
46
+ return {
47
+ ok: true,
48
+ markdown: renderMarkdown(summaries, detail, renderIndexedSearchResults(search.symbols)),
49
+ metadata: {
50
+ indexedSearchMode: 'symbols',
51
+ indexPath: search.indexPath,
52
+ isStale: search.isStale,
53
+ staleFiles: search.staleFiles,
54
+ refreshed: search.refreshed,
55
+ total: search.total,
56
+ summarizedFiles: paths.length,
57
+ omittedFiles: Math.max(0, allPaths.length - paths.length)
58
+ }
59
+ };
60
+ }
61
+ async function buildIndexedReferenceContext(workspace, input) {
62
+ const search = await findIndexedReferences(workspace, {
27
63
  ...input.indexedSearch,
28
64
  includePreview: true
29
65
  });
30
66
  if (!search.ok)
31
67
  return search;
32
- const paths = [...new Set(search.symbols.map(symbol => symbol.path))];
68
+ const allPaths = [...new Set(search.references.map(reference => reference.path))];
69
+ const paths = limitPaths(allPaths, input.maxFiles);
33
70
  const summaries = [];
34
71
  for (const filePath of paths) {
35
72
  const summary = await summarizeFile(workspace, filePath);
@@ -39,7 +76,17 @@ async function buildIndexedSearchContext(workspace, input) {
39
76
  }
40
77
  return {
41
78
  ok: true,
42
- markdown: renderMarkdown(summaries, input.detail, renderIndexedSearchResults(search.symbols))
79
+ markdown: renderMarkdown(summaries, input.detail, renderIndexedSearchResults(search.references)),
80
+ metadata: {
81
+ indexedSearchMode: 'references',
82
+ indexPath: search.indexPath,
83
+ isStale: search.isStale,
84
+ staleFiles: search.staleFiles,
85
+ refreshed: search.refreshed,
86
+ total: search.total,
87
+ summarizedFiles: paths.length,
88
+ omittedFiles: Math.max(0, allPaths.length - paths.length)
89
+ }
43
90
  };
44
91
  }
45
92
  function renderMarkdown(summaries, detail, intro) {
@@ -47,6 +94,11 @@ function renderMarkdown(summaries, detail, intro) {
47
94
  .filter(Boolean)
48
95
  .join('\n\n');
49
96
  }
97
+ function limitPaths(paths, maxFiles) {
98
+ if (maxFiles === undefined)
99
+ return paths;
100
+ return paths.slice(0, maxFiles);
101
+ }
50
102
  function renderIndexedSearchResults(symbols) {
51
103
  const lines = ['## Indexed Search Results'];
52
104
  if (symbols.length === 0) {
@@ -413,7 +413,7 @@ export async function findIndexedDefinitions(workspace, input) {
413
413
  selection_end_column
414
414
  FROM symbols
415
415
  WHERE ${where.join(' AND ')}
416
- ORDER BY file_path ASC, start_row ASC, name ASC
416
+ ORDER BY file_path ASC, start_row ASC, start_column ASC, name ASC
417
417
  LIMIT ?
418
418
  `, params);
419
419
  const definitions = [];
@@ -500,7 +500,7 @@ export async function findIndexedReferences(workspace, input) {
500
500
  end_column
501
501
  FROM reference_captures
502
502
  WHERE name = ?
503
- ORDER BY file_path ASC, start_row ASC
503
+ ORDER BY file_path ASC, start_row ASC, start_column ASC
504
504
  LIMIT ?
505
505
  `, [input.name, limit]);
506
506
  const references = [];
@@ -579,7 +579,7 @@ export async function searchSymbols(workspace, input) {
579
579
  selection_end_column
580
580
  FROM symbols
581
581
  WHERE ${where.join(' AND ')}
582
- ORDER BY name ASC, file_path ASC, start_row ASC
582
+ ORDER BY file_path ASC, start_row ASC, start_column ASC, name ASC
583
583
  LIMIT ?
584
584
  `, params);
585
585
  const symbols = [];
package/dist/tools.js CHANGED
@@ -148,15 +148,27 @@ export function registerTools(server, workspace) {
148
148
  inputSchema: {
149
149
  paths: z.array(z.string()).optional(),
150
150
  detail: detailSchema,
151
+ maxFiles: z.number().int().positive().max(100).optional(),
151
152
  indexedSearch: z
152
- .object({
153
- query: z.string(),
154
- kinds: z.array(symbolKindSchema).optional(),
155
- limit: z.number().int().positive().max(500).optional(),
156
- refreshIfStale: z.boolean().optional(),
157
- contextBefore: contextLineCountSchema.optional(),
158
- contextAfter: contextLineCountSchema.optional()
159
- })
153
+ .union([
154
+ z.object({
155
+ mode: z.literal('symbols').optional(),
156
+ query: z.string(),
157
+ kinds: z.array(symbolKindSchema).optional(),
158
+ limit: z.number().int().positive().max(500).optional(),
159
+ refreshIfStale: z.boolean().optional(),
160
+ contextBefore: contextLineCountSchema.optional(),
161
+ contextAfter: contextLineCountSchema.optional()
162
+ }),
163
+ z.object({
164
+ mode: z.literal('references'),
165
+ name: z.string(),
166
+ limit: z.number().int().positive().max(500).optional(),
167
+ refreshIfStale: z.boolean().optional(),
168
+ contextBefore: contextLineCountSchema.optional(),
169
+ contextAfter: contextLineCountSchema.optional()
170
+ })
171
+ ])
160
172
  .optional()
161
173
  }
162
174
  }, handlers.buildContext);
package/docs/tools.md ADDED
@@ -0,0 +1,208 @@
1
+ # Tool Reference
2
+
3
+ syntax-map-mcp의 주요 MCP 도구 입력과 응답 예시입니다. 응답 예시는 핵심 필드만 보여줍니다.
4
+
5
+ ## summarize_file
6
+
7
+ 입력:
8
+
9
+ ```json
10
+ {
11
+ "path": "src/index.ts"
12
+ }
13
+ ```
14
+
15
+ 응답 일부:
16
+
17
+ ```json
18
+ {
19
+ "ok": true,
20
+ "path": "src/index.ts",
21
+ "language": "typescript",
22
+ "imports": ["import { createServer } from './server.js';"],
23
+ "exports": ["export async function main() {"],
24
+ "sources": {
25
+ "symbols": "ast",
26
+ "imports": "ast",
27
+ "exports": "ast"
28
+ }
29
+ }
30
+ ```
31
+
32
+ ## search_symbols
33
+
34
+ 입력:
35
+
36
+ ```json
37
+ {
38
+ "query": "UserService",
39
+ "kinds": ["class"],
40
+ "refreshIfStale": true,
41
+ "contextBefore": 2,
42
+ "contextAfter": 2,
43
+ "includePreview": true
44
+ }
45
+ ```
46
+
47
+ 응답 일부:
48
+
49
+ ```json
50
+ {
51
+ "ok": true,
52
+ "isStale": false,
53
+ "refreshed": true,
54
+ "symbols": [
55
+ {
56
+ "path": "src/users.ts",
57
+ "name": "UserService",
58
+ "kind": "class",
59
+ "snippet": "export class UserService {",
60
+ "context": {
61
+ "before": ["export type UserId = User['id'];", ""],
62
+ "after": [" constructor(private readonly users: User[]) {}", ""]
63
+ },
64
+ "previewMarkdown": "src/users.ts:8\n\n```typescript\nexport type UserId = User['id'];\n\nexport class UserService {\n constructor(private readonly users: User[]) {}\n\n```"
65
+ }
66
+ ]
67
+ }
68
+ ```
69
+
70
+ ## find_indexed_definition
71
+
72
+ 입력:
73
+
74
+ ```json
75
+ {
76
+ "name": "UserService",
77
+ "refreshIfStale": true,
78
+ "contextBefore": 1,
79
+ "contextAfter": 1
80
+ }
81
+ ```
82
+
83
+ 응답 일부:
84
+
85
+ ```json
86
+ {
87
+ "ok": true,
88
+ "total": 1,
89
+ "definitions": [
90
+ {
91
+ "path": "src/users.ts",
92
+ "name": "UserService",
93
+ "kind": "class",
94
+ "snippet": "export class UserService {"
95
+ }
96
+ ]
97
+ }
98
+ ```
99
+
100
+ ## find_indexed_references
101
+
102
+ 입력:
103
+
104
+ ```json
105
+ {
106
+ "name": "formatUser",
107
+ "limit": 20,
108
+ "refreshIfStale": true
109
+ }
110
+ ```
111
+
112
+ 응답 일부:
113
+
114
+ ```json
115
+ {
116
+ "ok": true,
117
+ "references": [
118
+ {
119
+ "path": "src/users.ts",
120
+ "name": "formatUser",
121
+ "nodeType": "identifier",
122
+ "snippet": "formatUser(defaultUser);"
123
+ }
124
+ ]
125
+ }
126
+ ```
127
+
128
+ ## build_context
129
+
130
+ 파일 경로 기반 입력:
131
+
132
+ ```json
133
+ {
134
+ "paths": ["src/users.ts", "src/index.ts"],
135
+ "detail": "compact"
136
+ }
137
+ ```
138
+
139
+ 인덱스 심볼 검색 기반 입력:
140
+
141
+ ```json
142
+ {
143
+ "detail": "compact",
144
+ "maxFiles": 3,
145
+ "indexedSearch": {
146
+ "query": "UserService",
147
+ "kinds": ["class"],
148
+ "refreshIfStale": true,
149
+ "contextBefore": 1,
150
+ "contextAfter": 1
151
+ }
152
+ }
153
+ ```
154
+
155
+ 인덱스 참조 검색 기반 입력:
156
+
157
+ ```json
158
+ {
159
+ "detail": "compact",
160
+ "maxFiles": 3,
161
+ "indexedSearch": {
162
+ "mode": "references",
163
+ "name": "formatUser",
164
+ "refreshIfStale": true,
165
+ "contextBefore": 1,
166
+ "contextAfter": 1
167
+ }
168
+ }
169
+ ```
170
+
171
+ 응답 일부:
172
+
173
+ ```json
174
+ {
175
+ "ok": true,
176
+ "metadata": {
177
+ "indexedSearchMode": "symbols",
178
+ "indexPath": "/workspace/.syntax-map-mcp/index.sqlite",
179
+ "isStale": false,
180
+ "staleFiles": 0,
181
+ "refreshed": true,
182
+ "total": 1,
183
+ "summarizedFiles": 1,
184
+ "omittedFiles": 0
185
+ },
186
+ "markdown": "# Code Context\n\n## Indexed Search Results\n\n### UserService\n\nsrc/users.ts:8\n\n```typescript\nexport class UserService {\n```"
187
+ }
188
+ ```
189
+
190
+ ## get_index_status
191
+
192
+ 입력:
193
+
194
+ ```json
195
+ {}
196
+ ```
197
+
198
+ 응답 일부:
199
+
200
+ ```json
201
+ {
202
+ "ok": true,
203
+ "indexedFiles": 12,
204
+ "symbols": 84,
205
+ "references": 231,
206
+ "staleFiles": 0
207
+ }
208
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "syntax-map-mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Tree-sitter based code analysis MCP server",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "files": [
19
19
  "dist",
20
+ "docs/tools.md",
20
21
  "README.md",
21
22
  "CHANGELOG.md",
22
23
  "LICENSE"