fix: restore .lsp.json — empirically required for third-party LSP plugins - #2
Conversation
…gins The previous round of changes deleted .lsp.json based on the (correct) observation that official LSP plugins ship only README+LICENSE in their source. The (incorrect) inference: marketplace.json's lspServers must be the universal mechanism. Empirical testing on v2.1.119 disproved this — without .lsp.json in the cached plugin dir, the loader silently skips the entry. Two-path picture (verified by adding .lsp.json directly to the cached install dir at ~/.claude/plugins/cache/agent-sh/zig-lsp/0.1.0/.lsp.json and confirming `Loaded 1 LSP server(s) from plugin: zig-lsp` in claude --debug): - Official LSP plugins (typescript-lsp etc.) — hardcoded path inside the binary, gated on marketplace name 'claude-plugins-official'. Plugin source ships only README+LICENSE. - Third-party LSP plugins — must ship .lsp.json at plugin root in the source repo, which gets copied to the user's cache on install. The runtime loader reads from the cache path; that's what registers the server. Keep lspServers in marketplace.json too — identical to .lsp.json content. Matches the official-plugin shape and is the right metadata target if/when Anthropic wires the marketplace-side loader as the docs describe. Files: - plugins/zig-lsp/.lsp.json — restored - CLAUDE.md / AGENTS.md / CONTRIBUTING.md — non-negotiables updated to require both files, kept byte-equivalent on server config - README.md — layout reflects .lsp.json - agent-knowledge/claude-code-lsp-plugin-contribution.md — correction block expanded with the two-path picture + empirical evidence table - CHANGELOG.md — Unreleased entry rewritten
There was a problem hiding this comment.
Code Review
This pull request restores the .lsp.json configuration file at the plugin root and updates documentation across several files to reflect that third-party LSP plugins require both this file and an inline lspServers block in marketplace.json for proper registration. Feedback indicates that the semantic_tokens key in the new .lsp.json file is incorrect for ZLS and should be updated to enable_semantic_tokens to match the expected schema.
| "enable_argument_placeholders": true, | ||
| "enable_build_on_save": true, | ||
| "build_on_save_args": [], | ||
| "semantic_tokens": "full", |
There was a problem hiding this comment.
The semantic_tokens key with the value "full" appears to be incorrect for ZLS. According to the ZLS configuration schema, the relevant option is typically enable_semantic_tokens and it expects a boolean value. Note that this change should also be applied to the lspServers block in marketplace.json to adhere to the project's requirement of keeping these configurations byte-equal.
| "semantic_tokens": "full", | |
| "enable_semantic_tokens": true, |
Summary
The previous PR (#1) deleted
.lsp.jsonbased on the (correct) observation that official LSP plugins ship onlyREADME.md+LICENSEin their source. The (incorrect) inference was thatmarketplace.json'slspServersblock was the universal mechanism. Empirical testing on Claude Code v2.1.119 disproved that.Investigation summary
Two-path picture:
*-lsp@claude-plugins-official)README.md+LICENSE;lspServersin marketplace.json is metadata..lsp.jsonat plugin root in the source repo. It's copied to~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/.lsp.jsonon install. The runtime loader reads from that cache path. That's what registers the server.Empirical evidence (this session, v2.1.119)
agent-shmarketplace, onlylspServersin marketplace.jsonChecking plugin zig-lspappears, noLoaded ... LSP server(s)line follows.claude-plugins-officialmarketplace cacheagent-shmarketplace WITH.lsp.jsonat plugin root → cached in install dirLoaded 1 LSP server(s) from plugin: zig-lsp,Total LSP servers loaded: 3.Changes
plugins/zig-lsp/.lsp.json— restored, full ZLS config (extensions, initializationOptions, lifecycle)..claude-plugin/marketplace.json—lspServersblock kept (matches official-plugin shape; will be the right metadata target if/when the marketplace-side loader is wired as the docs describe).CLAUDE.md/AGENTS.md/CONTRIBUTING.mdnon-negotiables updated: both files required, kept byte-equivalent on server config.README.md— repo layout reflects.lsp.json.agent-knowledge/claude-code-lsp-plugin-contribution.md— correction block expanded with the two-path picture + empirical evidence table.CHANGELOG.md—[Unreleased]entry rewritten..gitignore— added.claude/settings.local.json.Test plan
claude plugin validate .passesclaude plugin validate plugins/zig-lsppassesagnix --target claude-codepasses (0 errors).zigfile:claude --debugshowsLoaded 1 LSP server(s) from plugin: zig-lsp(verified by pre-staging.lsp.jsonin the cache directly).zigfile → confirm LSP loads from a clean install pathRelated Anthropic issues
.lsp.jsonempirically still required for third-party plugins on v2.1.119).lsp.jsonruntime path is alive and well; only the marketplace-sidelspServersdiscovery is dead code for non-claude-plugins-officialmarketplaces.Note
Medium Risk
Medium risk because it changes how the LSP server is discovered/loaded at runtime by reintroducing a second source of truth (
plugins/zig-lsp/.lsp.json) that must stay in sync withmarketplace.json.Overview
Restores
plugins/zig-lsp/.lsp.jsonso Claude Code reliably registerszlsfor third-party installs, while keeping the samelspServersconfig inline in.claude-plugin/marketplace.json.Updates contributor/docs guidance (
CLAUDE.md,AGENTS.md,CONTRIBUTING.md,README.md, and theagent-knowledgenote) to reflect the dual-path requirement and the rule that.lsp.jsonand marketplacelspServersmust remain byte-equivalent. Also ignores.claude/settings.local.jsonin git.Reviewed by Cursor Bugbot for commit e669acd. Configure here.