MCP Lspdriver Ts
A TypeScript SDK bridges IDE's LSP capabilities with MCP, designed for who building AI coding agents.
Install / Use
/learn @OpticLM/MCP Lspdriver TsQuality Score
Category
Development & EngineeringSupported Platforms
README
MCP LSP Driver SDK
A TypeScript SDK that bridges Language Server Protocol (LSP) capabilities with the Model Context Protocol (MCP). Designed for IDE plugin developers building AI-assisted coding tools for VS Code, JetBrains, and other editors.
Table of Contents
- Core Philosophy
- Installation
- Quick Start
- MCP Tools
- MCP Resources
- Auto-Complete for File Paths
- Subscription and Change Notifications
- Symbol Resolution
- Merging Capabilities
- Pipe IPC (Out-of-Process)
- LSP Client (Built-in)
- Requirements
- License
Core Philosophy
- Fuzzy-to-Exact Resolution: LLMs interact via semantic anchors (
symbolName,lineHint), and the SDK resolves them to precise coordinates - Disk-Based Truth: All read operations reflect the state of files on disk, ignoring unsaved IDE buffers
- High Abstraction: Beyond LSP, it also provides functionality related to something like dual chains (graph capability) and metadata (frontmatter capability).
Installation
npm install mcp-lsp-driver
# or
pnpm add mcp-lsp-driver
Quick Start
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { installMcpLspDriver, type IdeCapabilities } from 'mcp-lsp-driver'
import * as fs from 'fs/promises'
// 1. Create your MCP server
const server = new McpServer({
name: 'my-ide-mcp-server',
version: '1.0.0'
})
// 2. Implement File Access (required)
const fileAccess = {
readFile: async (uri: string) => {
return await fs.readFile(uri, 'utf-8')
},
readDirectory: (uri: string) => yourIDE.workspace.readDirectory(uri)
}
// 3. Implement Edit Provider (required for edits)
const edit = {
// Option 1: Preview and apply with user approval
previewAndApplyEdits: async (operation) => {
// Show diff in your IDE and get user approval
return await showDiffDialog(operation)
},
// Option 2: Apply directly without preview (use one or both)
applyEdits: async (operation) => {
// Apply edits directly
return await yourIDE.applyEdits(operation)
}
}
// 4. Implement LSP Capability Providers
const definition = {
provideDefinition: async (uri, position) => {
// Call your IDE's LSP to get definition
return await lspClient.getDefinition(uri, position)
}
}
const diagnostics = {
provideDiagnostics: async (uri) => {
// Get diagnostics from your IDE for the file
return await lspClient.getDiagnostics(uri)
},
getWorkspaceDiagnostics: async () => {
// Optional: Get all diagnostics in the workspace
return await lspClient.getWorkspaceDiagnostics()
}
}
const outline = {
provideDocumentSymbols: async (uri) => {
// Get document symbols from your IDE
return await lspClient.getDocumentSymbols(uri)
}
}
// 5. Register LSP tools and resources on the server
const capabilities: IdeCapabilities = {
fileAccess,
edit,
definition,
diagnostics: {
...diagnostics,
onDiagnosticsChanged: (callback) => {
// Register for diagnostic changes
yourIDE.onDiagnosticsChanged((uri) => callback(uri))
},
},
outline,
filesystem,
// Add more capabilities as needed
}
installMcpLspDriver({ server, capabilities })
// 6. Connect to transport (you control the server lifecycle)
const transport = new StdioServerTransport()
await server.connect(transport)
MCP Tools
The SDK automatically registers tools based on which capabilities you provide:
goto_definition
Navigate to the definition of a symbol.
find_references
Find all references to a symbol.
call_hierarchy
Get call hierarchy for a function or method.
apply_edit
Apply a text edit to a file using hashline references (requires user approval).
The files:// resource returns file content in hashline format — each line is prefixed with <line>:<hash>|, where the hash is a 2-char CRC16 digest of the line's content. To edit a file, reference lines by these hashes. If the file has changed since the last read, the hashes won't match and the edit is rejected, preventing stale overwrites.
global_find
Search for text across the entire workspace.
get_link_structure
Get all links in the workspace, showing relationships between documents.
add_link
Add a link to a document by finding a text pattern and replacing it with a link.
get_frontmatter_structure
Get frontmatter property values across documents.
set_frontmatter
Set a frontmatter property on a document.
MCP Resources
The SDK automatically registers resources based on which capabilities you provide:
diagnostics://{path}
Get diagnostics (errors, warnings) for a specific file.
Resource URI Pattern: diagnostics://{+path}
Example: diagnostics://src/main.ts
Returns diagnostics formatted as markdown with location, severity, and message information.
Subscription Support: If your DiagnosticsProvider implements onDiagnosticsChanged, these resources become subscribable. When diagnostics change, the driver sends resource update notifications.
diagnostics://workspace
Get diagnostics across the entire workspace.
Resource URI: diagnostics://workspace
Only available if your DiagnosticsProvider implements the optional getWorkspaceDiagnostics() method.
Returns workspace diagnostics grouped by file, formatted as markdown.
Subscription Support: If your DiagnosticsProvider implements onDiagnosticsChanged, this resource becomes subscribable.
outline://{path}
Get the document outline (symbol tree) for a file.
Resource URI Pattern: outline://{+path}
Example: outline://src/components/Button.tsx
Returns document symbols formatted as a hierarchical markdown outline, including:
- Symbol names and kinds (class, function, method, etc.)
- Source locations
- Nested children (e.g., methods within classes)
No subscription support for this resource (read-only).
files://{path}
For directories: returns directory children (git-ignored files excluded, similar to ls). For files: returns content in hashline format with optional line range and regex filtering.
Hashline format: Each line is prefixed with <line>:<hash>|, where <line> is the 1-based line number and <hash> is a 2-char CRC16 hex digest of the line content. For example:
1:a3|function hello() {
2:f1| return "world"
3:0e|}
These hashes serve as content-addressed anchors for the apply_edit tool — if the file changes between read and edit, the hash mismatch is detected and the edit is safely rejected.
Resource URI Pattern: files://{+path}
Example: files://src, files://src/index.ts, files://src/index.ts#L1-L2, files://src/index.ts?pattern=^import, files://src/index.ts?pattern=TODO#L10-L50
No subscription support for this resource (read-only).
outlinks://{path}
Get outgoing links from a specific file.
Resource URI Pattern: outlinks://{+path}
Example: outlinks://notes/index.md
Returns a JSON array of links originating from the specified document.
No subscription support for this resource (read-only).
backlinks://{path}
Get incoming links (backlinks) to a specific file.
Resource URI Pattern: backlinks://{+path}
Example: backlinks://notes/topic-a.md
Returns a JSON array of links pointing to the specified document.
No subscription support for this resource (read-only).
frontmatter://{path}
Get frontmatter metadata for a specific file.
Resource URI Pattern: frontmatter://{+path}
Example: frontmatter://notes/index.md
Returns a JSON object containing all frontmatter properties and values for the document.
No subscription support for this resource (read-only).
Auto-Complete for File Paths
All resource templates with a {+path} variable (files://, diagnostics://, outline://, outlinks://, backlinks://, frontmatter://) support MCP auto-completion. When an MCP client calls completion/complete with a partial file path, the SDK uses readDirectory from your FileAccessProvider to suggest matching entries.
- Completion is case-insensitive and splits input into a directory and prefix (e.g.,
src/serreadssrc/and filters byser) - If
readDirectoryfails (e.g., the directory doesn't exist), an empty list is returned - Results are capped at 100 items by the MCP SDK
This works automatically — no additional configuration is needed.
Subscription and Change Notifications
Providers can implement optional onDiagnosticsChanged and onFileChanged callbacks to make resources subscribable:
const capabilities: IdeCapabilities = {
fileAccess: {
readFile: async (uri) => { /* ... */ },
readDirectory: async (path) => { /* ... */ },
onFileChanged: (callback) => {
// Register your IDE's file change listener
yourIDE.onFileChanged((uri) => callback(uri))
},
},
diagnostics: {
provideDiagnostics: async (uri) => { /* ... */ },
getWorkspaceDiagnostics: async () => { /* ... */ },
onDiagnosticsChanged: (callback) => {
// Register your IDE's diagnostic change listener
yourIDE.onDiagnosticsChanged((uri) => callback(uri))
},
},
}
When diagnostics or files change, call the registered callback with the affected file URI. The driver will send MCP resource update notifications to subscribers.
Symbol Resolution
The SDK uses a robust algorithm to handle imprecise LLM positioning:
- Target the
lineHint(converting 1-based to 0-based) - Search for
symbolNamein that line - Robustness Fallback: If not found, scan +/- 2 lines (configurable)
- Use `or
