Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ For background on the protocol itself, see the [MCP documentation](https://model
| [kotlin-mcp-server](./kotlin-mcp-server) | Server | STDIO, SSE | Tools, Resources, Prompts |
| [weather-stdio-server](./weather-stdio-server) | Server | STDIO | Tools |
| [kotlin-mcp-client](./kotlin-mcp-client) | Client | STDIO | Tool discovery & invocation |
| [roots-demo](./roots-demo) | Client + Server | In-memory | Roots |
| [notebooks](./notebooks) | Client (Notebook) | Streamable HTTP | Tool discovery & invocation |

## Getting Started
Expand Down Expand Up @@ -56,6 +57,12 @@ An interactive CLI client that connects to any MCP server over STDIO and routes
Anthropic's Claude API, bridging MCP tools with LLM conversations.
[Read more →](./kotlin-mcp-client)

### Roots Demo

A minimal in-memory sample demonstrating the MCP Roots capability — how a client exposes
filesystem roots to a server, and how the server reacts when the root list changes.
[Read more →](./roots-demo)

### MCP Client Notebook

A Kotlin notebook that connects to a remote MCP server via Streamable HTTP and demonstrates ping,
Expand Down
54 changes: 54 additions & 0 deletions samples/roots-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Roots Demo

A minimal sample demonstrating the MCP **Roots** capability using in-memory
[ChannelTransport](../../kotlin-sdk-testing/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransport.kt).

## Overview

[Roots](https://modelcontextprotocol.io/docs/concepts/roots) allow an MCP client to
expose filesystem locations to a server. The server can request the list of roots and
receive notifications when that list changes.

This sample shows the full roots lifecycle:

1. **Client declares roots capability** — the client advertises `roots` with
`listChanged = true` during initialization.
2. **Client registers roots** — `addRoot(uri, name)` adds roots that the server can
discover.
3. **Server queries roots** — `serverSession.listRoots()` sends a `roots/list`
request to the client.
4. **Client sends change notification** — after dynamically adding or removing roots,
the client calls `sendRootsListChanged()` so the server knows to re-fetch.
5. **Server reacts to changes** — the server listens for
`notifications/roots/list_changed` and re-queries the updated root list.

## Prerequisites

- JDK 17+

## Build & Run

```shell
./gradlew run
```

The program connects a client and server in-process using `ChannelTransport` and
prints the roots exchange to the console. No external server or network is required.

## MCP Capabilities

### Client

| Capability | Details |
|-----------|---------|
| `roots` | Advertised with `listChanged = true`. Registers project directories and notifies the server on changes. |

### Server

Demonstrates consuming the roots capability by calling `listRoots()` and listening for
`notifications/roots/list_changed`.

## Additional Resources

- [MCP Roots Specification](https://modelcontextprotocol.io/docs/concepts/roots)
- [Kotlin MCP SDK](https://github.com/modelcontextprotocol/kotlin-sdk)
27 changes: 27 additions & 0 deletions samples/roots-demo/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.shadow)
application
}

group = "org.example"
version = "0.1.0"

application {
mainClass.set("io.modelcontextprotocol.sample.roots.MainKt")
}

dependencies {
implementation(libs.mcp.kotlin.client)
implementation(libs.mcp.kotlin.server)
implementation(libs.mcp.kotlin.testing)
implementation(libs.slf4j.simple)
}

tasks.test {
useJUnitPlatform()
}

kotlin {
jvmToolchain(17)
}
5 changes: 5 additions & 0 deletions samples/roots-demo/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
org.gradle.configuration-cache=true
org.gradle.parallel=true
org.gradle.caching=true

#mcp.kotlin.overrideVersion=0.0.1-SNAPSHOT
15 changes: 15 additions & 0 deletions samples/roots-demo/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[versions]
kotlin = "2.3.21"
mcp-kotlin = "0.12.0"
shadow = "9.4.1"
slf4j = "2.0.17"

[libraries]
mcp-kotlin-client = { group = "io.modelcontextprotocol", name = "kotlin-sdk-client", version.ref = "mcp-kotlin" }
mcp-kotlin-server = { group = "io.modelcontextprotocol", name = "kotlin-sdk-server", version.ref = "mcp-kotlin" }
mcp-kotlin-testing = { group = "io.modelcontextprotocol", name = "kotlin-sdk-testing", version.ref = "mcp-kotlin" }
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
Binary file not shown.
9 changes: 9 additions & 0 deletions samples/roots-demo/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
248 changes: 248 additions & 0 deletions samples/roots-demo/gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading