kotlin-sdk/samples/kotlin-mcp-client
Repository files (latest commit first)
Filename Latest commit message Latest commit date
dependabot[bot] 6c9ca01922
chore(deps): bump io.modelcontextprotocol:kotlin-sdk-client from 0.14.0 to 0.15.0 in /samples/kotlin-mcp-client (#925)
Bumps
[io.modelcontextprotocol:kotlin-sdk-client](https://github.com/modelcontextprotocol/kotlin-sdk)
from 0.14.0 to 0.15.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/modelcontextprotocol/kotlin-sdk/releases">io.modelcontextprotocol:kotlin-sdk-client's
releases</a>.</em></p>
<blockquote>
<h2>0.15.0</h2>
<h2>Description</h2>
<p>This release makes inbound message handling concurrent after the
initialization handshake, adds SSE heartbeats and elicitation schema
validation, and fixes several Streamable HTTP response and
session-lifecycle bugs.</p>
<h3>Breaking Changes</h3>
<p><strong>Concurrent inbound dispatch</strong> by <a
href="https://github.com/devcrocod"><code>@​devcrocod</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/884">#884</a></p>
<p>Inbound messages were processed serially on the transport read loop,
so one slow or peer-awaiting handler (sampling, elicitation, roots)
blocked every later message on the connection, including the responses
and <code>notifications/cancelled</code> those handlers depend on. After
the handshake, handlers may now run concurrently; processing stays
serial during initialization.</p>
<ul>
<li><code>ProtocolOptions</code> / <code>ClientOptions</code> /
<code>ServerOptions</code> gain a <code>handlerCoroutineContext</code>
parameter (defaults to <code>Dispatchers.Default</code>). Concurrency is
bounded internally; there is no opt-out flag by design.</li>
<li><code>RequestHandlerExtra</code> is now a
<code>CoroutineContext.Element</code> and is no longer
user-constructible. It exposes <code>requestId</code>,
<code>method</code>, <code>sendRequest</code>, and
<code>sendNotification</code>, and can be read inside a handler via the
new <code>currentRequestHandlerExtra()</code>.</li>
</ul>
<pre lang="diff"><code>- val extra = RequestHandlerExtra()
+ val extra = currentRequestHandlerExtra()
</code></pre>
<p><strong>Duplicate feature names are rejected at registration</strong>
by <a href="https://github.com/devcrocod"><code>@​devcrocod</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/883">#883</a></p>

<p><code>addTool</code>/<code>addPrompt</code>/<code>addResource</code>/<code>addResourceTemplate</code>
silently replaced an existing entry and emitted a spurious
<code>list_changed</code> notification. They now throw
<code>IllegalArgumentException</code> and leave the existing
registration intact. The batch variants
(<code>addTools</code>/<code>addPrompts</code>/<code>addResources</code>)
are all-or-nothing. To replace a feature, remove it first.</p>
<pre lang="diff"><code>- server.addTool(name = &quot;search&quot;, ...)
// silently overwrote
+ server.removeTool(&quot;search&quot;)
+ server.addTool(name = &quot;search&quot;, ...)
</code></pre>
<p><strong><code>eventStore</code> removed from
<code>mcpStatelessStreamableHttp</code></strong> by <a
href="https://github.com/devcrocod"><code>@​devcrocod</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/909">#909</a></p>
<p>The parameter was never read or written: a stateless endpoint answers
<code>GET</code> with <code>405</code>, so no stream exists to store or
replay events on. The old overload is retained at
<code>DeprecationLevel.ERROR</code> with a <code>ReplaceWith</code>
migration hint. Use <code>mcpStreamableHttp</code> when you need
resumability.</p>
<pre lang="diff"><code>  application.mcpStatelessStreamableHttp(
      path = &quot;/mcp&quot;,
-     eventStore = myEventStore,
  ) { server }
</code></pre>
<h3>Features</h3>
<ul>
<li>Optional SSE heartbeats for Streamable HTTP servers via
<code>mcpStreamableHttp(sseHeartbeatConfig = { ... })</code>, keeping
long-lived streams alive against clients that disconnect on idle.
Heartbeats stay off by default, by <a
href="https://github.com/UnscientificJsZhai"><code>@​UnscientificJsZhai</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/761">#761</a></li>
<li>Accepted form-mode elicitation content is now validated against
<code>requestedSchema</code>; a mismatch fails
<code>createElicitation</code> with <code>McpException</code>
(<code>INVALID_PARAMS</code>) instead of reaching server code unchecked,
by <a href="https://github.com/rea9r"><code>@​rea9r</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/896">#896</a></li>
<li>The Streamable HTTP client now sends the standard
<code>Mcp-Method</code> and <code>Mcp-Name</code> POST headers, by <a
href="https://github.com/AndreKalberer"><code>@​AndreKalberer</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/894">#894</a></li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Stateless Streamable HTTP now closes its per-request session, fixing
unbounded growth of the session registry and notification subscriptions,
by <a
href="https://github.com/KlyneChrysler"><code>@​KlyneChrysler</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/872">#872</a></li>
<li>Streamable HTTP responses keep their status and body when the client
accepts only <code>text/event-stream</code>, by <a
href="https://github.com/devcrocod"><code>@​devcrocod</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/pull/911">#911</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="76b8e7d63a"><code>76b8e7d</code></a>
release: 0.15.0 (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/918">#918</a>)</li>
<li><a
href="dab340f9bf"><code>dab340f</code></a>
fix(server): drop inert eventStore from stateless Streamable HTTP (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/909">#909</a>)</li>
<li><a
href="24c3cd3a69"><code>24c3cd3</code></a>
fix(server): match Accept header as media ranges (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/912">#912</a>)</li>
<li><a
href="7fa438ce00"><code>7fa438c</code></a>
feat(client): send MCP standard POST headers (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/894">#894</a>)</li>
<li><a
href="94745e617c"><code>94745e6</code></a>
chore(deps): bump the kotest group across 1 directory with 3 updates (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/915">#915</a>)</li>
<li><a
href="2b494b7637"><code>2b494b7</code></a>
chore(deps): bump the other-dependencies group across 5 directories with
3 up...</li>
<li><a
href="82fe56e1e8"><code>82fe56e</code></a>
fix(server): preserve Streamable HTTP response status and body (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/911">#911</a>)</li>
<li><a
href="a06d699d6c"><code>a06d699</code></a>
feat: SSE heartbeat (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/761">#761</a>)</li>
<li><a
href="cab669b108"><code>cab669b</code></a>
fix: classify malformed request params as invalid params (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/886">#886</a>)</li>
<li><a
href="b6cb547d58"><code>b6cb547</code></a>
fix(server): preserve id on duplicate initialize errors (<a
href="https://redirect.github.com/modelcontextprotocol/kotlin-sdk/issues/868">#868</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/modelcontextprotocol/kotlin-sdk/compare/0.14.0...0.15.0">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-03 13:03:13 +02:00
..
gradle chore(deps): bump io.modelcontextprotocol:kotlin-sdk-client from 0.14.0 to 0.15.0 in /samples/kotlin-mcp-client (#925) 2026-08-03 13:03:13 +02:00
src/main docs: update client and server samples (#598) 2026-03-13 10:23:51 +01:00
build.gradle.kts Add Ktor BOM for dependency management across all modules, run Kotlin-MCP-Server Sample (#367) 2025-11-04 11:44:39 +02:00
gradle.properties #302 Update samples build workflow (#330) 2025-10-23 13:02:22 +03:00
gradlew chore(deps): bump the other-dependencies group across 5 directories with 3 updates (#854) 2026-06-24 15:31:46 +02:00
gradlew.bat chore(deps): bump the other-dependencies group across 5 directories with 3 updates (#854) 2026-06-24 15:31:46 +02:00
README.md docs: streamable server sample (#595) 2026-03-10 12:45:21 +01:00
settings.gradle.kts #302 Update samples build workflow (#330) 2025-10-23 13:02:22 +03:00

Kotlin MCP Client

An interactive CLI client that connects to any MCP server over STDIO and pipes queries through Anthropic's Claude API.

Overview

This sample demonstrates a complete MCP client workflow: launching an MCP server as a subprocess, discovering its tools, converting them to Anthropic's tool format, and running an interactive chat loop where Claude can call server tools on behalf of the user.

Prerequisites

  • JDK 17+
  • An ANTHROPIC_API_KEY environment variable set with a valid Anthropic API key
  • An MCP server script to connect to (.js, .py, or .jar)

Build & Run

Run the client, passing the path to an MCP server:

# Connect to a JVM server
./gradlew run --args="path/to/server.jar"

# Connect to a Python server
./gradlew run --args="path/to/server.py"

# Connect to a Node.js server
./gradlew run --args="path/to/build/index.js"

Note

The client uses STDIO transport, so it launches the MCP server as a subprocess. Ensure the server script is executable and is a valid .js, .py, or .jar file.

MCP Capabilities

From the client perspective, this sample demonstrates:

  • Tool discovery — lists tools from the connected server and converts them to Anthropic's tool format.
  • Tool invocation — when Claude's response requests a tool call, the client invokes the corresponding MCP tool and feeds the result back into the conversation.

Additional Resources