mcp-lsp-driver 0.0.1 → 0.1.0
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 +10 -26
- package/dist/capabilities.d.ts +0 -14
- package/dist/index.d.ts +1 -1
- package/dist/interfaces.d.ts +7 -0
- package/dist/server.js +82 -26
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -35,7 +35,9 @@ const server = new McpServer({
|
|
|
35
35
|
const fileAccess = {
|
|
36
36
|
readFile: async (uri: string) => {
|
|
37
37
|
return await fs.readFile(uri, 'utf-8')
|
|
38
|
-
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
getFileTree: (uri: string) => yourIDE.workspace.files
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
// 3. Implement User Interaction (required for edits)
|
|
@@ -72,13 +74,6 @@ const outline = {
|
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
const filesystem = {
|
|
76
|
-
getFileTree: async (folderPath) => {
|
|
77
|
-
// Get file tree excluding git-ignored files
|
|
78
|
-
return await yourIDE.getFileTree(folderPath)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
77
|
// 5. Register LSP tools and resources on the server
|
|
83
78
|
const capabilities: IdeCapabilities = {
|
|
84
79
|
fileAccess,
|
|
@@ -110,8 +105,10 @@ await server.connect(transport)
|
|
|
110
105
|
Provides disk access for reading files:
|
|
111
106
|
|
|
112
107
|
```typescript
|
|
108
|
+
// type UnifiedUri = string
|
|
113
109
|
interface FileAccessProvider {
|
|
114
110
|
readFile(uri: UnifiedUri): Promise<string>
|
|
111
|
+
getFileTree(folderPath: UnifiedUri): Promise<string[]>
|
|
115
112
|
}
|
|
116
113
|
```
|
|
117
114
|
|
|
@@ -174,16 +171,6 @@ interface OutlineProvider {
|
|
|
174
171
|
}
|
|
175
172
|
```
|
|
176
173
|
|
|
177
|
-
#### `FilesystemProvider`
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
interface FilesystemProvider {
|
|
181
|
-
getFileTree(folderPath: string): Promise<string[]>
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Provides filesystem access with git-ignore support. Returns file/folder paths in a directory tree, excluding git-ignored files.
|
|
186
|
-
|
|
187
174
|
#### `GlobalFindProvider`
|
|
188
175
|
|
|
189
176
|
```typescript
|
|
@@ -218,7 +205,6 @@ interface IdeCapabilities {
|
|
|
218
205
|
hierarchy?: HierarchyProvider // Enables call_hierarchy tool
|
|
219
206
|
diagnostics?: DiagnosticsProvider // Enables diagnostics resources
|
|
220
207
|
outline?: OutlineProvider // Enables outline resource
|
|
221
|
-
filesystem?: FilesystemProvider // Enables filesystem resource
|
|
222
208
|
globalFind?: GlobalFindProvider // Enables global_find and global_replace tools
|
|
223
209
|
onDiagnosticsChanged?: (callback: OnDiagnosticsChangedCallback) => void
|
|
224
210
|
}
|
|
@@ -299,7 +285,7 @@ The SDK automatically registers resources based on which capabilities you provid
|
|
|
299
285
|
|
|
300
286
|
Get diagnostics (errors, warnings) for a specific file.
|
|
301
287
|
|
|
302
|
-
**Resource URI Pattern:** `lsp://diagnostics/{path}`
|
|
288
|
+
**Resource URI Pattern:** `lsp://diagnostics/{+path}`
|
|
303
289
|
|
|
304
290
|
**Example:** `lsp://diagnostics/src/main.ts`
|
|
305
291
|
|
|
@@ -323,7 +309,7 @@ Returns workspace diagnostics grouped by file, formatted as markdown.
|
|
|
323
309
|
|
|
324
310
|
Get the document outline (symbol tree) for a file.
|
|
325
311
|
|
|
326
|
-
**Resource URI Pattern:** `lsp://outline/{path}`
|
|
312
|
+
**Resource URI Pattern:** `lsp://outline/{+path}`
|
|
327
313
|
|
|
328
314
|
**Example:** `lsp://outline/src/components/Button.tsx`
|
|
329
315
|
|
|
@@ -336,13 +322,11 @@ No subscription support for this resource (read-only).
|
|
|
336
322
|
|
|
337
323
|
### `lsp://files/{path}`
|
|
338
324
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
**Resource URI Pattern:** `lsp://files/{path}`
|
|
325
|
+
For directories: gets the file tree for a directory, excluding git-ignored files. For files: gets file content with optional line range.
|
|
342
326
|
|
|
343
|
-
**
|
|
327
|
+
**Resource URI Pattern:** `lsp://files/{+path}`
|
|
344
328
|
|
|
345
|
-
|
|
329
|
+
**Example:** `lsp://files/src`, `lsp://files/src/index.ts`, `lsp://files/src/index.ts#L1-L2`
|
|
346
330
|
|
|
347
331
|
No subscription support for this resource (read-only).
|
|
348
332
|
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -80,18 +80,6 @@ export interface OutlineProvider {
|
|
|
80
80
|
*/
|
|
81
81
|
provideDocumentSymbols(uri: UnifiedUri): Promise<DocumentSymbol[]>;
|
|
82
82
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Provides filesystem access with git-ignore support.
|
|
85
|
-
*/
|
|
86
|
-
export interface FilesystemProvider {
|
|
87
|
-
/**
|
|
88
|
-
* Gets the file tree for a directory, excluding git-ignored files.
|
|
89
|
-
*
|
|
90
|
-
* @param folderPath - The path to the folder to read
|
|
91
|
-
* @returns Array of file/folder paths in the directory tree
|
|
92
|
-
*/
|
|
93
|
-
getFileTree(folderPath: string): Promise<string[]>;
|
|
94
|
-
}
|
|
95
83
|
/**
|
|
96
84
|
* Search options for global find operations.
|
|
97
85
|
*/
|
|
@@ -165,8 +153,6 @@ export interface IdeCapabilities {
|
|
|
165
153
|
diagnostics?: DiagnosticsProvider;
|
|
166
154
|
/** Optional: Provides document outline (symbols) for files */
|
|
167
155
|
outline?: OutlineProvider;
|
|
168
|
-
/** Optional: Provides filesystem access with git-ignore support */
|
|
169
|
-
filesystem?: FilesystemProvider;
|
|
170
156
|
/** Optional: Provides global find and replace functionality */
|
|
171
157
|
globalFind?: GlobalFindProvider;
|
|
172
158
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
|
-
export type { DefinitionProvider, DiagnosticsProvider,
|
|
9
|
+
export type { DefinitionProvider, DiagnosticsProvider, GlobalFindMatch, GlobalFindOptions, GlobalFindProvider, HierarchyProvider, IdeCapabilities, OnDiagnosticsChangedCallback, OutlineProvider, ReferencesProvider, } from './capabilities.js';
|
|
10
10
|
export type { FileAccessProvider, UserInteractionProvider, } from './interfaces.js';
|
|
11
11
|
export type { ResolverConfig } from './resolver.js';
|
|
12
12
|
export { SymbolResolutionError, SymbolResolver } from './resolver.js';
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -20,6 +20,13 @@ export interface FileAccessProvider {
|
|
|
20
20
|
* @throws Error if the file cannot be read
|
|
21
21
|
*/
|
|
22
22
|
readFile(uri: UnifiedUri): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Gets the file tree for a directory, excluding git-ignored files.
|
|
25
|
+
*
|
|
26
|
+
* @param folderPath - The path to the folder to read
|
|
27
|
+
* @returns Array of file/folder paths in the directory tree
|
|
28
|
+
*/
|
|
29
|
+
getFileTree(folderPath: UnifiedUri): Promise<string[]>;
|
|
23
30
|
}
|
|
24
31
|
/**
|
|
25
32
|
* Provides user interaction capabilities for edit operations.
|
package/dist/server.js
CHANGED
|
@@ -7,6 +7,36 @@
|
|
|
7
7
|
import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { formatDiagnosticsAsMarkdown, formatSymbolsAsMarkdown, generateEditId, makeToolResult, normalizeUri, } from './formatting.js';
|
|
10
|
+
/**
|
|
11
|
+
* Parses a line range fragment from a URI (e.g., "#L21" or "#L21-L28").
|
|
12
|
+
* @returns null if no valid line range, or { start, end } with 1-based line numbers
|
|
13
|
+
*/
|
|
14
|
+
function parseLineRange(fragment) {
|
|
15
|
+
if (!fragment)
|
|
16
|
+
return null;
|
|
17
|
+
// Match #Lxx or #Lxx-Lyy
|
|
18
|
+
const match = fragment.match(/^L(\d+)(?:-L(\d+))?$/);
|
|
19
|
+
if (!match || !match[1])
|
|
20
|
+
return null;
|
|
21
|
+
const start = parseInt(match[1], 10);
|
|
22
|
+
const end = match[2] ? parseInt(match[2], 10) : start;
|
|
23
|
+
if (start < 1 || end < start)
|
|
24
|
+
return null;
|
|
25
|
+
return { start, end };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extracts lines from content based on a line range.
|
|
29
|
+
* @param content - The full file content
|
|
30
|
+
* @param range - 1-based line range { start, end }
|
|
31
|
+
* @returns The extracted lines as a string
|
|
32
|
+
*/
|
|
33
|
+
function extractLines(content, range) {
|
|
34
|
+
const lines = content.split(/\r?\n/);
|
|
35
|
+
// Convert to 0-based index
|
|
36
|
+
const startIdx = range.start - 1;
|
|
37
|
+
const endIdx = range.end;
|
|
38
|
+
return lines.slice(startIdx, endIdx).join('\n');
|
|
39
|
+
}
|
|
10
40
|
import { SymbolResolutionError, SymbolResolver, } from './resolver.js';
|
|
11
41
|
import { ApplyEditSchema, CallHierarchySchema, FuzzyPositionSchema, GlobalFindSchema, GlobalReplaceSchema, } from './schemas.js';
|
|
12
42
|
/**
|
|
@@ -57,15 +87,13 @@ function registerTools(server, capabilities, resolver) {
|
|
|
57
87
|
}
|
|
58
88
|
}
|
|
59
89
|
function registerResources(server, capabilities) {
|
|
90
|
+
registerFilesystemResource(server, capabilities);
|
|
60
91
|
if (capabilities.diagnostics) {
|
|
61
92
|
registerDiagnosticsResources(server, capabilities);
|
|
62
93
|
}
|
|
63
94
|
if (capabilities.outline) {
|
|
64
95
|
registerOutlineResource(server, capabilities);
|
|
65
96
|
}
|
|
66
|
-
if (capabilities.filesystem) {
|
|
67
|
-
registerFilesystemResource(server, capabilities);
|
|
68
|
-
}
|
|
69
97
|
}
|
|
70
98
|
/**
|
|
71
99
|
* Registers the goto_definition tool.
|
|
@@ -431,43 +459,71 @@ function registerApplyEditTool(server, capabilities, resolver) {
|
|
|
431
459
|
}
|
|
432
460
|
/**
|
|
433
461
|
* Registers the filesystem resource.
|
|
434
|
-
* - lsp://files/
|
|
462
|
+
* - lsp://files/path - file tree for a directory (git-ignored files excluded)
|
|
463
|
+
* - lsp://files/path/to/file.ext - read file content
|
|
464
|
+
* - lsp://files/path/to/file.ext#L21 - read specific line
|
|
465
|
+
* - lsp://files/path/to/file.ext#L21-L28 - read line range
|
|
435
466
|
*/
|
|
436
467
|
function registerFilesystemResource(server, capabilities) {
|
|
437
|
-
const
|
|
438
|
-
if (!filesystemProvider)
|
|
439
|
-
return;
|
|
468
|
+
const fileAccessProvider = capabilities.fileAccess;
|
|
440
469
|
const filesystemTemplate = new ResourceTemplate('lsp://files/{+path}', {
|
|
441
470
|
list: undefined, // Cannot enumerate all directories
|
|
442
471
|
});
|
|
443
472
|
server.registerResource('filesystem', filesystemTemplate, {
|
|
444
|
-
description: '
|
|
445
|
-
|
|
446
|
-
}, async (
|
|
473
|
+
description: 'Access filesystem resources. For directories: returns file tree (git-ignored files excluded). ' +
|
|
474
|
+
'For files: returns file content. Supports line ranges with #L23 or #L23-L30 fragment.',
|
|
475
|
+
}, async (uri, variables) => {
|
|
476
|
+
const uriString = uri.toString();
|
|
447
477
|
try {
|
|
448
|
-
const
|
|
478
|
+
const pathWithFragment = variables.path;
|
|
479
|
+
// Parse fragment for line range (e.g., #L23 or #L23-L30)
|
|
480
|
+
let fragment;
|
|
481
|
+
let path = pathWithFragment;
|
|
482
|
+
const hashIndex = pathWithFragment.indexOf('#');
|
|
483
|
+
if (hashIndex !== -1) {
|
|
484
|
+
fragment = pathWithFragment.slice(hashIndex + 1);
|
|
485
|
+
path = pathWithFragment.slice(0, hashIndex);
|
|
486
|
+
}
|
|
449
487
|
const normalizedPath = normalizeUri(path);
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
488
|
+
const lineRange = parseLineRange(fragment);
|
|
489
|
+
// Try reading as a file first
|
|
490
|
+
try {
|
|
491
|
+
const content = await fileAccessProvider.readFile(normalizedPath);
|
|
492
|
+
// If we have a line range, extract those lines
|
|
493
|
+
const resultContent = lineRange
|
|
494
|
+
? extractLines(content, lineRange)
|
|
495
|
+
: content;
|
|
496
|
+
return {
|
|
497
|
+
contents: [
|
|
498
|
+
{
|
|
499
|
+
uri: uriString,
|
|
500
|
+
mimeType: 'text/plain',
|
|
501
|
+
text: resultContent,
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
// File reading failed, try as directory
|
|
508
|
+
const files = await fileAccessProvider.getFileTree(normalizedPath);
|
|
509
|
+
return {
|
|
510
|
+
contents: [
|
|
511
|
+
{
|
|
512
|
+
uri: uriString,
|
|
513
|
+
mimeType: 'application/json',
|
|
514
|
+
text: JSON.stringify(files),
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
};
|
|
518
|
+
}
|
|
463
519
|
}
|
|
464
520
|
catch (error) {
|
|
465
521
|
const message = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
466
522
|
return {
|
|
467
523
|
contents: [
|
|
468
524
|
{
|
|
469
|
-
uri:
|
|
470
|
-
mimeType: 'text/
|
|
525
|
+
uri: uriString,
|
|
526
|
+
mimeType: 'text/plain',
|
|
471
527
|
text: message,
|
|
472
528
|
},
|
|
473
529
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-lsp-driver",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "MCP LSP Driver for IDE plugins to easily expose LSP features via an MCP server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,12 +25,18 @@
|
|
|
25
25
|
"vscode",
|
|
26
26
|
"jetbrains"
|
|
27
27
|
],
|
|
28
|
-
"author": "",
|
|
28
|
+
"author": "EFLKumo",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
32
31
|
"zod": "^4.3.4"
|
|
33
32
|
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/OpticLM/mcp-lspdriver-ts"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.25.1"
|
|
39
|
+
},
|
|
34
40
|
"devDependencies": {
|
|
35
41
|
"@biomejs/biome": "2.3.10",
|
|
36
42
|
"@types/node": "^22.19.3",
|