I found myself starting to write a lint tool for Backseat Driver (which is a CoPilot extension as well as an MCP server). However, then I thought that it would be better if clojure-lsp provided this tool. Any plans/ideas for language model and MCP extensions for clojure-lsp cooking, @ericdallo?
alternatively as long as relevant features are available as functions, clojure-lsp can just be used as a library by clojure-mcp to expose them as tools
next release (today hehe) will add support for custom linters, but they are custom using clojure-lsp analysis, not supported something external, I'm yet to explore that @pez since MCPs are exploding I just to avoid adding too much complexity to clojure-lsp, especially that MCP is a LSP like, I know there are people using lsp4clj to create MCPs but that's is more related to the infra than linting capabilities
Makes sense. I chose to make Backseat Driver a separate extension to get a cleaner cut.
Yeah, Nubank is pretty aggressive with AI, so I believe we will check MCPs and all of that integration with clojure soon
But at start, I don't see that much sense merge this AI stuff with clojure-lsp unless in a generic way like external linters and so
@bhauman has built what amounts to an editor for AI with his Clojure MCP tooling. It lints the code too.
Thinking about it, someone may have already made an LSP MCP server…
I think in addition to the command line interface Clojure LSP could also have an MCP interface, similar to how Playwright provides an interface to their browser stuff, maybe that’s the best way how to think about it? So nothing is added to Clojure-LSP, it’s just one extra layer around how to access it?
It's not clear to me how those things work yet, but if it's generic enough I'd agree it may make sense
My first web search hit: https://github.com/isaacphi/mcp-language-server
I think a issue would be nice so we can map and explore more, I have MCP protocol page opened for 2 weeks on my machine 😂 need to stop and learn about it
since lsp is already a standard protocol I wouldn't be surprised if there would be a generic MCP <-> LSP converter thing somewhere, without being specific to Clojure
oh that is the thing that @pez linked to
would be nice to test that with clojure-lsp
CoPilot can use it.
That's nice, Mr Clojure fan 😂
do you mean copilot can use lsp out of the box?
I mean copilot can use the MCP Language Server (the MCP <-> LSP) server.
CoPilot also has built in tools for all of this, so it is not actually adding features to CoPilot that it doesn’t have. But as a general answer to “can clojure-lsp features be exposed to MCP clients this way?” it works as a test. 😃
CoPilot is an LSP client too, I guess we can think of it as.
For the problem I am trying to solve, I may still need to make a direct use of clojure-lsp linting from Backseat Driver, but this was enlightening anyway. 😃
It’s beautiful: CoPilot MCP Client -> MCP Language Server -> clojure-lsp -> clj-kondo
My mcp.json, for anyone who wants to repeat the experiment:
{
"servers": {
"lsp": {
"type": "stdio",
"command": "/Users/pez/go/bin/mcp-language-server",
"args": [
"--workspace",
"${workspaceFolder}",
"--lsp",
"${extensionInstallFolder:betterthantomorrow.calva}/clojure-lsp"
]
}
}
}
that mcp-language-server is interesting indeed, @pez could you elabora a little bit more your case for backseat driver?
1. Inspired y Clojure MCP, I want to add an edit_file tool to Backseat Driver. Because the built in tool for this too often ends up trashing the bracket balance. I have a tool for balancing brackets, but it doesn’t work because it seems the main breakage happens at applying edits. 2. CoPilot has tools for checking the linters reports, but it most often the agent choses to ignore to use it. So it happily will announce that it has done stuff while clj-kondo is screaming “THIS WILL NOT WORK!“. So as part of editing I want to lint and feed this back to the agent, liek “file edited, but the linter doesn’t like it, here’s what it says: …“.
got it, and IIRC using the mcp-language-server you can have this MCP that communicates with clojure-lsp and then your and ask to check the diagnostics of it, right?
I don’t think I have much use for the MCP server for this. Backseat Driver is a VS Code extension, I will try to go straight at clojure-lsp, or straight at the error reports (I will try the latter first). Also: a value prop of Backseat Driver is zero conf. It doesn’t require any MCP things, and certainly not going through the loops and hoops installing go and then the server and doing all the conf for it.
I see, agree about the zero conf rationale
MCP is a way to describe a set of interfaces to the LLM, so it knows how to use it with a layer to then call those interfaces. So clojure-lsp could expose an MCP server endpoint, that an LLM can then call to retrieve a description of how it can go ahead and call it back in order to perform various tasks. It could tell the LLM how to use clojure-lsp to perform a code refactor, or to retrieve code information, and so on. Then an MCP client, something that wraps an LLM provides a layer to actually call the MCP server. The LLM fetches the interface descriptions from all the configured MCP servers (using the client) and then from that description it knows how to generate a "JSON" CALL, the client interprets that JSON and makes the actual call based on it, and then returns the results back to the LLM.
But clojure-lsp doesn't have to expose it's functionality through MCP, another thing could wrap it in an MCP server. And since lsp is already standard, maybe a generic MCP server for lsp could do it. That said, the description of the available functions maybe could benefit to be specialized to clojure-lsp so the model better understands when/how to leverage them.
thanks for the explanation @didibus! > And since lsp is already standard, maybe a generic MCP server for lsp could do it. agreed, as I mentioned https://clojurians.slack.com/archives/CPABC1H61/p1748352836049329?thread_ts=1748337784.287829&cid=CPABC1H61, that's how I see it, just not sure how smooth is that integration or if having that exposed in clojure-lsp would help further more > That said, the description of the available functions maybe could benefit to be specialized to clojure-lsp so the model better understands when/how to leverage them. Not sure what means making this description of the available functions available
The MCP server returns available tools like:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_weather",
"description": "Retrieve the current weather for a specified city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Name of the city to get the weather for."
}
},
"required": ["city"]
}
},
{
"name": "calculate_sum",
"description": "Calculate the sum of two numbers.",
"parameters": {
"type": "object",
"properties": {
"a": {
"type": "number",
"description": "First number."
},
"b": {
"type": "number",
"description": "Second number."
}
},
"required": ["a", "b"]
}
}
]
}
}
So making those specialized to Clojure might help the LLM decide when to use each and how best to use them.
Like if lsp just has something like: rename_module and a generic description "Renames the module"
It might work better if it is exposed to the LLM as rename_namespace "Rename the Clojure namespace and automatically rename all use of it throughout the Clojure project"Now if you prompt the LLM with: Can you rename my namespace foo to bar? It'll probably better understand that it can use the above tool for it.
Got it, that makes sense, actually there are multiple clojure-lsp commands that handle paredit cases and so on that I think would help LLM as well.
Yeah, interesting, I can see clojure-lsp working as a mcp server to enhance that, Ill take a closer look at this soon
> And since lsp is already standard, maybe a generic MCP server for lsp could do it. That has been confirmed to hold true earlier in this thread. 😃
yes for most features it works, but I believe is not great as there are custom commands and custom lsp features not included in default protocol (ex paredit custom commands) that we could describe better, well, needs some testing to confirm that too
clojure-lsp Released clojure-lsp https://github.com/clojure-lsp/clojure-lsp/releases/tag/2025.05.27-13.56.57 with exciting new features, performance improvements and fixes 🎉
• General
◦ Consider .lsp/config.edn as part of project code, removing false positives of unused-public-var linter.
◦ Consider full qualified symbols in edn files when checking for var references.
◦ Improve clojure-lsp linter capabilities, migrating unused-public-var and different-aliases linters to be built-in linters. https://github.com/clojure-lsp/clojure-lsp/pull/2050
▪︎ Migrate from clj-kondo custom-lint-fn but considering kondo settings to avoid breaking changes.
◦ Considerably improve performance of unused-public-var and different-aliases linters.
◦ Bump rewrite-clj to 1.2.50.
◦ New feature: Add support for custom project linters. https://github.com/clojure-lsp/clojure-lsp/issues/2043 https://github.com/clojure-lsp/clojure-lsp/issues/2058
◦ Publish to clojars com.github.clojure-lsp/clojure-lsp-test-helper to be able to test created custom linters.
◦ Bump clj-kondo to 2025.04.08-20250526.195207-12.
◦ Small performance improvements across clojure-lsp, especially on places with comparassions inside big loops.
◦ Bump clj-depend to 0.11.1.
◦ Provide analysis of unresolved namespaces, making features like definition, hover, references work.
• Editor
◦ Add support for LSP feature textDocument/selectionRange. https://github.com/clojure-lsp/clojure-lsp/issues/1961
◦ Fix outdated analysis for watched created/deleted files (git branch switchs for example). https://github.com/clojure-lsp/clojure-lsp/issues/2046
• API/CLI
◦ Replace tools.cli with babashka.cli. https://github.com/clojure-lsp/clojure-lsp/issues/2036
◦ Include :clj-kondo-settings to dump data.
Main highlights:
• Have you ever wanted to create custom linters about your whole project with awareness about your codebase? Like imagine linters about missing tests in your codebase (pic1) 🚀
◦ Now it's possible via clojure-lsp leveraging sci, more details https://clojure-lsp.io/features/#custom-linters, this was a huge work that @andreribeirocamargo and I did and I'm very excited about it, making possible to create complex linters for better code quality, with support for tests!
• No need to restart LSP anymore when changing git branches, clojure-lsp should lint the changed files automatically staying consistent.
• clojure-lsp now consider references of vars in edn files, avoiding false-positives of unused-public-var.
• We improved performance of LSP linters, completely migrating then to be built-in clojure-lsp, decoupling them from clj-kondo.
happy coding! 👋
the custom linter stuff looks very cool
i see i commented on one of the issues 3 weeks ago and never responded 🙈
ah yeah, that idea is interesting, but needs more thoughs