Content
# sbt-mcp
An MCP (Model Context Protocol) server that connects to a running sbt server, enabling AI coding assistants like Claude to compile and run tests without JVM/sbt startup latency.
Instead of spawning a new `sbt` process for every command, sbt-mcp talks to the already-running sbt server over its Unix domain socket using the LSP/JSON-RPC protocol.
## Installation
```sh
go install github.com/patriknw/sbt-mcp@latest
```
Or build from source:
```sh
go build .
```
## Usage
### 1. Start sbt in your project
In a terminal, start sbt and wait for the shell prompt:
```sh
cd your-sbt-project
sbt
```
### 2. Configure your AI assistant
Add to `.mcp.json` in your project root:
```json
{
"mcpServers": {
"sbt": {
"command": "sbt-mcp"
}
}
}
```
### 3. Use it
The assistant now has access to an `sbt` tool that executes commands on the running sbt server. Examples:
(Don't want to start sbt yourself? See [Autostart](#autostart).)
- `compile`
- `runtime-core/compile`
- `testOnly com.example.MySpec`
- `runtime-core/testOnly com.example.MySpec -- -t "should handle deposits"`
- `clean`, `reload`, `scalafmtAll`
## Autostart
By default sbt-mcp **attaches** to an sbt server you started yourself (see step 1) and
does nothing if none is running — a tool call then returns a clear "sbt server is not
running" error telling you to start `sbt`.
With **autostart** enabled, sbt-mcp will instead start the sbt server itself when no
usable one is running, and shut it down again when it exits. Enable it with the
`--autostart` flag or the `SBT_MCP_AUTOSTART=1` environment variable:
```json
{
"mcpServers": {
"sbt": {
"command": "sbt-mcp",
"args": ["--autostart"]
}
}
}
```
How it behaves when autostart is on:
- **No server running** (no `active.json`, or it points to a stale socket): sbt-mcp runs
`sbt` in the project directory, waits for the server to come up, then connects. The
first command can take a while on a large project (sbt/JVM boot). The wait timeout
defaults to 180s and is configurable with `--autostart-timeout <seconds>` or
`SBT_MCP_AUTOSTART_TIMEOUT`.
- **A server is already running**: sbt-mcp attaches to it and never starts a second one.
- **Lifecycle**: sbt-mcp owns only the server it started. When the MCP server exits — on
normal shutdown or on `SIGINT`/`SIGTERM` from the MCP host — it closes that server's
stdin and, if needed, kills its process group, so no orphan JVM is left behind. A
server you started yourself is never touched.
sbt's boot output is forwarded to sbt-mcp's stderr (the MCP log).
**When to use which:**
- *Default (attach-only)* — you keep an `sbt` shell open while working. You see its
output live and can run commands in it directly. Recommended for interactive use.
- *Autostart* — headless or CI environments where nothing else starts sbt, so the MCP
server has to bring it up (and tear it down) on its own.
## Tool parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `command` | string | *required* | sbt command, exactly as typed in the sbt shell |
| `directory` | string | `.` | Project directory containing `build.sbt` |
| `tail_lines` | number | `20` | Return last N lines of output. Set to 0 to disable. Ignored when `grep_pattern` is set. |
| `grep_pattern` | string | | Filter output with `grep -iE`. Supports extended regex, e.g. `FAILED\|should\|error`. |
| `head_lines` | number | `0` | Limit grep results to first N matches. Only applies with `grep_pattern`. |
| `timeout` | number | `300` | Command timeout in seconds. |
## Output filtering
sbt output can be verbose. The tool filters output to reduce token usage, mirroring common shell patterns:
```sh
# Default: last 20 lines (like sbt "compile" 2>&1 | tail -20)
{"command": "compile", "tail_lines": 20}
# Grep for failures (like ... | grep -iE "FAILED|error" | head -20)
{"command": "testOnly com.example.MySpec", "grep_pattern": "FAILED|error", "head_lines": 20}
# All output
{"command": "compile", "tail_lines": 0}
```
## How it works
1. Reads `project/target/active.json` to discover the sbt server's Unix domain socket
2. Connects and performs the LSP `initialize` handshake
3. Sends commands via `sbt/exec` JSON-RPC requests
4. Collects output from `window/logMessage` notifications
5. Filters output using `grep`/`tail`/`head` and returns structured JSON
The connection persists across tool calls — no reconnection overhead.
## Requirements
- sbt 1.1+ (sbt server support)
- macOS or Linux (Unix domain sockets)
- Go 1.23+ (to build from source)
## License
Apache License 2.0