[BUG] LSP plugins installed before the lspServers manifest-format change load as silent empty shells; `plugin update` reports the broken artifact as up-to-date

Status Open
Reported on v2.1.212
Maintainer reply ✓ Yes — bcherny
Activity 4 comments · opened Jul 17, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Summary

LSP plugins installed before the lspServers config moved into the marketplace manifest are silently non-functional: they load as empty shells (no LSP server launched, no error anywhere), and claude plugin update cannot repair them because it compares version strings and pyright-lsp has been 1.0.0 throughout. The only fix is uninstall + install, which nothing tells the user to do.

Related but distinct: #15148 (same symptom on a fresh install, Dec 2025), #61954 (update-says-latest-but-stale, path-based plugins, closed), #41922 (cache ignored marketplace SHA on install, closed — install is indeed fixed; update is not).

Environment

  • Claude Code 2.1.212, macOS 26.5.1 (arm64)
  • pyright-lsp@claude-plugins-official, originally installed 2026-01-29
  • pyright-langserver 1.1.411 on PATH (verified present in the Claude Code process environment)

Reproduction / diagnosis

  1. Plugin installed 2026-01-29; installed_plugins.json shows "version": "1.0.0", "lastUpdated": "2026-01-29" pinned to commit d49ad35, while sibling plugins with "version": "unknown" refreshed themselves continuously.
  2. The Jan-2026 cached artifact at ~/.claude/plugins/cache/claude-plugins-official/pyright-lsp/1.0.0/ contains a generated .claude-plugin/plugin.json with only name/description/version/author — no lspServers key. The current marketplace format instead declares lspServers in the marketplace.json entry (the plugin source dir itself now ships only LICENSE + README).
  3. Result: plugin "loads" successfully every session, /plugin shows it enabled with no errors, but no pyright-langserver child process is ever spawned, no diagnostics are delivered, and no LSP tool is registered. The only visible symptom is the plugin drifting into "Not used recently" (in our case: enabled since January, "not used in 41 days", user assumed it was Claude's choice).
  4. claude plugin update pyright-lsp@claude-plugins-official → ✔ pyright-lsp is already at the latest version (1.0.0). The version string never changed when the manifest format did, so the updater considers the stale, functionally-broken artifact current.
  5. Fix that works: claude plugin uninstall + claude plugin install, then restart. The fresh install materializes correctly (no generated plugin.json; claude plugin details now reports LSP servers (1) pyright), and after restart diagnostics push + the LSP tool both work.

Why this deserves its own issue

  • Every user who installed any official LSP plugin before the manifest change is in this state today, and nothing in the product surfaces it: no load error, no /plugin Errors entry (the binary-missing case does produce one; this case does not), and update actively reports the broken state as current.
  • The failure mode compounds: "Not used recently" reads as the model isn't choosing to use this plugin, not the plugin is broken, so users deprioritize rather than debug it.

Suggested fixes (any one would do)

  1. plugin update should compare content (marketplace entry hash / commit SHA of the plugin subtree) rather than the version string alone — mirroring what #41922 fixed for install.
  2. Marketplace policy: bump plugin versions on any manifest-affecting change (the format migration shipped behavior-relevant changes under an unchanged 1.0.0).
  3. Startup validation: a plugin whose category/marketplace entry declares lspServers but whose materialized artifact registers zero LSP servers should surface in /plugin Errors instead of loading silently as an empty shell.

View original on GitHub ↗

4 Comments

carsfox · 1 month ago

Confirming this reproduces on Claude Code 2.1.236, macOS (Darwin 25.6.0), with the JetBrains plugin (PyCharm) v0.1.14-beta also connected.

Both pyright-lsp@claude-plugins-official and typescript-lsp@claude-plugins-official (v1.0.0, scope: user) showed as installed/enabled with prerequisites (pyright, pyright-langserver, typescript-language-server) present on PATH, yet no LSP tools were ever exposed to the model — claude mcp list reported no servers, and the cached .claude-plugin/plugin.json for each plugin had only name/description/version/author, no lspServers key.

The documented workaround fixed it: claude plugin uninstall <plugin>@claude-plugins-official followed by claude plugin install <plugin>@claude-plugins-official for both plugins. After reinstall, claude plugin details now reports:

pyright-lsp: LSP servers (1)  pyright
typescript-lsp: LSP servers (1)  typescript

vs. previously showing no LSP servers at all. Restarting the session should be sufficient to pick up the tool — will confirm. This is a strong +1 that the auto-repair path (plugin update) needs to actually detect and fix this stale-shell state rather than reporting "already at the latest version."

bcherny collaborator · 1 month ago

Reproduced, thank you for the excellent diagnosis — all three parts of your report check out on the current release (2.1.233), and this isn't macOS-specific (I reproduced it on Linux).

With a stale plugin artifact in the state you describe (a cached plugin.json generated before the manifest-format change, containing only name/description/version/author and no LSP server declaration):

  1. Silent empty shell confirmed — the plugin loads with zero LSP servers registered, no language server process is spawned, and nothing surfaces in /plugin errors. Debug logging shows Total LSP servers loaded: 0 versus 1 for a freshly-installed copy of the same plugin at the same version. The stale artifact's own manifest takes precedence over the marketplace's current declaration, which is why the break is invisible.
  2. plugin update no-repair confirmed — claude plugin update pyright-lsp@claude-plugins-official reports already at the latest version (1.0.0) and leaves the broken artifact byte-for-byte untouched, because the comparison is on the version string alone and the version never changed across the format migration.
  3. Reinstall fix confirmed — removing the stale artifact and letting a fresh install materialize restores the LSP server registration immediately.

A recent release did fix a related update case (stale reporting when the marketplace had newer commits), but the variant here — same version string, stale artifact content — is still present, which matches your experience.

Until a fix lands, the workaround stands: claude plugin uninstall + claude plugin install for any LSP plugin installed before the manifest change, then restart. Marking this as reproduced.

🤖 Generated with Claude Code

StefanGentz · 14 days ago

Additional data point: on the desktop app this isn't only a stale artifact from before the manifest-format change. The desktop app still produces it, so uninstall + install doesn't stick there.

Environment: Claude desktop 2.16120.0 (bundled Claude Code 2.1.284), macOS 27, claude-plugins-official LSP plugins at 1.0.0.

  • The desktop app's plugin manager writes .claude-plugin/plugin.json into the cache of strict: false marketplace plugins ([marketplaceFileOps] Synthesized plugin.json for <name> (strict:false marketplace plugin)). The synthesized manifest carries name, description, version and author (its whitelist includes mcpServers) but not lspServers. It is created on install/update when missing and overwritten on a desktop marketplace refresh.
  • Once that file exists, the loader applies the documented rule (entry lspServers don't apply when a plugin.json is present). Every session logs Total LSP servers loaded: 0 and the LSP tool is never registered.
  • Here, a marketplace refresh plus a reinstall through the desktop UI rewrote the manifests of all eleven installed *-lsp plugins within twelve seconds. Each one was an exact projection of its marketplace entry minus category, source, strict and lspServers.
  • In this state claude plugin details still reports LSP servers (1) from the marketplace entry, so the failure looks healthy.

Workaround that survives the re-synthesis: put the server config into .lsp.json at the cached plugin root, e.g. ~/.claude/plugins/cache/claude-plugins-official/swift-lsp/1.0.0/.lsp.json:

{ "sourcekit-lsp": { "command": "sourcekit-lsp", "extensionToLanguage": { ".swift": "swift" } } }

The synthesizer never writes .lsp.json, and the loader reads it regardless of plugin.json. Verified with claude -p … --debug-file <file>: one Loaded 1 LSP server(s) from plugin: … line per plugin, Total LSP servers loaded: 8, and LSP in the init event's tools.

Suggested fix: include lspServers (and userConfig / channels) in the synthesized manifest, or skip synthesizing a plugin.json for strict: false entries, since the upstream plugin directories intentionally ship without one.

Related: #90114, #90926, #15148, #93474.

BluFinSolutions · 14 days ago

Another consequence of the same synthesis, for entries that declare skills instead of lspServers: with strict: false the synthesized manifest makes the whole plugin fail to load (Plugin <name> has conflicting manifests), as documented in plugins-reference. Seen with academy-guide, claude-api and discernment-nudge from anthropic-agent-skills on Claude Desktop 2.16120.0, Windows 11. There were six marketplace refreshes between 2026-09-12 and 2026-10-01; each one synthesized the three manifests, and after each one the plugins were missing from the skill listing.

Reading the 2.16120.0 bundle confirms the "overwritten on a desktop marketplace refresh" part. ensureManifestsForMarketplace calls ensurePluginManifestExists(pluginId, …, true) for every installed plugin of the refreshed marketplace, and the writer has no strict check. Install and update call it with overwrite: false. On refresh, the only skip is a real .claude-plugin/plugin.json in the plugin's source directory.

Catalog-side report: anthropics/skills#1943.