Skip to content

Commit 8f16539

Browse files
committed
misc: update main readme
1 parent 8de1821 commit 8f16539

1 file changed

Lines changed: 115 additions & 3 deletions

File tree

README.md

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,18 @@
55

66
An SDK for building [MCP](https://modelcontextprotocol.io/specification) (Model Context Protocol) servers and
77
clients in Scala 3 using boilerplate-less, type-safe APIs based on [Tapir](https://tapir.softwaremill.com/)
8-
and [sttp](https://github.com/softwaremill/sttp), supporting the variety of the Scala ecosystem. Both servers
9-
and clients can communicate over **Streamable HTTP** or **stdio**.
8+
and [sttp](https://github.com/softwaremill/sttp), supporting the variety of the Scala ecosystem.
9+
10+
### Transport
11+
12+
Chimp implements both streamable HTTP and stdio transports. Additional integration modules unlock streaming features of
13+
the MCP protocol with bidirectional communication between server and client. Currently supporting [Ox](https://ox.softwaremill.com/) and [ZIO](https://zio.dev/).
1014

1115
### Quickstart
1216

13-
Run a basic MCP server with Netty exposing a simple _adder_ tool:
17+
#### HTTP
18+
19+
Run a basic MCP server with Netty exposing a simple adder tool:
1420

1521
```scala
1622
//> using dep com.softwaremill.chimp::chimp-server:0.4.0
@@ -54,6 +60,112 @@ import io.circe.Json
5460
backend.close()
5561
```
5662

63+
#### stdio
64+
65+
Run a basic MCP server using stdio transport:
66+
67+
```scala
68+
//> using dep com.softwaremill.chimp::chimp-server:0.4.0
69+
70+
import chimp.server.*
71+
import chimp.server.transport.ServerStdioTransport
72+
import io.circe.Codec
73+
import sttp.tapir.*
74+
75+
case class EchoInput(message: String) derives Codec, Schema
76+
77+
@main def stdioServer(): Unit =
78+
val echo = tool("echo").description("Echoes the message").input[EchoInput]
79+
.handle(i => ToolResult.text(i.message))
80+
81+
ServerStdioTransport().serve(McpServer(tools = List(echo)))
82+
```
83+
84+
Start the server as a subprocess and invoke the tool as an MCP client:
85+
86+
```scala
87+
//> using dep com.softwaremill.chimp::chimp-client:0.4.0
88+
89+
import chimp.client.*
90+
import chimp.client.transport.ClientStdioTransport
91+
import chimp.protocol.*
92+
import io.circe.Json
93+
94+
@main def stdioClient(): Unit =
95+
val transport = ClientStdioTransport(List("scala-cli", "run", "stdioServer.scala"))
96+
val client = McpClient(transport, Implementation("my-client", "0.1.0"))
97+
98+
val result = client.callTool("echo", Json.obj("message" -> Json.fromString("hello")))
99+
val _ = result.content.collect { case ToolContent.Text(_, text) => println(text) }
100+
101+
client.close()
102+
```
103+
104+
#### Bidirectional streaming
105+
106+
With the integration modules (like `chimp-server-ox` and `chimp-client-ox` for ox) bidirectional communication becomes possible.
107+
For example, run a basic streaming MCP server using Netty and ox:
108+
109+
```scala
110+
//> using dep com.softwaremill.chimp::chimp-server-ox:0.4.0
111+
112+
import chimp.protocol.LoggingLevel
113+
import chimp.server.*
114+
import chimp.server.ox.OxServerHttpTransport
115+
import io.circe.{Codec, Json}
116+
import sttp.shared.Identity
117+
import sttp.tapir.*
118+
import sttp.tapir.server.netty.sync.NettySyncServer
119+
120+
case class WorkInput(steps: Int) derives Codec, Schema
121+
122+
@main def streamingServer(): Unit =
123+
val work = tool("work")
124+
.description("Reports progress and logs while running")
125+
.input[WorkInput]
126+
.streamingServerLogic[Identity]: (in, ctx, _) =>
127+
for step <- 1 to in.steps do
128+
ctx.reportProgress(step.toDouble / in.steps, total = Some(1.0))
129+
ctx.log(LoggingLevel.Info, Json.fromString(s"step $step of ${in.steps}"))
130+
ToolResult.text("done")
131+
132+
val server = StreamingMcpServer[Identity]().withLoggingLevel(_ => ()).addStreamingTool(work)
133+
NettySyncServer().port(8080).addEndpoint(OxServerHttpTransport(List("mcp")).serve(server)).startAndWait()
134+
```
135+
136+
Connect and invoke the tool as an MCP client, receiving server's notifications while the tool call is in flight:
137+
138+
```scala
139+
//> using dep com.softwaremill.chimp::chimp-client-ox:0.4.0
140+
141+
import chimp.client.McpClient
142+
import chimp.client.notifications.ServerNotification
143+
import chimp.client.transport.ox.OxClientHttpTransport
144+
import chimp.protocol.{Implementation, ToolContent}
145+
import io.circe.Json
146+
import ox.supervised
147+
import sttp.client4.DefaultSyncBackend
148+
import sttp.model.Uri.UriContext
149+
import sttp.shared.Identity
150+
151+
@main def streamingClient(): Unit =
152+
supervised:
153+
val backend = DefaultSyncBackend()
154+
val transport = OxClientHttpTransport(backend, uri"http://localhost:8080/mcp")
155+
val client = McpClient.bidirectional[Identity](transport, Implementation("my-client", "0.1.0"))
156+
157+
client.onServerNotification:
158+
case ServerNotification.Progress(params) => println(s"progress: ${params.progress}")
159+
case ServerNotification.LoggingMessage(params) => println(s"log: ${params.data}")
160+
case _ => ()
161+
162+
val result = client.callTool("work", Json.obj("steps" -> Json.fromInt(3)))
163+
val _ = result.content.collect { case ToolContent.Text(_, text) => println(text) }
164+
165+
client.close()
166+
backend.close()
167+
```
168+
57169
## Documentation
58170

59171
Full documentation is available at **[chimp.softwaremill.com](https://chimp.softwaremill.com/)**.

0 commit comments

Comments
 (0)