Ruby LSP Now Has Official Claude Code Support
Up until now, Claude Code has understood your Ruby codebase the same way grep does. It reads files, searches for patterns, and infers structure from text. That works surprisingly well for many tasks. But it has a ceiling.
Ask Claude to explain a Rails model and it reads through the file, piecing together what the associations mean, inferring what the enum values map to, and hoping it catches all the scopes. It’s reading text and making educated guesses about structure. Ask it to find all callers of a private method before you delete it and it’s doing a best-effort text scan that can miss dynamic calls or alias references.
That ceiling just got raised. Ruby LSP now has official support in Claude Code.
What Is a Language Server Protocol
LSP is a standard that separates language intelligence from the editor. Before LSP, every editor had to implement its own understanding of every language. After LSP, you write the intelligence once as a language server, and any editor that speaks the protocol gets the benefits.
The language server sits alongside your code and maintains a live semantic model of your project. It tracks definitions, references, and symbol relationships across your entire project. When your editor asks “where is this method defined?” or “what are all the callers of this function?”, the server answers in milliseconds with accurate, complete information.
Ruby LSP is Shopify’s language server for Ruby. It integrates with Standard Ruby for diagnostics. In codebases that use Sorbet or Steep, those tools run as separate language servers and provide type information independently of Ruby LSP. Beyond the core server, there’s a growing community of add-on developers building extensions for it. awesome-ruby-lsp is a good starting point if you want to see what’s available.
What You Get
With Ruby LSP connected, Claude gains semantic code intelligence instead of relying on text search. Here’s what that means in practice.
Go to definition. Claude can jump directly to where a method, class, or constant is defined, including inside gems. No more manually tracking down gem source paths.
Find all references. Before a refactor, Claude can ask the language server for every reference to a symbol across your entire project. Dynamic calls included, because the LSP understands Ruby’s metaprogramming patterns well enough to handle common cases.
Call hierarchy. Claude can trace the full call chain for a method, both incoming callers and outgoing calls, across your entire project. That’s useful when you need to understand the impact of a change before making it.
Hover documentation. Claude can pull the YARD documentation for any method without having to search for it manually.
The difference shows up most in refactoring. When you ask Claude to rename a method or change a signature, it can now ask the LSP for the complete reference list before making a single change. That’s the difference between a confident refactor and a hopeful one.
Installing Ruby LSP in Claude Code
You’ll need Ruby LSP installed as a gem first:
gem install ruby-lsp
If you’re working in a bundled project, add it to your Gemfile in the development group:
group :development do
gem "ruby-lsp"
end
Then install the plugin from inside Claude Code:
/plugin install ruby-lsp@claude-plugins-official
That’s it. The next time you start a Claude Code session in a Ruby project, the LSP server starts alongside it. You don’t need to configure anything. Install it once and it works.
What Changes in Practice
Here’s what it looks like in a real session. I asked Claude about a model in a Rails app:
❯ Use ruby-lsp and tell me about the membership model
⏺ Searched for 1 pattern (ctrl+o to expand)
⏺ LSP(operation: "documentSymbol", file: "app/models/membership.rb")
⎿ Found 6 symbols (ctrl+o to expand)
⏺ Read 1 file (ctrl+o to expand)
⏺ Now let me get hover info on a few key symbols:
⏺ LSP(operation: "hover", symbol: "enum", in: "app/models/membership.rb")
⎿ Hover info available (ctrl+o to expand)
⏺ LSP(operation: "findReferences", symbol: "Membership", in: "app/models/membership.rb")
⎿ Found 24 references across 12 files (ctrl+o to expand)
⏺ Here's a summary of the Membership model:
app/models/membership.rb
A join model representing a user's membership in a tenant (organization).
Key traits:
- acts_as_tenant :tenant — multi-tenant scoped via ActsAsTenant
- belongs_to :tenant, belongs_to :user
- Roles (integer enum): member (1), admin (2), owner (3)
- scope :owners — filters members with the owner role
- Validations: user_id must be unique per tenant (one membership per user per org), role must be present
Used in 12 files across:
- Controllers: invitations, memberships, organization settings
- Services: AcceptInvitation, ConfirmUserEmail
- Specs: model spec, tenant isolation spec, system specs
The model is minimal — it's purely a pivot between User and Tenant with role-based access control baked in.
That’s a thorough summary. But I was curious what actually came from the LSP versus what Claude found by reading the file. So I asked:
❯ What information did you get from the lsp vs your own search
⏺ Good question. Here's the breakdown:
From LSP:
- documentSymbol — gave me the class name, method names, and line numbers without reading the file
(associations, scope, validations)
- hover on enum — confirmed it's ActiveRecord::Enum#enum with a link to the gem source definition
- findReferences — found all 24 references across 12 files without me doing any grep search
From my own file read (Read tool):
- The actual source code content — the enum values (member: 1, admin: 2, owner: 3), the acts_as_tenant
call, the uniqueness scope on the validation (scope: :tenant_id)
In practice:
- LSP gave me structure and cross-references (what symbols exist, where they're used project-wide)
- The file read gave me content (the actual values and implementation details)
The documentSymbol result notably omitted the acts_as_tenant call and the enum values — it only surfaced
Rails macro calls it recognized as meaningful symbols. So LSP alone would have left me with an incomplete
picture of the model.
That second turn is the honest answer. LSP and file reads are complementary. The LSP gives Claude structure and cross-references instantly: what symbols exist, where they’re called across the project, without spending context window tokens on file searches. The file read fills in the content: actual values, implementation details, anything the symbol index doesn’t capture. Together they produce a more complete picture than either alone.
That kind of structural understanding also changes how refactoring works. When you ask Claude to rename a class or change how something is called across the project, it queries the LSP for the complete reference list before making a single change. It isn’t guessing at usage patterns. It knows them.
One thing worth knowing: Ruby LSP works well without a type system. In my testing without Sorbet or Steep, the heuristic inference still produced accurate results for associations, method definitions, and references. If your codebase does use Sorbet or Steep, those type checkers can run alongside the LSP to improve accuracy further, but they’re not required to get value from this integration.
Text search got Claude surprisingly far. Semantic understanding gets it the rest of the way.
If you want help getting Claude Code dialed in for your Ruby workflow, whether that’s 1:1 or across your team, take a look at how I work with developers and teams.
Further Reading
More on building real systems
I write about AI integration, architecture decisions, and what actually works in production.
Occasional emails, no fluff.