GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - softwaremill/chimp: Build type-safe, boilerplate-less MCP servers and clients in Scala

BranchesTags
Open more actions menu

Latest commit

 

History

296 Commits
296 Commits

Folders and files

NameName
Last commit message
Last commit date
.claude/skills/commit
.claude/skills/commit
 
 
.devcontainer
.devcontainer
 
 
.github
.github
 
 
client-conformance
client-conformance
 
 
client-streaming
client-streaming
 
 
client/src
client/src
 
 
core/src
core/src
 
 
docs
docs
 
 
examples
examples
 
 
generated-docs/out
generated-docs/out
 
 
project
project
 
 
server-conformance
server-conformance
 
 
server-streaming
server-streaming
 
 
server/src
server/src
 
 
.git-blame-ignore-revs
.git-blame-ignore-revs
 
 
.gitignore
.gitignore
 
 
.readthedocs.yaml
.readthedocs.yaml
 
 
.scala-steward.conf
.scala-steward.conf
 
 
.scalafmt.conf
.scalafmt.conf
 
 
AGENTS.md
AGENTS.md
 
 
CLAUDE.md
CLAUDE.md
 
 
LICENSE
LICENSE
 
 
README.md
README.md
 
 
build.sbt
build.sbt
 
 
conformance-baseline.yml
conformance-baseline.yml
 
 

Repository files navigation

Chimp MCP

CI Scala 3

An SDK for building MCP (Model Context Protocol) servers and clients in Scala 3 using boilerplate-less, type-safe APIs based on Tapir and sttp, supporting the variety of the Scala ecosystem.

Transport

Chimp implements both streamable HTTP and stdio transports. Additional integration modules unlock streaming features of the MCP protocol with bidirectional communication between server and client. Currently supporting Ox, ZIO and Pekko.

Quickstart

HTTP

Run a basic MCP server with Netty exposing a simple adder tool:

//> using dep com.softwaremill.chimp::chimp-server:0.6.0
//> using dep com.softwaremill.sttp.tapir::tapir-netty-server-sync:1.13.19

import chimp.server.*
import io.circe.Codec
import sttp.tapir.*
import sttp.tapir.server.netty.sync.NettySyncServer

case class AdderInput(a: Int, b: Int) derives Codec, Schema

@main def server(): Unit =
  val adder = tool("adder").description("Adds two numbers").input[AdderInput]
    .handle(i => ToolResult.text(s"Result: ${i.a + i.b}"))

  NettySyncServer().port(8080).addEndpoint(McpServer(tools = List(adder)).endpoint(List("mcp"))).startAndWait()

Connect and invoke the tool as an MCP client:

//> using dep com.softwaremill.chimp::chimp-client:0.6.0
//> using dep com.softwaremill.sttp.client4::core:4.0.24

import chimp.client.*
import chimp.client.transport.ClientHttpTransport
import chimp.protocol.*
import sttp.client4.*
import io.circe.Json

@main def client(): Unit =
  val backend = DefaultSyncBackend()
  val transport = ClientHttpTransport(backend, uri"http://localhost:8080/mcp")
  val client = McpClient(transport, Implementation("my-client", "0.1.0"))

  val result = client.callTool("adder", Json.obj("a" -> Json.fromInt(2), "b" -> Json.fromInt(3)))
  val _ = result.content.collect { case ToolContent.Text(_, text) => println(text) }

  client.close()
  backend.close()

stdio

Run a basic MCP server using stdio transport:

//> using dep com.softwaremill.chimp::chimp-server:0.6.0

import chimp.server.*
import chimp.server.transport.ServerStdioTransport
import io.circe.Codec
import sttp.tapir.*

case class EchoInput(message: String) derives Codec, Schema

@main def stdioServer(): Unit =
  val echo = tool("echo").description("Echoes the message").input[EchoInput]
    .handle(i => ToolResult.text(i.message))

  ServerStdioTransport().serve(McpServer(tools = List(echo)))

Start the server as a subprocess and invoke the tool as an MCP client:

//> using dep com.softwaremill.chimp::chimp-client:0.6.0

import chimp.client.*
import chimp.client.transport.ClientStdioTransport
import chimp.protocol.*
import io.circe.Json

@main def stdioClient(): Unit =
  val transport = ClientStdioTransport(List("scala-cli", "run", "stdioServer.scala"))
  val client = McpClient(transport, Implementation("my-client", "0.1.0"))

  val result = client.callTool("echo", Json.obj("message" -> Json.fromString("hello")))
  val _ = result.content.collect { case ToolContent.Text(_, text) => println(text) }

  client.close()

Bidirectional streaming

With the integration modules (like chimp-server-ox and chimp-client-ox for ox) bidirectional communication becomes possible. For example, run a basic streaming MCP server using Netty and ox:

//> using dep com.softwaremill.chimp::chimp-server-ox:0.6.0

import chimp.protocol.LoggingLevel
import chimp.server.*
import chimp.server.ox.OxServerHttpTransport
import io.circe.{Codec, Json}
import sttp.shared.Identity
import sttp.tapir.*
import sttp.tapir.server.netty.sync.NettySyncServer

case class WorkInput(steps: Int) derives Codec, Schema

@main def streamingServer(): Unit =
  val work = tool("work")
    .description("Reports progress and logs while running")
    .input[WorkInput]
    .streamingServerLogic[Identity]: (in, ctx, _) =>
      for step <- 1 to in.steps do
        ctx.reportProgress(step.toDouble / in.steps, total = Some(1.0))
        ctx.log(LoggingLevel.Info, Json.fromString(s"step $step of ${in.steps}"))
      ToolResult.text("done")

  val server = StreamingMcpServer[Identity]().withLoggingLevel(_ => ()).addStreamingTool(work)
  NettySyncServer().port(8080).addEndpoint(OxServerHttpTransport(List("mcp")).serve(server)).startAndWait()

Connect and invoke the tool as an MCP client, receiving server's notifications while the tool call is in flight:

//> using dep com.softwaremill.chimp::chimp-client-ox:0.6.0

import chimp.client.McpClient
import chimp.client.notifications.ServerNotification
import chimp.client.transport.ox.OxClientHttpTransport
import chimp.protocol.{Implementation, ToolContent}
import io.circe.Json
import ox.supervised
import sttp.client4.DefaultSyncBackend
import sttp.model.Uri.UriContext
import sttp.shared.Identity

@main def streamingClient(): Unit =
  supervised:
    val backend = DefaultSyncBackend()
    val transport = OxClientHttpTransport(backend, uri"http://localhost:8080/mcp")
    val client = McpClient.bidirectional[Identity](transport, Implementation("my-client", "0.1.0"))

    client.onServerNotification:
      case ServerNotification.Progress(params)       => println(s"progress: ${params.progress}")
      case ServerNotification.LoggingMessage(params) => println(s"log: ${params.data}")
      case _                                         => ()

    val result = client.callTool("work", Json.obj("steps" -> Json.fromInt(3)))
    val _ = result.content.collect { case ToolContent.Text(_, text) => println(text) }

    client.close()
    backend.close()

Documentation

Full documentation is available at chimp.softwaremill.com.

Contributing

Contributions are welcome! Please open issues or pull requests.

Commercial Support

We offer commercial support for Chimp and related technologies, as well as development services. Contact us to learn more about our offer!

Copyright

Copyright (C) 2025-2026 SoftwareMill https://softwaremill.com.

About

Build type-safe, boilerplate-less MCP servers and clients in Scala

chimp.softwaremill.com

Topics

aiclientmcppekkoscalaserverzio

Resources

Readme
Apache-2.0 license
Activity
Custom properties

Stars

104 stars

Watchers

3 watching

Forks

11 forks
Report repository

Releases

Packages

Used by

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.