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 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
- Get the file tree for a directory, excluding git-ignored files.
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
- **Example:** `lsp://files/src`
327
+ **Resource URI Pattern:** `lsp://files/{+path}`
344
328
 
345
- Returns a list of file/folder paths in the directory tree, formatted as markdown. Git-ignored files and directories are automatically excluded from the results.
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
 
@@ -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, FilesystemProvider, GlobalFindMatch, GlobalFindOptions, GlobalFindProvider, HierarchyProvider, IdeCapabilities, OnDiagnosticsChangedCallback, OutlineProvider, ReferencesProvider, } from './capabilities.js';
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';
@@ -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/{path} - file tree for a directory (git-ignored files excluded)
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 filesystemProvider = capabilities.filesystem;
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: 'File tree for a directory, excluding git-ignored files. Use the folder path after lsp://files/',
445
- mimeType: 'text/markdown',
446
- }, async (_uri, variables) => {
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 path = variables.path;
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 files = await filesystemProvider.getFileTree(normalizedPath);
451
- const markdown = files.length === 0
452
- ? 'No files found in directory.'
453
- : files.map((f) => `- ${f}`).join('\n');
454
- return {
455
- contents: [
456
- {
457
- uri: `lsp://files/${path}`,
458
- mimeType: 'text/markdown',
459
- text: markdown,
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: `lsp://files/${variables.path}`,
470
- mimeType: 'text/markdown',
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.1",
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",