The odd one out: proxies metals-mcp (Scalameta's MCP server for Metals) over HTTP rather than LSP directly. Exposes 17 semantic tools as opposed to raw LSP methods.
Metals' LSP server itself doesn't integrate cleanly in every client due to Metals-specific build-import notifications that not every LSP client handles. metals-mcp is the supported path for tool-augmented agents.
brew install metalsor
coursier install metals-mcpEither puts a metals-mcp binary on PATH.
Your Scala projects need the sbt-bloop plugin so Metals can find compiled class files:
// project/plugins.sbt
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "2.0.12")Run sbt bloopInstall once per project.
build.sbtbuild.scbuild.mill
metals-direct start # cwd walk-up
metals-direct call list-modules '{}'
metals-direct call get-usages '{"fqcn":"com.example.Service.method","module":"core"}'
metals-direct call get-source '{"fqcn":"com.example.Service"}'
metals-direct call inspect '{"fqcn":"com.example.Service","module":"core"}'
metals-direct call glob-search '{"query":"UserService","fileInFocus":"/abs/path/to/any.scala"}'
metals-direct tools # full listNavigation + docs:
get-usages— find all call sites by fully-qualified class nameget-source— read source of a symbol (with or without method bodies)inspect— members + signatures of a class/object/traitget-docs— scaladoc for a symbol
Search:
glob-search— symbol name substring searchtyped-glob-search— substring search + kind filter (class/object/method/trait/package)find-dep— find maven coords for a dependency
Build + diagnostics:
list-modules— all build targetscompile-file,compile-module,compile-fullimport-build— re-import afterbuild.sbtchange
Testing:
test— run a test class or method
Formatting + refactoring:
format-filelist-scalafix-rules,run-scalafix-rule,generate-scalafix-rule
- Cold
.bloop/import: firstmetals-direct starton a fresh checkout takes 30-120s while Bloop imports the build. Subsequent calls are ~30ms. - External server adoption: if
<workspace>/.metals/mcp.jsonalready exists (IDE or prior session spawned a metals-mcp),metals-directadopts that server instead of spawning a new one.metals-direct stopunregisters but doesn't kill the external process. fileInFocusrequired for some tools:glob-search,typed-glob-search,compile-file,format-file,get-usagesneed an absolute path to any file in the target module. Without it, errors "Missing fileInFocus and failed to infer it".- FQCN discovery: tools that take
fqcnrequire the exact fully-qualified name. Don't guess — look at the package line at the top of the source file first.
- Cold: 30-120s (Bloop import on fresh
.bloop/) - Cold with adoption: ~0.1s
- Warm: ~30ms per call
~/.cache/metals-direct/<workspace-hash>/{pid,port,workspace,session,log}