How to navigate and refactor code with a language server (lsp)
This guide shows how to give mm a language server, so the model can ask the compiler where a symbol is defined, who uses it, what its type is, what is wrong in a file, and rename it everywhere — instead of grepping and guessing. It assumes mm runs with a configuration file you can edit, and that at least one language server is installed.
Steps
-
Install the server for your language and check it is on the
PATH:Language Server Install command/argsGo gopls go install golang.org/x/tools/gopls@latestgoplsTypeScript, JavaScript typescript-language-server npm i -g typescript typescript-language-servertypescript-language-server,["--stdio"]Python pyright pip install pyrightornpm i -g pyrightpyright-langserver,["--stdio"]Rust rust-analyzer rustup component add rust-analyzerrust-analyzerC, C++ clangd apt install clangd,brew install llvmclangdJava Eclipse JDT LS your package manager, or the jdtls release jdtlsZig zls zigtoolchain release, orbrew install zlszlswhich gopls(or the server's name) must answer. A server that is configured but not found is skipped with a warning at start-up. -
Declare the servers in the YAML file. The file extension picks the server:
lsp: servers: - name: gopls command: gopls extensions: [".go"] - name: typescript command: typescript-language-server args: ["--stdio"] extensions: [".ts", ".tsx", ".js", ".jsx"] - name: python command: pyright-langserver args: ["--stdio"] extensions: [".py"] - name: rust command: rust-analyzer extensions: [".rs"]The shipped
agent.lsp.yamlisagent.dmr.yamlwith this block (gopls on, the others ready to uncomment) and a system prompt written for the tool — start from it. -
Tell the model when to use the tool. A small model does not reach for a new tool by itself; the system prompt has to say so. The block below is the one in
agent.lsp.yaml; paste it into your ownsystem::CODE NAVIGATION (lsp) You have an `lsp` tool: it asks the project's language server what the compiler knows. Its description says which file extensions it covers. Use it INSTEAD of grep or reading whole files for anything about a symbol: - "where is X defined / declared" → action definition, symbol X - "who calls / uses X", "find usages" → action references, symbol X - "what is the type / signature of X" → action hover, symbol X - "what does this file contain" → action symbols, path only - "are there errors", after every edit → action diagnostics, path only - "rename X to Y everywhere" → action rename, symbol X, new_name Y (rename NEEDS new_name — the new identifier; never rename with edit_file: {"action": "rename", "path": "main.go", "symbol": "Greet", "new_name": "Hello"}) Point at a symbol with its NAME (the `symbol` field), exactly as written in the code; add `line` only to pick one occurrence among several. Do not guess columns. Answers are "path:line:column <source line>": quote them, they are the evidence. Before changing a symbol used in several files, call references and say how many places are affected. After editing, call diagnostics on each edited file and fix what it reports before running tests.The
lsp-navigationskill shipped inskills/says the same at more length; withskillsDirpointing at it, the model can load it withread_skillwhen the task calls for it. -
Start
mmfrom the project directory (the servers get it as their workspace root) and read the banner:[warning: lsp server ghost (no-such-lsp-server) not found on the PATH — skipped] mm v0.1.7 | config: agent.lsp.yaml | provider: dmr | model: … | ctx: unknown | tools: bash, read_skill, read_file, write_file, edit_file, lsp | skills: 4lspin thetools:column means at least one server was found. Servers start on their first use, not here. -
Ask. A few prompts to test the six actions, on any project the server covers:
Prompt What you should see Where is the function greet defined in main.go? Use lsp.🔎 lsp: definition main.go greetthen onemain.go:6:6 func greet(…)line, quoted in the answer.Who uses greet? List every call site.🔎 lsp: references main.go greet,N reference(s):with one line per site.What is the signature of greet?🔎 lsp: hover main.go greet, the server's signature and doc comment.What does main.go contain? Give me its outline.🔎 lsp: symbols main.go, one line per function, type, variable.Are there compile errors in main.go?🔎 lsp: diagnostics main.go,no diagnostics: main.go is cleanor one line per error.Rename greet to hello everywhere, then check main.go for errors.🔎 lsp: rename main.go greet → hello,renamed to hello: N edit(s) in M file(s), then adiagnosticscall.Add a parameter to greet. Before you edit, tell me how many places call it.referencesfirst, thenedit_file, thendiagnostics— the procedure the skill teaches.The recap line after the answer counts the calls:
⚙ 0 command(s) · 🔎 2 lsp call(s).
From an editor (ACP) and in the window mode
Nothing to configure: the same YAML applies. Navigation calls show up as tool calls of kind read, with no permission dialog; rename is kind edit and asks for permission like edit_file does (when yolo: false). The servers start in the directory mm was launched in — for an editor, the project it opened.
Variants
- A project in several languages. List one server per extension family; each starts only when a file of its kind is asked about.
- A server with settings. Pass them as
args(many servers read a flag or a config file in the project root);mmanswers "no settings" to a server that asks for configuration, so it runs with its defaults. - Exact positions. When the model has an exact position — from
read_filewithnumbered=true— it may sendlineandcolumn(1-based, inside the identifier) instead ofsymbol.symbolis the reliable road: small models rarely count columns right. - No server on this machine. The tool is not declared and the banner says
lsp: none of the configured servers is on the PATH — the lsp tool is not declared. The configuration can stay; the agent works without it. - Slow first answer. The first call for a language starts the server and lets it index the project;
diagnosticsmay sayas last publishedorno diagnostics received … within 3s— ask again once.
See also
- Built-in tools reference — the
lspfields, outputs and failures - Configuration reference — the
lsp.serverskeys and validation errors - Why one
lsptool, and why asymbolfield - Add a skill — the
lsp-navigationskill's format
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
|