- Kotlin 98%
- TypeScript 1.7%
- Shell 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Bumps the all-dependencies group with 1 update in the /integration-test/src/jvmTest/typescript directory: [zod](https://github.com/colinhacks/zod). Updates `zod` from 4.3.6 to 4.4.3 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/colinhacks/zod/releases">zod's releases</a>.</em></p> <blockquote> <h2>v4.4.3</h2> <h2>Commits:</h2> <ul> <li>4c2fa95ce3f3390fbc522324e406b4e9e89b88f9 docs: use Zernio primary wordmark for gold sponsor logo</li> <li>2aeec83eb135e3a83756e973ef44845fc5a455d2 docs: prune lapsed gold sponsors and rebalance logo sizing</li> <li>7391be88ac1ee5cd02057f5ccc012a1f5df4efd0 docs: prune lapsed silver/bronze sponsors and add active ones</li> <li>2c703322a21b4e2b12f33f49ea8430c451a68b4f docs: normalize bronze sponsor logos to github avatar pattern</li> <li>9195250cab0e7950efe39c3926d6c203b4b0a170 docs: remove Mintlify from bronze sponsors (churned)</li> <li>b8dffe9e62f17e6571e6249d05cc5102b54d94e4 docs: remove Numeric and Speakeasy (2+ missed monthly cycles)</li> <li>1cab69383fcdeae2a366d5e2a2fc4d8fc765d168 fix(v4): restore catch handling for absent object keys (<a href="https://redirect.github.com/colinhacks/zod/issues/5937">#5937</a>) (<a href="https://redirect.github.com/colinhacks/zod/issues/5939">#5939</a>)</li> <li>c2be4f819064eed62c7c350a2d399b5faecd15f8 fix(v4): generalize optin/fallback to transform; restore preprocess on absent keys (<a href="https://redirect.github.com/colinhacks/zod/issues/5941">#5941</a>)</li> <li>f3c9ec03ba7a28ae72d25cc295f38674bee0f559 4.4.3</li> <li>1fb56a5c18c27102dbc92260a4007c7732a0ccca docs: document release procedure in AGENTS.md</li> </ul> <h2>v4.4.2</h2> <h2>Commits:</h2> <ul> <li>0c62df0ea19fd05abdf90473e9eef7eea530fab2 Clean up docs navigation and stale labels (<a href="https://redirect.github.com/colinhacks/zod/issues/5901">#5901</a>)</li> <li>20cc794895cc8604fe0c87d83a5d1c3f89fad0ac chore: add security policy and refresh tooling deps</li> <li>6fbe07b0177efdd1bf1c0b05160e70d7a0702337 fix(docs): heading anchor links now include the hash so it doesnt scoll all the way up, follows navbar logic (<a href="https://redirect.github.com/colinhacks/zod/issues/5791">#5791</a>)</li> <li>4bbed1b1c73eca4ce9e59b1189ed236aa6c8b5bd Tighten discriminated union option typing</li> <li>bbac3e567e7fccfaaf7cdc97f1ce30c295e2c908 Update PR guidance for agents</li> <li>cf0dc942a32805c292fff59ade20a7ace980735a Merge remote-tracking branch 'origin/main' into fix-discriminated-union-key-constraint</li> <li>292c894a5fd2aa42e527900b83d8d7a3009a709c docs: add Zernio gold sponsor</li> <li>1fc9f311c28dcf80d0bb5a36b177086cbc3d8eca docs: document codec inversion</li> <li>1373c85da9aeff704a9762d27bc58699618aefb7 docs: remove AI disclosure guidance</li> <li>e20d02b473c08e3a4e557bc610b1b5fac079b649 chore: ignore triage notes</li> <li>e58ea4d91b1dfe8194b73508203213cbc7e9c936 docs: test Zod Mini tab code heights</li> <li>905761a5d127e8d5dd2ebb3bc88c75cb0b8149ff docs: document preprocess input type narrowing</li> <li>bf64bac850d4dee2b7dde7e64909d5d796d32043 chore: tighten test guidance in AGENTS.md</li> <li>8ec4e73f4c4693b6361ad591be40fb41eb8a9f95 chore: update play.ts scratch</li> <li>02c2baf7d0d615872fa4528a8020603b71211702 Make z.preprocess defer optionality to inner schema (<a href="https://redirect.github.com/colinhacks/zod/issues/5929">#5929</a>)</li> <li>88015df8e25c44fb5385eb3ef28935119cd5edea fix(docs): drop deprecated <code>baseUrl</code> from tsconfig</li> <li>c59d4474e3b4cad1b323462186cf607178ce8267 4.4.2</li> </ul> <h2>v4.4.1</h2> <h2>Commits:</h2> <ul> <li>481f7be4238c83ed58183f921b2646f340a91c6a ci: gate release publishing on full test workflow</li> <li>95ccab423aec720b2523c3a64cdc7e3204537cc7 test(v3): restore optional undefined expectations</li> <li>cede2c63739a5823d6aa5093d291e9a111da943d fix(v4): reject tuple holes before required defaults (<a href="https://redirect.github.com/colinhacks/zod/issues/5900">#5900</a>)</li> <li>edd0bf0f5ada4a8dc581c259407d7bbad0a71ea7 release: 4.4.1</li> <li>180d83d1dbe6a59260710cc8637a3dea2281ee56 docs: remove Jazz featured sponsor</li> </ul> <h2>v4.4.0</h2> <h2>4.4.0</h2> <p>This is a minor release with a wide set of correctness and soundness fixes. Some fixes intentionally make Zod stricter, so code that depended on previously accepted invalid or ambiguous inputs may need small updates.</p> <h2>Potentially breaking bug fixes</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
| .claude/skills | ||
| .github | ||
| buildSrc | ||
| config/detekt | ||
| conformance-test | ||
| docs | ||
| gradle | ||
| integration-test | ||
| kotlin-sdk | ||
| kotlin-sdk-client | ||
| kotlin-sdk-core | ||
| kotlin-sdk-server | ||
| kotlin-sdk-testing | ||
| samples | ||
| test-utils | ||
| .editorconfig | ||
| .gitignore | ||
| AGENTS.md | ||
| build.gradle.kts | ||
| CLAUDE.md | ||
| CODE_OF_CONDUCT.md | ||
| context7.json | ||
| CONTRIBUTING.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| knit.properties | ||
| LICENSE | ||
| README.md | ||
| SECURITY.md | ||
| settings.gradle.kts | ||
MCP Kotlin SDK
Kotlin Multiplatform SDK for the Model Context Protocol. It enables Kotlin applications targeting JVM, Native, JS, and Wasm to implement MCP clients and servers using a standardized protocol interface.
Table of Contents
- Overview
- Installation
- Quickstart
- Core Concepts
- Transports
- Connecting your server
- Examples
- Documentation
- Contributing
- License
Overview
The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This Kotlin SDK implements the MCP specification, making it easy to:
- Build MCP clients that can connect to any MCP server
- Create MCP servers that expose resources, prompts, and tools
- Target JVM, Native, JS, and Wasm from a single codebase
- Use standard transports like stdio, SSE, Streamable HTTP, and WebSocket
- Handle MCP protocol messages and lifecycle events with coroutine-friendly APIs
Installation
Artifacts
io.modelcontextprotocol:kotlin-sdk– umbrella SDK (client + server APIs)io.modelcontextprotocol:kotlin-sdk-client– client-only APIsio.modelcontextprotocol:kotlin-sdk-server– server-only APIs
Gradle setup (JVM)
Add the Maven Central repository and the SDK dependency:
repositories {
mavenCentral()
}
dependencies {
// See the badge above for the latest version
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
}
Use kotlin-sdk-client or kotlin-sdk-server if you only need one side of the API:
dependencies {
implementation("io.modelcontextprotocol:kotlin-sdk-client:$mcpVersion")
implementation("io.modelcontextprotocol:kotlin-sdk-server:$mcpVersion")
}
Multiplatform
In a Kotlin Multiplatform project you can add the SDK to commonMain:
commonMain {
dependencies {
// Works as a common dependency as well as the platform one
implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
}
}
Ktor dependencies
The Kotlin MCP SDK uses Ktor, but it does not add Ktor engine dependencies transitively. You need to declare Ktor client/server dependencies yourself (or reuse the ones already used in your project), for example:
dependencies {
// MCP client with Ktor
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("io.modelcontextprotocol:kotlin-sdk-client:$mcpVersion")
// MCP server with Ktor
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("io.modelcontextprotocol:kotlin-sdk-server:$mcpVersion")
}
Quickstart
Let's create a simple MCP client and server to demonstrate the basic usage of the Kotlin SDK.
Creating a Client
Create an MCP client that connects to a server via Streamable HTTP transport and lists available tools:
import io.ktor.client.HttpClient
import io.ktor.client.plugins.sse.SSE
import io.modelcontextprotocol.kotlin.sdk.client.Client
import io.modelcontextprotocol.kotlin.sdk.client.StreamableHttpClientTransport
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import kotlinx.coroutines.runBlocking
fun main(args: Array<String>) = runBlocking {
val url = args.firstOrNull() ?: "http://localhost:3000/mcp"
val httpClient = HttpClient { install(SSE) }
val client = Client(
clientInfo = Implementation(
name = "example-client",
version = "1.0.0"
)
)
val transport = StreamableHttpClientTransport(
client = httpClient,
url = url
)
// Connect to server
client.connect(transport)
// List available tools
val tools = client.listTools().tools
println(tools)
}
Creating a Server
Create an MCP server that exposes a simple tool and runs on an embedded Ktor server with Streamable HTTP transport. For a full working project with all required dependencies, see the simple-streamable-server sample.
import io.ktor.server.cio.CIO
import io.ktor.server.engine.embeddedServer
import io.modelcontextprotocol.kotlin.sdk.server.Server
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
import io.modelcontextprotocol.kotlin.sdk.server.mcpStreamableHttp
import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
fun main(args: Array<String>) {
val port = args.firstOrNull()?.toIntOrNull() ?: 3000
val mcpServer = Server(
serverInfo = Implementation(
name = "example-server",
version = "1.0.0"
),
options = ServerOptions(
capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(listChanged = true),
),
)
)
mcpServer.addTool(
name = "example-tool",
description = "An example tool",
inputSchema = ToolSchema(
properties = buildJsonObject {
put("input", buildJsonObject { put("type", "string") })
}
)
) { request ->
CallToolResult(content = listOf(TextContent("Hello, world!")))
}
embeddedServer(CIO, host = "127.0.0.1", port = port) {
mcpStreamableHttp {
mcpServer
}
}.start(wait = true)
}
You can run the server and then connect to it using the client or test with the MCP Inspector:
npx -y @modelcontextprotocol/inspector
In the inspector UI, connect to http://localhost:3000/mcp.
Core Concepts
MCP Primitives
The MCP protocol defines core primitives that enable communication between servers and clients:
| Primitive | Server Role | Client Role | Description |
|---|---|---|---|
| Prompts | Provides prompt templates with optional arguments | Requests and uses prompts | Interactive templates for LLM interactions |
| Resources | Exposes data sources (files, API responses, etc.) | Reads and subscribes to resources | Contextual data for augmenting LLM context |
| Tools | Defines executable functions | Calls tools to perform actions | Functions the LLM can invoke to take actions |
| Sampling | Requests LLM completions from client | Executes LLM calls and returns results | Server-initiated LLM requests (reverse direction) |
Capabilities
Capabilities define what features a server or client supports. They are declared during initialization and determine what operations are available.
Server Capabilities
Servers declare their capabilities to inform clients what features they provide:
| Capability | Feature Flags | Description |
|---|---|---|
prompts |
listChanged |
Prompt template management and notifications |
resources |
subscribelistChanged |
Resource exposure, subscriptions, and update notifications |
tools |
listChanged |
Tool discovery, execution, and list change notifications |
logging |
- | Server logging to client console |
completions |
- | Argument autocompletion suggestions |
experimental |
Custom properties | Non-standard experimental features |
Client Capabilities
Clients declare their capabilities to inform servers what features they support:
| Capability | Feature Flags | Description |
|---|---|---|
sampling |
- | Client can sample from an LLM (execute model requests) |
roots |
listChanged |
Client exposes root directories and can notify of changes |
elicitation |
- | Client can display schema/form dialogs for structured input |
experimental |
Custom properties | Non-standard experimental features |
Server Features
The Server API lets you wire prompts, resources, and tools with only a few lines of Kotlin. Each feature is registered
up front and then resolved lazily when a client asks for it, so your handlers stay small and suspendable.
Prompts
Prompts are user-controlled templates that clients discover via prompts/list and fetch with prompts/get when a user
chooses one (think slash commands or saved flows). They’re best for repeatable, structured starters rather than ad-hoc
model calls.
val server = Server(
serverInfo = Implementation(
name = "example-server",
version = "1.0.0"
),
options = ServerOptions(
capabilities = ServerCapabilities(
prompts = ServerCapabilities.Prompts(listChanged = true),
),
)
)
server.addPrompt(
name = "code-review",
description = "Ask the model to review a diff",
arguments = listOf(
PromptArgument(name = "diff", description = "Unified diff", required = true),
),
) { request ->
GetPromptResult(
description = "Quick code review helper",
messages = listOf(
PromptMessage(
role = Role.User,
content = TextContent(text = "Review this change:\n${request.arguments?.get("diff")}"),
),
),
)
}
Use prompts for anything that deserves a template: bug triage questions, onboarding checklists, or saved searches. Set
listChanged = true only if your prompt catalog can change at runtime and your server will emit
notifications/prompts/list_changed when it does.
Resources
Resources are application-driven context that clients discover via resources/list or resources/templates/list, then
fetch with resources/read. Register each one with a stable URI and return a ReadResourceResult when asked—contents
can be text or binary blobs.
val server = Server(
serverInfo = Implementation(
name = "example-server",
version = "1.0.0"
),
options = ServerOptions(
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(subscribe = true, listChanged = true),
),
)
)
server.addResource(
uri = "note://release/latest",
name = "Release notes",
description = "Last deployment summary",
mimeType = "text/markdown",
) { request ->
ReadResourceResult(
contents = listOf(
TextResourceContents(
text = "Ship 42 reached production successfully.",
uri = request.uri,
mimeType = "text/markdown",
),
),
)
}
Resources can be static text, generated JSON, or blobs—anything the client can surface to the user or inject into the
model context. Set subscribe = true if you emit notifications/resources/updated for changes to specific URIs, and
listChanged = true if you’ll send notifications/resources/list_changed when the catalog itself changes.
Tools
Tools are model-controlled capabilities the client exposes to the model. Clients discover them via tools/list, invoke
them with tools/call, and your handlers receive JSON arguments, can emit streaming logs or progress, and return a
CallToolResult. Keep a human in the loop for sensitive operations.
val server = Server(
serverInfo = Implementation(
name = "example-server",
version = "1.0.0"
),
options = ServerOptions(
capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(listChanged = true),
),
)
)
server.addTool(
name = "echo",
description = "Return whatever the user sent back to them",
) { request ->
val text = request.arguments?.get("text")?.jsonPrimitive?.content ?: "(empty)"
CallToolResult(content = listOf(TextContent(text = "Echo: $text")))
}
Register as many tools as you need—long-running jobs can report progress via the request context, and tools can also
trigger sampling (see below) when they need the client’s LLM. Set listChanged = true only if your tool catalog can
change at runtime and your server will emit notifications/tools/list_changed when it does.
Completion
Completion provides argument suggestions for prompts or resource templates. Declare the completions capability and
handle completion/complete requests to return up to 100 ranked values (include total/hasMore if you paginate).
val server = Server(
serverInfo = Implementation(
name = "example-server",
version = "1.0.0"
),
options = ServerOptions(
capabilities = ServerCapabilities(
completions = ServerCapabilities.Completions,
),
)
)
val session = server.createSession(
StdioServerTransport(
inputStream = System.`in`.asSource().buffered(),
outputStream = System.out.asSink().buffered()
)
)
session.setRequestHandler<CompleteRequest>(Method.Defined.CompletionComplete) { request, _ ->
val options = listOf("kotlin", "compose", "coroutine")
val matches = options.filter { it.startsWith(request.argument.value.lowercase()) }
CompleteResult(
completion = CompleteResult.Completion(
values = matches.take(3),
total = matches.size,
hasMore = matches.size > 3,
),
)
}
Use context.arguments to refine suggestions for dependent fields (e.g., framework list filtered by chosen language).
Logging
Logging lets the server stream structured log notifications to the client using RFC 5424 levels (debug → emergency).
Declare the logging capability; clients can raise the minimum level with logging/setLevel, and the server emits
notifications/message with severity, optional logger name, and JSON data.
val server = Server(
serverInfo = Implementation("example-server", "1.0.0"),
options = ServerOptions(
capabilities = ServerCapabilities(
logging = ServerCapabilities.Logging,
),
)
)
val session = server.createSession(
StdioServerTransport(
inputStream = System.`in`.asSource().buffered(),
outputStream = System.out.asSink().buffered()
)
)
session.sendLoggingMessage(
LoggingMessageNotification(
LoggingMessageNotificationParams(
level = LoggingLevel.Info,
logger = "startup",
data = buildJsonObject { put("message", "Server started") },
),
),
)
Keep logs free of sensitive data, and expect clients to surface them in their own UI.
Pagination
List operations return paginated results with an opaque nextCursor, clients echo that cursor to fetch the next page.
Supported list calls: resources/list, resources/templates/list, prompts/list, and tools/list.
Treat cursors as opaque—don’t parse or persist them across sessions.
val server = Server(
serverInfo = Implementation("example-server", "1.0.0"),
options = ServerOptions(
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(),
),
)
)
val session = server.createSession(
StdioServerTransport(
inputStream = System.`in`.asSource().buffered(),
outputStream = System.out.asSink().buffered()
)
)
val resources = listOf(
Resource(uri = "note://1", name = "Note 1", description = "First"),
Resource(uri = "note://2", name = "Note 2", description = "Second"),
Resource(uri = "note://3", name = "Note 3", description = "Third"),
)
val pageSize = 2
session.setRequestHandler<ListResourcesRequest>(Method.Defined.ResourcesList) { request, _ ->
val start = request.params?.cursor?.toIntOrNull() ?: 0
val page = resources.drop(start).take(pageSize)
val next = if (start + page.size < resources.size) (start + page.size).toString() else null
ListResourcesResult(
resources = page,
nextCursor = next,
)
}
Include nextCursor only when more items remain an absent cursor ends pagination.
Client Features
Clients advertise their capabilities (roots, sampling, elicitation, etc.) during initialization. After that they can
serve requests from the server while still initiating calls such as listTools or callTool.
Roots
Roots let the client declare where the server is allowed to operate. Declare the roots capability, respond to
roots/list, and emit notifications/roots/list_changed if you set listChanged = true. URIs must be file://
paths.
val client = Client(
clientInfo = Implementation("demo-client", "1.0.0"),
options = ClientOptions(
capabilities = ClientCapabilities(roots = ClientCapabilities.Roots(listChanged = true)),
),
)
client.addRoot(
uri = "file:///Users/demo/projects",
name = "Projects",
)
client.sendRootsListChanged()
Call addRoot/removeRoot whenever your file system view changes, and use sendRootsListChanged() to notify the
server. Keep root lists user-controlled and revoke entries that are no longer authorized.
Sampling
Sampling lets the server ask the client to call its preferred LLM. Declare the sampling capability (and
sampling.tools if you allow tool-enabled sampling), and handle sampling/createMessage. Keep a human in the loop for
approvals.
val client = Client(
clientInfo = Implementation("demo-client", "1.0.0"),
options = ClientOptions(
capabilities = ClientCapabilities(
sampling = buildJsonObject { putJsonObject("tools") { } }, // drop tools if you don't support tool use
),
),
)
client.setRequestHandler<CreateMessageRequest>(Method.Defined.SamplingCreateMessage) { request, _ ->
val content = request.messages.lastOrNull()?.content
val prompt = if (content is TextContent) content.text else "your topic"
CreateMessageResult(
model = "gpt-4o-mini",
role = Role.Assistant,
content = TextContent(text = "Here is a short note about $prompt"),
)
}
Inside the handler you can pick any model/provider, require approvals, or reject the request. If you don’t support tool
use, omit sampling.tools from capabilities.
Transports
All transports share the same API surface, so you can change deployment style without touching business logic. Pick the transport that best matches where the server runs.
STDIO Transport
StdioClientTransport and StdioServerTransport tunnel MCP messages over stdin/stdout—perfect for editor plugins or
CLI tooling that spawns a helper process. No networking setup is required.
Streamable HTTP Transport
StreamableHttpClientTransport and the Ktor mcpStreamableHttp() / mcpStatelessStreamableHttp() helpers expose MCP
over a single HTTP endpoint with optional JSON-only or SSE streaming responses. This is the recommended choice for
remote deployments and integrates nicely with proxies or service meshes.
These helpers automatically install ContentNegotiation with McpJson — do not install it yourself, or a warning will
be logged. Both accept a path parameter (default: "/mcp") to mount the endpoint at any URL:
embeddedServer(CIO, port = 3000) {
mcpStreamableHttp(path = "/api/mcp") {
MyServer()
}
}.start(wait = true)
CORS for browser-based clients (e.g. MCP Inspector): if you connect from a browser-based client you need to install the Ktor CORS plugin so that MCP-specific headers are allowed and exposed:
install(CORS) {
anyHost() // restrict to specific origins in production
allowMethod(HttpMethod.Options)
allowMethod(HttpMethod.Get)
allowMethod(HttpMethod.Post)
allowMethod(HttpMethod.Delete)
allowNonSimpleContentTypes = true
allowHeader("Mcp-Session-Id")
allowHeader("Mcp-Protocol-Version")
exposeHeader("Mcp-Session-Id")
exposeHeader("Mcp-Protocol-Version")
}
SSE Transport
Server-Sent Events remain available for backwards compatibility with older MCP clients. Two Ktor helpers are provided:
Application.mcp { }— installs SSE andContentNegotiationwithMcpJsonautomatically, then registers MCP endpoints at/. Do not installContentNegotiationyourself — the SDK handles it.Route.mcp { }— registers MCP endpoints at the current route path; requiresinstall(SSE)in the application first. Use this to host MCP alongside other routes or under a path prefix:
embeddedServer(CIO, port = 3000) {
install(SSE)
routing {
route("/api/mcp") {
mcp { MyServer() }
}
}
}.start(wait = true)
Prefer Streamable HTTP for new projects.
WebSocket Transport
WebSocketClientTransport plus the matching server utilities provide full-duplex, low-latency connections—useful when
you expect lots of notifications or long-running sessions behind a reverse proxy that already terminates WebSockets.
ChannelTransport (testing)
ChannelTransport provides a simple, non-networked transport for testing and local development.
It uses Kotlin coroutines channels to provide a full-duplex connection between a client and server,
allowing for easy testing of MCP functionality without the need for network setup.
Connecting your server
-
Start a sample HTTP server on port 3000:
./gradlew :samples:kotlin-mcp-server:run -
Connect with the MCP Inspector or Claude Desktop/Code:
npx -y @modelcontextprotocol/inspector --connect http://localhost:3000 # or claude mcp add --transport http kotlin-mcp http://localhost:3000 -
In the Inspector, confirm prompts, tools, resources, completions, and logs show up. Iterate locally until you’re ready to host the server wherever you prefer.
Examples
The samples directory contains runnable projects demonstrating MCP server and client implementations with various transports. See the samples overview for a comparison table and detailed descriptions.
Documentation
Contributing
Please see the contribution guide and the Code of conduct before contributing.
License
This project is licensed under Apache 2.0 for new contributions, with existing code under MIT—see the LICENSE file for details.