Documents the full capability audit results (hover/definition/references/ symbols/formatting/codeAction/signatureHelp/foldingRange/workspaceSymbol) per language endpoint, plus concrete before/after examples captured from live requests against production.
iLSP — Internal LSP Proxy
Self-hosted Language Server Protocol proxy for LRU's internal tooling.
Runs at https://ilsp.i80.dk (Nomad/Docker, autobox.i80.dk).
Provides smart autocomplete on top of standard LSPs:
| Language | Endpoint | Extra completions |
|---|---|---|
| Bicep | wss://ilsp.i80.dk/bicep |
Internal ACR modules, versions, params |
| YAML | wss://ilsp.i80.dk/yaml |
AzDO pipeline templates, GHA reusable workflows |
| Python | wss://ilsp.i80.dk/python |
Jedi (standard) |
→ See EDITOR_SETUP.md for editor configuration and a full feature overview.
LSP feature coverage
Full capability audit against production (verified 2026-08-14) — everything below is confirmed working end-to-end over the live WebSocket endpoints, not just in theory:
| Feature | Bicep (/bicep) |
Python (/python) |
YAML (/yaml) |
|---|---|---|---|
| Completions (+ internal catalogs) | ✅ | ✅ | ✅ |
| Hover | ✅ | ✅ | ✅ |
| Go to definition | ✅ | ✅ | — |
| Find references | ✅ | ✅ | — |
| Document symbols | ✅ | ✅ | — |
| Document formatting | ✅ | ✅ | — |
| Code actions | ✅ | ✅ | — |
| Signature help | ✅ (iLSP-provided) | ✅ | — |
| Folding range | ✅ (iLSP-provided) | ✅ | — |
| Workspace symbol | ✅ (iLSP-provided) | ❌ (pylsp doesn't implement it) | — |
| Diagnostics | ✅ | ✅ | ✅ |
Bicep.LangServer itself (v0.46.1, latest) doesn't implement signature help,
folding range, or workspace symbol — iLSP answers these three locally in the
proxy (ilsp/bicep_lsp/local_features.py) instead of forwarding to a backend
that would just error or return nothing.
Confirmed examples (live against wss://ilsp.i80.dk/bicep)
Role-array completion — cursor inside roles: [...] returns all 682 Azure
built-in roles:
roles: ['KEY_VAULT_SECRETS_USER', 'STORAGE_BLOB_DATA_CONTRIBUTOR', <cursor>]
→ 682 items: ACCESS_REVIEW_OPERATOR_SERVICE_ROLE, ACRDELETE, ACRIMAGESIGNER, ACRPULL, ACRPUSH, ...
Go to definition — jumping from a variable usage to its declaration:
var storageName = '${projectName}storage' # ← declared here (line 2)
resource sa '...' = {
name: storageName # ← cursor here → jumps to line 2
}
Find references — from the storageName declaration, finds both places
it's used: the string interpolation ${projectName}storage isn't a reference,
but the resource sa { name: storageName } property assignment is — returns
the declaration itself plus that 1 real usage (2 results total).
Document symbols — outline for a file with params/vars/resources/modules:
location, projectName, storageName, sa, roleAssignment, storageId
Signature help — cursor right after resourceId(:
resourceId(resourceType, resourceName1, resourceName2...)
^ active parameter highlighted
Folding range — every multi-line {}/[]/() block returned as a
collapsible region (nested blocks included).
Workspace symbol — searching "storage" across all open documents in the
editor session returns storageName, storageId, etc.
Quick start
# Check service health
curl https://ilsp.i80.dk/health
# Run smoke tests
python3 scripts/smoke_test_completions.py
# Local dev
make run # build + start + health check
make run-quick # start without rebuilding
make logs # tail container logs
make health # curl health endpoint
Updating catalogs
Catalogs are baked into the Docker image at build time — commit + push to update
production (see EDITOR_SETUP.md for the full
workflow and a note on why push_catalogs.sh / /reload don't take effect in prod).
python3 scripts/sync_pipeline_templates.py # scan AzDO + GHA template repos
python3 scripts/sync_bicep_modules.py # from DevOpsMCP repo — needs az CLI + ACR access
git add *_catalog.json && git commit -m "chore: refresh catalogs" && git push
Deploy
Push to main → Gitea Actions builds image → Nomad deploys automatically.
# Manual deploy (if CI is down)
git push
ssh autobox.i80.dk 'nomad job run /opt/nomad/jobs/ilsp.nomad'