syntax-map-mcp 0.1.3 → 0.1.5
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 +15 -0
- package/README.md +3 -179
- package/dist/analysis/context.js +55 -3
- package/dist/analysis/index.js +3 -3
- package/dist/tools.js +20 -8
- package/dist/workspace.js +19 -1
- package/docs/tools.md +208 -0
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.1.5 - 2026-05-06
|
|
6
|
+
|
|
7
|
+
- `express-rate-limit` transitive 의존성을 override해 npm audit 취약점 경고를 제거했습니다.
|
|
8
|
+
- `index_workspace`가 `workspaceRoot`의 `.gitignore` 패턴에 매칭되는 소스 파일을 인덱싱 대상에서 제외하도록 했습니다.
|
|
9
|
+
|
|
10
|
+
## 0.1.4 - 2026-05-04
|
|
11
|
+
|
|
12
|
+
- `build_context` 인덱스 검색 metadata에 `summarizedFiles`와 `omittedFiles`를 추가했습니다.
|
|
13
|
+
- `build_context`에 `maxFiles` 옵션을 추가해 인덱스 검색 기반 파일 요약 수를 제한할 수 있게 했습니다.
|
|
14
|
+
- 인덱스 기반 심볼/정의/참조 검색 결과를 소스 위치 기준으로 안정 정렬하도록 했습니다.
|
|
15
|
+
- `build_context`의 인덱스 검색 응답에 `metadata`를 추가해 stale 상태와 refresh 여부를 확인할 수 있게 했습니다.
|
|
16
|
+
- `build_context.indexedSearch`가 symbols 모드와 references 모드를 모두 지원하도록 확장했습니다.
|
|
17
|
+
- README의 도구별 입력/응답 예시를 `docs/tools.md`로 분리했습니다.
|
|
18
|
+
- npm 패키지에 `docs/tools.md`를 포함해 README의 도구 레퍼런스 링크가 패키지 안에서도 유지되도록 했습니다.
|
|
19
|
+
|
|
5
20
|
## 0.1.3 - 2026-05-04
|
|
6
21
|
|
|
7
22
|
- README에 `summarize_file`, 인덱스 검색 도구, `get_index_status`의 입력/응답 예시를 추가했습니다.
|
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ npx -y syntax-map-mcp --workspace-root /path/to/workspace
|
|
|
45
45
|
|
|
46
46
|
## SQLite 인덱스
|
|
47
47
|
|
|
48
|
-
`index_workspace`는 `workspaceRoot` 아래의 `.syntax-map-mcp/index.sqlite`에 인덱스를 저장합니다. 인덱싱 대상은 `.js`, `.jsx`, `.ts`, `.tsx`, `.py` 파일이며, `.git`, `.syntax-map-mcp`, `dist`, `node_modules`
|
|
48
|
+
`index_workspace`는 `workspaceRoot` 아래의 `.syntax-map-mcp/index.sqlite`에 인덱스를 저장합니다. 인덱싱 대상은 `.js`, `.jsx`, `.ts`, `.tsx`, `.py` 파일이며, `.git`, `.syntax-map-mcp`, `dist`, `node_modules` 디렉터리와 `workspaceRoot`의 `.gitignore`에 매칭되는 파일은 제외합니다.
|
|
49
49
|
|
|
50
50
|
파일 변경 여부는 `mtimeMs`와 `size`로 판단합니다. 다시 `index_workspace`를 호출하면 변경된 파일만 재파싱하고, 삭제된 파일은 인덱스에서 제거합니다. `search_symbols`, `find_indexed_definition`, `find_indexed_references`는 `isStale`, `staleFiles`, `refreshed`를 반환해 검색 결과가 최신 인덱스 기반인지 알려줍니다. 세 도구에 `refreshIfStale: true`를 전달하면 stale 파일이 있을 때 먼저 인덱스를 갱신한 뒤 검색합니다. 인덱스 검색 도구는 인덱스에 저장된 위치를 조회한 뒤 현재 파일에서 해당 줄 snippet을 읽어 반환합니다. `contextBefore`, `contextAfter`를 0-10 사이 정수로 전달하면 snippet 주변 라인도 함께 반환합니다. `includePreview: true`를 전달하면 `path:line` 헤더와 코드블록으로 구성된 `previewMarkdown`도 함께 반환합니다. 자동 watch 모드는 아직 포함하지 않았습니다.
|
|
51
51
|
|
|
@@ -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
|
-
|
|
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
|
|
package/dist/analysis/context.js
CHANGED
|
@@ -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
|
|
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.
|
|
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) {
|
package/dist/analysis/index.js
CHANGED
|
@@ -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
|
|
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
|
-
.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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/dist/workspace.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdir, readFile, realpath, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import ignore from 'ignore';
|
|
3
4
|
const SUPPORTED_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.py']);
|
|
4
5
|
const EXCLUDED_DIRECTORIES = new Set(['.git', '.syntax-map-mcp', 'dist', 'node_modules']);
|
|
5
6
|
function isInsideRoot(root, candidate) {
|
|
@@ -9,8 +10,22 @@ function isInsideRoot(root, candidate) {
|
|
|
9
10
|
function failure(code, message) {
|
|
10
11
|
return { ok: false, error: { code, message } };
|
|
11
12
|
}
|
|
13
|
+
async function loadGitignore(root) {
|
|
14
|
+
const matcher = ignore();
|
|
15
|
+
try {
|
|
16
|
+
matcher.add(await readFile(path.join(root, '.gitignore'), 'utf8'));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// A workspace without .gitignore indexes all supported source files.
|
|
20
|
+
}
|
|
21
|
+
return matcher;
|
|
22
|
+
}
|
|
23
|
+
function toGitignorePath(relativePath) {
|
|
24
|
+
return relativePath.split(path.sep).join('/');
|
|
25
|
+
}
|
|
12
26
|
export async function createWorkspace(workspaceRoot) {
|
|
13
27
|
const root = await realpath(path.resolve(workspaceRoot));
|
|
28
|
+
const gitignore = await loadGitignore(root);
|
|
14
29
|
async function readSourceFile(inputPath) {
|
|
15
30
|
const resolved = path.resolve(root, inputPath);
|
|
16
31
|
if (!isInsideRoot(root, resolved)) {
|
|
@@ -58,7 +73,10 @@ export async function createWorkspace(workspaceRoot) {
|
|
|
58
73
|
}
|
|
59
74
|
continue;
|
|
60
75
|
}
|
|
61
|
-
|
|
76
|
+
const relativeEntryPath = path.relative(root, absolutePath);
|
|
77
|
+
if (!entry.isFile() ||
|
|
78
|
+
!SUPPORTED_EXTENSIONS.has(path.extname(entry.name)) ||
|
|
79
|
+
gitignore.ignores(toGitignorePath(relativeEntryPath))) {
|
|
62
80
|
continue;
|
|
63
81
|
}
|
|
64
82
|
let actualPath;
|
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
|
+
"version": "0.1.5",
|
|
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"
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
38
|
"@types/sql.js": "^1.4.11",
|
|
39
|
+
"ignore": "^7.0.5",
|
|
38
40
|
"sql.js": "^1.14.1",
|
|
39
41
|
"tree-sitter": "^0.21.1",
|
|
40
42
|
"tree-sitter-javascript": "^0.21.4",
|
|
@@ -47,5 +49,8 @@
|
|
|
47
49
|
"tsx": "^4.0.0",
|
|
48
50
|
"typescript": "^5.8.0",
|
|
49
51
|
"vitest": "^3.0.0"
|
|
52
|
+
},
|
|
53
|
+
"overrides": {
|
|
54
|
+
"express-rate-limit": "7.5.1"
|
|
50
55
|
}
|
|
51
56
|
}
|