An LSP server for SingleStore SQL providing context-aware code completion and semantic syntax highlighting. Communicates using JSON-RPC 2.0 over TCP or WebSocket protocols.
sudo dpkg -i s2-language-server_<version>_linux_amd64.debThe binary is installed to /usr/bin/s2-language-server and grammar files to /usr/share/s2-language-server/grammar_dir/.
sudo rpm -i s2-language-server_<version>_linux_amd64.rpmSame paths as deb: binary at /usr/bin/s2-language-server, grammar at /usr/share/s2-language-server/grammar_dir/.
tar xzf s2-language-server_<version>_<os>_<arch>.tar.gzExtracts s2-language-server binary and grammar_dir/ (containing the default ddl.bin/dml.bin plus any version-specific grammars such as ddl_9.0.30.bin) into the current directory. Place them wherever you prefer and reference grammar_dir with the -grammar_dir flag.
Extract s2-language-server_<version>_windows_amd64.zip. Contains s2-language-server.exe and grammar_dir\ with the grammar binary files.
Prebuilt container images are published to GitHub Container Registry. Pull the image with:
docker pull ghcr.io/singlestore-labs/language-serverRun the language server in a container, exposing the listening port to the host:
docker run --rm -p 8080:8080 ghcr.io/singlestore-labs/language-serverThe image ships with the s2-language-server binary and the grammar files preinstalled, so no additional setup is required. Flags can be passed after the image name to override defaults — for example, to run in WebSocket mode:
docker run --rm -p 8080:8080 ghcr.io/singlestore-labs/language-server \
-mode=websocket -addr=:8080s2-language-server [flags]| Flag | Default | Description |
|---|---|---|
-mode |
tcp |
Communication protocol: tcp or websocket |
-addr |
:8080 |
Address to listen on |
-grammar_dir |
"" |
Path to the directory containing grammar binary files (default ddl.bin, dml.bin and optional version-specific <name>_<semver>.bin) |
-max_files_with_states |
100 |
Maximum number of files to keep parser states for |
-max_states_per_file |
1000 |
Maximum number of parser states to keep per file |
-allow_all_origins |
true |
Allow WebSocket connections from any origin (WebSocket mode only) |
-generate_bin |
false |
Generate binary grammar files from source grammar and exit |
-tls |
false |
Enable TLS on the listener (wss:// for -mode=websocket, TLS-over-TCP for -mode=tcp) |
-tls_cert |
"" |
Path to PEM-encoded TLS server certificate (required when -tls) |
-tls_key |
"" |
Path to PEM-encoded TLS server private key (required when -tls) |
-tls_min_version |
"1.2" |
Minimum TLS version: 1.2 or 1.3 |
-health_addr |
"127.0.0.1:8081" |
Address for the plain-HTTP health probe server (/healthz). Set to empty ("") to disable. See Health Probe. |
TCP mode (default):
s2-language-server -mode=tcp -addr=:8080 -grammar_dir=/usr/share/s2-language-server/grammar_dirWebSocket mode:
s2-language-server -mode=websocket -addr=:8080 -grammar_dir=/usr/share/s2-language-server/grammar_dirtar.gz / zip install (grammar directory next to the binary):
./s2-language-server -mode=tcp -addr=:3000 -grammar_dir=./grammar_dirThe server supports two communication protocols:
Direct TCP socket communication. Suitable for local connections and scenarios where a persistent TCP connection is available. This is the default mode.
s2-language-server -addr=:8080 -grammar_dir=./grammar_dirOr explicitly:
s2-language-server -mode=tcp -addr=:8080 -grammar_dir=./grammar_dirWebSocket-based communication over HTTP. Useful for browser-based clients, remote connections, or scenarios requiring HTTP transport. The server listens on an HTTP address and upgrades connections to WebSocket on the root path (/).
s2-language-server -mode=websocket -addr=:8080 -grammar_dir=./grammar_dirClients connect to ws://<host>:8080/ (or wss:// for secure WebSocket).
By default, the server accepts WebSocket connections from any origin. To restrict connections to same-origin only, use:
s2-language-server -mode=websocket -addr=:8080 -allow_all_origins=false -grammar_dir=./grammar_dirBoth transports support TLS. Pass -tls together with a PEM cert and key, and the same listener will speak wss:// (in WebSocket mode) or accept TLS-wrapped TCP (in TCP mode):
s2-language-server -mode=websocket -addr=:8443 -grammar_dir=./grammar_dir \
-tls -tls_cert=/etc/lsp/tls.crt -tls_key=/etc/lsp/tls.keys2-language-server -mode=tcp -addr=:8444 -grammar_dir=./grammar_dir \
-tls -tls_cert=/etc/lsp/tls.crt -tls_key=/etc/lsp/tls.keyClients connect to wss://<host>:8443/ for the WebSocket transport, or use tls.Dial (or your language's equivalent) for the TLS-over-TCP transport. The minimum TLS version defaults to 1.2 and can be bumped to 1.3 with -tls_min_version=1.3.
The server provides a plain HTTP health endpoint on a dedicated port for Kubernetes probes and cloud load balancer health checks. This endpoint is independent of the LSP transport: it works the same in both -mode=tcp and -mode=websocket, with or without -tls, and never requires authentication.
GET /healthz → 200 "ok" when the LSP listener is accepting connections
GET /healthz → 503 "not ready..." while the listener has not yet started
By default, the health probe server is disabled. To enable it, specify the address to listen on using -health_addr. For example, in a containerized environment, bind to all interfaces so that kubelet and cloud load balancers can reach the probe:
s2-language-server -health_addr=:8081In the default Docker image, the readiness probe listens on 127.0.0.1:8081. Adjust the address as needed for your deployment. To disable the probe server entirely, pass -health_addr="".
The probe handler does not log individual requests—this avoids flooding the LSP log stream with frequent kubelet polls. Probe traffic is always plain HTTP, so certificate rotation on the LSP transport never affects the health endpoint. For a reference Kubernetes deployment (compatible with EKS, GKE, and AKS) that configures livenessProbe, readinessProbe, and cloud LB health checks at this endpoint, see deploy/k8s.yaml.
The -max_files_with_states and -max_states_per_file flags control memory usage. Lower values reduce memory consumption at the cost of re-parsing files more often. The defaults (100 files, 1000 states per file) are suitable for most workloads.
The grammar directory must contain a default DDL and DML grammar, and may optionally contain additional grammars targeted at specific SingleStore engine versions:
ddl.bin— compiled default DDL grammar (required)dml.bin— compiled default DML grammar (required)ddl_<version>.bin/dml_<version>.bin— optional version-specific grammars (e.g.ddl_9.0.30.bin,dml_9.0.30.bin)
The default grammars are mandatory; the server fails to start if either is missing. Version-specific grammars are optional and use the form <grammar>_<semver>.bin, where <semver> matches the value returned by SingleStore's @@memsql_version.
When the client provides database connection details in initializationOptions.database, the server queries @@memsql_version from the connected engine and picks the closest matching grammar for both DDL and DML, independently:
- If a grammar file matches the engine version exactly, it is used.
- Otherwise the server looks for a grammar with the same major and minor version, preferring the highest patch version that is less than or equal to the engine's patch. If none qualify, the lowest patch version greater than the engine's patch is used.
- If no grammar shares the engine's major and minor version, the default grammar (
ddl.bin/dml.bin) is used.
If no database connection is configured (or the connection fails), the server falls back to the default grammar.
| Client | Platform | Marketplace | Repository |
|---|---|---|---|
| SingleStore VS Code | VS Code | Extension | GitHub |
| SingleStore Vim | Vim 8+ / Neovim | GitHub |
Language server clients must supply two groups of parameters inside initializationOptions when sending the initialize request.
The server connects to a SingleStore engine on startup to introspect schemas, tables, columns, and other metadata used for completions.
Provide connection details under initializationOptions.database:
| Field | JSON key | Type | Required | Description |
|---|---|---|---|---|
| Host | host |
string | yes | Hostname or IP address of the SingleStore engine (e.g. "svc-xxx.svc.singlestore.com") |
| Port | port |
integer | yes | MySQL-protocol port (typically 3306) |
| Username | username |
string | yes | Database user with at least read access to the schemas that should be completed |
| Password | password |
string | yes | Password for the database user |
| Database | database |
string | no | Default database |
| SSL | ssl |
boolean | no | When true, the connection will use TLS. Defaults to false |
Note: If
initializationOptions.databaseis not provided or the connection to the database cannot be established, the language server will still start and operate normally. However, completions based on the actual database schema (tables, columns, procedures, etc.) will not be available. The server will notify the client about the connection issue via awindow/showMessagewarning.
Provide the name and version of the language client under initializationOptions.client:
| Field | JSON key | Type | Required | Description |
|---|---|---|---|---|
| Name | name |
string | yes | Human-readable client name (e.g. "vscode-singlestore") |
| Version | version |
string | yes | SemVer version string of the client (e.g. "1.2.0") |
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"processId": 12345,
"rootUri": null,
"capabilities": {},
"initializationOptions": {
"database": {
"host": "127.0.0.1",
"port": 3306,
"username": "root",
"password": "s3cret",
"database": "mydb",
"ssl": false
},
"client": {
"name": "vscode-singlestore",
"version": "1.2.0"
}
}
}
}After initialization, clients can change the database the server is connected to (for example, when a user switches workspaces or rotates credentials) without restarting the language server. Send a workspace/didChangeConfiguration notification with the updated settings.
The settings payload accepts the same database block used during initialization, plus an optional client block. When client is omitted, the server reuses the language client identity supplied in the original initialize call. If database.host is empty, the notification is ignored.
| Settings field | Required | Description |
|---|---|---|
database |
yes | Same shape as initializationOptions.database |
client |
no | Override the language client name/version |
On success the server stops the previous schema cache updater, opens a fresh connection, restarts the cache updater, and clears any prior connection error. On failure the error is logged on the server side and the previously running cache updater is left stopped. Because workspace/didChangeConfiguration is a notification, no response is returned to the client.
{
"jsonrpc": "2.0",
"method": "workspace/didChangeConfiguration",
"params": {
"settings": {
"database": {
"host": "svc-new.svc.singlestore.com",
"port": 3306,
"username": "analyst",
"password": "n3w-s3cret",
"database": "analytics",
"ssl": true
}
}
}
}The language server provides a dedicated method for validating SQL queries without executing them against a live database. This is exposed via the custom LSP request queryChecker/validateQuery. Clients can use this method to check the syntax and structure of a query, receive error locations, and get suggestions for valid tokens at the point of failure.
To validate a query, send a JSON-RPC request with the following structure:
{
"jsonrpc": "2.0",
"id": 1,
"method": "queryChecker/validateQuery",
"params": {
"databaseVersion": "9.0.30", // optional: engine version for grammar selection
"query": "LOAD DATA CONCURRENT LOCAL FILE 'myfile'" // the SQL query to validate
}
}The response will indicate whether the query is valid, and if not, will provide the error token, its position, and a list of alternative tokens that would be valid at that point:
{
"isValid": false, // indicates wheter the query is grammatically correct
"errorTokenPosition": { "line": 0, "character": 27 }, // position of the first incorrect token
"errorToken": "FILE", // first incorrect token
"alternativeTokens": { // list of alternatives, expected instead of the incorrect tocken
"isIncomplete": false,
"items": [
{
"label": "INFILE",
...
},
...
]
}
}If the query is valid, isValid will be true and the other fields will be omitted.
If invalid, errorToken and errorTokenPosition point to the problematic token, and alternativeTokens suggests valid completions.
This method is useful for editor integrations, CI pipelines, and any tool that needs fast, offline query validation using the same grammar as the language server’s completion engine.