How MCP Language Servers Work with the Language Server Protocol
Learn how MCP and LSP differ, how an MCP-to-LSP adapter works, and how to build, configure, debug, and secure one.
Direct answer: the Language Server Protocol (LSP) standardizes communication between an editor or IDE and a language server. The Model Context Protocol (MCP) standardizes communication between an AI application and servers that expose tools, resources, and prompts. They are different protocol boundaries. An MCP server can wrap a language server and translate selected MCP tool calls into LSP requests, but neither specification requires a universal MCP-to-LSP bridge.
LSP lets one language server provide features such as auto complete, go to definition, find all references, and documentation on hover to many development tools. MCP lets an AI host discover capabilities and invoke tools or attach resources in an AI workflow. Both use JSON-RPC-style messages, but their clients, methods, capability negotiation, and intended users differ.
Architecture: two connections, one optional adapter
Microsoft describes LSP as standardizing how language servers and development tools communicate. MCP’s architecture separates a JSON-RPC data layer from a transport layer and defines an AI host, clients, and servers. A bridge is an implementation pattern that connects those worlds.
AI host / model
│ MCP client: discovery, tool calls, resources, prompts
▼
MCP server or adapter
│ LSP client: editor-style language requests (optional)
▼
Language server
│
└── source workspace / language-specific analysis
Editor or IDE ───────────── LSP ─────────────► Language server
The upper path is optional. An MCP server may call a database, API, or filesystem without using LSP. The lower path is LSP’s documented editor-to-language-server relationship. Read the MCP architecture documentation and the official LSP overview for the protocol roles.
MCP and LSP compared
| Question | MCP | LSP |
|---|---|---|
| Who connects? | AI application or host to an MCP server | Editor or IDE to a language server |
| Primary purpose | Expose tools, resources, and prompts to AI workflows | Expose language intelligence to development tools |
| Typical operation | Discover a tool, then call it with structured arguments | Request completion, definition, references, hover information, and related features |
| Message foundation | JSON-RPC data layer plus a transport | JSON-RPC messages between the development tool and server |
| Replacement? | No; it solves a different integration boundary | No; an implementation may bridge selected capabilities |
MCP servers can provide resources (read-only context), prompts (reusable templates), and tools (actions such as file operations or external API calls). Client support and presentation vary by product. See the MCP base protocol and VS Code’s MCP guide.
What happens during an MCP-to-LSP tool call
- The AI host launches or connects to an MCP server using a supported transport.
- The MCP client discovers capabilities, including tools, with
tools/list. - The model selects a tool and the host sends a JSON-RPC request with its arguments.
- The MCP server validates the input schema and runs the handler.
- If the server is an LSP adapter, the handler opens or reuses a language-server session, translates the operation into an LSP request, and maps the result back to MCP content.
- The host gives the result to the model or attaches it as context.
Steps 4 and 5 are implementation details. MCP and LSP do not define a mandatory method mapping, supported language set, workspace policy, or write behavior. Document those choices for your adapter.
JSON-RPC is shared, semantics are not
LSP and MCP both use JSON-RPC message structures, so an adapter can reuse framing and request concepts. A matching JSON shape does not make methods interchangeable. For example, an LSP completion request carries a text-document position; an MCP tool call carries a tool name and arguments. Validate each protocol independently.
// Conceptual MCP request (the exact tool name is implementation-defined)
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "find_definition",
"arguments": {
"uri": "file:///workspace/src/app.ts",
"line": 12,
"character": 18
}
}
}
Build a minimal MCP server in TypeScript
The official TypeScript SDK v2 demonstrates McpServer, registered tools with input schemas, and serveStdio. This example exposes a safe, read-only definition lookup placeholder. A production adapter would start an LSP client, send textDocument/definition, and translate the response.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "lsp-adapter", version: "1.0.0" });
server.registerTool(
"find_definition",
{
description: "Find a symbol definition through the configured language server",
inputSchema: {
uri: z.string().url(),
line: z.number().int().nonnegative(),
character: z.number().int().nonnegative()
}
},
async ({ uri, line, character }) => {
// Replace this with an LSP client request:
// textDocument/definition at { uri, line, character }
return {
content: [{
type: "text",
text: JSON.stringify({ uri, line, character, locations: [] })
}]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Install the SDK and run this with a current Node.js runtime according to the MCP TypeScript SDK v2 documentation. The schema prevents malformed arguments before the handler runs. Keep the adapter’s filesystem root, language-server command, and permitted methods explicit.
Connecting the LSP side
An adapter normally performs these operations:
- Start the language-server process or connect to one.
- Send the LSP
initializerequest with workspace and client capabilities. - Send
initialized, then synchronize documents withtextDocument/didOpen,didChange, anddidCloseas needed. - Translate an MCP tool’s arguments into an LSP request such as
textDocument/completion,textDocument/definition,textDocument/references, ortextDocument/hover. - Normalize locations, markdown, diagnostics, and errors into stable MCP content.
- Shut down the language server cleanly when the MCP session ends.
The exact initialization options, document synchronization model, URI rules, and supported methods depend on the language server. LSP version 3.18 is identified as current on Microsoft’s page; verify the version before publishing because protocol support changes.
Transport and state
MCP separates its data layer from transport. Clients may use a local command process or a remote HTTP server, depending on the host. A long-running stdio process or HTTP connection is not the same thing as conversation state: protocol requests carry the information needed to process them, while an implementation may maintain caches, open documents, or language-server processes internally.
For VS Code, local servers are configured with a command and arguments; remote servers use an HTTP URL. Workspace configuration can travel with a project, while user configuration applies across workspaces. Other MCP hosts use different files and transports, so follow their documentation.
Configuration checklist
- Choose the language-server executable and pin its version.
- Set an allowed workspace root and reject paths outside it.
- Map file URIs consistently, including Windows paths when supported.
- Declare only the MCP tools you implement.
- Define request timeouts and cancellation behavior.
- Decide whether the adapter is read-only; require explicit approval for edits.
- Limit document size, result count, and recursion depth.
- Record protocol errors without logging secrets or source contents unnecessarily.
- Advertise capabilities accurately during initialization.
Security and trust
A local MCP server can execute arbitrary code on the machine. Review the publisher, command, arguments, environment variables, and workspace permissions before enabling one. VS Code documents sandboxing for local stdio servers on macOS and Linux with configured filesystem and network access; its guide says this sandboxing is unavailable on Windows. These are VS Code-specific behaviors, documented on its page dated 2026-09-16.
For an adapter, apply least privilege: allow only required directories, avoid passing untrusted model text to shells, validate every URI and position, cap output sizes, and redact credentials from logs. Treat language-server extensions and package installation as part of the trust boundary.
Reliability and performance
- Process reuse: keeping one language-server process per workspace avoids startup cost, but recycle it after crashes or unrecoverable state.
- Warm-up: initialize and index before accepting expensive requests; report a clear “not ready” result rather than returning partial data silently.
- Concurrency: serialize operations when a language server requires ordered document updates. Use request IDs and cancellation for overlapping model calls.
- Caching: cache immutable results only when document versions, configuration, and workspace revision are part of the cache key.
- Bounds: cap files scanned, references returned, response bytes, and per-call duration.
- Failures: distinguish invalid MCP arguments, unavailable language servers, parse errors, timeouts, and permission failures so clients can recover correctly.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Tool does not appear | Server failed during startup or capability discovery | Inspect host server output, validate JSON configuration, and confirm the tool is registered. |
| “Method not found” | The adapter sent an unsupported MCP or LSP method | Check negotiated capabilities and the language server’s documented methods. |
| Empty definitions or completions | Document was never opened, URI mapping is wrong, or indexing is incomplete | Send initialization and document-sync notifications, normalize the URI, and wait for readiness. |
| Requests hang | No timeout, dead language-server process, or blocked workspace scan | Add cancellation and deadlines, monitor child-process health, and cap scan scope. |
| Results refer to the wrong file | Line/character encoding or zero-based indexing mismatch | Use the LSP position convention and test UTF-16 positions with non-ASCII text. |
| Server can read too much | Workspace root and filesystem permissions are unrestricted | Apply an allowlist, reject traversal, and use host sandbox controls where available. |
| VS Code cannot launch it | Command, working directory, or environment differs from a shell test | Use absolute paths, inspect the configured environment, and view the server output from VS Code. |
Testing an adapter
- Unit-test argument schemas and URI/path validation.
- Use a fixture workspace with known definitions, references, diagnostics, and Unicode identifiers.
- Test initialization, document changes, cancellation, malformed requests, and server crashes.
- Run the same fixture through the editor’s LSP client and the MCP tool, then compare normalized results.
- Test concurrent calls and stale document versions.
Or skip the browser setup
If your agent also needs website screenshots for documentation or visual checks, ScreenshotNeo provides a single website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the verdict and billing result. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for the 63 capture options, authentication, and MCP setup. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Is every MCP server a language server?
No. MCP servers can expose tools, resources, and prompts for many systems. Only an adapter that deliberately connects to an LSP server provides language-server functionality.
Does LSP define AI tools?
No. LSP defines editor-to-language-server communication. An MCP adapter chooses how to expose selected LSP operations as AI tools.
Can an adapter support multiple languages?
Yes, if it can select and manage the appropriate language server for each workspace or document. The supported languages and routing rules are implementation-specific.
Should an adapter allow edits?
Start read-only. If you add workspace edits, expose separate tools, validate ranges, show diffs, and require explicit host approval.
Which protocol version should I target?
Pin the MCP and LSP revisions your client and server support, document them, and recheck the official specifications before release.


