Type Server Protocol (TSP)
Het protocol maakt gebruik van Language Server Protocol (LSP) datastructuren en conventies waar deze nuttig zijn, zoals Position en Range, en representeert URI's als strings op dezelfde manier als LSP. TSP-methoden maken gebruik van het prefix typeServer/.
De officiële definities van het protocol bevinden zich in de volgende bestanden:
typeServerProtocol.ts: Definieert de TypeScript-interfaces en request-typen.tsp.json: Het machineleesbare protocolmodel.tsp.schema.json: Het JSON-schema voor het protocolmodel.
Versiegeschiedenis
TSP maakt gebruik van semantische versie-strings voor compatibiliteitscontroles tussen client en server. Zolang het protocol zich in de 0.x-lijn bevindt, kunnen wijzigingen in de minor-versie breaking changes bevatten. Wijzigingen in de patch-versie blijven achterwaarts compatibel binnen dezelfde minor-lijn.
| Versie | Wijzigingen |
|---|---|
| 0.1.0 | Initiële protocolversie. |
| 0.2.0 | Request-typen en velden toegevoegd. |
| 0.3.0 | Overgeschakeld naar complexere type-vormen. |
| 0.4.0 | Overgeschakeld naar de Type union en stub-gebaseerde type-payloads. |
| 0.4.1 | Multi-connection onderhandeling en het typeServer/connection controle-verzoek toegevoegd. |
Connectiemodel
Een client start een type server en communiceert hiermee via de hoofdverbinding voor JSON-RPC, gebruikmakend van een transportmethode die door de server wordt ondersteund, zoals stdio of Node IPC. Bij gebruik van stdio moet stdout gereserveerd blijven voor JSON-RPC-protocolberichten.
Van een type server wordt verwacht dat deze voldoende workspace- en documentstatus ontvangt om de ondersteunde TSP-verzoeken te kunnen beantwoorden. Clients maken gewoonlijk gebruik van de standaard LSP-initialisatie en documentsynchronisatie voor deze status, en verzenden vervolgens typeServer/* verzoeken via dezelfde JSON-RPC-verbinding.
TSP ondersteunt daarnaast optionele extra read-only verbindingen. De oorspronkelijke verbinding blijft de hoofdverbinding en is verantwoordelijk voor initialisatie, workspace-status, documentsynchronisatie, statuswijzigende meldingen en verbindingscontrole. Extra verbindingen worden alleen geopend nadat de client en server tijdens de initialisatie ondersteuning hiervoor hebben onderhandeld.
Opstartvolgorde
Een typische sessie volgt deze volgorde:
- De client start de type server of maakt verbinding ermee.
- De client en server initialiseren gedeelde workspace-status, gewoonlijk via de LSP
initializeeninitializedberichten. - De client roept
typeServer/getSupportedProtocolVersionaan en controleert of de geretourneerde semver-string compatibel is met de verwachte protocolversie. - De client roept
typeServer/getSnapshotaan om de eerste snapshot-identificatie te verkrijgen. - De client verzendt type-query, import-resolutie of zoekpad-verzoeken, inclusief de huidige snapshot wanneer dit vereist is door de request-structuur.
- De server verzendt
typeServer/snapshotChangedwanneer eerdere type-resultaten niet langer geldig zijn.
Multi-Connection Mode
De multi-connection mode is een optionele prestatiefunctie. Hiermee kan een client extra lokale kanalen openen naar dezelfde type server voor read-only TSP-verzoeken, terwijl de hoofdverbinding de mutabele workspace-status blijft beheren.
Clients dienen de multi-connection mode alleen te gebruiken wanneer zij kunnen profiteren van gelijktijdige read-only taken, zoals een groot volume aan type-query's of achtergrondprocessen die de hoofdverbinding voor JSON-RPC niet mogen blokkeren. Een server moet nog steeds correct werken in single-connection mode; ondersteuning voor multi-connection mag geen vereiste zijn voor de correctheid van het protocol.
Capability Handshake
Ondersteuning voor multi-connection wordt onderhandeld via het standaard LSP initialize verzoek en antwoord. Een client die extra TSP-kanalen kan openen, adverteert de ondersteunde transporttypen onder capabilities.experimental.typeServerMultiConnection:
{
"capabilities": {
"experimental": {
"typeServerMultiConnection": {
"supportedTransports": ["ipc"]
}
}
}
}
Een server die extra TSP-kanalen kan bedienen, retourneert dezelfde capability-structuur in het initialisatie-resultaat:
{
"capabilities": {
"experimental": {
"typeServerMultiConnection": {
"supportedTransports": ["ipc"]
}
}
}
}
De multi-connection mode wordt alleen geactiveerd wanneer beide partijen typeServerMultiConnection adverteren en de twee supportedTransports lijsten ten minste één gemeenschappelijk transport hebben. Als een van beide partijen de capability weglaat, of als er geen gemeenschappelijk transport is, blijft de sessie in single-connection mode en mag de client geen typeServer/connection verzenden.
Het momenteel ondersteunde ingebouwde transporttype voor extra verbindingen is ipc. Deze onderhandeling staat los van de hoofdverbinding, die stdio of Node IPC kan gebruiken.
Een extra verbinding openen
Na de initialisatie en transportonderhandeling opent de client een extra kanaal door typeServer/connection via de hoofdverbinding te verzenden:
{
"type": "open",
"kind": "ipc",
"args": ["<ipc-endpoint>"]
}
Voor ipc identificeert het args array het eindpunt of de eindpunten waar de server verbinding mee moet maken. De server moet het verzoek valideren, prompt verbinding maken met het opgegeven eindpunt en een resultaat met success: true retourneren bij acceptatie. Indien de server het kanaal niet kan openen, moet deze success: false retourneren, eventueel inclusief een kort bericht voor de logs.
IPC-eindpunt argumenten
Van een ipc-eindpunt wordt verwacht dat het een full-duplex JSON-RPC-stream levert wanneer het IPC-mechanisme van het platform dit ondersteunt. In dat geval bevat args één endpoint-string:
{
"type": "open",
"kind": "ipc",
"args": ["<full-duplex-ipc-endpoint>"]
}
Als het IPC-mechanisme van het platform alleen eenrichtings-eindpunten biedt, bevat args twee endpoint-strings. De namen van de eindpunten worden gezien vanuit het perspectief van de type server: het eerste eindpunt is de input-stream van de server, en het tweede eindpunt is de output-stream van de server.
{
"type": "open",
"kind": "ipc",
"args": ["<input-ipc-endpoint>", "<output-ipc-endpoint>"]
}
Een extra verbinding sluiten
De client sluit een extra kanaal door typeServer/connection via de hoofdverbinding te verzenden met hetzelfde transporttype en dezelfde endpoint-identiteit:
{
"type": "close",
"kind": "ipc",
"args": ["<ipc-endpoint>"]
}
De server moet stoppen met het bedienen van het overeenkomstige extra kanaal en het transport prompt sluiten. Een client kan zijn lokale transport ook beëindigen als de server de sluiting niet binnen het timeout-budget van de client voltooit.
Het close-verzoek moet dezelfde args-structuur gebruiken als die is gebruikt om het kanaal te openen: één endpoint voor een full-duplex IPC-stream, of hetzelfde paar input/output-endpoints voor een gesplitste IPC-stream.
Toegestaan Verkeer
De hoofdverbinding is eigenaar van al het verkeer dat de status wijzigt. De volgende zaken moeten exclusief via de hoofdverbinding verlopen:
- LSP-initialisatie en lifecycle-berichten.
- Meldingen over workspace, configuratie, file-watcher en documentsynchronisatie.
- Server-naar-client snapshot-meldingen.
- Het
typeServer/connectioncontrole-verzoek.
Extra verbindingen zijn uitsluitend voor TSP en zijn read-only. Ze mogen worden gebruikt voor verzoeken die de serverstatus niet wijzigen en waarvan de resultaten geldig zijn voor de snapshot die door de client is aangeleverd.
De toegestane TSP-berichten op een extra verbinding zijn gebonden aan de onderhandelde TSP-versie. Als een server ondersteuning voor een TSP-versie rapporteert en multi-connection ondersteuning adverteert voor die sessie, moet deze elk extra-connection TSP-bericht ondersteunen dat voor die versie is gedefinieerd.
De onderstaande tabel geeft de TSP-versie aan waarin elk bericht voor het eerst is toegestaan op een extra verbinding:
| TSP-versie | Bericht voor extra verbinding |
|---|---|
| 0.4.1 | typeServer/getSupportedProtocolVersion |
| 0.4.1 | typeServer/getSnapshot |
| 0.4.1 | typeServer/getPythonSearchPaths |
| 0.4.1 | typeServer/resolveImport |
| 0.4.1 | typeServer/getComputedType |
| 0.4.1 | typeServer/getDeclaredType |
| 0.4.1 | typeServer/getExpectedType |
Servers moeten LSP-verkeer, statuswijzigende meldingen, typeServer/connection en elk TSP-bericht dat niet is toegestaan voor de onderhandelde TSP-versie weigeren wanneer deze op een extra verbinding worden ontvangen.
Snapshots
Een snapshot is een niet-negatief geheel getal dat een momentopname identificeert van de analysestatus van de type server. Type-handles, declaraties, symbolen en andere geretourneerde objecten zijn alleen geldig voor de snapshot waarin ze zijn geretourneerd, tenzij een verzoek expliciet anders vermeldt.
De type server moet de snapshot vooruitschuiven telkens wanneer een eerder geretourneerd type-analyseresultaat ongeldig kan worden. Typische gebeurtenissen die dit veroorzaken zijn wijzigingen in geopende bestanden, gesloten bestanden, configuratiewijzigingen, wijzigingen in bewaakte bestanden, wijzigingen in afhankelijkheden of elke cache-reset die de analyseresultaten beïnvloedt.
Snapshot-regels:
typeServer/getSnapshotretourneert de huidige snapshot-identificatie.- Snapshot-identificaties mogen tijdens een sessie niet afnemen.
typeServer/snapshotChangedrapporteert de vorige en de nieuwe snapshot-identificaties.- Verzoeken die een verouderde snapshot bevatten, moeten falen met een JSON-RPC-fout waardoor de client kan opnieuw proberen met een verse snapshot. De LSP
ServerCancelledfoutcode (-32802) is de voorkeursfout voor dit scenario.
Posities en Ranges
TSP volgt de positie-conventies van LSP:
- Regels zijn nul-gebaseerd.
- Karakter-offsets zijn nul-gebaseerde UTF-16 code units.
- Ranges identificeren bronbereiken met behulp van een startpositie en een eindpositie.
Als een type server bronlocaties in een andere codering opslaat, moet deze locaties converteren op de protocolgrenzen. Onjuiste conversie kan ertoe leiden dat de client informatie opvraagt voor de verkeerde syntax-node.
Nodes, Declaraties en Handles
De type-query verzoeken (typeServer/getComputedType, typeServer/getDeclaredType, typeServer/getExpectedType) identificeren een bronconstruct met een argument dat ofwel een node of een declaration is.
- Een node is een URI plus een bron-range.
- Een declaration beschrijft waar en hoe een symbool is gedefinieerd. Dit is ofwel een reguliere declaratie (gebaseerd op een bron-AST-node) of een synthetische declaratie (gecreëerd door de type checker zonder bron-node).
InvalidHandle is -1 en representeert een ongeldige of niet-beschikbare handle. Clients en servers moeten dit behandelen als een sentinel-waarde, niet als een geldig object-identificatiekenmerk.
Type-resultaten
Type-resultaten zijn JSON-serialiseerbare "tagged objects". Elk type bevat een discriminator die de client vertelt hoe het restant van het object moet worden geïnterpreteerd. Het protocol ondersteunt built-in types, declared types, functies, klassen, unions, modules, type-variabelen, overload-sets, synthetische typen en type-referenties.
Elk type-object bevat een numerieke kind discriminator, een numeriek id en een bitveld van flags:
| Kind | Type | Beschrijving | ||
|---|---|---|---|---|
| 0 | BuiltIn | unknown, any, unbound, ellipsis, never, etc. | ||
| 1 | Declared | Basis voor in de bron gedeclareerde typen (zelden direct gebruikt) | ||
| 2 | Function | Functies en methoden uit def statements | ||
| 3 | Class | Klassen en hun instanties | ||
| 4 | Union | Union-typen (`int \ | str \ | None`) |
| 5 | Module | Module-objecten (import os produceert een Module-type voor os) | ||
| 6 | TypeVar | Type-variabelen (T, P, Ts) | ||
| 7 | Overloaded | Functies met meerdere @overload signatures | ||
| 8 | Synthesized | Typen gesynthetiseerd door de type checker | ||
| 9 | TypeReference | Referentie naar een ander type via id, gebruikt voor deduplicatie en cycli |
Type-objecten zijn snapshot-gebonden en type-ID's zijn response-lokaal:
- Een geretourneerd type-object is alleen geldig voor de snapshot die is gebruikt door het verzoek dat het object produceerde.
- Type-ID's worden gebruikt om
TypeReference-objecten te verbinden met eerdere type-objecten in dezelfde geserialiseerde respons-graaf. - Type-ID's zijn geen stabiele globale identiteit voor een snapshot en mogen niet worden vergeleken over verschillende responsen.
- Type-referenties moeten worden gebruikt om het dupliceren van grote of recursieve type-grafen binnen één respons te voorkomen.
Protocoloppervlak
Het huidige protocol is opzettelijk klein gehouden. De exacte parameters en resultaatvormen zijn gedefinieerd in de protocol-artifacts; de onderstaande tabel lijst elk TSP-bericht op met de richting en de payload-vorm.
| Bericht | Richting | Parameters | Resultaat | ||
|---|---|---|---|---|---|
typeServer/getSupportedProtocolVersion | client → server | geen | string (semver) | ||
typeServer/getSnapshot | client → server | geen | number | ||
typeServer/snapshotChanged | server → client | { old: number, new: number } | notification | ||
typeServer/connection | client → server | `{ type: "open" \ | "close", kind, args? }` | { success, message? } | |
typeServer/getPythonSearchPaths | client → server | { fromUri, snapshot } | `string[] \ | undefined` | |
typeServer/resolveImport | client → server | { sourceUri, moduleDescriptor, snapshot } | `string \ | undefined` | |
typeServer/getComputedType | client → server | `{ arg: Declaration \ | Node, snapshot }` | `Type \ | undefined` |
typeServer/getDeclaredType | client → server | `{ arg: Declaration \ | Node, snapshot }` | `Type \ | undefined` |
typeServer/getExpectedType | client → server | `{ arg: Declaration \ | Node, snapshot }` | `Type \ | undefined` |
Een type server moet de berichten implementeren die vereist zijn door de client waarmee hij integreert, en undefined alleen retourneren waar het resultaat-type van het protocol dit toelaat.
Type-query's
De drie type-query verzoeken verschillen in welk type ze rapporteren voor dezelfde node of declaratie:
typeServer/getComputedType: retourneert het type dat is afgeleid uit de codestroom. Na het narrowen vananaarint, is het berekende type vanbinb = a + 1eenint.typeServer/getDeclaredType: retourneert het type dat expliciet in de bron is geschreven. Voordef foo(a: int | str), is het gedeclareerde type vanaint | str.typeServer/getExpectedType: retourneert het type dat de omringende context verwacht. Voor de aanroepfoo(4), is het verwachte type van het argumentint | str.
Foutafhandeling
Gebruik standaard JSON-RPC foutresponses.
| Situatie | Foutcode | Richtlijn |
|---|---|---|
| Verouderde snapshot | -32802 (ServerCancelled) | De client kan een verse snapshot opvragen en het verzoek opnieuw proberen. |
| Ongeldige parameters | -32602 (InvalidParams) | Vereiste velden ontbreken of zijn malformed. |
| Interne fout | -32603 (InternalError) | De server is onverwacht gefaald tijdens het verwerken van een geldig verzoek. |
Fouten moeten voldoende detail bevatten zodat een client-log het gefaalde verzoek kan identificeren, maar ze mogen geen lokale geheimen of implementatie-specifieke status blootstellen.
Compatibiliteitschecklist
Een compatibele type server moet de volgende behaviors verifiëren:
typeServer/getSupportedProtocolVersionretourneert een semver-string die wordt ondersteund door de client.- Alle request- en notification-payloads worden geserialiseerd en gedeserialiseerd volgens de protocol-artifacts.
typeServer/getSnapshotretourneert een niet-negatieve snapshot en snapshots gaan niet terug in de tijd.- Wijzigingen in documenten, configuraties en bewaakte bestanden maken snapshots ongeldig wanneer zij de geretourneerde analysedata kunnen beïnvloeden.
- Verzoeken met verouderde snapshots falen met een herstelbare cancellation-fout.
- Positie- en range-conversies gebruiken de nul-gebaseerde UTF-16 conventies van LSP.
- Import-resolutie verzoeken retourneren document-URI's of geen resultaat, conform het resultaat-type van het protocol.
- Type-query's (berekend, gedeclareerd en verwacht) retourneren goed gevormde
Type-objecten, ofundefined, voor de door de client opgegeven snapshot. - Multi-connection ondersteuning wordt alleen geadverteerd wanneer de server het onderhandelde extra transport kan bedienen.
- Extra verbindingen accepteren elk TSP-verzoek dat is toegestaan voor de onderhandelde TSP-versie en weigeren LSP-verkeer, statuswijzigende meldingen,
typeServer/connectionen TSP-verzoeken die niet zijn toegestaan voor die versie.
Laatste inhoudelijke update afgestemd op Pylance 2026.4.1.
Groetjes,