Skip to content

cleanup/0.1.0 #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
run: go install golang.org/x/tools/gopls@latest

- name: Run Go integration tests
run: go test ./integrationtests/languages/go/...
run: go test ./integrationtests/tests/go/...

python-integration-tests:
name: Python Integration Tests
Expand All @@ -77,7 +77,7 @@ jobs:
run: npm install -g pyright

- name: Run Python integration tests
run: go test ./integrationtests/languages/python/...
run: go test ./integrationtests/tests/python/...

rust-integration-tests:
name: Rust Integration Tests
Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
- run: rust-analyzer --version

- name: Run Rust integration tests
run: go test ./integrationtests/languages/rust/...
run: go test ./integrationtests/tests/rust/...

typescript-integration-tests:
name: TypeScript Integration Tests
Expand All @@ -130,4 +130,4 @@ jobs:
run: npm install -g typescript typescript-language-server

- name: Run TypeScript integration tests
run: go test ./integrationtests/languages/typescript/...
run: go test ./integrationtests/tests/typescript/...
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ test-output/
# Temporary files
*~

CLAUDE.md
CLAUDE.md*
.mcp.json
.goosehints
270 changes: 160 additions & 110 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,130 +5,180 @@
[![GoDoc](https://pkg.go.dev/badge/github.com/isaacphi/mcp-language-server)](https://pkg.go.dev/github.com/isaacphi/mcp-language-server)
[![Go Version](https://img.shields.io/github/go-mod/go-version/isaacphi/mcp-language-server)](https://github.com/isaacphi/mcp-language-server/blob/main/go.mod)

A Model Context Protocol (MCP) server that runs a language server and provides tools for communicating with it.
This is an [MCP](https://modelcontextprotocol.io/introduction) server that runs and exposes a [language server](https://microsoft.github.io/language-server-protocol/) to LLMs. Not a language server for MCP, whatever that would be.

## Motivation
## Demo

Claude desktop with the [filesystem](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) server feels like magic when working on small projects. This starts to fall apart after you add a few files and imports. With this project, I want to create that experience when working with large projects.
`mcp-language-server` helps MCP enabled clients navigate codebases more easily by giving them access semantic tools like get definition, references, rename, and diagnostics.

Language servers excel at tasks that LLMs often struggle with, such as precisely understanding types, understanding relationships, and providing accurate symbol references. This project aims to makes bring those tools to LLMs. LSP also seems like a clear inspiration for MCP so why not jam them together?

## Status

⚠️ Pre-beta Quality ⚠️

I have tested this server with the following language servers

- gopls (Go)
- pyright (Python)
- typescript-language-server (TypeScript)
- rust-analyzer (Rust)

But it should be compatible with many more.

## Tools

- `read_definition`: Retrieves the complete source code definition of any symbol (function, type, constant, etc.) from your codebase.
- `find_references`: Locates all usages and references of a symbol throughout the codebase.
- `get_diagnostics`: Provides diagnostic information for a specific file, including warnings and errors.
- `get_codelens`: Retrieves code lens hints for additional context and actions on your code.
- `execute_codelens`: Runs a code lens action.
- `apply_text_edit`: Allows making multiple text edits to a file programmatically.
- `hover`: Display documentation, type hints, or other hover information for a given location.
- `rename_symbol`: Rename a symbol across a project.

Behind the scenes, this MCP server can act on `workspace/applyEdit` requests from the language server, so it can apply things like refactor requests, adding imports, formatting code, etc.

Each tool supports various options for customizing output, such as including line numbers or additional context. See the tool documentation for detailed usage. Line numbers are necessary for `apply_text_edit` to be able to make accurate edits.

## About

This codebase makes use of edited code from [gopls](https://go.googlesource.com/tools/+/refs/heads/master/gopls/internal/protocol) to handle LSP communication. See ATTRIBUTION for details.

[mcp-go](https://github.com/mark3labs/mcp-go) is used for MCP communication.

## Prerequisites

Install Go: Follow instructions at <https://golang.org/doc/install>

Fetch or update this server:

```bash
go install github.com/isaacphi/mcp-language-server@latest
```

Install a language server for your codebase:

- Python (pyright): `npm install -g pyright`
- TypeScript (typescript-language-server): `npm install -g typescript typescript-language-server`
- Go (gopls): `go install golang.org/x/tools/gopls@latest`
- Rust (rust-analyzer): `rustup component add rust-analyzer`
- Or use any language server
![Demo](demo.gif)

## Setup

Add something like the following configuration to your Claude Desktop settings (or similar MCP-enabled client):
1. **Install Go**: Follow instructions at <https://golang.org/doc/install>
2. **Install or update this server**: `go install github.com/isaacphi/mcp-language-server@latest`
3. **Install a language server**: _follow one of the guides below_
4. **Configure your MCP client**: _follow one of the guides below_

```json
<details>
<summary>Go (gopls)</summary>
<div>
<p><strong>Install gopls</strong>: <code>go install golang.org/x/tools/gopls@latest</code></p>
<p><strong>Configure your MCP client</strong>: This will be different but similar for each client. For Claude Desktop, add the following to <code>~/Library/Application\ Support/Claude/claude_desktop_config.json</code></p>

<pre>
{
"mcpServers": {
"language-server": {
"command": "mcp-language-server",
"args": ["--workspace", "/Users/you/dev/yourproject/", "--lsp", "gopls"],
"env": {
"PATH": "/opt/homebrew/bin:/Users/you/go/bin",
"GOPATH": "/users/you/go",
"GOCACHE": "/users/you/Library/Caches/go-build",
"GOMODCACHE": "/Users/you/go/pkg/mod"
}
}
}
}
</pre>

<p><strong>Note</strong>: Not all clients will need these environment variables. For Claude Desktop you will need to update the environment variables above based on your machine and username:</p>
<ul>
<li><code>PATH</code> needs to contain the path to <code>go</code> and to <code>gopls</code>. Get this with <code>echo $(which go):$(which gopls)</code></li>
<li><code>GOPATH</code>, <code>GOCACHE</code>, and <code>GOMODCACHE</code> may be different on your machine. These are the defaults.</li>
</ul>

</div>
</details>
<details>
<summary>Rust (rust-analyzer)</summary>
<div>
<p><strong>Install rust-analyzer</strong>: <code>rustup component add rust-analyzer</code></p>
<p><strong>Configure your MCP client</strong>: This will be different but similar for each client. For Claude Desktop, add the following to <code>~/Library/Application\ Support/Claude/claude_desktop_config.json</code></p>

<pre>
{
"mcpServers": {
"language-server": {
"command": "mcp-language-server",
"args": [
"--workspace",
"/Users/you/dev/yourcodebase",
"/Users/you/dev/yourproject/",
"--lsp",
"/opt/homebrew/bin/pyright-langserver",
"rust-analyzer"
]
}
}
}
</pre>
</div>
</details>
<details>
<summary>Python (pyright)</summary>
<div>
<p><strong>Install pyright</strong>: <code>npm install -g pyright</code></p>
<p><strong>Configure your MCP client</strong>: This will be different but similar for each client. For Claude Desktop, add the following to <code>~/Library/Application\ Support/Claude/claude_desktop_config.json</code></p>

<pre>
{
"mcpServers": {
"language-server": {
"command": "mcp-language-server",
"args": [
"--workspace",
"/Users/you/dev/yourproject/",
"--lsp",
"pyright-langserver",
"--",
"--stdio"
],
"env": {
"LOG_LEVEL": "INFO"
}
]
}
}
}
```
</pre>
</div>
</details>
<details>
<summary>Typescript (typescript-language-server)</summary>
<div>
<p><strong>Install typescript-language-server</strong>: <code>npm install -g typescript typescript-language-server</code></p>
<p><strong>Configure your MCP client</strong>: This will be different but similar for each client. For Claude Desktop, add the following to <code>~/Library/Application\ Support/Claude/claude_desktop_config.json</code></p>

<pre>
{
"mcpServers": {
"language-server": {
"command": "mcp-language-server",
"args": [
"--workspace",
"/Users/you/dev/yourproject/",
"--lsp",
"typescript-language-server",
"--",
"--stdio"
]
}
}
}
</pre>
</div>
</details>
<details>
<summary>Other</summary>
<div>
<p>I have only tested this repo with the servers above but it should be compatible with many more. Note:</p>
<ul>
<li>The language server must communicate over stdio.</li>
<li>Any aruments after <code>--</code> are sent as arguments to the language server.</li>
<li>Any env variables are passed on to the language server.</li>
</ul>
</div>
</details>

Replace:
## Tools

- `/Users/you/dev/yourcodebase` with the absolute path to your project
- `/opt/homebrew/bin/pyright-langserver` with the path to your language server (found using `which` command e.g. `which pyright-langserver`)
- Any aruments after `--` are sent as arguments to your language server.
- Any env variables are passed on to the language server. Some may be necessary for you language server. For example, `gopls` required `GOPATH` and `GOCACHE` in order for me to get it working properly.
- `LOG_LEVEL` is optional. See below.
- `definition`: Retrieves the complete source code definition of any symbol (function, type, constant, etc.) from your codebase.
- `references`: Locates all usages and references of a symbol throughout the codebase.
- `diagnostics`: Provides diagnostic information for a specific file, including warnings and errors.
- `hover`: Display documentation, type hints, or other hover information for a given location.
- `rename_symbol`: Rename a symbol across a project.
- `edit_file`: Allows making multiple text edits to a file based on line numbers. Provides a more reliable and context-economical way to edit files compared to search and replace based edit tools.

## Development
## About

Clone the repository:
This codebase makes use of edited code from [gopls](https://go.googlesource.com/tools/+/refs/heads/master/gopls/internal/protocol) to handle LSP communication. See ATTRIBUTION for details. Everything here is covered by a permissive BSD style license.

```bash
git clone https://github.com/isaacphi/mcp-language-server.git
cd mcp-language-server
```
[mcp-go](https://github.com/mark3labs/mcp-go) is used for MCP communication. Thank you for your service.

Install dev dependencies:
This is beta software. Please let me know by creating an issue if you run into any problems or have suggestions of any kind.

```bash
go mod download
```
## Contributing

Build:
Please keep PRs small and open Issues first for anything substantial. AI slop OK as long as it is tested, passes checks, and doesn't smell too bad.

```bash
go build
```
### Setup

Run tests:
Clone the repo:

```bash
go test ./...
git clone https://github.com/isaacphi/mcp-language-server.git
cd mcp-language-server
```

Update test snapshots:
A [justfile](https://just.systems/man/en/) is included for convenience:

```bash
UPDATE_SNAPSHOTS=true go test ./integrationtests/...
just -l
Available recipes:
build # Build
check # Run code audit checks
fmt # Format code
generate # Generate LSP types and methods
help # Help
install # Install locally
snapshot # Update snapshot tests
test # Run tests
```

Configure your Claude Desktop (or similar) to use the local binary:
Expand All @@ -142,7 +192,7 @@ Configure your Claude Desktop (or similar) to use the local binary:
"--workspace",
"/path/to/workspace",
"--lsp",
"/path/to/language/server"
"language-server-executable"
],
"env": {
"LOG_LEVEL": "DEBUG"
Expand All @@ -154,28 +204,28 @@ Configure your Claude Desktop (or similar) to use the local binary:

Rebuild after making changes.

## Feedback
### Logging

Setting the `LOG_LEVEL` environment variable to DEBUG enables verbose logging to stderr for all components including messages to and from the language server and the language server's logs.

Include
### LSP interaction

- `internal/lsp/methods.go` contains generated code to make calls to the connected language server.
- `internal/protocol/tsprotocol.go` contains generated code for LSP types. I borrowed this from `gopls`'s source code. Thank you for your service.
- LSP allows language servers to return different types for the same methods. Go doesn't like this so there are some ugly workarounds in `internal/protocol/interfaces.go`.

### Local Development and Snapshot Tests

There is a snapshot test suite that makes it a lot easier to try out changes to tools. These run actual language servers on mock workspaces and capture output and logs.

You will need the language servers installed locally to run them. There are tests for go, rust, python, and typescript.

```
env: {
"LOG_LEVEL": "DEBUG",
}
integrationtests/
├── tests/ # Tests are in this folder
├── snapshots/ # Snapshots of tool outputs
├── test-output/ # Gitignored folder showing the final state of each workspace and logs after each test run
└── workspaces/ # Mock workspaces that the tools run on
```

To get detailed LSP and application logs. Setting `LOG_LEVEL` to DEBUG enables verbose logging for all components. Adding `LOG_COMPONENT_LEVELS` with `wire:DEBUG` shows raw LSP JSON messages. Please include as much information as possible when opening issues.

The following features are on my radar:

- [x] Read definition
- [x] Get references
- [x] Apply edit
- [x] Get diagnostics
- [x] Code lens
- [x] Hover info
- [x] Rename symbol
- [ ] Code actions
- [ ] Better handling of context and cancellation
- [ ] Add LSP server configuration options and presets for common languages
- [ ] Create tools at a higher level of abstraction, combining diagnostics, code lens, hover, and code actions when reading definitions or references.
To update snapshots, run `UPDATE_SNAPSHOTS=true go test ./integrationtests/...`
Loading
Loading