[BUG] Plugin-registered LSP servers emit 'Async hook Stop completed' messages on every turn
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] I can reproduce this bug consistently
Bug Description
Plugin-registered LSP servers (those configured via .lsp.json in a plugin directory) emit Async hook Stop completed messages after every user turn. The number of messages matches the number of plugin-registered LSP servers running. Built-in LSP servers (TypeScript, Pyright) do not produce these messages.
Steps to Reproduce
- Install LSP servers that use the plugin
.lsp.jsonregistration mechanism. For example, create a local plugin with:
bash-language-server plugin (.lsp.json):
{
"bash": {
"command": "bash-language-server",
"args": ["start"],
"extensionToLanguage": {
".sh": "shellscript"
}
}
}
vscode-langservers plugin (.lsp.json):
{
"html": {
"command": "vscode-html-language-server",
"args": ["--stdio"],
"transport": "stdio",
"extensionToLanguage": { ".html": "html" }
},
"css": {
"command": "vscode-css-language-server",
"args": ["--stdio"],
"transport": "stdio",
"extensionToLanguage": { ".css": "css" }
}
}
- Start a Claude Code session in a project with
.sh,.html, or.cssfiles - Send any message and receive a response
- Observe 3x
Async hook Stop completedmessages appearing (one per LSP server)
Expected Behavior
No Async hook Stop completed messages should appear from LSP server lifecycle events. Built-in LSPs (TypeScript, Pyright) correctly suppress these messages.
Actual Behavior
After every assistant response, 3 messages appear:
└ Async hook Stop completed
└ Async hook Stop completed
└ Async hook Stop completed
The count matches the number of plugin-registered LSP servers (bash, CSS, HTML = 3).
Key Observation
The official typescript-lsp and pyright-lsp plugins from claude-plugins-official are stub plugins (README-only, no .lsp.json). Their LSP support is built into Claude Code natively and does not produce these messages. Only LSP servers registered through the plugin .lsp.json mechanism trigger the spurious output.
This suggests the plugin-based LSP lifecycle management uses a different code path that doesn't suppress async hook completion messages the way the built-in LSP path does.
Related
- #32082 requests suppression for Stop/SubagentStop hooks generally. This issue is specifically about LSP plugin lifecycle events leaking through as Stop hook messages.
Environment
- Claude Code version: latest (2026-03-08)
- Platform: Linux (WSL2)
- Node: v24.2.0
Priority
Low -- cosmetic only, no functional impact. The messages are noise but don't affect LSP functionality.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗