Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sbt/sbt/llms.txt

Use this file to discover all available pages before exploring further.

The Build Server Protocol (BSP) is a standardized protocol that allows IDEs and development tools to communicate with build servers. sbt implements BSP to provide a consistent interface for tools like IntelliJ IDEA, VS Code (Metals), and other build clients.

What is BSP?

BSP defines a JSON-RPC-based protocol for communication between:
  • Build Client - IDE or development tool
  • Build Server - sbt or other build tool
This allows IDEs to:
  • Request compilation
  • Get build target information
  • Receive compilation diagnostics
  • Run tests
  • Resolve dependencies
BSP is language-agnostic and is also implemented by other build tools like Bazel, Mill, Gradle, and Maven. Learn more at build-server-protocol.github.io.

BSP in sbt Architecture

sbt’s BSP implementation is spread across two main areas:

1. Protocol Module

Location: protocol/src/main/contraband-scala/sbt/internal/bsp/ Defines BSP data types using Contraband:
FilePurpose
BuildTarget.scalaBuild target representation
BuildTargetIdentifier.scalaUnique target IDs
CompileParams.scalaCompilation request parameters
CompileReport.scalaCompilation results
BuildServerCapabilities.scalaServer capability declarations
BuildClientCapabilities.scalaClient capability declarations
BspConnectionDetails.scalaConnection configuration
BspCompileResult.scalaBSP-specific compile results
Example data types:
// BuildTargetIdentifier uniquely identifies a build target
case class BuildTargetIdentifier(uri: URI)

// BuildTarget describes a compilable unit
case class BuildTarget(
  id: BuildTargetIdentifier,
  displayName: String,
  baseDirectory: URI,
  tags: Seq[String],
  languageIds: Seq[String],
  dependencies: Seq[BuildTargetIdentifier],
  capabilities: BuildTargetCapabilities,
  dataKind: Option[String],
  data: Option[JValue]
)

2. Server Implementation

Location: main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala:1 Implements the BSP server within sbt:
object BuildServerProtocol {
  private val capabilities = BuildServerCapabilities(
    CompileProvider(BuildServerConnection.languages),
    TestProvider(BuildServerConnection.languages),
    RunProvider(BuildServerConnection.languages),
    DebugProvider(Vector.empty),
    dependencySourcesProvider = true,
    resourcesProvider = true,
    outputPathsProvider = true,
    canReload = true,
    jvmRunEnvironmentProvider = true,
    jvmTestEnvironmentProvider = true,
  )
}

Server Capabilities

sbt’s BSP server advertises these capabilities:
Supports compiling build targets.Languages: Scala, JavaHandles buildTarget/compile requests to trigger incremental compilation via Zinc.
Supports running tests.Languages: Scala, JavaHandles buildTarget/test requests to execute test frameworks.
Supports running main classes.Languages: Scala, JavaHandles buildTarget/run requests to execute applications.
Provides source JARs for dependencies.Handles buildTarget/dependencySources to enable IDE navigation to library code.
Provides resource directories.Handles buildTarget/resources to inform IDEs about resource files.
Provides compilation output directories.Handles buildTarget/outputPaths (added in sbt 1.8.0) to tell IDEs where compiled classes are located.
Provides JVM runtime configuration.Handles buildTarget/jvmRunEnvironment and buildTarget/jvmTestEnvironment for proper JVM setup.
Supports reloading the build.Handles build file changes and project reloading without restarting the server.

BSP Keys in sbt

sbt provides several keys for BSP configuration: Key file: main/src/main/scala/sbt/Keys.scala:1
// Create or update BSP connection files
val bspConfig = taskKey[Unit](
  "Create or update the BSP connection files"
)

// Enable/disable BSP for this build
val bspEnabled = settingKey[Boolean](
  "Enable/Disable BSP for this build, project or configuration"
)

// Should BSP export meta-targets for the SBT build itself?
val bspSbtEnabled = settingKey[Boolean](
  "Should BSP export meta-targets for the SBT build itself?"
)

// Mapping of BSP build targets to sbt scopes
val bspWorkspace = settingKey[Map[BuildTargetIdentifier, Scope]](
  "Mapping of BSP build targets to sbt scopes"
)

// List all BSP build targets
val bspWorkspaceBuildTargets = taskKey[Seq[BuildTarget]](
  "List all the BSP build targets"
)

// Description of the BSP build target
val bspBuildTarget = taskKey[BuildTarget](
  "Description of the BSP build targets"
)

Setting Up BSP

1

Enable BSP

BSP is enabled by default. To explicitly enable or disable:
// In build.sbt
Global / bspEnabled := true
2

Generate Connection File

Run the bspConfig task to create the BSP connection file:
sbt bspConfig
This creates .bsp/sbt.json in your project directory.
3

Connect from IDE

Your IDE (IntelliJ, VS Code with Metals) will detect the .bsp/sbt.json file and connect automatically.The connection file contains:
  • Server name and version
  • Command to start the BSP server
  • Supported languages

BSP Connection Details

The .bsp/sbt.json file contains:
{
  "name": "sbt",
  "version": "2.0.0-RC9",
  "bspVersion": "2.1.0",
  "languages": ["scala", "java"],
  "argv": [
    "sbt",
    "bspReload"
  ]
}
The argv field contains the command to start the BSP server. The IDE launches this command and communicates via stdin/stdout.

BSP Request Handlers

Implementation location: main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala:47 Key BSP endpoints implemented in sbt:

build/initialize

Initializes the BSP connection and exchanges capabilities.
// Returns BuildServerCapabilities
private val capabilities = BuildServerCapabilities(
  CompileProvider(BuildServerConnection.languages),
  TestProvider(BuildServerConnection.languages),
  // ... other capabilities
)

workspace/buildTargets

Returns all build targets in the workspace.
// Maps sbt projects to BSP build targets
val bspWorkspaceBuildTargets = taskKey[Seq[BuildTarget]](
  "List all the BSP build targets"
)

buildTarget/compile

Compiles specified build targets.
// Triggers incremental compilation and returns results
// Returns BspCompileResult with diagnostics

buildTarget/test

Runs tests for specified build targets.

buildTarget/dependencySources

Provides source JARs for dependencies.

bspReload

Custom sbt command for BSP reload: Key file: main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala:71
val bspReload = "bspReload"

lazy val commands: Seq[Command] = Seq(
  Command.single(bspReload) { (state, reqId) =>
    try {
      val newState = BuiltinCommands.doLoadProject(
        state, 
        Project.LoadAction.Current
      )
      exchange.respondEvent(JNull, Some(reqId), state.source)
      newState
    } catch {
      case NonFatal(e) =>
        val msg = ErrorHandling.reducedToString(e)
        exchange.respondError(
          ErrorCodes.InternalError, 
          msg, 
          Some(reqId), 
          state.source
        )
        state.fail
    }
  }
)

Build Target Mapping

sbt maps projects to BSP build targets:
  • Each sbt project becomes a BSP build target
  • Each configuration (Compile, Test) can be a separate target
  • Target IDs are URI-based for uniqueness
Example mapping:
sbt project: myproject
  → BSP target: myproject-compile (Compile configuration)
  → BSP target: myproject-test (Test configuration)

Configuration Scope

BSP can be configured at different scopes:
// Global scope - affects entire build
Global / bspEnabled := true

// Project scope - affects single project
ThisProject / bspEnabled := true

// Configuration scope - affects Test or Compile
Test / bspEnabled := false

Meta-targets for SBT Build

The bspSbtEnabled setting controls whether BSP exports targets for the sbt build itself (meta-build):
Global / bspSbtEnabled := true
This allows IDEs to:
  • Navigate sbt build definition code
  • Get autocompletion in build.sbt
  • Compile project/ directory

Error Handling

BSP implementation includes robust error handling: Key file: main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala:92
if (bspEnabled.value) {
  BuildServerConnection.writeConnectionFile(
    sbtVersion.value,
    (ThisBuild / baseDirectory).value
  )
} else {
  val logger = streams.value.log
  logger.warn("BSP is disabled for this build")
  logger.info("add 'Global / bspEnabled := true' to enable BSP")
}
If BSP is disabled and an IDE tries to connect, sbt will log a warning. Enable BSP with Global / bspEnabled := true in your build definition.

Debugging BSP

To debug BSP communication:
1

Enable verbose logging

sbt -DBSP_DEBUG=true
2

Check connection file

Verify .bsp/sbt.json exists and contains correct information.
3

Monitor BSP traffic

Use your IDE’s BSP debugging tools to view JSON-RPC messages.
4

Check sbt logs

BSP errors are logged to sbt’s standard output.

Limitations and Known Issues

Only one sbt instance should handle BSP per project directory. Running multiple instances may cause conflicts.
Some build file changes require BSP reload. IDEs should trigger bspReload when detecting build changes.
Debug support is currently limited (DebugProvider returns empty capabilities).

BSP Protocol Version

sbt implements BSP version 2.1.0, which includes:
  • Output paths support (since sbt 1.8.0)
  • JVM environment providers
  • Enhanced compilation reporting
See BSP Specification for protocol details.

Integration Examples

VS Code with Metals

Metals uses BSP to communicate with sbt:
  1. Metals detects .bsp/sbt.json
  2. Launches sbt BSP server
  3. Requests build targets
  4. Triggers compilation on file changes
  5. Displays diagnostics in editor

IntelliJ IDEA

IntelliJ can use BSP instead of its native sbt integration:
  1. Import project as BSP
  2. IntelliJ uses BSP for all build operations
  3. Benefits from standardized protocol

Further Reading

BSP Specification

Official Build Server Protocol documentation

Architecture

How BSP fits into sbt’s architecture

Protocol Module

Source code for protocol definitions

Server Implementation

BSP server implementation in sbt