Mirror of https://github.com/modelcontextprotocol/kotlin-sdk — The official Kotlin SDK for Model Context Protocol servers and clients. Maintained in collaboration with JetBrains
  • Kotlin 98%
  • TypeScript 1.7%
  • Shell 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
dependabot[bot] 232582db19
Bump kotest from 6.0.3 to 6.0.4 (#324)
Bumps `kotest` from 6.0.3 to 6.0.4.
Updates `io.kotest:kotest-assertions-core` from 6.0.3 to 6.0.4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/kotest/kotest/releases">io.kotest:kotest-assertions-core's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.4</h2>
<h2>What's Changed</h2>
<ul>
<li>Restore io.kotest.assertions.fail by <a
href="https://github.com/sksamuel"><code>@​sksamuel</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5083">kotest/kotest#5083</a></li>
<li>feat: add Arb for kotlin.time.Instant by <a
href="https://github.com/tKe"><code>@​tKe</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5085">kotest/kotest#5085</a></li>
<li>Positive Boolean matchers return non-null by contract by <a
href="https://github.com/JordanLongstaff"><code>@​JordanLongstaff</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5088">kotest/kotest#5088</a></li>
<li>Update spring.md by <a
href="https://github.com/noojung"><code>@​noojung</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5099">kotest/kotest#5099</a></li>
<li>Update extension modules list in release notes by <a
href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5102">kotest/kotest#5102</a></li>
<li>Accommodate a recent breaking change of annotation name from
EnabledCondition to Condition by <a
href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5111">kotest/kotest#5111</a></li>
<li>Record kotest-assertions-api changes in release_6.0.md by <a
href="https://github.com/TWiStErRob"><code>@​TWiStErRob</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5115">kotest/kotest#5115</a></li>
<li>add days when daylight saving time is on/off to edge cases of
Arb.datesBetween by <a
href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5092">kotest/kotest#5092</a></li>
<li>Add missing Extension marker to core extension and listeners. by <a
href="https://github.com/Jadarma"><code>@​Jadarma</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5097">kotest/kotest#5097</a></li>
<li>Increment the assertion counter in Arrow assertions that do not call
core assertions by <a
href="https://github.com/jenssuhr"><code>@​jenssuhr</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5090">kotest/kotest#5090</a></li>
<li>Fix kotest plugin in project with Kotlin 2.2.20 by <a
href="https://github.com/walterbrebels"><code>@​walterbrebels</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5106">kotest/kotest#5106</a></li>
<li><code>PackageConfig</code> checked bug by <a
href="https://github.com/alfonsoristorato"><code>@​alfonsoristorato</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5131">kotest/kotest#5131</a></li>
<li>Alex/fail softly (<a
href="https://redirect.github.com/kotest/kotest/issues/5128">#5128</a>)
by <a href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5129">kotest/kotest#5129</a></li>
<li>Restrict <a
href="https://github.com/ApplyExtension"><code>@​ApplyExtension</code></a>
to the target class by <a
href="https://github.com/sksamuel"><code>@​sksamuel</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5133">kotest/kotest#5133</a></li>
<li>Enable to call <code>afterSpec</code> hooks on
<code>InstancePerLeaf</code> correctly by <a
href="https://github.com/T45K"><code>@​T45K</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5114">kotest/kotest#5114</a></li>
<li>Add timeout handling to <code>eventually</code> by <a
href="https://github.com/sksamuel"><code>@​sksamuel</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5134">kotest/kotest#5134</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/noojung"><code>@​noojung</code></a> made
their first contribution in <a
href="https://redirect.github.com/kotest/kotest/pull/5099">kotest/kotest#5099</a></li>
<li><a href="https://github.com/jenssuhr"><code>@​jenssuhr</code></a>
made their first contribution in <a
href="https://redirect.github.com/kotest/kotest/pull/5090">kotest/kotest#5090</a></li>
<li><a
href="https://github.com/walterbrebels"><code>@​walterbrebels</code></a>
made their first contribution in <a
href="https://redirect.github.com/kotest/kotest/pull/5106">kotest/kotest#5106</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/kotest/kotest/compare/v6.0.3...v6.0.4">https://github.com/kotest/kotest/compare/v6.0.3...v6.0.4</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7b2ed08529"><code>7b2ed08</code></a>
Add tests to validate tag useage</li>
<li><a
href="f69f07c569"><code>f69f07c</code></a>
Add timeout handling to <code>eventually</code> (<a
href="https://redirect.github.com/kotest/kotest/issues/5134">#5134</a>)</li>
<li><a
href="c199226179"><code>c199226</code></a>
Enable to call <code>afterSpec</code> hooks on
<code>InstancePerLeaf</code> correctly (<a
href="https://redirect.github.com/kotest/kotest/issues/5114">#5114</a>)</li>
<li><a
href="a903c8ff8c"><code>a903c8f</code></a>
Restrict <a
href="https://github.com/ApplyExtension"><code>@​ApplyExtension</code></a>
to the target class (<a
href="https://redirect.github.com/kotest/kotest/issues/5133">#5133</a>)</li>
<li><a
href="7c28f438a3"><code>7c28f43</code></a>
Add tests for nested classes and anonymous types in equality matchers;
handle...</li>
<li><a
href="c1c73bba16"><code>c1c73bb</code></a>
Correct spelling of <code>DISABLE_NAN_NEQUALITY</code> constant and add
deprecated alias...</li>
<li><a
href="01ae08d376"><code>01ae08d</code></a>
Alex/fail softly (<a
href="https://redirect.github.com/kotest/kotest/issues/5128">#5128</a>)
(<a
href="https://redirect.github.com/kotest/kotest/issues/5129">#5129</a>)</li>
<li><a
href="d80b9559d8"><code>d80b955</code></a>
<code>PackageConfig</code> checked bug (<a
href="https://redirect.github.com/kotest/kotest/issues/5131">#5131</a>)</li>
<li><a
href="77f15153f5"><code>77f1515</code></a>
Refactor <code>TestEngineLauncher</code> usage in system property
timeout tests to use `...</li>
<li><a
href="bcc1cd0e8a"><code>bcc1cd0</code></a>
Fix kotest plugin in project with Kotlin 2.2.20 (<a
href="https://redirect.github.com/kotest/kotest/issues/5106">#5106</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/kotest/kotest/compare/6.0.3...6.0.4">compare
view</a></li>
</ul>
</details>
<br />

Updates `io.kotest:kotest-assertions-json` from 6.0.3 to 6.0.4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/kotest/kotest/releases">io.kotest:kotest-assertions-json's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.4</h2>
<h2>What's Changed</h2>
<ul>
<li>Restore io.kotest.assertions.fail by <a
href="https://github.com/sksamuel"><code>@​sksamuel</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5083">kotest/kotest#5083</a></li>
<li>feat: add Arb for kotlin.time.Instant by <a
href="https://github.com/tKe"><code>@​tKe</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5085">kotest/kotest#5085</a></li>
<li>Positive Boolean matchers return non-null by contract by <a
href="https://github.com/JordanLongstaff"><code>@​JordanLongstaff</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5088">kotest/kotest#5088</a></li>
<li>Update spring.md by <a
href="https://github.com/noojung"><code>@​noojung</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5099">kotest/kotest#5099</a></li>
<li>Update extension modules list in release notes by <a
href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5102">kotest/kotest#5102</a></li>
<li>Accommodate a recent breaking change of annotation name from
EnabledCondition to Condition by <a
href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5111">kotest/kotest#5111</a></li>
<li>Record kotest-assertions-api changes in release_6.0.md by <a
href="https://github.com/TWiStErRob"><code>@​TWiStErRob</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5115">kotest/kotest#5115</a></li>
<li>add days when daylight saving time is on/off to edge cases of
Arb.datesBetween by <a
href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5092">kotest/kotest#5092</a></li>
<li>Add missing Extension marker to core extension and listeners. by <a
href="https://github.com/Jadarma"><code>@​Jadarma</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5097">kotest/kotest#5097</a></li>
<li>Increment the assertion counter in Arrow assertions that do not call
core assertions by <a
href="https://github.com/jenssuhr"><code>@​jenssuhr</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5090">kotest/kotest#5090</a></li>
<li>Fix kotest plugin in project with Kotlin 2.2.20 by <a
href="https://github.com/walterbrebels"><code>@​walterbrebels</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5106">kotest/kotest#5106</a></li>
<li><code>PackageConfig</code> checked bug by <a
href="https://github.com/alfonsoristorato"><code>@​alfonsoristorato</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5131">kotest/kotest#5131</a></li>
<li>Alex/fail softly (<a
href="https://redirect.github.com/kotest/kotest/issues/5128">#5128</a>)
by <a href="https://github.com/AlexCue987"><code>@​AlexCue987</code></a>
in <a
href="https://redirect.github.com/kotest/kotest/pull/5129">kotest/kotest#5129</a></li>
<li>Restrict <a
href="https://github.com/ApplyExtension"><code>@​ApplyExtension</code></a>
to the target class by <a
href="https://github.com/sksamuel"><code>@​sksamuel</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5133">kotest/kotest#5133</a></li>
<li>Enable to call <code>afterSpec</code> hooks on
<code>InstancePerLeaf</code> correctly by <a
href="https://github.com/T45K"><code>@​T45K</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5114">kotest/kotest#5114</a></li>
<li>Add timeout handling to <code>eventually</code> by <a
href="https://github.com/sksamuel"><code>@​sksamuel</code></a> in <a
href="https://redirect.github.com/kotest/kotest/pull/5134">kotest/kotest#5134</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/noojung"><code>@​noojung</code></a> made
their first contribution in <a
href="https://redirect.github.com/kotest/kotest/pull/5099">kotest/kotest#5099</a></li>
<li><a href="https://github.com/jenssuhr"><code>@​jenssuhr</code></a>
made their first contribution in <a
href="https://redirect.github.com/kotest/kotest/pull/5090">kotest/kotest#5090</a></li>
<li><a
href="https://github.com/walterbrebels"><code>@​walterbrebels</code></a>
made their first contribution in <a
href="https://redirect.github.com/kotest/kotest/pull/5106">kotest/kotest#5106</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/kotest/kotest/compare/v6.0.3...v6.0.4">https://github.com/kotest/kotest/compare/v6.0.3...v6.0.4</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7b2ed08529"><code>7b2ed08</code></a>
Add tests to validate tag useage</li>
<li><a
href="f69f07c569"><code>f69f07c</code></a>
Add timeout handling to <code>eventually</code> (<a
href="https://redirect.github.com/kotest/kotest/issues/5134">#5134</a>)</li>
<li><a
href="c199226179"><code>c199226</code></a>
Enable to call <code>afterSpec</code> hooks on
<code>InstancePerLeaf</code> correctly (<a
href="https://redirect.github.com/kotest/kotest/issues/5114">#5114</a>)</li>
<li><a
href="a903c8ff8c"><code>a903c8f</code></a>
Restrict <a
href="https://github.com/ApplyExtension"><code>@​ApplyExtension</code></a>
to the target class (<a
href="https://redirect.github.com/kotest/kotest/issues/5133">#5133</a>)</li>
<li><a
href="7c28f438a3"><code>7c28f43</code></a>
Add tests for nested classes and anonymous types in equality matchers;
handle...</li>
<li><a
href="c1c73bba16"><code>c1c73bb</code></a>
Correct spelling of <code>DISABLE_NAN_NEQUALITY</code> constant and add
deprecated alias...</li>
<li><a
href="01ae08d376"><code>01ae08d</code></a>
Alex/fail softly (<a
href="https://redirect.github.com/kotest/kotest/issues/5128">#5128</a>)
(<a
href="https://redirect.github.com/kotest/kotest/issues/5129">#5129</a>)</li>
<li><a
href="d80b9559d8"><code>d80b955</code></a>
<code>PackageConfig</code> checked bug (<a
href="https://redirect.github.com/kotest/kotest/issues/5131">#5131</a>)</li>
<li><a
href="77f15153f5"><code>77f1515</code></a>
Refactor <code>TestEngineLauncher</code> usage in system property
timeout tests to use `...</li>
<li><a
href="bcc1cd0e8a"><code>bcc1cd0</code></a>
Fix kotest plugin in project with Kotlin 2.2.20 (<a
href="https://redirect.github.com/kotest/kotest/issues/5106">#5106</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/kotest/kotest/compare/6.0.3...6.0.4">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-22 14:37:23 +03:00
.github #302 [part 1] Add GitHub workflow to validate samples (#327) 2025-10-22 14:20:52 +03:00
buildSrc Add StreamableHttpClientTest with test infrastructure setup and dependencies (#316) 2025-10-17 16:03:52 +03:00
docs Add module documentation and enhance Dokka configuration (#261) 2025-09-15 17:44:43 +02:00
gradle Bump kotest from 6.0.3 to 6.0.4 (#324) 2025-10-22 14:37:23 +03:00
kotlin-sdk Prerelease 0.7.2-alpha1 (#278) 2025-09-18 15:18:12 +02:00
kotlin-sdk-client Fix Streamable handling to support JSON-only responses (#309) 2025-10-21 14:05:39 +02:00
kotlin-sdk-core Bump org.openapi.generator from 7.15.0 to 7.16.0 (#305) 2025-10-13 22:47:35 +03:00
kotlin-sdk-server Adds 'instructions' string to the Server and propagates to the InitializeResult (#290) 2025-10-03 18:12:37 +02:00
kotlin-sdk-test Add StreamableHttpClientTest with test infrastructure setup and dependencies (#316) 2025-10-17 16:03:52 +03:00
samples Change sample server host from 0.0.0.0 to 127.0.0.1 (#307) 2025-10-08 11:59:04 +03:00
.editorconfig Add ktlint (#213) 2025-08-05 13:10:44 +00:00
.gitignore Add module documentation and enhance Dokka configuration (#261) 2025-09-15 17:44:43 +02:00
build.gradle.kts Update badges in README and set version to 0.7.4-SNAPSHOT (#326) 2025-10-22 13:59:17 +03:00
CODE_OF_CONDUCT.md Update license 2024-12-16 21:20:35 +01:00
CONTRIBUTING.md Update package name across the project (#2) 2024-12-17 07:28:51 +01:00
gradle.properties #196 Update build configuration and workflows for enhanced local publ… (#275) 2025-09-16 21:43:50 +03:00
gradlew Fix release (#155) 2025-07-18 14:08:05 +00:00
gradlew.bat Fix release (#155) 2025-07-18 14:08:05 +00:00
LICENSE Update license 2024-12-16 21:20:35 +01:00
README.md Update badges in README and set version to 0.7.4-SNAPSHOT (#326) 2025-10-22 13:59:17 +03:00
settings.gradle.kts Conditionally imports the samples only if its the root project (#292) 2025-09-27 14:54:39 +03:00

MCP Kotlin SDK

Maven Central Build Kotlin Kotlin Multiplatform JVM License

Kotlin Multiplatform implementation of the Model Context Protocol (MCP), providing both client and server capabilities for integrating with LLM surfaces across various platforms.

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 SDK implements the MCP specification for Kotlin, enabling you to build applications that can communicate using MCP on the JVM, WebAssembly and iOS.

  • Build MCP clients that can connect to any MCP server
  • Create MCP servers that expose resources, prompts and tools
  • Use standard transports like stdio, SSE, and WebSocket
  • Handle all MCP protocol messages and lifecycle events

Samples

  • kotlin-mcp-server: demonstrates a multiplatform (JVM, Wasm) MCP server setup with various features and transports.
  • weather-stdio-server: shows how to build a Kotlin MCP server providing weather forecast and alerts using STDIO transport.
  • kotlin-mcp-client: demonstrates building an interactive Kotlin MCP client that connects to an MCP server via STDIO and integrates with Anthropic’s API.

Installation

Add the new repository to your build file:

repositories {
    mavenCentral()
}

Add the dependency:

dependencies {
    // See the badge above for the latest version
    implementation("io.modelcontextprotocol:kotlin-sdk:$mcpVersion")
}

MCP SDK uses Ktor, but does not come with a specific engine dependency. You should add Ktor client and/or Ktor server dependency to your project yourself, e.g.:

dependencies {
    // for client:
    implementation("io.ktor:ktor-client-cio:$ktorVersion")
    // for server:
    implementation("io.ktor:ktor-server-netty:$ktorVersion")
    
}

Quick Start

Creating a Client

import io.modelcontextprotocol.kotlin.sdk.client.Client
import io.modelcontextprotocol.kotlin.sdk.client.StdioClientTransport
import io.modelcontextprotocol.kotlin.sdk.Implementation

val client = Client(
    clientInfo = Implementation(
        name = "example-client",
        version = "1.0.0"
    )
)

val transport = StdioClientTransport(
    inputStream = processInputStream,
    outputStream = processOutputStream
)

// Connect to server
client.connect(transport)

// List available resources
val resources = client.listResources()

// Read a specific resource
val resourceContent = client.readResource(
    ReadResourceRequest(uri = "file:///example.txt")
)

Creating a Server

import io.modelcontextprotocol.kotlin.sdk.server.Server
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities

val server = Server(
    serverInfo = Implementation(
        name = "example-server",
        version = "1.0.0"
    ),
    options = ServerOptions(
        capabilities = ServerCapabilities(
            resources = ServerCapabilities.Resources(
                subscribe = true,
                listChanged = true
            )
        )
    )
){ 
    "This server provides example resources and demonstrates MCP capabilities." 
}

// Add a resource
server.addResource(
    uri = "file:///example.txt",
    name = "Example Resource",
    description = "An example text file",
    mimeType = "text/plain"
) { request ->
    ReadResourceResult(
        contents = listOf(
            TextResourceContents(
                text = "This is the content of the example resource.",
                uri = request.uri,
                mimeType = "text/plain"
            )
        )
    )
}

// Start server with stdio transport
val transport = StdioServerTransport()
server.connect(transport)

Using SSE Transport

Directly in Ktor's Application:

import io.ktor.server.application.*
import io.modelcontextprotocol.kotlin.sdk.server.mcp

fun Application.module() {
    mcp {
        Server(
            serverInfo = Implementation(
                name = "example-sse-server",
                version = "1.0.0"
            ),
            options = ServerOptions(
                capabilities = ServerCapabilities(
                    prompts = ServerCapabilities.Prompts(listChanged = null),
                    resources = ServerCapabilities.Resources(subscribe = null, listChanged = null)
                )
            ),
        ) { 
            "This SSE server provides prompts and resources via Server-Sent Events."
        }
    }
}

Inside a custom Ktor's Route:

import io.ktor.server.application.*
import io.ktor.server.sse.SSE
import io.modelcontextprotocol.kotlin.sdk.server.mcp

fun Application.module() {
    install(SSE)

    routing {
        route("myRoute") {
            mcp {
                Server(
                    serverInfo = Implementation(
                        name = "example-sse-server",
                        version = "1.0.0"
                    ),
                    options = ServerOptions(
                        capabilities = ServerCapabilities(
                            prompts = ServerCapabilities.Prompts(listChanged = null),
                            resources = ServerCapabilities.Resources(subscribe = null, listChanged = null)
                        )
                    ),
                ) {
                    "Connect via SSE to interact with this MCP server."
                }
            }
        }
    }
}

Contributing

Please see the contribution guide and the Code of conduct before contributing.

License

This project is licensed under the MIT License—see the LICENSE file for details.