claude-code-flutter-lsp
A Flutter-tuned Dart language server plugin for Claude Code.
Gives Claude real semantic code intelligence on Flutter/Dart projects — goToDefinition, findReferences, workspaceSymbol, callHierarchy, and more — instead of relying on grep.
What it is
The Dart SDK already ships a full LSP server (dart language-server --protocol=lsp). Claude Code already knows how to talk to LSP servers. This plugin:
- Tells Claude Code how to spawn the Dart analysis server.
- Tunes its
initializationOptions and workspace settings for Claude's usage patterns (suppressing notification streams Claude ignores, disabling server features that only benefit IDE UIs).
- Bumps the startup timeout so cold Flutter boots don't get killed.
- Runs the server behind a small stdio proxy (
bin/proxy.dart) that works around two Claude Code limitations — see Proxy workarounds below.
No Dart SDK is bundled — you supply your own dart on PATH (which you already have if you run Flutter).
Requirements
- Claude Code 2.1.50+
- Dart SDK on
PATH (comes with Flutter)
Install
/plugin marketplace add stwime/claude-code-flutter-lsp
/plugin install dart-lsp@flutter-lsp
Restart Claude Code. Ask Claude to find references to a symbol — it should use LSP, not Grep.
What it tunes
Compared to a minimal dart language-server invocation:
| Setting | Value | Rationale |
|---|
startupTimeout | 120000 (120s) | Cold Flutter boot on large apps can exceed the default 60s |
initializationOptions.closingLabels | false | Dart-specific notification Claude Code doesn't consume — suppress the stream |
initializationOptions.outline | false | Same |
initializationOptions.flutterOutline | false | Same |
initializationOptions.allowOpenUri | false | Claude Code doesn't handle dart/openUri |
settings.dart.showTodos | false | Reduces diagnostic noise Claude sees |
settings.dart.inlayHints | false | Claude Code doesn't render hints; saves server work |
settings.dart.enableSnippets | false | Claude Code doesn't use completion snippets |
settings.dart.completeFunctionCalls | false | Same |
settings.dart.documentation | "full" | Best-quality hovers |
settings.dart.includeDependenciesInWorkspaceSymbols | true | Lets Claude find symbols from Flutter SDK / pub packages |
Project-specific excludes (build/, generated code, etc.) belong in your analysis_options.yaml — the Dart LSP honors the same analyzer.exclude: that flutter analyze does, so no duplication needed.
Proxy workarounds
Claude Code's LSP tool has two rough edges that hurt day-to-day use. Both are fixed in the plugin by a ~130-line stdio proxy (bin/proxy.dart) that sits between Claude Code and dart language-server. Every LSP message is forwarded byte-for-byte in both directions with exactly two intercepts:
1. workspaceSymbol query injection
The LSP tool schema is missing the query parameter that workspace/symbol requires by LSP spec. As a result Claude Code always sends {"query": ""} to the server, and the Dart analyzer returns nothing for empty queries. Tracked upstream at anthropics/claude-code#17149.
The proxy fixes this by reading a query from a sidechannel file and rewriting params.query in flight. Usage from an agent:
1. Write the query to `.claude/lsp_query.txt` in the project root.
2. Call LSP with operation=workspaceSymbol (the other arguments are ignored).
3. Results come back as normal.
The file is read on every workspace/symbol request. Empty or missing file = no rewrite (falls back to upstream behavior, which returns nothing).
Remove this workaround once #17149 ships upstream.
2. Dropped server notifications
LSP servers emit a stream of async notifications — diagnostics, log messages, progress reports — that Claude Code aggregates onto the next tool response as <new-diagnostics> system-reminders. For an agent working in discrete steps this means every tool call returns extra unrelated context: the answer to your query plus whatever the server pushed while you were waiting. On a codebase mid-refactor this can dominate every response.
The proxy drops these notifications on the server→client path: