Developer tooling

Type Server Protocol (TSP): how Pylance can now use Pyrefly instead of Pyright, with a working client

What Microsoft's Type Server Protocol is, a working TSP client, and how to make Pylance use Pyrefly instead of Pyright in VS Code.

Kemo
10 min read
A black two-pin plug held just in front of a white wall socket, about to be plugged in.
On this page
  1. What is the Type Server Protocol (TSP)?
  2. TSP requests and the snapshot model
  3. Talk to a type server yourself: a working TSP client
  4. How to use Pyrefly as Pylance's type checker in VS Code
  5. Is Pyrefly faster than Pyright?
  6. Who supports TSP, and why basedpyright refuses
  7. What to do
  8. Questions people ask about the Type Server Protocol
  9. Sources and further reading

VS Code's Python extension no longer has to use Microsoft's own type checker. On 21 October 2026 Microsoft announced the Type Server Protocol (TSP), a JSON-RPC protocol that lets Pylance hand type analysis to a separate type server, and Meta's Pyrefly is the first alternative you can switch to. Pyright stays the default; Pylance keeps completions, Quick Info, environment management and the GitHub Copilot integration on top of whichever checker you choose.

This article explains what TSP is and how it differs from the Language Server Protocol, walks through a real TSP session against Pyright's type server, shows how to switch Pylance to Pyrefly, and covers who supports it, who refuses to, and why. It is for Python developers using VS Code, and for anyone building Python tooling who wants structured type information rather than hover text.

WhatJSON-RPC protocol for querying Python type-analysis state; methods prefixed typeServer/ (spec)
Current version0.4.1 (0.x: a minor version may break compatibility)
NeedsPylance 2026.4.1 or newer; Pyrefly 0.60.0 or newer to use Pyrefly
Type servers todayPyright (default), Pyrefly; a ty implementation is proposed but not merged
Announced21 October 2026 by Microsoft (Brett Cannon) and the Pyrefly team
The Type Server Protocol at a glance, from the spec and the announcements

What is the Type Server Protocol (TSP)?

TSP is a protocol for asking a type server about types: what a variable's inferred type is, what was declared, what the context expects, and where an import resolves. The spec describes a server that "maintains the type-analysis state for a workspace" and answers requests for the protocol version, snapshots, Python search paths, import resolution and type queries.

It reuses LSP rather than replacing it. Initialisation goes through the normal LSP initialize and initialized messages, documents are synchronised with textDocument/didOpen and didChange, and positions and ranges use LSP's shapes: zero-based lines and UTF-16 code-unit offsets. What TSP adds is a set of typeServer/* requests whose answers are structured type objects, not the Markdown strings an LSP hover returns.

Interchangeable type serversVS CodePylancecompletions, Quick Info, CopilotPyrightdefault type serverPyrefly0.60.0+typroposed, not mergedLSPTSPTSPTSP (prototype)
  • VS Code → Pylance: LSP
  • Pylance → Pyright: TSP
  • Pylance → Pyrefly: TSP
  • Pylance → ty: TSP (prototype)
Pylance becomes the editor layer; the type checker underneath speaks TSP and can be swapped.

Microsoft's framing is that Pylance and Pyright "were previously tightly coupled", and TSP separates them so Pylance is the VS Code intelligence layer and the checker "becomes an interchangeable component underneath it" (Microsoft).

TSP requests and the snapshot model

There are nine messages in version 0.4.1. Every type query names a construct with an arg that is either a Node (a URI plus a source range) or a Declaration, and carries the snapshot it was computed against.

MessageDirectionReturns
typeServer/getSupportedProtocolVersionclient → serversemver string
typeServer/getSnapshotclient → serversnapshot number
typeServer/snapshotChangedserver → clientnotification { old, new }
typeServer/getComputedTypeclient → serverType inferred from code flow
typeServer/getDeclaredTypeclient → serverType written in the source
typeServer/getExpectedTypeclient → serverType the context expects
typeServer/resolveImportclient → serverdocument URI or nothing
typeServer/getPythonSearchPathsclient → serverlist of paths
typeServer/connectionclient → serveropens or closes an extra read-only IPC connection
Type Server Protocol 0.4.1 messages, from the spec

Snapshots are what make it safe. A snapshot is a non-negative integer that never decreases; type handles are only valid for the snapshot that produced them. Edit a file, change configuration or touch a watched file, and the server bumps the snapshot and sends snapshotChanged. A request that still carries the old number fails with ServerCancelled (-32802), and the client retries with a fresh one. Results are tagged objects with a numeric kind: 3 is a class, 4 a union, 2 a function, 9 a reference to another type by id.

Talk to a type server yourself: a working TSP client

Pyright's type server is on npm as pyright-typeserver and runs over stdio; basedpyright's docs describe it as started by a client, not run interactively. This 60-line client speaks LSP framing over the server's stdin and stdout, opens one file, asks for the computed type of value, then edits the file and replays the old snapshot to show the stale-snapshot error.

Project layout
  • tsp/
    • tsp_client.pythe client below
    • demo/
      • app.pythe file we ask about
    • node_modules/
      • .bin/
        • pyright-typeservernpm install pyright-typeserver
tsp_client.pyPython
"""Minimal Type Server Protocol client: ask a type server what `value` is."""import json, pathlib, subprocess, sys ROOT = pathlib.Path(sys.argv[1]).resolve()FILE = ROOT / "app.py"SERVER = sys.argv[2:] or ["npx", "pyright-typeserver", "--stdio"] proc = subprocess.Popen(SERVER, stdin=subprocess.PIPE, stdout=subprocess.PIPE)next_id = 0 def send(msg):    body = json.dumps({"jsonrpc": "2.0", **msg}).encode()    proc.stdin.write(b"Content-Length: %d\r\n\r\n" % len(body) + body)    proc.stdin.flush() def read():    length = 0    while (line := proc.stdout.readline().strip()):        if line.lower().startswith(b"content-length:"):            length = int(line.split(b":")[1])    return json.loads(proc.stdout.read(length)) def request(method, params=None):    global next_id    next_id += 1    send({"id": next_id, "method": method, "params": params})    while True:        msg = read()        if msg.get("id") == next_id and "method" not in msg:            return msg        if msg.get("method") == "typeServer/snapshotChanged":            print("<- snapshotChanged", msg["params"])        elif "id" in msg and "method" in msg:  # server-to-client request            send({"id": msg["id"], "result": None}) def notify(method, params):    send({"method": method, "params": params}) uri = FILE.as_uri()request("initialize", {"processId": None, "rootUri": ROOT.as_uri(), "capabilities": {},                       "workspaceFolders": [{"uri": ROOT.as_uri(), "name": "demo"}]})notify("initialized", {})notify("textDocument/didOpen", {"textDocument": {    "uri": uri, "languageId": "python", "version": 1, "text": FILE.read_text()}}) print("protocol", request("typeServer/getSupportedProtocolVersion")["result"])snap = request("typeServer/getSnapshot")["result"]print("snapshot", snap) # `value` on line 6 (zero-based 5), characters 0-5node = {"uri": uri, "range": {"start": {"line": 5, "character": 0},                              "end": {"line": 5, "character": 5}}}t = request("typeServer/getComputedType", {"arg": node, "snapshot": snap})["result"]print("computed type of value:", json.dumps(t)[:300]) # Edit the file: the old snapshot is now stale.notify("textDocument/didChange", {"textDocument": {"uri": uri, "version": 2},       "contentChanges": [{"text": FILE.read_text() + "\nother = 1\n"}]})stale = request("typeServer/getComputedType", {"arg": node, "snapshot": snap})print("stale request:", stale.get("error"))proc.terminate()

Here is a real run on 28 October 2026 with Node 22.23.4 and pyright-typeserver 1.1.414 on Linux. The only edit is the long typeshed path, shortened with …; the type is cut at 300 characters by the client itself.

A TSP session against pyright-typeserverShell
$ npm install pyright-typeserver@1.1.414added 1 package in 3s$ cat demo/app.pydef parse(raw: str | None) -> int | str:    if raw is None:        return 0    return int(raw) if raw.isdigit() else rawvalue = parse("42")$ python3 tsp_client.py demo node_modules/.bin/pyright-typeserver --stdio<- snapshotChanged {'old': 0, 'new': 1}<- snapshotChanged {'old': 1, 'new': 2}<- snapshotChanged {'old': 2, 'new': 3}<- snapshotChanged {'old': 3, 'new': 4}<- snapshotChanged {'old': 4, 'new': 5}protocol 0.4.1snapshot 5computed type of value: {"id": 0, "kind": 4, "flags": 2, "subTypes": [{"id": 1, "kind": 3, "flags": 2, "declaration": {"kind": 0, "category": 6, "node": {"uri": "file:///…/typeshed-fallback/stdlib/builtins.pyi",<- snapshotChanged {'old': 5, 'new': 6}<- snapshotChanged {'old': 6, 'new': 7}stale request: {'code': -32802, 'message': 'server cancelled'}

Three things show up. The server bumped the snapshot five times while it loaded the workspace, so a client should always call getSnapshot after start-up rather than assume 0. The answer for value is a union (kind 4) whose members are classes (kind 3) declared in typeshed's builtins.pyi, which is int | str in machine-readable form. And after the edit, the old snapshot was refused with -32802 exactly as the spec says.

How to use Pyrefly as Pylance's type checker in VS Code

You need Pylance 2026.4.1 or newer and Pyrefly 0.60.0 or newer. Pylance can detect Pyrefly on its own: when a workspace's environment has it installed, or has a pyrefly.toml or a [tool.pyrefly] section in pyproject.toml, it prompts you to switch (Pyrefly).

  1. Install Pyrefly into the project environment

    Install PyreflyShell
    python -m pip install "pyrefly>=0.60.0"
  2. Accept the prompt, or pick the diagnostics source yourself

    When Pylance asks "Pyrefly was found in your Python environment. Would you like to use it as your source of diagnostics in Visual Studio Code?", choose Switch to Pyrefly. To do it by hand, open Settings and set Python › Analysis: Diagnostics Source to Pylance + Pyrefly. Python › Analysis: Pyrefly Version pins a version, points at a local executable, or stays empty to track the latest release.

  3. Check the status bar

    The status bar shows the active diagnostics source next to the interpreter. Click it to switch between Pylance, Pylance + Pyright and Pylance + Pyrefly.

Should you? Pyrefly's own post recommends its standalone extension for most people, since it gives diagnostics, completions, go-to-definition and refactoring straight from Pyrefly's language server. The Pylance route makes sense if your team standardises on Pylance, or you depend on the Copilot integration, which Microsoft says gets type context, code understanding for Copilot Chat and MCP tools for Python codebases through Pylance (Microsoft).

Is Pyrefly faster than Pyright?

On Meta's own benchmark, by a wide margin on large projects. The figures below are from the Pyrefly team's April 2026 comparison: full checks with no cache, five runs after a warm-up, on a MacBook M4 with 64 GB RAM, using Pyrefly 0.60.0, ty 0.0.29, Pyright 1.1.408 and mypy 1.19.1 (mypy in strict mode). It is a vendor benchmark, so treat it as a starting point and time your own codebase.

Seconds to type-check a whole project, no cacheLower is better. Pyrefly team's benchmark, MacBook M4.
PyrightPyreflytymypy
0 s50 s100 s150 spandasnumpysympysqlalchemyflask

Source: [Pyrefly: speed and memory comparison](https://pyrefly.org/blog/speed-and-memory-comparison/), 10 April 2026

Seconds to type-check a whole project, no cache
PyrightPyreflytymypy
pandas144.4 s1.9 s1.5 s13.9 s
numpy70.9 s4.8 s30.6 s8 s
sympy79.9 s4 s1.6 s15.5 s
sqlalchemy8.1 s0.5 s0.6 s4.5 s
flask1.2 s0.2 s0.2 s2 s

Whole-project time matters most in CI and on first open. In the editor, the checker works incrementally, so the gap you feel day to day will be smaller; the benchmark does not measure it. On memory, the post gives only one direct comparison: numpy took over 3 GB with Pyright and 1 GB with Pyrefly.

Who supports TSP, and why basedpyright refuses

CheckerTSP statusSource
PyrightDefault type server; ships pyright-typeserverMicrosoft
PyreflySupported from 0.60.0, co-designed with MicrosoftPyrefly
ty (Astral)Prototype by the Pylance team; Astral will accept it after clean-up, not yet merged at last updateruff discussion #23432
basedpyrightWill not support itbasedpyright docs
mypyNot mentioned by any of the announcements—
Type Server Protocol support among Python type checkers, as of late October 2026

Astral's position is lukewarm but open. In March, Micha Reiser wrote that he did not "see a reason not to support TSP", but that "shipping a stable version of ty remains our top priority" and asked the Pylance team to own the interface (discussion). One open problem is that ty commonly infers intersection types, and TSP has no standard representation for them yet. The Pylance team's prototype benchmarks, where ty was faster in 21 of 28 comparisons, are their figures, not Astral's.

basedpyright, the community fork of Pyright, is the loudest critic. Its docs say TSP "benefits nobody except microsoft", "adds needless complexity to any language server that chooses to support it", and call it "a desperate attempt from microsoft to prevent pylance from becoming obsolete" (basedpyright). The argument is that other checkers do the hard work while the features users see, such as Copilot integration, stay in closed-source, VS Code-only Pylance. On Hacker News the same question came up repeatedly: why a separate protocol rather than a new LSP capability?

The technical answer from the spec is that LSP returns presentation text, while TSP returns types a program can inspect, versioned by snapshot. That is useful outside VS Code too: stub generators, refactoring tools and coding agents all want int | str as data rather than as a hover string. Whether that becomes an open standard or stays a Pylance plug-in interface depends on whether anyone other than Microsoft and Meta implements both sides.

What to do

  • To do: On VS Code with Pylance: update to 2026.4.1+, install Pyrefly 0.60.0+ in one project and compare diagnostics with Pyright before switching a team.
  • To do: Pin python.analysis.pyreflyVersion once you switch, so diagnostics do not change under you with each Pyrefly release.
  • To do: Keep CI on the standalone checker you chose, so the editor and the pipeline report the same errors.
  • To do: Building tooling? Try pyright-typeserver --stdio with the client above before scraping hover text from LSP.
  • To do: On Neovim, Zed or basedpyright: nothing changes for you; TSP is consumed by Pylance today.

Questions people ask about the Type Server Protocol

What is the Type Server Protocol?

TSP is a JSON-RPC protocol, built on LSP conventions, for asking a Python type server for structured type information: computed, declared and expected types, import resolution and search paths. Microsoft and Meta announced it on 21 October 2026.

Is TSP a replacement for LSP?

No. It initialises and synchronises documents through normal LSP messages and adds typeServer/* requests on the same connection. LSP still drives the editor; TSP answers type questions with data rather than display text.

How do I switch Pylance to Pyrefly?

Install Pyrefly 0.60.0 or newer in your environment, update Pylance to 2026.4.1 or newer, and accept the Switch to Pyrefly prompt, or set Python › Analysis: Diagnostics Source to Pylance + Pyrefly in Settings.

Does Pylance still work the same with Pyrefly?

Completions, Quick Info, environment management and Copilot integration stay in Pylance. Diagnostics, the errors and warnings you see, come from the type server you selected.

Does ty support TSP?

Not in a release yet. The Pylance team built a prototype for ty, and Astral said in March 2026 it would accept it after clean-up, but stable ty comes first and intersection types have no TSP representation yet.

Is Pyrefly faster than Pyright?

On the Pyrefly team's own benchmark, full checks were far faster, for example 1.9 s against 144.4 s on pandas. It is a vendor benchmark of whole-project checks, so measure your own codebase.

Can I use TSP outside VS Code?

Yes. pyright-typeserver is on npm and speaks TSP over stdio, so any tool can start it and send requests. Today Pylance is the only editor client the announcements mention.

Sources and further reading

Discussion

Join the discussion

Sign in with Google, Apple or an email code to comment and reply.

One practical article a week

Follow k13 in any RSS reader. No account needed.