AGENTS.md

  1# AGENTS.md
  2
  3This file provides guidance to AI coding agents when working with code in this repository.
  4
  5## Common Commands
  6
  7```bash
  8# Build the project
  9cargo build
 10
 11# Build in release mode
 12cargo build --release
 13
 14# Run the project (requires a config file)
 15cargo run -- --config sample_config.json
 16
 17# Run tests
 18cargo test
 19
 20# Run a specific test
 21cargo test <test_name>
 22
 23# Check for compilation errors without building
 24cargo check
 25
 26# Format code
 27cargo fmt
 28
 29# Run clippy linter
 30cargo clippy
 31```
 32
 33## Architecture Overview
 34
 35**lsp2mcp** is a bridge that translates arbitrary LSP (Language Server Protocol) servers into MCP (Model Context Protocol) servers, enabling AI coding agents to receive real-time diagnostics and ground truth about the code they write.
 36
 37### Three-Layer Architecture
 38
 39The application operates with three concurrent layers:
 40
 411. **MCP Server Layer** (`MCPServer` struct)
 42   - Exposes tools to AI agents via MCP protocol
 43   - Runs on stdio transport (reads from stdin, writes to stdout)
 44   - Currently implements a `read` tool that returns file content + LSP diagnostics
 45
 462. **LSP Client Layer** (`async_lsp::MainLoop`)
 47   - Acts as an LSP client to communicate with external LSP servers
 48   - Spawns the LSP server process as a subprocess (stdin/stdout pipes)
 49   - Handles LSP notifications (Progress, PublishDiagnostics, ShowMessage)
 50   - Router-based event handling with `LSPClientState`
 51
 523. **LSP Server Process** (spawned subprocess)
 53   - External process (e.g., rust-analyzer, typescript-language-server)
 54   - Configured via JSON config file (`sample_config.json`)
 55   - Killed automatically when parent process exits (`kill_on_drop: true`)
 56
 57### Execution Flow
 58
 59```
 60AI Agent (MCP Client)
 61    ↓ stdio
 62MCPServer::read() tool
 63    ↓ LSPServerSocket::document_diagnostic()
 64async_lsp MainLoop (LSP Client)
 65    ↓ subprocess stdin/stdout pipes
 66LSP Server Process (e.g., rust-analyzer)
 67```
 68
 69### Critical Concurrency Detail
 70
 71The application uses `futures::join!()` to run two async tasks concurrently:
 72- `mainloop_fut`: The LSP client mainloop reading/writing from LSP server subprocess
 73- `server.waiting()`: The MCP server awaiting requests from AI agents
 74
 75Both must run simultaneously because MCP tool calls need to send LSP requests and await responses.
 76
 77### Configuration
 78
 79The config file (`sample_config.json`) specifies:
 80- `lsp_server_command`: Command and args to spawn the LSP server subprocess
 81- `project_root`: Workspace root directory (passed to LSP server initialization and used for resolving relative file paths)
 82
 83File paths in tool arguments are relative to `project_root` and are canonicalized before use.
 84
 85### Key Framework Usage Patterns
 86
 87**rmcp (MCP server framework):**
 88- Use `#[tool_router]` macro on impl blocks to register tools
 89- Use `#[tool(description = "...")]` macro on methods to expose them as MCP tools
 90- Tool arguments must be deserializable (`serde::Deserialize`) and have JSON schema (`schemars::JsonSchema`)
 91- Extract parameters with `Parameters<T>` wrapper
 92- Return `Result<CallToolResult, MCPError>`
 93
 94**async-lsp (LSP client framework):**
 95- Use `Router::new()` with a state type (`LSPClientState`)
 96- Chain `.notification::<NotificationType>(handler)` to handle LSP notifications
 97- Handlers return `ControlFlow::Continue(())` or `ControlFlow::Break(result)`
 98- The `MainLoop::new_client()` creates both the mainloop runner and the `ServerSocket` client handle
 99- `ServerSocket` is cloneable and can be shared across async contexts (used in `MCPServer`)
100
101### Current Limitations and TODOs
102
103- Only the `read` tool is implemented; other tools like `edit`, `complete`, `definition`, etc. are not yet implemented
104- Diagnostics are fetched on-demand via `document_diagnostic` (pull-based), not via `PublishDiagnostics` notifications (push-based)
105- Commented-out `did_open` code suggests file opening/tracking may be needed for some LSP servers
106- Unused variables and dead code indicate work-in-progress state (see compiler warnings)
107
108### Adding New MCP Tools
109
110To add a new tool that leverages LSP capabilities:
111
1121. Add a method to the `#[tool_router] impl MCPServer` block
1132. Define argument and output structs with `serde::Deserialize` + `schemars::JsonSchema`
1143. Use `self.lsp_server.clone().<lsp_method>()` to call LSP methods
1154. Construct and return `CallToolResult` with diagnostics/results in `structured_content`
116
117Example LSP methods available via `LSPServerSocket`:
118- `document_diagnostic()` - Get diagnostics for a file
119- `completion()` - Get code completions
120- `goto_definition()` - Find symbol definitions
121- `hover()` - Get hover information
122- `did_open()`, `did_change()`, `did_close()` - Notify file changes