Library / SDK
yioneko/vtsls avatar
yioneko/vtsls

vtsls exposes the TypeScript server VSCode already ships

LSP wrapper for typescript extension of vscode

1,099 stars27 forksTypeScriptNOASSERTION

At a glance

What is it?
vtsls is a language server that wraps the TypeScript extension bundled with VSCode, so editors outside VSCode can reuse Microsoft's own implementation instead of maintaining a separate one. It suits teams that want VSCode quality of TypeScript support in another client, and it accepts that the wrapped extension is the limit of what it can promise.
Who is it for?
vtsls fits a team that wants the TypeScript behaviour people already have in VSCode inside another editor, and that would rather patch a bundled extension than maintain a TypeScript server of its own. It does not fit a project that needs a server with a guaranteed support contract, because the project describes itself as a best-effort product rather than a reliable alternative to functionally identical tools.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A language server around the extension VSCode ships

vtsls is a language server protocol wrapper around the TypeScript extension that VSCode bundles. The project links the extension source in the VSCode repository and says plainly that all features and performance are nearly the same, which is the whole point of the design. Rather than writing a TypeScript language server, or forking the extension into a permanent divergence, this project takes the extension as it is and speaks LSP to it.

The audience is developers whose editor is not VSCode but who still want the TypeScript experience people have in it. Renaming, go to definition, refactors, code actions, project config navigation and the tsserver log all come from Microsoft's implementation rather than from a third party reimplementation, and the project is explicit that the feature set and the performance are close to the original.

The shape of the repository follows from that. It is a pnpm workspace with a packages directory, and three published packages are visible in the release list: server at v0.3.0, service at v0.3.0 and vscode-fuzzy at v0.1.0, all tagged on 2025-12-24. The vscode-fuzzy package is the counterpart to the completion filtering described later, and the root manifest shows the tooling around them, with release-please for versioning, Renovate for dependency updates, eslint and typescript-eslint for linting, and simple-git-hooks running lint-staged before a commit. A .gitmodules entry in the tree points at the upstream extension being wrapped, which is how the wrapper stays close to what VSCode ships.

The last push to the repository was on 2026-10-15 and the project is not archived, so the wrapper is being kept current rather than left at the December 2025 tags.

Minimal patches instead of a maintained fork

The design decision that distinguishes this project from similar ones is stated in the readme in one sentence: it is implemented by filling the VSCode APIs and applying minimal patches onto the extension, so that it can keep up with upstream updates and drastically reduce the maintenance burden.

That is a deliberate trade. The usual way to run a TypeScript server outside VSCode is to take the extension code and keep it compiling, which means resolving every upstream change by hand, forever. A wrapper that provides the VSCode API surface the extension expects can absorb most upstream updates without changes, because the extension mostly does not need to know it is being driven by something else. The cost is the other side of the same coin: the extension's assumptions about its host now have to be met by the wrapper, and any behaviour that depends on VSCode UI internals has to be reimplemented rather than inherited.

The readme is honest about the ceiling this creates. It says the server is suitable for regular use scenarios at the current stage, that there is no absolute guarantee for its behaviour, that the behaviour of the wrapped VSCode extension does not always stay clear to its author, and that users should view the server as a best-effort product, possibly not as reliable as functionally identical alternatives.

That sentence is the most important thing in the documentation. A wrapper inherits the upstream implementation's behaviour, including the parts nobody has fully explained, and the person maintaining the wrapper cannot promise what they have not fully understood. Anyone adopting vtsls on the strength of it being nearly identical to VSCode should read that notice as part of the feature description.

Installing the server and starting it over stdio

Usage is short because the transport is standard. The server is published to npm as @vtsls/language-server, and it is started with the stdio flag, which is what an LSP client that speaks over standard input and output expects:

bash
npm install -g @vtsls/language-server
vtsls --stdio

The readme states that node 16 or later is required. The workspace manifest in the repository declares a stricter floor, with engines set to node 18 or later, and the development toolchain pins pnpm 10.34.5 as the package manager. If you are on node 16, the published package may still run, but the repository's own build expectations are higher, and that is worth knowing before you file a problem against it.

A client that wants a different TypeScript version or a different set of settings can read the configuration schema the project publishes at packages/service/configuration.schema.json. The settings are almost the same as the original VSCode extension, with a few additional ones excluded for this server, and everything specific to vtsls is prefixed with vtsls.

For a client that cannot handle the extra commands the server expects, the readme points at a known issues page as the first place to look, which is a reasonable first stop before debugging configuration.

The bundled TypeScript version wins unless you change it

The server ships with its own TypeScript, in the same way VSCode does, and the bundled version is the default. The readme describes the two ways to override that. You can run the command typescript.selectTypeScriptVersion, which is the same command VSCode exposes, or you can set the configuration option vtsls.autoUseWorkspaceTsdk to true, which makes the server pick up the TypeScript installed in the workspace. To ignore the bundled version entirely, set vtsls.typescript.globalTsdk to the path of the alternative.

The distinction between the two matters when you are debugging. The selectTypeScriptVersion command is a one-off choice recorded in the client, the way it is inside VSCode, while autoUseWorkspaceTsdk is a policy the server applies on its own. The globalTsdk option is the blunt instrument, pointing at a specific installation and ignoring everything else.

Since the server follows VSCode's behaviour, the practical advice is to think in VSCode terms. If a problem is fixed by changing the TypeScript version in VSCode, the same switch is available here, and if it is fixed by a tsconfig or a plugin setting, that setting exists too under the typescript. prefix, with the vtsls. prefix reserved for the handful of options the server adds.

Commands the client has to run for you

This is where a wrapper differs from the real thing, and the readme documents it in detail. Some LSP features need the client to execute a command that the server proposes, and a client that does not implement those commands will simply not have the feature.

Code lens is the example. The command editor.action.showReferences has to be executed by the client, with arguments that follow the shape of the LSP positions and locations:

text
[uri: DocumentUri, codeLensStart: Position, locations: Location[]]

The list of server commands includes the ones you would expect from the VSCode extension, such as typescript.restartTsServer, typescript.reloadProjects and typescript.selectTypeScriptVersion, plus the generic escape hatches typescript.tsserverRequest, which takes a request type and arguments, and _typescript.configurePlugin. The import maintenance commands are there too, including typescript.organizeImports, typescript.sortImports and typescript.removeUnusedImports, and the JavaScript equivalents of the last two.

The code action list is the VSCode one, with the readme warning that it may be outdated. Source actions include source.organizeImports, source.sortImports, source.fixAll.ts and source.addMissingImports.ts, and the refactor group includes the extract family, the move family and the rewrite family. One refactor is disabled on purpose. The move to file action is off by default because it needs special handling on the client, and enabling it with vtsls.enableMoveToFileCodeAction exposes it with a _typescript.moveToFileRefactoring command whose arguments start as an action, a uri and a range. The client is expected to ask the user for a target file, append that path, and then send the workspace/executeCommand request with the modified arguments.

There is a similar requirement for renames. The client has to send the workspace/didRenameFiles notification when a file or folder is renamed, otherwise the server cannot update the paths it holds.

Memory limits and slow completion are the two real failures

The troubleshooting section names two failure modes, and both are properties of tsserver rather than of the wrapper.

The first is a server that is stuck or crashed on a large repository. The readme suggests disabling any TypeScript plugins first, since plugins can be specified in tsconfig.json or in the vtsls.tsserver.globalPlugins setting, and then raising the memory ceiling. The setting typescript.tsserver.maxTsServerMemory is passed to the tsserver node process as the --max-old-space-size argument, VSCode defaults it to 3072, and 8192 is the value the readme suggests for large projects. The explanation is that tsserver memory grows with the scale of the project, and that frequent garbage collection in a small heap can leave the server stuck or killed by an allocation failure.

The second is sluggish completion. tsserver can return a large number of candidates, most of them globally accessible variables, modules or namespaces, and a client with poor fuzzy matching performance can take a noticeable time filtering them. The server side options can take the edge off that. vtsls.experimental.completion.enableServerSideFuzzyMatch performs the fuzzy matching on the server and drops entries with no match before returning them, and vtsls.experimental.completion.entriesLimit caps how many completion entries are returned. The readme also lists typescript.preferences.includePackageJsonAutoImports set to off, and typescript.preferences.autoImportFileExcludePatterns, as further ways to reduce the candidate set.

The existence of the server side fuzzy matching package explains the shape of these options. The matching that VSCode does in the client has to happen somewhere, and for a non VSCode client the server is the only place to do it.

Where a client that speaks tsserver directly fits better

The alternative is not another LSP wrapper, it is the arrangement the wrapper is imitating. A client that talks to the TypeScript server directly, or simply running VSCode, gets the same features without a translation layer, and it gets the client side commands, the code lens handling, the move to file flow and the file rename notification for free, because the host and the extension are designed together.

The difference in approach is the wrapper against the native thing. The wrapper exists so that other editors can have the VSCode TypeScript behaviour without embedding VSCode, and it pays for that with the client obligations described above and with the best-effort caveat in its own documentation. The native path has no such obligations and no such caveat, at the cost of being tied to one host.

On the practical side, there is a version question in every case. The server bundles its own TypeScript and defaults to it, so a client that wants the workspace version has to ask for it, and a client that wants the latest TypeScript has to keep the wrapper current, which is exactly what the release list and the Renovate configuration are for.

Licensing is worth a note, because it is unusual. The licence for this repository is not one GitHub can classify, and the project says to see the LICENSE file for the actual terms. A team embedding the server in a product, as opposed to running it on a developer machine, should read that file before shipping.

Editorial conclusion

vtsls fits a team that wants the TypeScript behaviour people already have in VSCode inside another editor, and that would rather patch a bundled extension than maintain a TypeScript server of its own. It does not fit a project that needs a server with a guaranteed support contract, because the project describes itself as a best-effort product rather than a reliable alternative to functionally identical tools. Verify first which client commands your editor can execute, since code lens and the move to file refactor depend on client side handling, and check the node version, because the readme and the workspace manifest disagree about the floor.

Frequently asked questions

How do I install and run the vtsls language server?

Install the published package with npm install -g @vtsls/language-server and start the server with vtsls --stdio, which is how an LSP client that speaks over standard input and output connects. The readme states that node 16 or later is required.

Does vtsls use the TypeScript version from my workspace?

Not by default. The server bundles the latest TypeScript and uses it, and you switch with the typescript.selectTypeScriptVersion command or by setting vtsls.autoUseWorkspaceTsdk to true. To ignore the bundled version, set vtsls.typescript.globalTsdk to the alternative path.

Why does vtsls crash on a large repository?

The memory used by tsserver grows with the size of the project, and VSCode's default limit of 3072 may be too small. The readme suggests disabling TypeScript plugins and raising typescript.tsserver.maxTsServerMemory to 8192, which is passed to tsserver as the --max-old-space-size argument.

How can I make code completion faster in vtsls?

Enable vtsls.experimental.completion.enableServerSideFuzzyMatch so the server filters candidates before returning them, and cap the response with vtsls.experimental.completion.entriesLimit. Turning off typescript.preferences.includePackageJsonAutoImports and using typescript.preferences.autoImportFileExcludePatterns also shrinks the candidate set.

Official sources

  1. Issues
  2. README
  3. Releases
  4. yioneko/vtsls on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/yioneko-vtsls.svg)](https://hysenlabs.com/projects/yioneko-vtsls)

Was this useful?