scala-mcp

[md]Quick start

This page walks you from an empty sbt project to a runnable MCP server. The same tool is wired to two transports below — pick stdio for subprocess-based clients (Claude Desktop, Claude Code) or HTTP for a networked server.

Adding the dependency

// For stdio-based servers
libraryDependencies ++= Seq(
  "net.andimiller.mcp" %%% "mcp-core"  % "0.10.0",
  "net.andimiller.mcp" %%% "mcp-stdio" % "0.10.0"
)

// For HTTP-based servers
libraryDependencies ++= Seq(
  "net.andimiller.mcp" %%% "mcp-core"   % "0.10.0",
  "net.andimiller.mcp" %%% "mcp-http4s" % "0.10.0"
)

// Optional: Redis-backed session/state for stateful HTTP servers
libraryDependencies += "net.andimiller.mcp" %% "mcp-redis" % "0.10.0"

Defining a tool

A tool needs typed request/response case classes — derives JsonSchema plus a circe codec for each. The fluent tool builder gives the schemas + handler to the server:

import cats.effect.IO
import io.circe.{Decoder, Encoder}
import net.andimiller.mcp.core.schema.JsonSchema
import net.andimiller.mcp.core.server.*

case class GreetRequest(name: String) derives JsonSchema, Decoder
case class GreetResponse(message: String) derives JsonSchema, Encoder.AsObject

val greetTool: Tool.Resolved[IO] =
  tool.name("greet")
    .description("Greet someone by name")
    .in[GreetRequest]
    .out[GreetResponse]
    .run(req => IO.pure(GreetResponse(s"Hello, ${req.name}!")))

Stdio server

A Server[IO] built with ServerBuilder becomes a stdio server by handing it to StdioTransport.run. This is what Claude Desktop / Claude Code expect for local MCP servers:

import cats.effect.{IO, IOApp}
import net.andimiller.mcp.core.server.*
import net.andimiller.mcp.stdio.StdioTransport

object MyStdioServer extends IOApp.Simple:
  def server: IO[Server[IO]] =
    ServerBuilder[IO]("my-server", "1.0.0")
      .withTool(greetTool)
      .build

  def run: IO[Unit] = server.flatMap(StdioTransport.run[IO])

Run it with sbt run (or nativeLink for a Scala Native binary) and register the resulting command in your client's MCP config.

HTTP server

Same tool, wired into McpHttp.basic[IO] and served on a port via http4s Ember. This is what you want for networked clients (Cursor's HTTP transport, remote agents, multi-user setups):

import cats.effect.{IO, IOApp}
import com.comcast.ip4s.*
import net.andimiller.mcp.http4s.McpHttp

object MyHttpServer extends IOApp.Simple:
  def run: IO[Unit] =
    McpHttp.basic[IO]
      .name("my-server").version("1.0.0")
      .port(port"8080")
      .withTool(greetTool)
      .serve
      .useForever

Hit it at http://localhost:8080/mcp.

Where to next

The fluent builders shown above (tool, resource, resourceTemplate, prompt, plus their contextual* variants) are documented in detail in Tools, Resources, and Prompts. Server construction covers the streaming HTTP builder with per-session state, authentication, and the embedded Explorer UI. Examples has six end-to-end runnable servers covering every transport.