Third-party LSP plugins: `.lsp.json` at plugin root works on v2.1.119; marketplace.json `lspServers` alone is silently ignored

Resolved 💬 3 comments Opened Apr 26, 2026 by avifenesh Closed Jun 24, 2026

Summary

Working through the third-party LSP plugin path on Claude Code v2.1.119, I found a gap between the marketplace-side docs and what actually loads, plus two documented config fields that the runtime rejects. Posting the empirical findings as a heads-up for anyone else hitting the same wall.

Setup

  • Claude Code 2.1.119 on Windows (also tested under WSL)
  • Plugin: zig-lsp distributed via agent-sh/zig-lsp — a third-party marketplace, not claude-plugins-official
  • Language server: zls 0.16.0 on PATH
  • Test workspace: a real Zig 0.17 project with ~900 LOC

What worked, what didn't

| Setup | Result |
|---|---|
| lspServers block inline in marketplace.json's plugin entry; no .lsp.json in the plugin source | Plugin shows enabled, but the LSP never registers. Debug log shows Checking plugin zig-lsp and then nothing — no Loaded N LSP server(s) from plugin line follows. |
| Same plugin entry copied into the local claude-plugins-official marketplace cache (marketplace.json + an empty plugin source dir) | Loads fine: Loaded 1 LSP server(s) from plugin: zig-lsp-test. The behavior is gated on the marketplace name, not on schema. |
| .lsp.json at the plugin root in the source repo (which gets copied to ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/.lsp.json on install) | Loads. Loaded 1 LSP server(s) from plugin: zig-lsp, Total LSP servers loaded: <N>. ZLS spawns and post-edit diagnostics flow as expected. |
| lspServers inline in .claude-plugin/plugin.json (alongside name/version/etc., not in marketplace.json) | Also loads. Third valid path; not in either docs page. |
| Manifest with restartOnCrash: true or maxRestarts: N (in any of the three config locations) | Initialization fails. Loader logs [ERROR] LSP server '<id>': restartOnCrash is not yet implemented. Remove this field from the configuration. The entire LSP server is rejected, not just the field. .lsp.json files get No LSP server available. startupTimeout works fine. |

Two paths the docs describe, three that actually work, two fields that don't

For a third-party LSP plugin on 2.1.119, the loader will pick up its server config from any of:

  1. .lsp.json at the plugin root (documented at /plugins#add-lsp-servers-to-your-plugin)
  2. lspServers inline in .claude-plugin/plugin.json (undocumented; verified via michelsciortino/dart-lsp and a local test)
  3. lspServers inline in .claude-plugin/marketplace.jsononly loads for claude-plugins-official; inert for third-party marketplaces (documented at /plugins-reference#lsp-servers)

The restartOnCrash and maxRestarts fields are listed in the marketplace lspServers schema in the plugins reference, but the runtime rejects them at init with the error above. Either remove them from the docs or implement the runtime side; right now a plugin author copying the documented schema gets a non-loading LSP.

Related / why a fresh issue

  • #15521 was closed as completed in v2.1.76 with the claim that LSP plugin loading was fixed. On 2.1.119 the marketplace-level lspServers path remains inert for third-party marketplaces. The .lsp.json runtime path does work, but that distinction isn't anywhere in the closed issue's comments.
  • #31468 was closed as NOT_PLANNED with a comment that "no configuration-based workaround exists." Empirically there is a working configuration — .lsp.json at the plugin root, or lspServers in plugin.json — and neither is mentioned in any of the comments on that thread. Opening this issue so the next person searching for "third-party LSP plugin not loading" finds the working answer.

What a new user should do today (Claude Code 2.1.119)

For a third-party LSP plugin to register:

<marketplace-repo>/
├── .claude-plugin/
│   └── marketplace.json    # plugin entry can include `lspServers`
│                             (matches official-plugin shape;
│                             harmless if currently ignored)
└── plugins/
    └── <plugin-name>/
        ├── .claude-plugin/
        │   └── plugin.json   # identity metadata; lspServers can
        │                       also be inlined here as the third
        │                       valid config location
        ├── .lsp.json         # ← THIS file registers the LSP at
        │                       runtime (recommended path)
        └── README.md

Schema is server-name → { command, args, extensionToLanguage, initializationOptions, ... } — exactly as documented at /plugins-reference#lsp-servers. But strip restartOnCrash and maxRestarts — they're documented but rejected at runtime today. startupTimeout is fine.

Verify after install with claude --debug:

  • ✓ Working: log contains Loaded N LSP server(s) from plugin: <name> and Total LSP servers loaded: M.
  • ✗ Plugin not picking up config: log contains Checking plugin <name> with no following Loaded line — .lsp.json isn't reaching the cache.
  • ✗ Bad field: log contains Failed to initialize LSP server <id> — usually restartOnCrash or another unimplemented field. Strip it from the manifest and reinstall.

Working reference: agent-sh/zig-lsp v0.1.1 ships .lsp.json + marketplace.json lspServers (no restartOnCrash); verified end-to-end on 2.1.119 (uninstall → reinstall → cold session → Loaded 1 LSP server(s) from plugin: zig-lsp, documentSymbol/hover return real results from a Zig 0.17 codebase).

Asks

  1. Doc note on the lspServers marketplace reference clarifying:
  • For third-party marketplaces, .lsp.json at the plugin root is what registers the server today.
  • lspServers inline in plugin.json is also accepted as an alternative.
  • restartOnCrash and maxRestarts are not yet implemented; including them in any config location fails initialization.
  1. Wire the marketplace lspServers field for third-party marketplaces so plugin authors can ship a single source of truth instead of keeping .lsp.json and the marketplace.json block byte-equivalent.
  2. Implement restartOnCrash / maxRestarts or remove them from the schema until they are.

Happy to test fixes against agent-sh/zig-lsp.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗