diff --git a/.claude/skills/kdoc/SKILL.md b/.claude/skills/kdoc/SKILL.md
index cfacfc3..4760293 100644
--- a/.claude/skills/kdoc/SKILL.md
+++ b/.claude/skills/kdoc/SKILL.md
@@ -5,62 +5,40 @@ description: "Add KDoc documentation to Kotlin public API. Use whenever the user
# KDoc Generator
-Add KDoc to all public Kotlin declarations (classes, interfaces, objects, functions, properties, enums, type aliases, annotations — including `@Deprecated`). Read the implementation, not just the signature, to write accurate descriptions.
+Add KDoc comments to public Kotlin API declarations.
-## Tag placement
+## What to document
-| Where the declaration lives | Tag |
-|---|---|
-| Primary-ctor `val` / `var` (public, internal, or protected) | `@property` in class KDoc |
-| Primary-ctor `name: Type` (no `val`/`var`) | `@param` in class KDoc |
-| Primary-ctor `private val` / `private var` | `@param` in class KDoc |
-| Body of class/object/interface/sealed interface | inline `/** */` above the declaration |
-| Function parameter | `@param` |
-| Non-Unit return type | `@return` |
+All public declarations (no explicit `private`/`internal`/`protected`):
+- Classes, interfaces, objects, sealed classes/interfaces
+- Functions, extension functions
+- Properties
+- Enum classes and entries
+- Type aliases, annotation classes
-## Style
+Include `@Deprecated` elements.
-- Summary: one concise sentence, third-person verb ("Creates", "Returns", "Represents"). Expand to 2-3 sentences only when genuinely complex.
-- **Example block**: mandatory for DSL builders (show full receiver-scope usage); add for complex functions or non-obvious extension functions; skip for one-liners and simple getters.
-- KDoc links (`[ClassName]`, `[methodName]`) only where they add navigational value.
-- Don't write `@throws` or "suspend" notes.
-- Rewrite existing KDoc if incomplete or low quality.
+## Context
-## Examples
+Read the implementation, not just the signature, to write accurate descriptions. Understanding what the code actually does prevents superficial or misleading documentation.
-**Class with mixed primary-constructor parameters:**
+## KDoc format
+
+**Class/interface example:**
````kotlin
/**
* Manages active client sessions and their lifecycle.
*
+ * Sessions are created on first connection and cleaned up
+ * when the transport closes.
+ *
* @property maxSessions upper limit on concurrent sessions
- * @param scope coroutine scope for session lifecycle tasks
- * @param secret used internally for session token signing
+ * @property timeout idle timeout before a session is evicted
*/
-public class SessionManager(
- public val maxSessions: Int, // public val → @property
- scope: CoroutineScope, // no val/var → @param
- private val secret: ByteArray, // private val → @param
-)
````
-**Body and interface properties (inline `/** */`):**
-
-````kotlin
-/** Represents an entity that includes additional metadata. */
-public interface WithMeta {
- /** Optional metadata attached to this entity. */
- public val meta: JsonObject?
-}
-
-public class Connection(public val id: String) {
- /** Capabilities negotiated during the handshake, or `null` before initialize completes. */
- public val capabilities: ServerCapabilities? get() = ...
-}
-````
-
-**Function with Example block:**
+**Function example:**
````kotlin
/**
@@ -68,7 +46,10 @@ public class Connection(public val id: String) {
*
* Example:
* ```kotlin
- * server.addTool(name = "echo", description = "Echoes input back") { request ->
+ * server.addTool(
+ * name = "echo",
+ * description = "Echoes input back",
+ * ) { request ->
* CallToolResult(content = listOf(TextContent("Echo: ${request.arguments}")))
* }
* ```
@@ -79,3 +60,15 @@ public class Connection(public val id: String) {
* @return the registered tool definition
*/
````
+
+## Rules
+
+- Summary: concise first sentence starting with a third-person verb ("Creates", "Returns", "Represents"). Expand to 2-3 sentences only when genuinely complex
+- **@property** in class-level KDoc for all public properties (never as individual KDoc on the property); **@param** for function parameters
+- **@return** for non-Unit return types
+- **Example block**: add for DSL builders, complex functions, extension functions with non-obvious usage. Skip for trivial one-liners and simple getters
+- **DSL builders**: always include an Example showing full usage with the receiver scope — this is critical for discoverability
+- KDoc links (`[ClassName]`, `[methodName]`): only where it adds clear navigational value
+- No **@throws** — don't document exceptions
+- No **suspend** notes — coroutine nature is visible from the signature
+- **Existing KDoc**: rewrite if incomplete (missing @param/@return/@property) or low quality
\ No newline at end of file
diff --git a/.github/workflows/apidocs.yaml b/.github/workflows/apidocs.yaml
index 67770a7..76f4434 100644
--- a/.github/workflows/apidocs.yaml
+++ b/.github/workflows/apidocs.yaml
@@ -24,24 +24,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK ${{ env.JAVA_VERSION }}
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
cache: gradle
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
- name: Generate Dokka Site
run: |-
./gradlew knit docs:classes docs:knitCheck dokkaGenerate
- name: Upload artifact
- uses: actions/upload-pages-artifact@v5
+ uses: actions/upload-pages-artifact@v4
with:
path: build/dokka/html
diff --git a/.github/workflows/automations.yml b/.github/workflows/automations.yml
index 60a9d3b..4f88d37 100644
--- a/.github/workflows/automations.yml
+++ b/.github/workflows/automations.yml
@@ -27,7 +27,7 @@ jobs:
name: Mark/unmark stale PRs and issues
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@v11
+ - uses: actions/stale@v10
with:
debug-only: ${{ env.DRY_RUN }}
exempt-draft-pr: true
@@ -66,7 +66,7 @@ jobs:
name: Close stale PRs and issues
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@v11
+ - uses: actions/stale@v10
with:
debug-only: ${{ env.DRY_RUN }}
only-labels: 'stale'
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ff99581..08242f7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -34,7 +34,7 @@ jobs:
include:
- os: ubuntu-latest
job-name: "Linux Tests"
- gradle-tasks: "build"
+ gradle-tasks: "ktlintCheck build"
max-workers: 3
- os: macos-latest
@@ -43,17 +43,17 @@ jobs:
max-workers: 2
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK ${{ env.JAVA_VERSION }}
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Set up Node.js
if: matrix.os == 'ubuntu-latest'
- uses: actions/setup-node@v7
+ uses: actions/setup-node@v6
with:
node-version: '22' # increase only after https://github.com/nodejs/node/issues/56645 will be fixed
@@ -63,7 +63,7 @@ jobs:
run: npm ci
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
validate-wrappers: true
add-job-summary: 'always'
@@ -104,6 +104,22 @@ jobs:
report_paths: '**/test-results/**/TEST-*.xml'
truncate_stack_traces: false
+ - name: Upload coverage reports to Codecov
+ if: ${{ !cancelled() && matrix.os == 'ubuntu-latest' }}
+ uses: codecov/codecov-action@v6
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ report_type: coverage
+ directory: ./build/reports/kover
+ files: report.xml
+
+ - name: Upload test results to Codecov
+ if: ${{ !cancelled() && matrix.os == 'ubuntu-latest' }}
+ uses: codecov/codecov-action@v6
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ report_type: test_results
+
- name: Archive Maven Local Repository
if: ${{ success() && matrix.os == 'ubuntu-latest' }}
@@ -126,16 +142,16 @@ jobs:
contents: read
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK ${{ env.JAVA_VERSION }}
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
validate-wrappers: true
add-job-summary: 'always'
@@ -168,16 +184,16 @@ jobs:
name: "Build Sample: ${{ matrix.sample }}"
timeout-minutes: 10
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK ${{ env.JAVA_VERSION }}
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
add-job-summary: 'always'
cache-read-only: true
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 2e7c58f..5697b61 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -25,22 +25,22 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v7
+ uses: actions/checkout@v6
- name: Set up JDK ${{ env.JAVA_VERSION }}
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.JAVA_VERSION }}
- name: Initialize CodeQL
- uses: github/codeql-action/init@v4.38.0
+ uses: github/codeql-action/init@v4
with:
languages: java-kotlin
build-mode: manual
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
validate-wrappers: true
cache-read-only: true
@@ -52,6 +52,6 @@ jobs:
--no-daemon --rerun-tasks --stacktrace
- name: Analyze
- uses: github/codeql-action/analyze@v4.38.0
+ uses: github/codeql-action/analyze@v4
with:
category: '/language:java-kotlin'
diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml
index b3e3935..4058d02 100644
--- a/.github/workflows/conformance.yml
+++ b/.github/workflows/conformance.yml
@@ -25,16 +25,16 @@ jobs:
timeout-minutes: 20
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
@@ -76,16 +76,16 @@ jobs:
- sse-retry
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
@@ -107,16 +107,16 @@ jobs:
timeout-minutes: 20
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
@@ -146,16 +146,16 @@ jobs:
- auth/cross-app-access-complete-flow
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml
index e7e94c3..6073684 100644
--- a/.github/workflows/dependabot-auto-merge.yml
+++ b/.github/workflows/dependabot-auto-merge.yml
@@ -11,7 +11,7 @@ jobs:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- - uses: dependabot/fetch-metadata@v3
+ - uses: dependabot/fetch-metadata@v2
id: metadata
- if: >-
diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml
index f01f9c8..6b60e87 100644
--- a/.github/workflows/gradle-publish.yml
+++ b/.github/workflows/gradle-publish.yml
@@ -17,23 +17,18 @@ jobs:
packages: write
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK 21
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
- - name: Set up Node.js
- uses: actions/setup-node@v7
- with:
- node-version: '22' # increase only after https://github.com/nodejs/node/issues/56645 will be fixed
-
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
- - name: Check
- run: ./gradlew check --no-configuration-cache
+ - name: Build
+ run: ./gradlew build --rerun-tasks --max-workers 2 --continue
- name: Publish to Maven Central Portal
id: publish
diff --git a/.github/workflows/samples.yml b/.github/workflows/samples.yml
index 7463a9e..2c910fd 100644
--- a/.github/workflows/samples.yml
+++ b/.github/workflows/samples.yml
@@ -36,16 +36,16 @@ jobs:
name: Build Sample (${{ matrix.sample }})
timeout-minutes: 10
steps:
- - uses: actions/checkout@v7
+ - uses: actions/checkout@v6
- name: Set up JDK ${{ env.JAVA_VERSION }}
- uses: actions/setup-java@v6
+ uses: actions/setup-java@v5
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
- name: Setup Gradle
- uses: gradle/actions/setup-gradle@v6.3.0
+ uses: gradle/actions/setup-gradle@v6
with:
validate-wrappers: true
add-job-summary: 'always'
diff --git a/.gitignore b/.gitignore
index 805579c..d9f0fde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,7 +56,7 @@ node_modules
dist
### SWE agents ###
-.claude/
+.claude/settings.local.json
.junie/
### Conformance test results ###
diff --git a/.idea/detekt.xml b/.idea/detekt.xml
new file mode 100644
index 0000000..d89f908
--- /dev/null
+++ b/.idea/detekt.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/.idea/externalDependencies.xml b/.idea/externalDependencies.xml
new file mode 100644
index 0000000..0a081df
--- /dev/null
+++ b/.idea/externalDependencies.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml
new file mode 100644
index 0000000..e8bd90c
--- /dev/null
+++ b/.idea/ktlint-plugin.xml
@@ -0,0 +1,7 @@
+
+
+
+ DISTRACT_FREE
+ DEFAULT
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index aefadcf..58a2cad 100644
--- a/README.md
+++ b/README.md
@@ -729,7 +729,7 @@ val client = Client(
)
client.setRequestHandler(Method.Defined.SamplingCreateMessage) { request, _ ->
- val content = request.messages.lastOrNull()?.content?.firstOrNull()
+ val content = request.messages.lastOrNull()?.content
val prompt = if (content is TextContent) content.text else "your topic"
CreateMessageResult(
model = "gpt-4o-mini",
diff --git a/build.gradle.kts b/build.gradle.kts
index 2c2dbe3..72c5553 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,8 @@
+import dev.detekt.gradle.extensions.FailOnSeverity
+
plugins {
id("mcp.dokka")
+ alias(libs.plugins.detekt)
alias(libs.plugins.ktlint)
alias(libs.plugins.kover)
}
@@ -20,6 +23,16 @@ dependencies {
subprojects {
apply(plugin = "org.jlleitschuh.gradle.ktlint")
apply(plugin = "org.jetbrains.kotlinx.kover")
+
+ if (name != "conformance-test" && name != "docs") {
+ apply(plugin = "dev.detekt")
+
+ detekt {
+ config = files("$rootDir/config/detekt/detekt.yml")
+ buildUponDefaultConfig = true
+ failOnSeverity.set(FailOnSeverity.Error)
+ }
+ }
}
dokka {
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 4a057ce..84676a4 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -8,7 +8,6 @@ repositories {
}
dependencies {
- implementation(libs.detekt.gradle)
implementation(libs.kotlin.gradle)
implementation(libs.kotlin.serialization)
implementation(libs.kotlinx.atomicfu.gradle)
diff --git a/buildSrc/src/main/kotlin/mcp.detekt.gradle.kts b/buildSrc/src/main/kotlin/mcp.detekt.gradle.kts
deleted file mode 100644
index 3c1419e..0000000
--- a/buildSrc/src/main/kotlin/mcp.detekt.gradle.kts
+++ /dev/null
@@ -1,23 +0,0 @@
-import dev.detekt.gradle.Detekt
-import dev.detekt.gradle.extensions.FailOnSeverity
-
-plugins {
- id("dev.detekt")
-}
-
-detekt {
- config = files("$rootDir/config/detekt/detekt.yml")
- buildUponDefaultConfig = false
- parallel = true
- failOnSeverity = FailOnSeverity.Error
-}
-
-tasks.withType().configureEach {
- exclude("**/generated/**", "**/generated-sources/**", "**/build/**")
-}
-
-pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
- tasks.named("detekt").configure {
- dependsOn("detektMainJvm")
- }
-}
diff --git a/buildSrc/src/main/kotlin/mcp.multiplatform.gradle.kts b/buildSrc/src/main/kotlin/mcp.multiplatform.gradle.kts
index 98a0a5e..a331139 100644
--- a/buildSrc/src/main/kotlin/mcp.multiplatform.gradle.kts
+++ b/buildSrc/src/main/kotlin/mcp.multiplatform.gradle.kts
@@ -45,6 +45,11 @@ kotlin {
events("failed")
}
systemProperty("kotest.output.ansi", "true")
+ reports {
+ junitXml.required.set(true)
+ junitXml.includeSystemOutLog.set(true)
+ junitXml.includeSystemErrLog.set(true)
+ }
}
}
macosArm64()
@@ -57,3 +62,12 @@ kotlin {
explicitApi = ExplicitApiMode.Strict
jvmToolchain(21)
}
+
+tasks.named("detekt").configure {
+ dependsOn(
+ "detektMainJvm",
+ "detektCommonMainSourceSet",
+ "detektTestJvm",
+ "detektCommonTestSourceSet",
+ )
+}
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..9135aae
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,51 @@
+# Codecov configuration for Mokksy and AI-Mocks
+# https://docs.codecov.com/docs/codecov-yaml
+
+codecov:
+ require_ci_to_pass: yes
+
+coverage:
+ precision: 2
+ round: down
+ status:
+ project:
+ default:
+ target: auto
+ threshold: 1%
+ patch:
+ default:
+ target: auto
+ threshold: 1%
+
+comment:
+ layout: "header, diff, flags, components"
+ behavior: default
+ require_changes: true
+
+component_management:
+ default_rules:
+ statuses:
+ - type: project
+ target: auto
+ individual_components:
+
+ - component_id: kotlin-sdk-core
+ name: SDK Core
+ paths:
+ - kotlin-sdk-core/**
+
+ - component_id: kotlin-sdk-client
+ name: SDK Client
+ paths:
+ - kotlin-sdk-client/**
+
+ - component_id: kotlin-sdk-server
+ name: SDK Server
+ paths:
+ - kotlin-sdk-server/**
+
+ - component_id: kotlin-sdk-testing
+ name: SDK Testing
+ paths:
+ - kotlin-sdk-testing/**
+
diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml
index a82b404..4d44734 100644
--- a/config/detekt/detekt.yml
+++ b/config/detekt/detekt.yml
@@ -3,839 +3,41 @@ config:
warningsAsErrors: false
checkExhaustiveness: false
-processors:
- active: false
-
-console-reports:
- active: true
- exclude:
- - 'ProjectStatisticsReport'
- - 'ComplexityReport'
- - 'NotificationReport'
- - 'IssuesReport'
- - 'FileBasedIssuesReport'
- # - 'LiteIssuesReport'
-
-comments:
- active: true
- AbsentOrWrongFileLicense:
- active: false
- licenseTemplateIsRegex: false
- licenseTemplate: ''
- DeprecatedBlockTag:
- active: true
- DocumentationOverPrivateFunction:
- active: false
- DocumentationOverPrivateProperty:
- active: false
- EndOfSentenceFormat:
- active: false
- endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
- KDocReferencesNonPublicProperty:
- active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- OutdatedDocumentation:
- active: false
- matchTypeParameters: true
- matchDeclarationsOrder: true
- allowParamOnConstructorProperties: false
- UndocumentedPublicClass:
- active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- searchInNestedClass: true
- searchInInnerClass: true
- searchInInnerObject: true
- searchInInnerInterface: true
- searchInProtectedClass: false
- ignoreDefaultCompanionObject: true
- UndocumentedPublicFunction:
- active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- searchProtectedFunction: false
- UndocumentedPublicProperty:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- searchProtectedProperty: false
- ignoreEnumEntries: false
-
complexity:
- active: true
- CognitiveComplexMethod:
- active: false
- allowedComplexity: 20
- ComplexCondition:
- active: true
- allowedConditions: 4
- ComplexInterface:
- active: false
- allowedDefinitions: 10
- includeStaticDeclarations: false
- includePrivateDeclarations: false
- ignoreOverloaded: false
- CyclomaticComplexMethod:
- active: false
- allowedComplexity: 15
- ignoreSingleWhenExpression: false
- ignoreSimpleWhenEntries: false
- ignoreNestingFunctions: false
- ignoreLocalFunctions: false
- nestingFunctions:
- - 'also'
- - 'apply'
- - 'forEach'
- - 'isNotNull'
- - 'ifNull'
- - 'let'
- - 'run'
- - 'use'
- - 'with'
- LabeledExpression:
- active: false
- ignoredLabels: []
LargeClass:
- active: false
- allowedLines: 700
+ excludes: &testFolders
+ - '**/test/**'
+ - '**/commonTest/**'
+ - '**/jvmTest/**'
+ - '**/jsTest/**'
+ - '**/iosTest/**'
LongMethod:
- active: false
- allowedLines: 110
- LongParameterList:
- active: false
- allowedFunctionParameters: 8
- allowedConstructorParameters: 8
- ignoreDefaultParameters: false
- ignoreDataClasses: true
- ignoreAnnotatedParameter: []
- MethodOverloading:
- active: false
- allowedOverloads: 6
- NamedArguments:
- active: false
- allowedArguments: 3
- ignoreMethods: []
- ignoreArgumentsMatchingNames: false
- NestedBlockDepth:
- active: true
- allowedDepth: 4
- NestedScopeFunctions:
- active: false
- allowedDepth: 1
- functions:
- - 'kotlin.apply'
- - 'kotlin.run'
- - 'kotlin.with'
- - 'kotlin.let'
- - 'kotlin.also'
- ReplaceSafeCallChainWithRun:
- active: false
- StringLiteralDuplication:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- allowedDuplications: 3
- ignoreAnnotation: true
- allowedWithLengthLessThan: 5
- ignoreStringsRegex: '$^'
- TooManyFunctions:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- allowedFunctionsPerFile: 20
- allowedFunctionsPerClass: 20
- allowedFunctionsPerInterface: 20
- allowedFunctionsPerObject: 20
- allowedFunctionsPerEnum: 11
- ignoreDeprecated: true
- ignorePrivate: true
- ignoreInternal: true
- ignoreOverridden: false
- ignoreAnnotatedFunctions: []
+ excludes: *testFolders
coroutines:
- active: true
- CoroutineLaunchedInTestWithoutRunTest:
- active: false
- GlobalCoroutineUsage:
- active: true
InjectDispatcher:
- active: false
- dispatcherNames:
- - 'IO'
- - 'Default'
- - 'Unconfined'
- RedundantSuspendModifier:
- active: true
- SleepInsteadOfDelay:
- active: true
- SuspendFunInFinallySection:
- active: false
- SuspendFunSwallowedCancellation:
- active: false
- SuspendFunWithCoroutineScopeReceiver:
- active: false
- aliases: ['SuspendFunctionOnCoroutineScope']
- SuspendFunWithFlowReturnType:
- active: true
+ excludes: *testFolders
empty-blocks:
- active: true
- EmptyCatchBlock:
- active: true
- allowedExceptionNameRegex: '_|(ignore|expected).*'
- EmptyClassBlock:
- active: true
- EmptyDefaultConstructor:
- active: true
- EmptyDoWhileBlock:
- active: true
- EmptyElseBlock:
- active: true
- EmptyFinallyBlock:
- active: true
- EmptyForBlock:
- active: true
EmptyFunctionBlock:
- active: true
- ignoreOverridden: false
- EmptyIfBlock:
- active: true
- EmptyInitBlock:
- active: true
- EmptyKotlinFile:
- active: true
- EmptySecondaryConstructor:
- active: true
- EmptyTryBlock:
- active: true
- EmptyWhenBlock:
- active: true
- EmptyWhileBlock:
- active: true
+ excludes: *testFolders
-exceptions:
- active: true
- ErrorUsageWithThrowable:
- active: false
- ExceptionRaisedInUnexpectedLocation:
- active: true
- methodNames:
- - 'equals'
- - 'finalize'
- - 'hashCode'
- - 'toString'
- InstanceOfCheckForException:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- NotImplementedDeclaration:
- active: true
- ObjectExtendsThrowable:
- active: true
- PrintStackTrace:
- active: true
- RethrowCaughtException:
- active: true
- ReturnFromFinally:
- active: true
- ignoreLabeled: false
- SwallowedException:
- active: true
- ignoredExceptionTypes:
- - 'InterruptedException'
- - 'MalformedURLException'
- - 'NumberFormatException'
- - 'ParseException'
- allowedExceptionNameRegex: '_|(ignore|expected).*'
- ThrowingExceptionFromFinally:
- active: true
- ThrowingExceptionInMain:
- active: false
- ThrowingExceptionsWithoutMessageOrCause:
- active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- exceptions:
- - 'ArrayIndexOutOfBoundsException'
- - 'Exception'
- - 'IllegalArgumentException'
- - 'IllegalMonitorStateException'
- - 'IllegalStateException'
- - 'IndexOutOfBoundsException'
- - 'NullPointerException'
- - 'RuntimeException'
- - 'Throwable'
- ThrowingNewInstanceOfSameException:
- active: true
- TooGenericExceptionCaught:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- exceptionNames:
- - 'ArrayIndexOutOfBoundsException'
- - 'Error'
- - 'Exception'
- - 'IllegalMonitorStateException'
- - 'IndexOutOfBoundsException'
- - 'NullPointerException'
- - 'RuntimeException'
- - 'Throwable'
- allowedExceptionNameRegex: '_|(ignore|expected).*'
- TooGenericExceptionThrown:
- active: false
- exceptionNames:
- - 'Error'
- - 'Exception'
- - 'RuntimeException'
- - 'Throwable'
-
-naming:
- active: true
- BooleanPropertyNaming:
- active: false
- allowedPattern: '^(is|has|are)'
- ClassNaming:
- active: true
- aliases: ['ClassName']
- classPattern: '[A-Z][a-zA-Z0-9]*'
- ConstructorParameterNaming:
- active: true
- parameterPattern: '[a-z][A-Za-z0-9]*'
- privateParameterPattern: '[a-z][A-Za-z0-9]*'
- excludeClassPattern: '$^'
- EnumNaming:
- active: true
- aliases: ['EnumEntryName']
- enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
- ForbiddenClassName:
- active: false
- forbiddenName: []
- FunctionNameMaxLength:
- active: false
- aliases: ['FunctionMaxNameLength']
- maximumFunctionNameLength: 30
- FunctionNameMinLength:
- active: false
- aliases: ['FunctionMinNameLength']
- minimumFunctionNameLength: 3
- FunctionNaming:
- active: true
- aliases: ['FunctionName']
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- functionPattern: '[a-z][a-zA-Z0-9]*'
- excludeClassPattern: '$^'
- FunctionParameterNaming:
- active: true
- parameterPattern: '[a-z][A-Za-z0-9]*'
- excludeClassPattern: '$^'
- InvalidPackageDeclaration:
- active: true
- aliases: ['PackageDirectoryMismatch']
- rootPackage: ''
- requireRootInDeclaration: false
- LambdaParameterNaming:
- active: false
- parameterPattern: '[a-z][A-Za-z0-9]*|_'
- MatchingDeclarationName:
- active: false
- mustBeFirst: true
- multiplatformTargets:
- - 'ios'
- - 'android'
- - 'js'
- - 'jvm'
- - 'native'
- - 'iosArm64'
- - 'iosX64'
- - 'macosX64'
- - 'mingwX64'
- - 'linuxX64'
- MemberNameEqualsClassName:
- active: true
- ignoreOverridden: true
- NoNameShadowing:
- active: true
- NonBooleanPropertyPrefixedWithIs:
- active: true
- ObjectPropertyNaming:
- active: true
- aliases: ['ObjectPropertyName']
- constantPattern: '[A-Za-z][_A-Za-z0-9]*'
- propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
- privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
- PackageNaming:
- active: true
- aliases: ['PackageName']
- packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
- TopLevelPropertyNaming:
- active: true
- constantPattern: '[A-Z][_A-Z0-9]*'
- propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
- privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
- VariableMaxLength:
- active: false
- maximumVariableNameLength: 64
- VariableMinLength:
- active: false
- minimumVariableNameLength: 1
- VariableNaming:
- active: false
- aliases: ['PropertyName']
- variablePattern: '[a-z][A-Za-z0-9]*'
- privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
- excludeClassPattern: '$^'
-
-performance:
- active: true
- ArrayPrimitive:
- active: true
- CouldBeSequence:
- active: false
- allowedOperations: 3
- ForEachOnRange:
- active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- SpreadOperator:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- UnnecessaryInitOnArray:
- active: false
- UnnecessaryPartOfBinaryExpression:
- active: true
- UnnecessaryTemporaryInstantiation:
- active: true
- UnnecessaryTypeCasting:
- active: true
-
-potential-bugs:
- active: true
- AvoidReferentialEquality:
- active: true
- forbiddenTypePatterns:
- - 'kotlin.String'
- CastNullableToNonNullableType:
- active: true
- ignorePlatformTypes: true
- CastToNullableType:
- active: false
- CharArrayToStringCall:
- active: false
- Deprecation:
- active: false
- aliases: ['DEPRECATION']
- DontDowncastCollectionTypes:
- active: false
- DoubleMutabilityForCollection:
- active: true
- aliases: ['DoubleMutability']
- mutableTypes:
- - 'kotlin.collections.MutableList'
- - 'kotlin.collections.MutableMap'
- - 'kotlin.collections.MutableSet'
- - 'java.util.ArrayList'
- - 'java.util.LinkedHashSet'
- - 'java.util.HashSet'
- - 'java.util.LinkedHashMap'
- - 'java.util.HashMap'
- ElseCaseInsteadOfExhaustiveWhen:
- active: false
- ignoredSubjectTypes: []
- EqualsAlwaysReturnsTrueOrFalse:
- active: true
- EqualsWithHashCodeExist:
- active: true
- ExitOutsideMain:
- active: false
- ExplicitGarbageCollectionCall:
- active: true
- HasPlatformType:
- active: true
- IgnoredReturnValue:
- active: true
- restrictToConfig: true
- returnValueAnnotations:
- - 'CheckResult'
- - '*.CheckResult'
- - 'CheckReturnValue'
- - '*.CheckReturnValue'
- ignoreReturnValueAnnotations:
- - 'CanIgnoreReturnValue'
- - '*.CanIgnoreReturnValue'
- returnValueTypes:
- - 'kotlin.Function*'
- - 'kotlin.sequences.Sequence'
- - 'kotlinx.coroutines.flow.*Flow'
- - 'java.util.stream.*Stream'
- ignoreFunctionCall: []
- ImplicitDefaultLocale:
- active: true
- ImplicitUnitReturnType:
- active: false
- ignoreAnnotated: ['Test']
- allowExplicitReturnType: true
- InvalidRange:
- active: true
- IteratorHasNextCallsNextMethod:
- active: true
- IteratorNotThrowingNoSuchElementException:
- active: true
- LateinitUsage:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- ignoreOnClassesPattern: ''
- MapGetWithNotNullAssertionOperator:
- active: true
- MissingPackageDeclaration:
- active: true
- excludes: ['**/*.kts']
- MissingSuperCall:
- active: false
- mustInvokeSuperAnnotations:
- - 'androidx.annotation.CallSuper'
- - 'javax.annotation.OverridingMethodsMustInvokeSuper'
- MissingUseCall:
- active: false
- ignoreClass:
- - 'java.io.ByteArrayInputStream'
- - 'java.io.ByteArrayOutputStream'
- NullCheckOnMutableProperty:
- active: false
- NullableToStringCall:
- active: false
- PropertyUsedBeforeDeclaration:
- active: false
- UnconditionalJumpStatementInLoop:
- active: false
- UnnamedParameterUse:
- active: false
- allowAdjacentDifferentTypeParams: true
- allowSingleParamUse: true
- ignoreArgumentsMatchingNames: true
- ignoreFunctionCall: []
- UnnecessaryNotNullCheck:
- active: false
- UnnecessaryNotNullOperator:
- active: true
- UnnecessarySafeCall:
- active: true
- UnreachableCatchBlock:
- active: true
- UnreachableCode:
- active: true
- UnsafeCallOnNullableType:
- active: true
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**']
- UnsafeCast:
- active: true
- aliases: ['UNCHECKED_CAST']
- UnusedUnaryOperator:
- active: true
- UselessPostfixExpression:
- active: true
- WrongEqualsTypeParameter:
- active: true
-
-style:
- active: true
- AbstractClassCanBeConcreteClass:
- active: true
- AbstractClassCanBeInterface:
- active: true
- AlsoCouldBeApply:
- active: false
- BracesOnIfStatements:
- active: false
- singleLine: 'never'
- multiLine: 'always'
- BracesOnWhenStatements:
- active: false
- singleLine: 'necessary'
- multiLine: 'consistent'
- CanBeNonNullable:
- active: false
- CascadingCallWrapping:
- active: false
- includeElvis: true
- ClassOrdering:
- active: false
- CollapsibleIfStatements:
- active: false
- DataClassContainsFunctions:
- active: false
- conversionFunctionPrefix:
- - 'to'
- allowOperators: false
- DataClassShouldBeImmutable:
- active: false
- DestructuringDeclarationWithTooManyEntries:
- active: true
- maxDestructuringEntries: 3
- DoubleNegativeExpression:
- active: false
- DoubleNegativeLambda:
- active: false
- negativeFunctions:
- - reason: 'Use `takeIf` instead.'
- value: 'takeUnless'
- - reason: 'Use `all` instead.'
- value: 'none'
- negativeFunctionNameParts:
- - 'not'
- - 'non'
- EqualsNullCall:
- active: true
- EqualsOnSignatureLine:
- active: false
- ExplicitCollectionElementAccessMethod:
- active: false
- ExplicitItLambdaMultipleParameters:
- active: true
- ExplicitItLambdaParameter:
- active: false
- ExpressionBodySyntax:
- active: false
- includeLineWrapping: false
- ForbiddenAnnotation:
- active: true
- annotations:
- - reason: 'it is a java annotation. Use `Suppress` instead.'
- value: 'java.lang.SuppressWarnings'
- - reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.'
- value: 'java.lang.Deprecated'
- - reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.'
- value: 'java.lang.annotation.Documented'
- - reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.'
- value: 'java.lang.annotation.Target'
- - reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.'
- value: 'java.lang.annotation.Retention'
- - reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.'
- value: 'java.lang.annotation.Repeatable'
- ForbiddenComment:
- active: true
- comments:
- - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.'
- value: 'FIXME:'
- - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.'
- value: 'STOPSHIP:'
- - reason: 'Forbidden TODO todo marker in comment, please do the changes.'
- value: 'TODO:'
- allowedPatterns: ''
- ForbiddenImport:
- active: false
- forbiddenImports: []
- allowedImports: []
- ForbiddenMethodCall:
- active: true
- methods:
- - reason: 'print does not allow you to configure the output stream. Use a logger instead.'
- value: 'kotlin.io.print'
- - reason: 'println does not allow you to configure the output stream. Use a logger instead.'
- value: 'kotlin.io.println'
- - reason: 'using `BigDecimal(Double)` can result in unexpected floating point precision behavior. Use `BigDecimal.valueOf(Double)` or `String.toBigDecimalOrNull()` instead.'
- value: 'java.math.BigDecimal.(kotlin.Double)'
- - reason: 'using `BigDecimal(String)` can result in a `NumberFormatException`. Use `String.toBigDecimalOrNull()`'
- value: 'java.math.BigDecimal.(kotlin.String)'
- - reason: 'It is marked as obsolete. Use `kotlin.time.measureTime` instead.'
- value: 'kotlin.system.measureTimeMillis'
- ForbiddenNamedParam:
- active: false
- methods: []
- ForbiddenOptIn:
- active: false
- markerClasses: []
- ForbiddenSuppress:
- active: false
- rules: []
- ForbiddenVoid:
- active: true
- ignoreOverridden: false
- ignoreUsageInGenerics: false
- FunctionOnlyReturningConstant:
- active: true
- ignoreOverridableFunction: true
- ignoreActualFunction: true
- excludedFunctions: []
- LoopWithTooManyJumpStatements:
- active: true
- maxJumpCount: 1
- MagicNumber:
- active: false
- excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts']
- ignoreNumbers:
- - '-1'
- - '0'
- - '1'
- - '2'
- ignoreHashCodeFunction: true
- ignorePropertyDeclaration: true
- ignoreLocalVariableDeclaration: true
- ignoreConstantDeclaration: true
- ignoreCompanionObjectPropertyDeclaration: true
- ignoreAnnotation: false
- ignoreNamedArgument: true
- ignoreEnums: false
- ignoreRanges: false
- ignoreExtensionFunctions: true
- MandatoryBracesLoops:
- active: false
- MaxChainedCallsOnSameLine:
- active: false
- maxChainedCalls: 5
- MaxLineLength:
- active: false
- maxLineLength: 120
- excludePackageStatements: true
- excludeImportStatements: true
- excludeCommentStatements: false
- excludeRawStrings: true
- MayBeConstant:
- active: true
- ModifierOrder:
- active: true
- MultilineLambdaItParameter:
- active: false
- MultilineRawStringIndentation:
- active: false
- indentSize: 4
- trimmingMethods:
- - 'trimIndent'
- - 'trimMargin'
- NestedClassesVisibility:
- active: true
- NewLineAtEndOfFile:
- active: true
- NoTabs:
- active: false
- NullableBooleanCheck:
- active: false
- ObjectLiteralToLambda:
- active: true
- OptionalAbstractKeyword:
- active: true
- OptionalUnit:
- active: false
- ProtectedMemberInFinalClass:
- active: true
- RangeUntilInsteadOfRangeTo:
- active: true
- RedundantConstructorKeyword:
- active: false
- RedundantExplicitType:
- active: false
- RedundantHigherOrderMapUsage:
- active: true
- RedundantVisibilityModifier:
- active: false
- ReturnCount:
- active: false
- max: 2
- excludedFunctions:
- - 'equals'
- excludeLabeled: false
- excludeReturnFromLambda: true
- excludeGuardClauses: false
- SafeCast:
- active: true
- SerialVersionUIDInSerializableClass:
- active: true
- SpacingAfterPackageAndImports:
- active: false
- StringShouldBeRawString:
- active: false
- maxEscapedCharacterCount: 2
- ignoredCharacters: []
- ThrowsCount:
- active: false
- max: 2
- excludeGuardClauses: false
- TrailingWhitespace:
- active: false
- TrimMultilineRawString:
- active: false
- trimmingMethods:
- - 'trimIndent'
- - 'trimMargin'
- UnderscoresInNumericLiterals:
- active: false
- acceptableLength: 4
- allowNonStandardGrouping: false
- UnnecessaryAny:
- active: false
- UnnecessaryApply:
- active: true
- UnnecessaryBackticks:
- active: false
- UnnecessaryBracesAroundTrailingLambda:
- active: false
- UnnecessaryFilter:
- active: true
- UnnecessaryFullyQualifiedName:
- active: true
- UnnecessaryInheritance:
- active: true
- UnnecessaryInnerClass:
- active: false
- UnnecessaryLet:
- active: false
- UnnecessaryParentheses:
- active: false
- allowForUnclearPrecedence: false
- UnnecessaryReversed:
- active: false
- UnusedImport:
- active: false
- additionalOperatorSet: []
- UnusedParameter:
- active: true
- aliases: ['UNUSED_PARAMETER', 'unused']
- allowedNames: 'ignored|expected'
- UnusedPrivateClass:
- active: true
- aliases: ['unused']
- UnusedPrivateFunction:
- active: true
- aliases: ['unused']
- allowedNames: ''
- UnusedPrivateProperty:
- active: true
- aliases: ['unused']
- allowedNames: 'ignored|expected|serialVersionUID'
- UnusedVariable:
- active: true
- aliases: ['UNUSED_VARIABLE', 'unused']
- allowedNames: 'ignored|_'
- UseAnyOrNoneInsteadOfFind:
- active: true
- UseArrayLiteralsInAnnotations:
- active: true
- UseCheckNotNull:
- active: true
- UseCheckOrError:
- active: true
- UseDataClass:
- active: false
- allowVars: false
- UseEmptyCounterpart:
- active: false
- UseIfEmptyOrIfBlank:
- active: false
- UseIfInsteadOfWhen:
- active: false
- ignoreWhenContainingVariableDeclaration: false
- UseIsNullOrEmpty:
- active: true
- UseLet:
- active: false
- UseOrEmpty:
- active: true
- UseRequire:
- active: true
- UseRequireNotNull:
- active: true
- UseSumOfInsteadOfFlatMapSize:
- active: false
- UselessCallOnNotNull:
- active: true
- UtilityClassWithPublicConstructor:
- active: true
- VarCouldBeVal:
- active: true
- aliases: ['CanBeVal']
- ignoreLateinitVar: false
- WildcardImport:
- active: false
- excludeImports:
- - 'java.util.*'
+comments:
+ UndocumentedPublicClass:
+ active: true
+ excludes: &testAndGeneratedFolders
+ - '**/test/**'
+ - '**/commonTest/**'
+ - '**/jvmTest/**'
+ - '**/jsTest/**'
+ - '**/iosTest/**'
+ - '**/generated-sources/**'
+ ignoreDefaultCompanionObject: true
+ UndocumentedPublicFunction:
+ active: true
+ excludes: *testAndGeneratedFolders
+ UndocumentedPublicProperty:
+ active: true
+ excludes: *testAndGeneratedFolders
+ searchProtectedProperty: false
+ ignoreEnumEntries: true
diff --git a/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceServer.kt b/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceServer.kt
index 8a18957..03d5ee3 100644
--- a/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceServer.kt
+++ b/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceServer.kt
@@ -12,6 +12,8 @@ fun main() {
val port = System.getenv("MCP_PORT")?.toIntOrNull() ?: 3001
embeddedServer(CIO, port = port) {
mcpStreamableHttp(
+ enableDnsRebindingProtection = true,
+ allowedHosts = listOf("localhost", "127.0.0.1", "localhost:$port", "127.0.0.1:$port"),
eventStore = InMemoryEventStore(),
) {
createConformanceServer()
diff --git a/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt b/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt
index c81c98c..54d5c8c 100644
--- a/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt
+++ b/conformance-test/src/main/kotlin/io/modelcontextprotocol/kotlin/sdk/conformance/ConformanceTools.kt
@@ -45,6 +45,7 @@ internal const val WAV_BASE64 = "UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABA
private val logger = KotlinLogging.logger {}
+@Suppress("LongMethod")
fun Server.registerConformanceTools() {
// 1. Simple text
addTool(
@@ -175,17 +176,11 @@ fun Server.registerConformanceTools() {
CreateMessageRequest(
CreateMessageRequestParams(
maxTokens = 10000,
- messages = listOf(SamplingMessage(Role.User, listOf(TextContent(prompt)))),
+ messages = listOf(SamplingMessage(Role.User, TextContent(prompt))),
),
),
)
- val sampledText = result.content.joinToString("\n") { content ->
- when (content) {
- is TextContent -> content.text
- else -> "Non-text sampling content: ${content::class.simpleName}"
- }
- }
- CallToolResult(listOf(TextContent(sampledText)))
+ CallToolResult(listOf(TextContent(result.content.toString())))
}
// 9. Elicitation
diff --git a/context7.json b/context7.json
index 5fde250..a28c0fc 100644
--- a/context7.json
+++ b/context7.json
@@ -12,6 +12,7 @@
"CONTRIBUTING.md",
"LICENSE",
"SECURITY.md",
+ "codecov.yml",
"gradlew",
"gradlew.bat",
".editorconfig",
diff --git a/gradle.properties b/gradle.properties
index a5655c3..09a658b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -8,4 +8,4 @@ kotlin.daemon.jvmargs=-Xmx4G
kotlin.native.ignoreDisabledTargets=true
group=io.modelcontextprotocol
-version=0.15.0
+version=0.11.0
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a8d034f..78f55fe 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,33 +1,34 @@
[versions]
# plugins version
-atomicfu = "0.33.0"
-binaryCompatibilityValidatorPlugin = "0.18.2"
-detekt = "2.0.0-alpha.6"
+atomicfu = "0.32.1"
+binaryCompatibilityValidatorPlugin = "0.18.1"
+detekt = "2.0.0-alpha.2"
dokka = "2.2.0"
-kotlin = "2.4.10"
-kover = "0.9.9"
+kotlin = "2.3.10"
+kover = "0.9.8"
ktlint = "14.2.0"
knit = "0.5.1"
-mavenPublish = "0.37.0"
-netty = "4.2.18.Final"
+mavenPublish = "0.36.0"
+netty = "4.2.12.Final"
+openapi-generator = "7.21.0"
# libraries version
awaitility = "4.3.0"
-collections-immutable = "0.5.2"
-coroutines = "1.11.0"
-kotest = "6.2.5"
-kotlinx-io = "0.9.1"
-ktor = "3.5.2"
-logging = "8.0.4"
-mockk = "1.14.11"
-serialization = "1.11.0"
-slf4j = "2.0.19"
-logback = "1.6.3"
-junit = "5.14.4"
+collections-immutable = "0.4.0"
+coroutines = "1.10.2"
+kotest = "6.1.9"
+kotlinx-io = "0.9.0"
+ktor = "3.3.3"
+logging = "8.0.01"
+mockk = "1.14.9"
+mokksy = "0.8.1"
+serialization = "1.10.0"
+slf4j = "2.0.17"
+logback = "1.5.32"
+junit = "5.14.3"
[libraries]
# Plugins
-detekt-gradle = { module = "dev.detekt:detekt-gradle-plugin", version.ref = "detekt" }
dokka-gradle = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
@@ -64,6 +65,7 @@ kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor
ktor-client-mock = { group = "io.ktor", name = "ktor-client-mock", version.ref = "ktor" }
ktor-server-test-host = { group = "io.ktor", name = "ktor-server-test-host", version.ref = "ktor" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
+mokksy = { group = "dev.mokksy", name = "mokksy", version.ref = "mokksy" }
netty-bom = { group = "io.netty", name = "netty-bom", version.ref = "netty" }
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
@@ -74,7 +76,9 @@ ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "
ktor-server-cio = { group = "io.ktor", name = "ktor-server-cio", version.ref = "ktor" }
[plugins]
+detekt = { id = "dev.detekt", version.ref = "detekt" }
kotlinx-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibilityValidatorPlugin" }
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
knit = { id = "org.jetbrains.kotlinx.knit", version.ref = "knit" }
+openapi-generator = { id = "org.openapi.generator", version.ref = "openapi-generator" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index eddabd2..d997cfc 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ad7845b..c61a118 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,9 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
-retries=0
-retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 249efbb..739907d 100755
--- a/gradlew
+++ b/gradlew
@@ -20,7 +20,7 @@
##############################################################################
#
-# gradlew start up script for POSIX generated by Gradle.
+# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
-# ksh gradlew
+# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
diff --git a/gradlew.bat b/gradlew.bat
index 8508ef6..c4bdd3a 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,82 +1,93 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem gradlew startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables, and ensure extensions are enabled
-setlocal EnableExtensions
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-"%COMSPEC%" /c exit 1
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-"%COMSPEC%" /c exit 1
-
-:execute
-@rem Setup the command line
-
-
-
-@rem Execute gradlew
-@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
-@rem which allows us to clear the local environment before executing the java command
-endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
-
-:exitWithErrorLevel
-@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
-"%COMSPEC%" /c exit %ERRORLEVEL%
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/integration-test/build.gradle.kts b/integration-test/build.gradle.kts
index 7a3a4a3..30afe9e 100644
--- a/integration-test/build.gradle.kts
+++ b/integration-test/build.gradle.kts
@@ -1,6 +1,5 @@
plugins {
id("mcp.multiplatform")
- id("mcp.detekt")
}
kotlin {
diff --git a/integration-test/detekt-baseline-test.xml b/integration-test/detekt-baseline-test.xml
new file mode 100644
index 0000000..2df611a
--- /dev/null
+++ b/integration-test/detekt-baseline-test.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ AbstractClassCanBeConcreteClass:AbstractPromptIntegrationTest.kt:AbstractPromptIntegrationTest$AbstractPromptIntegrationTest
+ AbstractClassCanBeConcreteClass:AbstractResourceIntegrationTest.kt:AbstractResourceIntegrationTest$AbstractResourceIntegrationTest
+ AbstractClassCanBeConcreteClass:AbstractToolIntegrationTest.kt:AbstractToolIntegrationTest$AbstractToolIntegrationTest
+ AbstractClassCanBeConcreteClass:BaseTransportTest.kt:BaseTransportTest$BaseTransportTest
+ CyclomaticComplexMethod:AbstractToolIntegrationTest.kt:AbstractToolIntegrationTest$private fun setupCalculatorTool
+ ForbiddenComment:StdioClientTransportTest.kt:StdioClientTransportTest$// TODO: fix running on windows
+ MatchingDeclarationName:PromptIntegrationTestSse.kt:SchemaPromptIntegrationTestSse : AbstractPromptIntegrationTest
+ ThrowsCount:AbstractPromptIntegrationTest.kt:AbstractPromptIntegrationTest$override fun configureServer
+ UseCheckOrError:ClientTest.kt:ClientTest.<no name provided>$throw IllegalStateException("Test error")
+ UseOrEmpty:AbstractToolIntegrationTest.kt:AbstractToolIntegrationTest$(request.params.arguments?.get("tags") as? JsonArray)?.mapNotNull { (it as? JsonPrimitive)?.content } ?: emptyList()
+ UseRequire:AbstractPromptIntegrationTest.kt:AbstractPromptIntegrationTest$throw IllegalArgumentException("Missing required argument: $argName")
+
+
diff --git a/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt b/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt
index 099329d..d0acda5 100644
--- a/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt
+++ b/integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt
@@ -14,17 +14,13 @@ import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageResult
import io.modelcontextprotocol.kotlin.sdk.types.DoubleSchema
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestFormParams
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestURLParams
import io.modelcontextprotocol.kotlin.sdk.types.ElicitResult
-import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotification
-import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotificationParams
import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequest
import io.modelcontextprotocol.kotlin.sdk.types.InitializeResult
import io.modelcontextprotocol.kotlin.sdk.types.IntegerSchema
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCNotification
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCResponse
import io.modelcontextprotocol.kotlin.sdk.types.LATEST_PROTOCOL_VERSION
@@ -50,10 +46,8 @@ import io.modelcontextprotocol.kotlin.sdk.types.Tool
import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
import io.modelcontextprotocol.kotlin.sdk.types.UntitledMultiSelectEnumSchema
import io.modelcontextprotocol.kotlin.sdk.types.UntitledSingleSelectEnumSchema
-import io.modelcontextprotocol.kotlin.sdk.types.UrlElicitationRequiredException
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.TimeoutCancellationException
-import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll
@@ -73,7 +67,6 @@ import kotlin.test.assertIs
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
-import kotlin.time.Duration.Companion.seconds
class ClientTest {
@Test
@@ -113,7 +106,7 @@ class ClientTest {
),
options = ClientOptions(
capabilities = ClientCapabilities(
- sampling = ClientCapabilities.sampling,
+ sampling = EmptyJsonObject,
),
),
)
@@ -159,7 +152,7 @@ class ClientTest {
),
options = ClientOptions(
capabilities = ClientCapabilities(
- sampling = ClientCapabilities.sampling,
+ sampling = EmptyJsonObject,
),
),
)
@@ -346,7 +339,7 @@ class ClientTest {
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(
- capabilities = ClientCapabilities(sampling = ClientCapabilities.sampling),
+ capabilities = ClientCapabilities(sampling = EmptyJsonObject),
),
)
@@ -546,15 +539,6 @@ class ClientTest {
val serverSession = serverSessionResult.await()
- // Register the tap after createSession so it chains onto the session's subscription instead
- // of pre-empting the handshake.
- val cancelledOnWire = CompletableDeferred()
- serverTransport.onMessage { message ->
- if (message is JSONRPCNotification && message.method == Method.Defined.NotificationsCancelled.value) {
- cancelledOnWire.complete(Unit)
- }
- }
-
serverSession.setRequestHandler(Method.Defined.ResourcesList) { _, _ ->
// Simulate delay
def.complete(Unit)
@@ -580,7 +564,6 @@ class ClientTest {
def.await()
runCatching { job.cancel("Cancelled by test") }
defCancel.await()
- cancelledOnWire.await()
defTimeOut.await()
}
@@ -619,9 +602,17 @@ class ClientTest {
val serverSession = serverSessionResult.await()
serverSession.setRequestHandler(Method.Defined.ResourcesList) { _, _ ->
- // Never respond: the handler runs on a real dispatcher while the 1 ms timeout runs on the
- // virtual test clock, so a real-time delay could race it. Parking keeps the test deterministic.
- awaitCancellation()
+ // Simulate a delayed response
+ // Wait ~100ms unless canceled
+ try {
+ withTimeout(100L) {
+ // Just delay here, if timeout is 0 on the client side, this won't return in time
+ delay(100)
+ }
+ } catch (_: Exception) {
+ // If aborted, just rethrow or return early
+ }
+ ListResourcesResult(resources = emptyList())
}
// Request with 1 msec timeout should fail immediately
@@ -642,16 +633,18 @@ class ClientTest {
),
options = ClientOptions(
capabilities = ClientCapabilities(
- sampling = ClientCapabilities.sampling,
+ sampling = EmptyJsonObject,
),
),
)
client.setRequestHandler(Method.Defined.SamplingCreateMessage) { _, _ ->
CreateMessageResult(
- role = Role.Assistant,
- content = TextContent(text = "Test response"),
model = "test-model",
+ role = Role.Assistant,
+ content = TextContent(
+ text = "Test response",
+ ),
)
}
@@ -677,7 +670,7 @@ class ClientTest {
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(
- capabilities = ClientCapabilities(sampling = ClientCapabilities.sampling),
+ capabilities = ClientCapabilities(sampling = EmptyJsonObject),
),
)
@@ -900,8 +893,8 @@ class ClientTest {
),
)
- // The handler runs on Dispatchers.Default after concurrent dispatch is enabled; await the deferred.
- val rootListChangedReceived = CompletableDeferred()
+ // Track notifications
+ var rootListChangedNotificationReceived = false
val serverSessionResult = CompletableDeferred()
@@ -920,13 +913,16 @@ class ClientTest {
serverSession.setNotificationHandler(
Method.Defined.NotificationsRootsListChanged,
) {
- rootListChangedReceived.complete(Unit)
+ rootListChangedNotificationReceived = true
CompletableDeferred(Unit)
}
client.sendRootsListChanged()
- withTimeout(5.seconds) { rootListChangedReceived.await() }
+ assertTrue(
+ rootListChangedNotificationReceived,
+ "Notification should be sent when sendRootsListChanged is called",
+ )
}
@Test
@@ -1064,7 +1060,7 @@ class ClientTest {
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities(
- elicitation = ClientCapabilities.Elicitation(),
+ elicitation = EmptyJsonObject,
),
),
)
@@ -1124,103 +1120,6 @@ class ClientTest {
assertEquals(elicitationResultContent, result.content)
}
- @Test
- fun `should reject accepted elicitation content that does not match requested schema`() = runTest {
- val requestedSchema = ElicitRequestParams.RequestedSchema(
- properties = mapOf("name" to StringSchema()),
- required = listOf("name"),
- )
- val (client, serverSession) = setupElicitationPair {
- ElicitResult(action = ElicitResult.Action.Accept, content = buildJsonObject { put("name", 42) })
- }
-
- val exception = assertFailsWith {
- serverSession.createElicitation(message = "mismatch", requestedSchema = requestedSchema)
- }
- assertTrue(
- exception.message.orEmpty()
- .startsWith("Elicitation response content does not match requested schema: "),
- "Unexpected message: ${exception.message}",
- )
- assertTrue("'name' must be string" in exception.message.orEmpty(), "Unexpected message: ${exception.message}")
-
- client.close()
- }
-
- @Test
- fun `should reject accepted elicitation without content when schema has required properties`() = runTest {
- val requestedSchema = ElicitRequestParams.RequestedSchema(
- properties = mapOf("name" to StringSchema()),
- required = listOf("name"),
- )
- val (client, serverSession) = setupElicitationPair {
- ElicitResult(action = ElicitResult.Action.Accept, content = null)
- }
-
- val exception = assertFailsWith {
- serverSession.createElicitation(message = "no content", requestedSchema = requestedSchema)
- }
- assertTrue(
- "must have required property 'name'" in exception.message.orEmpty(),
- "Unexpected message: ${exception.message}",
- )
-
- client.close()
- }
-
- @Test
- fun `should accept elicitation without content when schema has no required properties`() = runTest {
- val requestedSchema = ElicitRequestParams.RequestedSchema(
- properties = mapOf("name" to StringSchema()),
- )
- val (client, serverSession) = setupElicitationPair {
- ElicitResult(action = ElicitResult.Action.Accept, content = null)
- }
-
- val result = serverSession.createElicitation(message = "no required", requestedSchema = requestedSchema)
-
- assertEquals(ElicitResult.Action.Accept, result.action)
- assertNull(result.content)
-
- client.close()
- }
-
- @Test
- fun `should pass through declined elicitation without validation`() = runTest {
- val requestedSchema = ElicitRequestParams.RequestedSchema(
- properties = mapOf("name" to StringSchema()),
- required = listOf("name"),
- )
- val (client, serverSession) = setupElicitationPair {
- ElicitResult(action = ElicitResult.Action.Decline)
- }
-
- val result = serverSession.createElicitation(message = "decline", requestedSchema = requestedSchema)
-
- assertEquals(ElicitResult.Action.Decline, result.action)
- assertNull(result.content)
-
- client.close()
- }
-
- @Test
- fun `should pass through cancelled elicitation without validation`() = runTest {
- val requestedSchema = ElicitRequestParams.RequestedSchema(
- properties = mapOf("name" to StringSchema()),
- required = listOf("name"),
- )
- val (client, serverSession) = setupElicitationPair {
- ElicitResult(action = ElicitResult.Action.Cancel)
- }
-
- val result = serverSession.createElicitation(message = "cancel", requestedSchema = requestedSchema)
-
- assertEquals(ElicitResult.Action.Cancel, result.action)
- assertNull(result.content)
-
- client.close()
- }
-
@Test
fun `should apply elicitation defaults for missing fields in empty content`() = runTest {
val schema = defaultsTestSchema()
@@ -1354,189 +1253,6 @@ class ClientTest {
client.close()
}
- // ── URL-mode elicitation (SEP-1036) ─────────────────────────────────
-
- @Test
- fun `should handle URL mode elicitation end-to-end`() = runTest {
- val client = Client(
- Implementation(name = "test client", version = "1.0"),
- ClientOptions(
- capabilities = ClientCapabilities(
- elicitation = ClientCapabilities.Elicitation(url = EmptyJsonObject),
- ),
- ),
- )
-
- val elicitationId = "550e8400-e29b-41d4-a716-446655440000"
- val url = "https://oauth.example.com/authorize"
-
- client.setElicitationHandler { request ->
- val params = assertIs(request.params)
- assertEquals(elicitationId, params.elicitationId)
- assertEquals(url, params.url)
- ElicitResult(action = ElicitResult.Action.Accept)
- }
-
- val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
- val server = Server(
- serverInfo = Implementation(name = "test server", version = "1.0"),
- options = ServerOptions(capabilities = ServerCapabilities()),
- )
-
- val serverSessionResult = CompletableDeferred()
- listOf(
- launch { client.connect(clientTransport) },
- launch { serverSessionResult.complete(server.createSession(serverTransport)) },
- ).joinAll()
- val serverSession = serverSessionResult.await()
-
- val result = serverSession.createElicitation(
- message = "Authorize access to continue",
- elicitationId = elicitationId,
- url = url,
- )
-
- assertEquals(ElicitResult.Action.Accept, result.action)
- assertNull(result.content)
-
- client.close()
- }
-
- @Test
- fun `should reject URL mode elicitation when client supports only form mode`() = runTest {
- val (client, serverSession) = setupElicitationPair {
- ElicitResult(action = ElicitResult.Action.Accept)
- }
-
- val exception = assertFailsWith {
- serverSession.createElicitation(
- message = "Authorize",
- elicitationId = "id-1",
- url = "https://example.com/auth",
- )
- }
- assertTrue(exception.message!!.contains("elicitation.url"))
-
- client.close()
- }
-
- @Test
- fun `should deliver elicitation complete notification to client`() = runTest {
- val received = CompletableDeferred()
- val client = Client(
- Implementation(name = "test client", version = "1.0"),
- ClientOptions(
- capabilities = ClientCapabilities(
- elicitation = ClientCapabilities.Elicitation(url = EmptyJsonObject),
- ),
- ),
- )
- client.setElicitationCompleteHandler { received.complete(it) }
-
- val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
- val server = Server(
- serverInfo = Implementation(name = "test server", version = "1.0"),
- options = ServerOptions(capabilities = ServerCapabilities()),
- )
-
- val serverSessionResult = CompletableDeferred()
- listOf(
- launch { client.connect(clientTransport) },
- launch { serverSessionResult.complete(server.createSession(serverTransport)) },
- ).joinAll()
- val serverSession = serverSessionResult.await()
-
- val elicitationId = "complete-id-1"
- serverSession.sendElicitationComplete(
- ElicitationCompleteNotification(ElicitationCompleteNotificationParams(elicitationId = elicitationId)),
- )
-
- val notification = received.await()
- assertEquals(elicitationId, notification.params.elicitationId)
-
- client.close()
- }
-
- @Test
- fun `should reject elicitation complete when client supports only form mode`() = runTest {
- val (client, serverSession) = setupElicitationPair {
- ElicitResult(action = ElicitResult.Action.Accept)
- }
-
- val exception = assertFailsWith {
- serverSession.sendElicitationComplete(
- ElicitationCompleteNotification(ElicitationCompleteNotificationParams(elicitationId = "id-1")),
- )
- }
- assertTrue(exception.message!!.contains("elicitation.url"))
-
- client.close()
- }
-
- @Test
- fun `setElicitationCompleteHandler should require url capability`() = runTest {
- val client = Client(
- Implementation(name = "test client", version = "1.0"),
- ClientOptions(
- capabilities = ClientCapabilities(
- elicitation = ClientCapabilities.Elicitation(),
- ),
- ),
- )
-
- assertFailsWith {
- client.setElicitationCompleteHandler { }
- }
- }
-
- @Test
- fun `should surface URL elicitation required error to client as typed exception`() = runTest {
- val client = Client(
- Implementation(name = "test client", version = "1.0"),
- ClientOptions(
- capabilities = ClientCapabilities(
- elicitation = ClientCapabilities.Elicitation(url = EmptyJsonObject),
- ),
- ),
- )
-
- val elicitationId = "auth-required-1"
- val url = "https://oauth.example.com/authorize"
-
- val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
- val server = Server(
- serverInfo = Implementation(name = "test server", version = "1.0"),
- options = ServerOptions(capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(true))),
- )
- server.addTool("needs-auth", "Requires URL elicitation") {
- throw UrlElicitationRequiredException(
- listOf(
- ElicitRequestURLParams(
- message = "Authorize to continue",
- elicitationId = elicitationId,
- url = url,
- ),
- ),
- )
- }
-
- val serverSessionResult = CompletableDeferred()
- listOf(
- launch { client.connect(clientTransport) },
- launch { serverSessionResult.complete(server.createSession(serverTransport)) },
- ).joinAll()
- serverSessionResult.await()
-
- val exception = assertFailsWith {
- client.callTool(name = "needs-auth", arguments = emptyMap())
- }
- val elicitation = exception.elicitations.single()
- assertEquals(elicitationId, elicitation.elicitationId)
- assertEquals(url, elicitation.url)
-
- client.close()
- }
-
private fun defaultsTestSchema(): ElicitRequestParams.RequestedSchema = ElicitRequestParams.RequestedSchema(
properties = mapOf(
"name" to StringSchema(description = "User name", default = "John Doe"),
@@ -1556,7 +1272,7 @@ class ClientTest {
): Pair = kotlinx.coroutines.coroutineScope {
val client = Client(
Implementation(name = "test client", version = "1.0"),
- ClientOptions(capabilities = ClientCapabilities(elicitation = ClientCapabilities.Elicitation())),
+ ClientOptions(capabilities = ClientCapabilities(elicitation = EmptyJsonObject)),
)
client.setElicitationHandler(handler)
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransportTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransportTest.kt
index fc3c1d3..585af3f 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransportTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransportTest.kt
@@ -7,13 +7,9 @@ import io.modelcontextprotocol.kotlin.sdk.types.McpException
import io.modelcontextprotocol.kotlin.test.utils.createSleepyProcessBuilder
import io.modelcontextprotocol.kotlin.test.utils.createTeeProcessBuilder
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withTimeout
-import kotlinx.coroutines.withTimeoutOrNull
-import kotlinx.io.Buffer
-import kotlinx.io.RawSource
import kotlinx.io.asSink
import kotlinx.io.asSource
import kotlinx.io.buffered
@@ -22,12 +18,10 @@ import org.junit.jupiter.api.Timeout
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.OS
-import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.concurrent.atomics.AtomicBoolean
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.test.assertFalse
-import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.fail
import kotlin.time.Duration.Companion.seconds
@@ -115,43 +109,6 @@ class StdioClientTransportTest : BaseTransportTest() {
}
}
- @OptIn(ExperimentalAtomicApi::class)
- @Test
- fun `should close cleanly while stdin read is blocked`(): Unit = runBlocking(Dispatchers.IO) {
- val input = BlockingRawSource()
-
- val transport = StdioClientTransport(
- input = input.buffered(),
- output = Buffer(),
- )
-
- val didClose = AtomicBoolean(false)
- transport.onClose { didClose.store(true) }
- transport.onError { error ->
- fail("Unexpected error while closing transport: $error")
- }
-
- transport.start()
-
- eventually(2.seconds) {
- assertTrue(input.readStarted, "Transport should start reading stdin before close")
- }
-
- val closeJob = async {
- transport.close()
- }
-
- val closed = withTimeoutOrNull(1.seconds) {
- closeJob.await()
- }
-
- input.close()
- closeJob.await()
-
- assertNotNull(closed, "Transport.close() should not wait for stdin to produce data or EOF")
- assertTrue(didClose.load(), "Transport should be closed after close() call")
- }
-
@Test
fun `should read messages`() = runTest {
val processBuilder = createTeeProcessBuilder()
@@ -190,22 +147,4 @@ class StdioClientTransportTest : BaseTransportTest() {
process.waitFor()
process.destroyForcibly()
}
-
- private class BlockingRawSource : RawSource {
- private val closed = CountDownLatch(1)
-
- @Volatile
- var readStarted: Boolean = false
- private set
-
- override fun readAtMostTo(sink: Buffer, byteCount: Long): Long {
- readStarted = true
- closed.await()
- return -1L
- }
-
- override fun close() {
- closed.countDown()
- }
- }
}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/ProtocolConcurrencyIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/ProtocolConcurrencyIntegrationTest.kt
deleted file mode 100644
index d8d64ec..0000000
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/ProtocolConcurrencyIntegrationTest.kt
+++ /dev/null
@@ -1,306 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.integration
-
-import io.kotest.matchers.shouldBe
-import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
-import io.modelcontextprotocol.kotlin.sdk.client.Client
-import io.modelcontextprotocol.kotlin.sdk.client.ClientOptions
-import io.modelcontextprotocol.kotlin.sdk.server.Server
-import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
-import io.modelcontextprotocol.kotlin.sdk.server.ServerSession
-import io.modelcontextprotocol.kotlin.sdk.shared.InMemoryTransport
-import io.modelcontextprotocol.kotlin.sdk.shared.currentRequestHandlerExtra
-import io.modelcontextprotocol.kotlin.sdk.testing.ChannelTransport
-import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageResult
-import io.modelcontextprotocol.kotlin.sdk.types.Implementation
-import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequest
-import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.InitializedNotification
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCError
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCResponse
-import io.modelcontextprotocol.kotlin.sdk.types.LATEST_PROTOCOL_VERSION
-import io.modelcontextprotocol.kotlin.sdk.types.ListRootsRequest
-import io.modelcontextprotocol.kotlin.sdk.types.ListRootsResult
-import io.modelcontextprotocol.kotlin.sdk.types.ListToolsRequest
-import io.modelcontextprotocol.kotlin.sdk.types.ListToolsResult
-import io.modelcontextprotocol.kotlin.sdk.types.Method
-import io.modelcontextprotocol.kotlin.sdk.types.RequestId
-import io.modelcontextprotocol.kotlin.sdk.types.Role
-import io.modelcontextprotocol.kotlin.sdk.types.SamplingMessage
-import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.TextContent
-import io.modelcontextprotocol.kotlin.sdk.types.toJSON
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withTimeout
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.CopyOnWriteArrayList
-import java.util.concurrent.atomic.AtomicReference
-import kotlin.coroutines.cancellation.CancellationException
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.time.Duration.Companion.seconds
-
-/**
- * End-to-end tests for concurrent inbound dispatch enabled after the MCP handshake.
- *
- * Handlers run on a real [kotlinx.coroutines.Dispatchers.Default], so these use [runBlocking] with
- * explicit [withTimeout] guards. A virtual test clock would false-fire timeouts against cross-thread
- * real dispatchers.
- */
-@OptIn(ExperimentalMcpApi::class)
-class ProtocolConcurrencyIntegrationTest {
-
- private fun rootsClient(): Client = Client(
- clientInfo = Implementation(name = "test client", version = "1.0"),
- options = ClientOptions(
- capabilities = ClientCapabilities(roots = ClientCapabilities.Roots(listChanged = null)),
- ),
- )
-
- private fun toolsServer(): Server = Server(
- serverInfo = Implementation(name = "test server", version = "1.0"),
- options = ServerOptions(
- capabilities = ServerCapabilities(tools = ServerCapabilities.Tools(listChanged = null)),
- ),
- )
-
- // ChannelTransport delivers inbound messages as soon as its read loop starts, which is before
- // start() marks it operational. Creating the session first keeps the client's initialize from
- // landing on a transport that would reject the session's response.
- private suspend fun connect(
- client: Client,
- clientTransport: ChannelTransport,
- server: Server,
- serverTransport: ChannelTransport,
- ): ServerSession {
- val serverSession = server.createSession(serverTransport)
- client.connect(clientTransport)
- return serverSession
- }
-
- // The motivating deadlock over real serial read loops: a server tool handler makes a
- // server-to-client request (roots/list) while still handling the client's tools/call.
- @Test
- fun `server tool handler can call roots list mid-handling without deadlock`() = runBlocking {
- val client = rootsClient()
- val server = toolsServer()
- val (clientTransport, serverTransport) = ChannelTransport.createLinkedPair()
-
- val serverSession = connect(client, clientTransport, server, serverTransport)
-
- // Register the tools/call handler directly on the session so it can call back to the client
- // mid-handling. Before concurrent dispatch this would deadlock: the server read loop is
- // blocked by the tool handler and can never deliver the roots/list response.
- serverSession.setRequestHandler(Method.Defined.ToolsCall) { _, _ ->
- val roots = serverSession.request(ListRootsRequest())
- CallToolResult(content = listOf(TextContent(text = "roots=${roots.roots.size}")))
- }
-
- val result = withTimeout(10.seconds) {
- client.callTool(CallToolRequest(CallToolRequestParams("needs-roots")))
- }
- val text = (result as CallToolResult).content.filterIsInstance().single().text
- text shouldBe "roots=0"
-
- client.close()
- server.close()
- }
-
- // Client-side mirror: a client sampling handler makes a client-to-server request (tools/list)
- // while still handling the server's sampling/createMessage.
- @Test
- fun `client sampling handler can call the server mid-handling without deadlock`() = runBlocking {
- val client = Client(
- clientInfo = Implementation(name = "test client", version = "1.0"),
- options = ClientOptions(
- capabilities = ClientCapabilities(sampling = ClientCapabilities.Sampling()),
- ),
- )
- val server = toolsServer()
- val (clientTransport, serverTransport) = ChannelTransport.createLinkedPair()
-
- client.setRequestHandler(Method.Defined.SamplingCreateMessage) { _, _ ->
- val tools = client.request(ListToolsRequest())
- CreateMessageResult(
- role = Role.Assistant,
- content = TextContent(text = "tools=${tools.tools.size}"),
- model = "test-model",
- )
- }
-
- val serverSession = connect(client, clientTransport, server, serverTransport)
-
- val result = withTimeout(10.seconds) {
- serverSession.request(
- CreateMessageRequest(
- CreateMessageRequestParams(
- maxTokens = 100,
- messages = listOf(SamplingMessage(role = Role.User, content = TextContent(text = "hi"))),
- ),
- ),
- )
- }
- val text = result.content.filterIsInstance().single().text
- text shouldBe "tools=0"
-
- client.close()
- server.close()
- }
-
- // Nested-cancel cascade: cancelling the client's tools/call must reach the server tool handler
- // over the wire and cascade into its nested roots/list request, cancelling the parked
- // client-side roots handler. No tools/call response is ever sent.
- @Test
- fun `cancelling a tool call cascades into its nested roots list request`() = runBlocking {
- val client = rootsClient()
- val server = toolsServer()
- val (clientTransport, serverTransport) = ChannelTransport.createLinkedPair()
-
- val capturedToolCallId = AtomicReference(null)
- val toolHandlerCancelled = CompletableDeferred()
- val rootsHandlerEntered = CompletableDeferred()
- val rootsHandlerCancelled = CompletableDeferred()
- val parkForever = CompletableDeferred()
- val unexpectedToolCallResponse = CompletableDeferred()
-
- // Tap the server->client direction (client transport inbound) to prove no tools/call
- // response is ever delivered once the call is cancelled.
- clientTransport.onMessage { message ->
- if (message is JSONRPCResponse && message.id == capturedToolCallId.get()) {
- unexpectedToolCallResponse.complete(Unit)
- }
- }
-
- val serverSession = connect(client, clientTransport, server, serverTransport)
-
- // Client roots handler parks until cancelled and records the cancellation.
- client.setRequestHandler(Method.Defined.RootsList) { _, _ ->
- rootsHandlerEntered.complete(Unit)
- try {
- parkForever.await()
- } catch (e: CancellationException) {
- rootsHandlerCancelled.complete(e)
- throw e
- }
- }
-
- // Tool handler makes a nested server->client request and records its own cancellation.
- serverSession.setRequestHandler(Method.Defined.ToolsCall) { _, _ ->
- val extra = currentRequestHandlerExtra()!!
- capturedToolCallId.set(extra.requestId)
- try {
- extra.sendRequest(ListRootsRequest())
- } catch (e: CancellationException) {
- toolHandlerCancelled.complete(e)
- throw e
- }
- CallToolResult(content = listOf(TextContent(text = "unreachable")))
- }
-
- val call = launch { client.callTool(CallToolRequest(CallToolRequestParams("nested"))) }
-
- withTimeout(10.seconds) { rootsHandlerEntered.await() }
- call.cancel(CancellationException("user aborted"))
-
- withTimeout(10.seconds) { toolHandlerCancelled.await() }
- withTimeout(10.seconds) { rootsHandlerCancelled.await() }
-
- // The server suppresses the response for a cancelled request, so none is ever sent.
- assertFalse(
- unexpectedToolCallResponse.isCompleted,
- "no tools/call response should be delivered after the call was cancelled",
- )
-
- client.close()
- server.close()
- }
-
- // Init gate plus context element in a high-level handler: initialize and tools/list are
- // pipelined before notifications/initialized and answered serially in order. After the
- // handshake a high-level addTool handler can observe currentRequestHandlerExtra().
- @Test
- fun `pipelined initialize and tools list are processed serially then context element is available`(): Unit =
- runBlocking {
- val server = toolsServer()
- server.addTool(name = "ctx-tool", description = "returns its request id") {
- val extra = currentRequestHandlerExtra()
- checkNotNull(extra) { "currentRequestHandlerExtra() must be non-null inside a tool handler" }
- CallToolResult(content = listOf(TextContent(text = extra.requestId.asText())))
- }
-
- val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
-
- val arrivalOrder = CopyOnWriteArrayList()
- val pending = ConcurrentHashMap>()
- clientTransport.onMessage { message ->
- val id = when (message) {
- is JSONRPCResponse -> message.id
- is JSONRPCError -> message.id
- else -> null
- }
- if (id != null) {
- arrivalOrder.add(id)
- pending[id]?.complete(message)
- }
- }
-
- // Drive the client side manually over the raw transport.
- server.createSession(serverTransport)
-
- val initRequest = InitializeRequest(
- InitializeRequestParams(
- protocolVersion = LATEST_PROTOCOL_VERSION,
- capabilities = ClientCapabilities(),
- clientInfo = Implementation(name = "raw-client", version = "1.0"),
- ),
- ).toJSON()
- val toolsListRequest = ListToolsRequest().toJSON()
-
- val initAnswered = pending.expect(initRequest.id)
- val toolsListAnswered = pending.expect(toolsListRequest.id)
-
- // Pipelined before notifications/initialized: both handled in the serial dispatch phase.
- clientTransport.send(initRequest)
- clientTransport.send(toolsListRequest)
-
- withTimeout(10.seconds) { initAnswered.await() }
- withTimeout(10.seconds) { toolsListAnswered.await() }
-
- // Deterministic serial ordering: initialize answered before tools/list.
- assertEquals(
- listOf(initRequest.id, toolsListRequest.id),
- arrivalOrder.toList(),
- "pipelined requests must be answered in arrival order",
- )
-
- // Complete the handshake, then call the high-level tool.
- clientTransport.send(InitializedNotification().toJSON())
-
- val toolCallRequest = CallToolRequest(CallToolRequestParams("ctx-tool")).toJSON()
- val toolCallAnswered = pending.expect(toolCallRequest.id)
- clientTransport.send(toolCallRequest)
-
- val toolCallResponse = withTimeout(10.seconds) { toolCallAnswered.await() } as JSONRPCResponse
- val text = (toolCallResponse.result as CallToolResult).content.filterIsInstance().single().text
- // The handler echoed its request id, proving currentRequestHandlerExtra() was available.
- text shouldBe toolCallRequest.id.asText()
- }
-
- private fun ConcurrentHashMap>.expect(
- id: RequestId,
- ): CompletableDeferred = CompletableDeferred().also { put(id, it) }
-
- private fun RequestId.asText(): String = when (this) {
- is RequestId.StringId -> value
- is RequestId.NumberId -> value.toString()
- }
-}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt
index fb97782..d14ae88 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt
@@ -6,15 +6,9 @@ import io.kotest.matchers.string.shouldContain
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptResult
-import io.modelcontextprotocol.kotlin.sdk.types.ListPromptsRequest
-import io.modelcontextprotocol.kotlin.sdk.types.ListPromptsResult
import io.modelcontextprotocol.kotlin.sdk.types.McpException
-import io.modelcontextprotocol.kotlin.sdk.types.Method
-import io.modelcontextprotocol.kotlin.sdk.types.PaginatedRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.Prompt
import io.modelcontextprotocol.kotlin.sdk.types.PromptArgument
import io.modelcontextprotocol.kotlin.sdk.types.PromptMessage
-import io.modelcontextprotocol.kotlin.sdk.types.RPCError
import io.modelcontextprotocol.kotlin.sdk.types.Role
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
@@ -24,9 +18,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
-import java.util.concurrent.CopyOnWriteArrayList
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -698,76 +690,11 @@ abstract class AbstractPromptIntegrationTest : KotlinTestBase() {
val expectedMessage = "Prompt not found: non-existent-prompt"
- withClue("Exception code should be INVALID_PARAMS: ${RPCError.ErrorCode.INVALID_PARAMS}") {
- exception.code shouldBe RPCError.ErrorCode.INVALID_PARAMS
+ withClue("Exception code should be INTERNAL_ERROR: -32603") {
+ exception.code shouldBe -32603
}
withClue("Unexpected error message for non-existent prompt") {
exception.message shouldBe expectedMessage
}
}
-
- @Test
- fun testListPromptsPagination() = runBlocking(Dispatchers.IO) {
- val receivedCursors = CopyOnWriteArrayList()
- val page1 = listOf(Prompt(name = "p-1", description = "desc"), Prompt(name = "p-2", description = "desc"))
- val page2 = listOf(Prompt(name = "p-3", description = "desc"), Prompt(name = "p-4", description = "desc"))
- val page3 = listOf(Prompt(name = "p-5", description = "desc"))
-
- server.sessions.forEach { (_, session) ->
- session.setRequestHandler(Method.Defined.PromptsList) { request, _ ->
- receivedCursors += request.cursor
- when (request.cursor) {
- null -> ListPromptsResult(prompts = page1, nextCursor = "cursor-2")
- "cursor-2" -> ListPromptsResult(prompts = page2, nextCursor = "cursor-3")
- "cursor-3" -> ListPromptsResult(prompts = page3, nextCursor = null)
- else -> error("Unexpected cursor: ${request.cursor}")
- }
- }
- }
-
- val collected = mutableListOf()
- var cursor: String? = null
- do {
- val request = if (cursor == null) {
- ListPromptsRequest()
- } else {
- ListPromptsRequest(PaginatedRequestParams(cursor = cursor))
- }
- val response = client.listPrompts(request)
- collected += response.prompts
- cursor = response.nextCursor
- } while (cursor != null)
-
- assertEquals(
- listOf(null, "cursor-2", "cursor-3"),
- receivedCursors.toList(),
- "Client must forward each nextCursor into the next request",
- )
- assertEquals(
- listOf("p-1", "p-2", "p-3", "p-4", "p-5"),
- collected.map { it.name },
- "Client must accumulate pages in order without duplicates or reordering",
- )
- }
-
- @Test
- fun testListPromptsInvalidCursor() = runBlocking(Dispatchers.IO) {
- val invalidCursor = "not-a-valid-cursor"
-
- server.sessions.forEach { (_, session) ->
- session.setRequestHandler(Method.Defined.PromptsList) { _, _ ->
- throw McpException(
- code = RPCError.ErrorCode.INVALID_PARAMS,
- message = "Invalid cursor: $invalidCursor",
- )
- }
- }
-
- val exception = assertFailsWith {
- client.listPrompts(ListPromptsRequest(PaginatedRequestParams(cursor = invalidCursor)))
- }
-
- assertEquals(RPCError.ErrorCode.INVALID_PARAMS, exception.code)
- assertEquals("Invalid cursor: $invalidCursor", exception.message)
- }
}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractResourceIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractResourceIntegrationTest.kt
index ab2a854..4fc53a5 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractResourceIntegrationTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractResourceIntegrationTest.kt
@@ -1,16 +1,11 @@
package io.modelcontextprotocol.kotlin.sdk.integration.kotlin
import io.modelcontextprotocol.kotlin.sdk.types.BlobResourceContents
-import io.modelcontextprotocol.kotlin.sdk.types.ListResourcesRequest
-import io.modelcontextprotocol.kotlin.sdk.types.ListResourcesResult
import io.modelcontextprotocol.kotlin.sdk.types.McpException
-import io.modelcontextprotocol.kotlin.sdk.types.Method
-import io.modelcontextprotocol.kotlin.sdk.types.PaginatedRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.RPCError
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceRequest
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceResult
-import io.modelcontextprotocol.kotlin.sdk.types.Resource
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.SubscribeRequest
import io.modelcontextprotocol.kotlin.sdk.types.SubscribeRequestParams
@@ -23,10 +18,8 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
-import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -77,6 +70,23 @@ abstract class AbstractResourceIntegrationTest : KotlinTestBase() {
)
}
+ server.addResource(
+ uri = testResourceUri,
+ name = testResourceName,
+ description = testResourceDescription,
+ mimeType = "text/plain",
+ ) { request ->
+ ReadResourceResult(
+ contents = listOf(
+ TextResourceContents(
+ text = testResourceContent,
+ uri = request.params.uri,
+ mimeType = "text/plain",
+ ),
+ ),
+ )
+ }
+
server.addResource(
uri = binaryResourceUri,
name = binaryResourceName,
@@ -299,76 +309,4 @@ abstract class AbstractResourceIntegrationTest : KotlinTestBase() {
assertTrue(result.contents.isNotEmpty(), "Result contents should not be empty")
}
}
-
- @Test
- fun testListResourcesPagination() = runBlocking(Dispatchers.IO) {
- val receivedCursors = CopyOnWriteArrayList()
- val page1 = listOf(
- Resource(uri = "test://r-1", name = "r-1"),
- Resource(uri = "test://r-2", name = "r-2"),
- Resource(uri = "test://r-3", name = "r-3"),
- )
- val page2 = listOf(
- Resource(uri = "test://r-4", name = "r-4"),
- Resource(uri = "test://r-5", name = "r-5"),
- )
- val page3 = listOf(Resource(uri = "test://r-6", name = "r-6"))
-
- server.sessions.forEach { (_, session) ->
- session.setRequestHandler(Method.Defined.ResourcesList) { request, _ ->
- receivedCursors += request.cursor
- when (request.cursor) {
- null -> ListResourcesResult(resources = page1, nextCursor = "cursor-2")
- "cursor-2" -> ListResourcesResult(resources = page2, nextCursor = "cursor-3")
- "cursor-3" -> ListResourcesResult(resources = page3, nextCursor = null)
- else -> error("Unexpected cursor: ${request.cursor}")
- }
- }
- }
-
- val collected = mutableListOf()
- var cursor: String? = null
- do {
- val request = if (cursor == null) {
- ListResourcesRequest()
- } else {
- ListResourcesRequest(PaginatedRequestParams(cursor = cursor))
- }
- val response = client.listResources(request)
- collected += response.resources
- cursor = response.nextCursor
- } while (cursor != null)
-
- assertEquals(
- listOf(null, "cursor-2", "cursor-3"),
- receivedCursors.toList(),
- "Client must forward each nextCursor into the next request",
- )
- assertEquals(
- listOf("test://r-1", "test://r-2", "test://r-3", "test://r-4", "test://r-5", "test://r-6"),
- collected.map { it.uri },
- "Client must accumulate pages in order without duplicates or reordering",
- )
- }
-
- @Test
- fun testListResourcesInvalidCursor() = runBlocking(Dispatchers.IO) {
- val invalidCursor = "not-a-valid-cursor"
-
- server.sessions.forEach { (_, session) ->
- session.setRequestHandler(Method.Defined.ResourcesList) { _, _ ->
- throw McpException(
- code = RPCError.ErrorCode.INVALID_PARAMS,
- message = "Invalid cursor: $invalidCursor",
- )
- }
- }
-
- val exception = assertFailsWith {
- client.listResources(ListResourcesRequest(PaginatedRequestParams(cursor = invalidCursor)))
- }
-
- assertEquals(RPCError.ErrorCode.INVALID_PARAMS, exception.code)
- assertEquals("Invalid cursor: $invalidCursor", exception.message)
- }
}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt
index b802122..7da82cc 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractToolIntegrationTest.kt
@@ -6,15 +6,8 @@ import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
import io.modelcontextprotocol.kotlin.sdk.types.ContentBlock
import io.modelcontextprotocol.kotlin.sdk.types.ImageContent
-import io.modelcontextprotocol.kotlin.sdk.types.ListToolsRequest
-import io.modelcontextprotocol.kotlin.sdk.types.ListToolsResult
-import io.modelcontextprotocol.kotlin.sdk.types.McpException
-import io.modelcontextprotocol.kotlin.sdk.types.Method
-import io.modelcontextprotocol.kotlin.sdk.types.PaginatedRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.RPCError
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.TextContent
-import io.modelcontextprotocol.kotlin.sdk.types.Tool
import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -31,9 +24,7 @@ import org.junit.jupiter.api.Test
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.Locale
-import java.util.concurrent.CopyOnWriteArrayList
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -800,75 +791,4 @@ abstract class AbstractToolIntegrationTest : KotlinTestBase() {
"Error message should indicate the tool was not found",
)
}
-
- @Test
- fun testListToolsPagination() = runBlocking(Dispatchers.IO) {
- val receivedCursors = CopyOnWriteArrayList()
- val page1 = listOf(
- Tool(name = "t-1", inputSchema = ToolSchema()),
- Tool(name = "t-2", inputSchema = ToolSchema()),
- )
- val page2 = listOf(
- Tool(name = "t-3", inputSchema = ToolSchema()),
- Tool(name = "t-4", inputSchema = ToolSchema()),
- )
- val page3 = listOf(Tool(name = "t-5", inputSchema = ToolSchema()))
-
- server.sessions.forEach { (_, session) ->
- session.setRequestHandler(Method.Defined.ToolsList) { request, _ ->
- receivedCursors += request.cursor
- when (request.cursor) {
- null -> ListToolsResult(tools = page1, nextCursor = "cursor-2")
- "cursor-2" -> ListToolsResult(tools = page2, nextCursor = "cursor-3")
- "cursor-3" -> ListToolsResult(tools = page3, nextCursor = null)
- else -> error("Unexpected cursor: ${request.cursor}")
- }
- }
- }
-
- val collected = mutableListOf()
- var cursor: String? = null
- do {
- val request = if (cursor == null) {
- ListToolsRequest()
- } else {
- ListToolsRequest(PaginatedRequestParams(cursor = cursor))
- }
- val response = client.listTools(request)
- collected += response.tools
- cursor = response.nextCursor
- } while (cursor != null)
-
- assertEquals(
- listOf(null, "cursor-2", "cursor-3"),
- receivedCursors.toList(),
- "Client must forward each nextCursor into the next request",
- )
- assertEquals(
- listOf("t-1", "t-2", "t-3", "t-4", "t-5"),
- collected.map { it.name },
- "Client must accumulate pages in order without duplicates or reordering",
- )
- }
-
- @Test
- fun testListToolsInvalidCursor() = runBlocking(Dispatchers.IO) {
- val invalidCursor = "not-a-valid-cursor"
-
- server.sessions.forEach { (_, session) ->
- session.setRequestHandler(Method.Defined.ToolsList) { _, _ ->
- throw McpException(
- code = RPCError.ErrorCode.INVALID_PARAMS,
- message = "Invalid cursor: $invalidCursor",
- )
- }
- }
-
- val exception = assertFailsWith {
- client.listTools(ListToolsRequest(PaginatedRequestParams(cursor = invalidCursor)))
- }
-
- assertEquals(RPCError.ErrorCode.INVALID_PARAMS, exception.code)
- assertEquals("Invalid cursor: $invalidCursor", exception.message)
- }
}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/AbstractStreamableHttpIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/AbstractStreamableHttpIntegrationTest.kt
index 535393e..9bc0c17 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/AbstractStreamableHttpIntegrationTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/AbstractStreamableHttpIntegrationTest.kt
@@ -3,7 +3,6 @@ package io.modelcontextprotocol.kotlin.sdk.integration.streamablehttp
import io.ktor.server.cio.CIOApplicationEngine
import io.ktor.server.engine.EmbeddedServer
import io.ktor.server.engine.embeddedServer
-import io.ktor.server.sse.Heartbeat
import io.modelcontextprotocol.kotlin.sdk.server.Server
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
import io.modelcontextprotocol.kotlin.sdk.server.mcpStreamableHttp
@@ -18,10 +17,7 @@ import io.ktor.server.cio.CIO as ServerCIO
open class AbstractStreamableHttpIntegrationTest {
- suspend fun initTestServer(
- name: String? = null,
- sseHeartbeatConfig: (Heartbeat.() -> Unit)? = null,
- ): StreamableHttpTestServer {
+ suspend fun initTestServer(name: String? = null): StreamableHttpTestServer {
val mcpServer = Server(
Implementation(name = name ?: DEFAULT_SERVER_NAME, version = VERSION),
ServerOptions(
@@ -56,7 +52,7 @@ open class AbstractStreamableHttpIntegrationTest {
host = URL,
port = PORT,
) {
- mcpStreamableHttp(sseHeartbeatConfig = sseHeartbeatConfig) { mcpServer }
+ mcpStreamableHttp { mcpServer }
}
return StreamableHttpTestServer(
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpClientTransportIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpClientTransportIntegrationTest.kt
deleted file mode 100644
index 598825f..0000000
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpClientTransportIntegrationTest.kt
+++ /dev/null
@@ -1,270 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.integration.streamablehttp
-
-import io.kotest.matchers.collections.shouldContain
-import io.kotest.matchers.shouldBe
-import io.ktor.client.HttpClient
-import io.ktor.client.plugins.sse.SSE
-import io.ktor.http.ContentType
-import io.ktor.http.HttpStatusCode
-import io.ktor.server.engine.embeddedServer
-import io.ktor.server.request.receiveText
-import io.ktor.server.response.header
-import io.ktor.server.response.respond
-import io.ktor.server.response.respondText
-import io.ktor.server.response.respondTextWriter
-import io.ktor.server.routing.Route
-import io.ktor.server.routing.delete
-import io.ktor.server.routing.get
-import io.ktor.server.routing.post
-import io.ktor.server.routing.routing
-import io.modelcontextprotocol.kotlin.sdk.client.Client
-import io.modelcontextprotocol.kotlin.sdk.client.ClientOptions
-import io.modelcontextprotocol.kotlin.sdk.client.StreamableHttpClientTransport
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.types.Implementation
-import io.modelcontextprotocol.kotlin.sdk.types.Tool
-import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
-import io.modelcontextprotocol.kotlin.test.utils.actualPort
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
-import kotlinx.serialization.json.Json
-import kotlinx.serialization.json.buildJsonObject
-import kotlinx.serialization.json.jsonObject
-import kotlinx.serialization.json.jsonPrimitive
-import kotlinx.serialization.json.put
-import kotlinx.serialization.json.putJsonObject
-import kotlin.test.Test
-import io.ktor.client.engine.cio.CIO as ClientCIO
-import io.ktor.server.cio.CIO as ServerCIO
-
-private const val SESSION_ID_HEADER = "mcp-session-id"
-private const val HOST = "127.0.0.1"
-private const val MCP_PATH = "/mcp"
-
-private val INITIALIZE_RESULT = """
- {
- "capabilities": {"tools": {"listChanged": false}},
- "protocolVersion": "2025-03-26",
- "serverInfo": {"name": "test-server", "version": "1.0.0"}
- }
-""".trimIndent()
-
-private val TOOLS_LIST_RESULT = """
- {
- "tools": [
- {
- "name": "get_weather",
- "title": "Weather Information Provider",
- "description": "Get current weather information for a location",
- "inputSchema": {
- "type": "object",
- "properties": {
- "location": {"type": "string", "description": "City name or zip code"}
- },
- "required": ["location"]
- },
- "outputSchema": {
- "type": "object",
- "properties": {
- "temperature": {"type": "number", "description": "Temperature, Celsius"}
- },
- "required": ["temperature"]
- },
- "_meta": {}
- }
- ]
- }
-""".trimIndent()
-
-private const val PROGRESS_NOTIFICATION_1 =
- """{"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}"""
-
-private const val PROGRESS_NOTIFICATION_2 =
- """{"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":75,"total":100}}"""
-
-internal class StreamableHttpClientTransportIntegrationTest {
-
- @Test
- fun `client receives progress notifications via GET SSE and lists tools`(): Unit = runBlocking(Dispatchers.IO) {
- val sessionId = "sid-progress"
- runWithServer({
- mcpPostRoute(sessionId, extraResults = mapOf("tools/list" to TOOLS_LIST_RESULT))
- get(MCP_PATH) {
- call.response.header(SESSION_ID_HEADER, sessionId)
- call.respondTextWriter(contentType = ContentType.Text.EventStream) {
- write(sseEvent(id = "1", data = PROGRESS_NOTIFICATION_1))
- flush()
- delay(200)
- write(sseEvent(id = "2", data = PROGRESS_NOTIFICATION_2))
- flush()
- awaitCancellation()
- }
- }
- }) { url ->
- val client = newClient()
- client.connect(StreamableHttpClientTransport(client = newHttpClient(), url = url))
-
- val listToolsResult = client.listTools()
-
- listToolsResult.tools shouldContain Tool(
- name = "get_weather",
- title = "Weather Information Provider",
- description = "Get current weather information for a location",
- inputSchema = ToolSchema(
- properties = buildJsonObject {
- putJsonObject("location") {
- put("type", "string")
- put("description", "City name or zip code")
- }
- },
- required = listOf("location"),
- ),
- outputSchema = ToolSchema(
- properties = buildJsonObject {
- putJsonObject("temperature") {
- put("type", "number")
- put("description", "Temperature, Celsius")
- }
- },
- required = listOf("temperature"),
- ),
- annotations = null,
- meta = EmptyJsonObject,
- )
-
- client.close()
- }
- }
-
- @Test
- fun `terminateSession sends DELETE request and clears sessionId`(): Unit = runBlocking(Dispatchers.IO) {
- val sessionId = "sid-terminate"
- runWithServer({
- mcpPostRoute(sessionId)
- get(MCP_PATH) {
- call.response.header(SESSION_ID_HEADER, sessionId)
- call.respondTextWriter(contentType = ContentType.Text.EventStream) {
- awaitCancellation()
- }
- }
- delete(MCP_PATH) {
- call.respond(HttpStatusCode.OK)
- }
- }) { url ->
- val client = newClient()
- val transport = StreamableHttpClientTransport(client = newHttpClient(), url = url)
- client.connect(transport)
-
- transport.sessionId shouldBe sessionId
- transport.terminateSession()
- transport.sessionId shouldBe null
-
- client.close()
- }
- }
-
- @Test
- fun `client survives 405 on GET SSE`(): Unit = runBlocking(Dispatchers.IO) {
- val sessionId = "sid-405"
- runWithServer({
- mcpPostRoute(sessionId, extraResults = mapOf("ping" to "{}"))
- get(MCP_PATH) {
- call.respondText(
- text = "",
- contentType = ContentType.Text.EventStream,
- status = HttpStatusCode.MethodNotAllowed,
- )
- }
- }) { url ->
- val client = newClient()
- client.connect(StreamableHttpClientTransport(client = newHttpClient(), url = url))
-
- client.ping()
-
- client.close()
- }
- }
-
- @Test
- fun `client survives non streaming JSON response on GET SSE`(): Unit = runBlocking(Dispatchers.IO) {
- val sessionId = "sid-json"
- runWithServer({
- mcpPostRoute(sessionId, extraResults = mapOf("ping" to "{}"))
- get(MCP_PATH) {
- call.response.header(SESSION_ID_HEADER, sessionId)
- call.respondText(
- text = "",
- contentType = ContentType.Application.Json,
- status = HttpStatusCode.OK,
- )
- }
- }) { url ->
- val client = newClient()
- client.connect(StreamableHttpClientTransport(client = newHttpClient(), url = url))
-
- client.ping()
-
- client.close()
- }
- }
-
- private fun Route.mcpPostRoute(sessionId: String, extraResults: Map = emptyMap()) {
- post(MCP_PATH) {
- val body = call.receiveText()
- val parsed = Json.parseToJsonElement(body).jsonObject
- val method = parsed["method"]?.jsonPrimitive?.content
- val idJson = parsed["id"]?.toString() ?: "null"
- call.response.header(SESSION_ID_HEADER, sessionId)
- when {
- method == "initialize" -> call.respondText(
- text = jsonRpcResult(idJson, INITIALIZE_RESULT),
- contentType = ContentType.Application.Json,
- status = HttpStatusCode.OK,
- )
-
- method == "notifications/initialized" -> call.respond(HttpStatusCode.Accepted)
-
- method != null && method in extraResults -> call.respondText(
- text = jsonRpcResult(idJson, extraResults.getValue(method)),
- contentType = ContentType.Application.Json,
- status = HttpStatusCode.OK,
- )
-
- else -> call.respond(HttpStatusCode.NotFound)
- }
- }
- }
-
- private suspend fun runWithServer(routes: Route.() -> Unit, block: suspend (url: String) -> Unit) {
- val server = embeddedServer(ServerCIO, host = HOST, port = 0) {
- routing { routes() }
- }.startSuspend(wait = false)
- try {
- val port = server.actualPort()
- block("http://$HOST:$port$MCP_PATH")
- } finally {
- server.stopSuspend(1000, 2000)
- }
- }
-
- private fun newClient(): Client = Client(
- clientInfo = Implementation(name = "client1", version = "1.0.0"),
- options = ClientOptions(capabilities = ClientCapabilities()),
- )
-
- private fun newHttpClient(): HttpClient = HttpClient(ClientCIO) {
- install(SSE)
- }
-
- private fun jsonRpcResult(idJson: String, resultJson: String): String =
- """{"jsonrpc":"2.0","id":$idJson,"result":$resultJson}"""
-
- private fun sseEvent(id: String, data: String): String = buildString {
- append("event: message\n")
- append("id: $id\n")
- append("data: $data\n\n")
- }
-}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpHeartbeatIntegrationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpHeartbeatIntegrationTest.kt
deleted file mode 100644
index 8019557..0000000
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpHeartbeatIntegrationTest.kt
+++ /dev/null
@@ -1,174 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.integration.streamablehttp
-
-import io.kotest.matchers.shouldBe
-import io.ktor.client.HttpClient
-import io.ktor.client.plugins.sse.SSE
-import io.ktor.client.request.header
-import io.ktor.client.request.post
-import io.ktor.client.request.prepareGet
-import io.ktor.client.request.setBody
-import io.ktor.client.statement.bodyAsChannel
-import io.ktor.http.ContentType
-import io.ktor.http.HttpHeaders
-import io.ktor.http.HttpStatusCode
-import io.ktor.http.contentType
-import io.ktor.sse.ServerSentEvent
-import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.readUTF8Line
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.Implementation
-import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequest
-import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
-import io.modelcontextprotocol.kotlin.sdk.types.LATEST_PROTOCOL_VERSION
-import io.modelcontextprotocol.kotlin.sdk.types.toJSON
-import io.modelcontextprotocol.kotlin.test.utils.actualPort
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withTimeoutOrNull
-import kotlinx.serialization.json.Json
-import kotlin.test.Test
-import kotlin.test.assertNotNull
-import kotlin.time.Duration.Companion.milliseconds
-import io.ktor.client.engine.cio.CIO as ClientCIO
-
-private const val SESSION_ID_HEADER = "mcp-session-id"
-private const val PROTOCOL_VERSION_HEADER = "mcp-protocol-version"
-private const val DEFAULT_HEARTBEAT_LINE = ": heartbeat"
-private const val CUSTOM_HEARTBEAT_LINE = ": mcp-heartbeat"
-
-class StreamableHttpHeartbeatIntegrationTest : AbstractStreamableHttpIntegrationTest() {
-
- @Test
- fun `GET SSE stream emits configured heartbeat`(): Unit = runBlocking(Dispatchers.IO) {
- var server: StreamableHttpTestServer? = null
- var httpClient: HttpClient? = null
-
- try {
- server = initTestServer("heartbeat-test") {
- period = 50.milliseconds
- event = ServerSentEvent(comments = "mcp-heartbeat")
- }
- val mcpUrl = "http://$URL:${server.ktorServer.actualPort()}/mcp"
- httpClient = HttpClient(ClientCIO) { install(SSE) }
- val sessionId = initializeSession(httpClient, mcpUrl)
-
- httpClient.prepareGet(mcpUrl) {
- addSseHeaders(sessionId)
- }.execute { response ->
- response.status shouldBe HttpStatusCode.OK
- response.headers[SESSION_ID_HEADER] shouldBe sessionId
-
- response.bodyAsChannel().readLineMatching(CUSTOM_HEARTBEAT_LINE) shouldBe CUSTOM_HEARTBEAT_LINE
- }
- } finally {
- httpClient?.close()
- server?.ktorServer?.stopSuspend(1000, 2000)
- }
- }
-
- @Test
- fun `GET SSE stream does not emit heartbeat by default`(): Unit = runBlocking(Dispatchers.IO) {
- var server: StreamableHttpTestServer? = null
- var httpClient: HttpClient? = null
-
- try {
- server = initTestServer("no-heartbeat-test")
- val mcpUrl = "http://$URL:${server.ktorServer.actualPort()}/mcp"
- httpClient = HttpClient(ClientCIO) { install(SSE) }
- val sessionId = initializeSession(httpClient, mcpUrl)
-
- httpClient.prepareGet(mcpUrl) {
- addSseHeaders(sessionId)
- }.execute { response ->
- response.status shouldBe HttpStatusCode.OK
- response.headers[SESSION_ID_HEADER] shouldBe sessionId
-
- response.bodyAsChannel().readLineMatching(timeoutMillis = 150) { line ->
- line.isHeartbeatSseLine()
- } shouldBe null
- }
- } finally {
- httpClient?.close()
- server?.ktorServer?.stopSuspend(1000, 2000)
- }
- }
-
- @Test
- fun `GET SSE stream emits configured heartbeat repeatedly`(): Unit = runBlocking(Dispatchers.IO) {
- var server: StreamableHttpTestServer? = null
- var httpClient: HttpClient? = null
-
- try {
- server = initTestServer("repeating-heartbeat-test") {
- period = 50.milliseconds
- event = ServerSentEvent(comments = "mcp-heartbeat")
- }
- val mcpUrl = "http://$URL:${server.ktorServer.actualPort()}/mcp"
- httpClient = HttpClient(ClientCIO) { install(SSE) }
- val sessionId = initializeSession(httpClient, mcpUrl)
-
- httpClient.prepareGet(mcpUrl) {
- addSseHeaders(sessionId)
- }.execute { response ->
- response.status shouldBe HttpStatusCode.OK
- response.headers[SESSION_ID_HEADER] shouldBe sessionId
-
- val channel = response.bodyAsChannel()
- channel.readLineMatching(CUSTOM_HEARTBEAT_LINE) shouldBe CUSTOM_HEARTBEAT_LINE
- channel.readLineMatching(CUSTOM_HEARTBEAT_LINE) shouldBe CUSTOM_HEARTBEAT_LINE
- }
- } finally {
- httpClient?.close()
- server?.ktorServer?.stopSuspend(1000, 2000)
- }
- }
-
- private suspend fun initializeSession(client: HttpClient, mcpUrl: String): String {
- val response = client.post(mcpUrl) {
- contentType(ContentType.Application.Json)
- header(
- HttpHeaders.Accept,
- "${ContentType.Application.Json}, ${ContentType.Text.EventStream}",
- )
- setBody(Json.encodeToString(buildInitPayload()))
- }
-
- response.status shouldBe HttpStatusCode.OK
- return assertNotNull(response.headers[SESSION_ID_HEADER])
- }
-
- private fun buildInitPayload(): JSONRPCRequest = InitializeRequest(
- InitializeRequestParams(
- protocolVersion = LATEST_PROTOCOL_VERSION,
- capabilities = ClientCapabilities(),
- clientInfo = Implementation(name = "heartbeat-test-client", version = "1.0.0"),
- ),
- ).toJSON()
-
- private fun io.ktor.client.request.HttpRequestBuilder.addSseHeaders(sessionId: String) {
- header(HttpHeaders.Accept, ContentType.Text.EventStream.toString())
- header(SESSION_ID_HEADER, sessionId)
- header(PROTOCOL_VERSION_HEADER, LATEST_PROTOCOL_VERSION)
- }
-
- private suspend fun ByteReadChannel.readLineMatching(expectedLine: String, timeoutMillis: Long = 2_000): String? =
- readLineMatching(timeoutMillis) { line -> line == expectedLine }
-
- private suspend fun ByteReadChannel.readLineMatching(
- timeoutMillis: Long = 2_000,
- matches: (String) -> Boolean,
- ): String? = withTimeoutOrNull(timeoutMillis.milliseconds) {
- var line = readUTF8Line()
- while (line != null) {
- if (matches(line)) return@withTimeoutOrNull line
- line = readUTF8Line()
- }
- null
- }
-
- private fun String.isHeartbeatSseLine(): Boolean = this == DEFAULT_HEARTBEAT_LINE ||
- this == CUSTOM_HEARTBEAT_LINE ||
- (startsWith(":") && contains("heartbeat", ignoreCase = true)) ||
- (startsWith("event:") && contains("heartbeat", ignoreCase = true))
-}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpSseReconnectTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpSseReconnectTest.kt
deleted file mode 100644
index 751f8dd..0000000
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/streamablehttp/StreamableHttpSseReconnectTest.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.integration.streamablehttp
-
-import io.kotest.matchers.nulls.shouldNotBeNull
-import io.kotest.matchers.shouldBe
-import io.ktor.client.HttpClient
-import io.ktor.client.plugins.sse.SSE
-import io.ktor.client.request.header
-import io.ktor.client.request.post
-import io.ktor.client.request.prepareGet
-import io.ktor.client.request.setBody
-import io.ktor.client.statement.bodyAsChannel
-import io.ktor.http.ContentType
-import io.ktor.http.HttpHeaders
-import io.ktor.http.HttpStatusCode
-import io.ktor.http.contentType
-import io.ktor.utils.io.readUTF8Line
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.Implementation
-import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequest
-import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
-import io.modelcontextprotocol.kotlin.sdk.types.LATEST_PROTOCOL_VERSION
-import io.modelcontextprotocol.kotlin.sdk.types.toJSON
-import io.modelcontextprotocol.kotlin.test.utils.actualPort
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import kotlinx.serialization.json.Json
-import kotlin.test.Test
-import kotlin.test.assertNotNull
-import io.ktor.client.engine.cio.CIO as ClientCIO
-
-private const val SESSION_ID_HEADER = "mcp-session-id"
-private const val PROTOCOL_VERSION_HEADER = "mcp-protocol-version"
-
-/**
- * Integration tests for GET SSE stream reconnection using a real embedded CIO server.
- *
- * Verifies that the transport correctly evicts stale STANDALONE_SSE_STREAM_ID
- * entries when a client reconnects after a disconnect, rather than silently
- * rejecting the new stream.
- */
-class StreamableHttpSseReconnectTest : AbstractStreamableHttpIntegrationTest() {
-
- /**
- * Verifies that after a GET SSE stream disconnects and the client
- * immediately reconnects, the server evicts the stale stream mapping
- * and allows the new stream to succeed.
- */
- @Test
- fun `GET SSE reconnect after disconnect should succeed`(): Unit = runBlocking(Dispatchers.IO) {
- var server: StreamableHttpTestServer? = null
- var httpClient: HttpClient? = null
-
- try {
- server = initTestServer("reconnect-test")
- val port = server.ktorServer.actualPort()
- val mcpUrl = "http://$URL:$port/mcp"
-
- httpClient = HttpClient(ClientCIO) { install(SSE) }
-
- // Step 1: Initialize session via POST
- val initResponse = httpClient.post(mcpUrl) {
- contentType(ContentType.Application.Json)
- header(
- HttpHeaders.Accept,
- "${ContentType.Application.Json}, ${ContentType.Text.EventStream}",
- )
- setBody(Json.encodeToString(buildInitPayload()))
- }
- initResponse.status shouldBe HttpStatusCode.OK
- val sessionId = assertNotNull(initResponse.headers[SESSION_ID_HEADER])
-
- // Step 2: Open GET SSE stream, consume the flush event, then disconnect
- httpClient.prepareGet(mcpUrl) {
- header(HttpHeaders.Accept, ContentType.Text.EventStream.toString())
- header(SESSION_ID_HEADER, sessionId)
- header(PROTOCOL_VERSION_HEADER, LATEST_PROTOCOL_VERSION)
- }.execute { response ->
- response.status shouldBe HttpStatusCode.OK
- response.bodyAsChannel().readUTF8Line()
- }
-
- // Step 3: Immediately reconnect. The transport detects that the
- // previous stream's coroutine is no longer active and evicts the
- // stale mapping, allowing the new stream to succeed.
- httpClient.prepareGet(mcpUrl) {
- header(HttpHeaders.Accept, ContentType.Text.EventStream.toString())
- header(SESSION_ID_HEADER, sessionId)
- header(PROTOCOL_VERSION_HEADER, LATEST_PROTOCOL_VERSION)
- }.execute { response ->
- response.status shouldBe HttpStatusCode.OK
- response.headers[SESSION_ID_HEADER] shouldBe sessionId
-
- val channel = response.bodyAsChannel()
- val firstLine = channel.readUTF8Line()
- firstLine.shouldNotBeNull()
- channel.isClosedForRead shouldBe false
- }
- } finally {
- httpClient?.close()
- server?.ktorServer?.stopSuspend(1000, 2000)
- }
- }
-
- /**
- * Verifies that a second concurrent GET SSE stream on the same session
- * closes the old stream and takes over. The new stream should be live.
- */
- @Test
- fun `concurrent GET SSE stream closes old stream and takes over`(): Unit = runBlocking(Dispatchers.IO) {
- var server: StreamableHttpTestServer? = null
- var httpClient: HttpClient? = null
-
- try {
- server = initTestServer("takeover-test")
- val port = server.ktorServer.actualPort()
- val mcpUrl = "http://$URL:$port/mcp"
-
- httpClient = HttpClient(ClientCIO) { install(SSE) }
-
- // Step 1: Initialize session via POST
- val initResponse = httpClient.post(mcpUrl) {
- contentType(ContentType.Application.Json)
- header(
- HttpHeaders.Accept,
- "${ContentType.Application.Json}, ${ContentType.Text.EventStream}",
- )
- setBody(Json.encodeToString(buildInitPayload()))
- }
- initResponse.status shouldBe HttpStatusCode.OK
- val sessionId = assertNotNull(initResponse.headers[SESSION_ID_HEADER])
-
- // Step 2: Open first GET SSE stream and keep it open
- httpClient.prepareGet(mcpUrl) {
- header(HttpHeaders.Accept, ContentType.Text.EventStream.toString())
- header(SESSION_ID_HEADER, sessionId)
- header(PROTOCOL_VERSION_HEADER, LATEST_PROTOCOL_VERSION)
- }.execute { firstResponse ->
- firstResponse.status shouldBe HttpStatusCode.OK
- firstResponse.bodyAsChannel().readUTF8Line()
-
- // Step 3: Open a second GET — closes old stream, new one takes over
- httpClient.prepareGet(mcpUrl) {
- header(HttpHeaders.Accept, ContentType.Text.EventStream.toString())
- header(SESSION_ID_HEADER, sessionId)
- header(PROTOCOL_VERSION_HEADER, LATEST_PROTOCOL_VERSION)
- }.execute { secondResponse ->
- secondResponse.status shouldBe HttpStatusCode.OK
- secondResponse.headers[SESSION_ID_HEADER] shouldBe sessionId
-
- // New stream is alive
- val secondChannel = secondResponse.bodyAsChannel()
- val firstLine = secondChannel.readUTF8Line()
- firstLine.shouldNotBeNull()
- secondChannel.isClosedForRead shouldBe false
- }
- }
- } finally {
- httpClient?.close()
- server?.ktorServer?.stopSuspend(1000, 2000)
- }
- }
-
- private fun buildInitPayload(): JSONRPCRequest = InitializeRequest(
- InitializeRequestParams(
- protocolVersion = LATEST_PROTOCOL_VERSION,
- capabilities = ClientCapabilities(),
- clientInfo = Implementation(name = "reconnect-test-client", version = "1.0.0"),
- ),
- ).toJSON()
-}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionLoggingTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionLoggingTest.kt
index a1cf797..81a3b41 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionLoggingTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionLoggingTest.kt
@@ -1,7 +1,5 @@
package io.modelcontextprotocol.kotlin.sdk.server
-import io.kotest.assertions.nondeterministic.continually
-import io.kotest.assertions.nondeterministic.eventually
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
@@ -19,9 +17,6 @@ import kotlinx.serialization.json.JsonPrimitive
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.EnumSource
-import java.util.concurrent.CopyOnWriteArrayList
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
class ClientConnectionLoggingTest : AbstractServerFeaturesTest() {
@@ -61,7 +56,7 @@ class ClientConnectionLoggingTest : AbstractServerFeaturesTest() {
@ParameterizedTest
@EnumSource(LoggingLevel::class)
fun `notification should filter logging messages below level`(minLevel: LoggingLevel): Unit = runBlocking {
- val receivedMessages = CopyOnWriteArrayList()
+ val receivedMessages = mutableListOf()
client.setNotificationHandler(Method.Defined.NotificationsMessage) {
receivedMessages.add(it)
CompletableDeferred(Unit)
@@ -84,10 +79,8 @@ class ClientConnectionLoggingTest : AbstractServerFeaturesTest() {
client.callTool(CallToolRequest(CallToolRequestParams("test-notification-filtered")))
- // Handlers dispatch on Dispatchers.Default after the handshake, so await the expected count
- // before asserting order.
val expectedLevels = LoggingLevel.entries.filter { it >= minLevel }
- eventually(5.seconds) { receivedMessages shouldHaveSize expectedLevels.size }
+ receivedMessages shouldHaveSize expectedLevels.size
receivedMessages.map { it.params.level } shouldBe expectedLevels
}
@@ -122,7 +115,7 @@ class ClientConnectionLoggingTest : AbstractServerFeaturesTest() {
@ParameterizedTest
@EnumSource(LoggingLevel::class)
fun `sendLoggingMessage should filter messages below level`(minLevel: LoggingLevel): Unit = runBlocking {
- val receivedMessages = CopyOnWriteArrayList()
+ val receivedMessages = mutableListOf()
client.setNotificationHandler(Method.Defined.NotificationsMessage) {
receivedMessages.add(it)
CompletableDeferred(Unit)
@@ -145,16 +138,14 @@ class ClientConnectionLoggingTest : AbstractServerFeaturesTest() {
client.callTool(CallToolRequest(CallToolRequestParams("test-logging-level")))
- // Handlers dispatch on Dispatchers.Default after the handshake, so await the expected count
- // before asserting order.
val expectedLevels = LoggingLevel.entries.filter { it >= minLevel }
- eventually(5.seconds) { receivedMessages shouldHaveSize expectedLevels.size }
+ receivedMessages shouldHaveSize expectedLevels.size
receivedMessages.map { it.params.level } shouldBe expectedLevels
}
@Test
fun `sendLoggingMessage should send no messages when level is set to highest`(): Unit = runBlocking {
- val receivedMessages = CopyOnWriteArrayList()
+ val receivedMessages = mutableListOf()
client.setNotificationHandler(Method.Defined.NotificationsMessage) {
receivedMessages.add(it)
CompletableDeferred(Unit)
@@ -176,7 +167,6 @@ class ClientConnectionLoggingTest : AbstractServerFeaturesTest() {
}
client.callTool(CallToolRequest(CallToolRequestParams("test-logging-highest")))
- // Handlers dispatch asynchronously, so hold for a window to confirm nothing ever arrives.
- continually(500.milliseconds) { receivedMessages.shouldBeEmpty() }
+ receivedMessages.shouldBeEmpty()
}
}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionTest.kt
index 581e5ae..ee8953a 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionTest.kt
@@ -9,7 +9,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotification
import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotificationParams
-import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.ListRootsRequest
@@ -62,7 +61,6 @@ class ClientConnectionTest : AbstractServerFeaturesTest() {
override fun getClientCapabilities(): ClientCapabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(listChanged = true),
- elicitation = ClientCapabilities.Elicitation(url = EmptyJsonObject),
)
private val sampleRoots = listOf(Root("file:///project", "Project Root"))
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SamplingTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SamplingTest.kt
deleted file mode 100644
index f161d8e..0000000
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SamplingTest.kt
+++ /dev/null
@@ -1,251 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.server
-
-import io.modelcontextprotocol.kotlin.sdk.client.Client
-import io.modelcontextprotocol.kotlin.sdk.client.ClientOptions
-import io.modelcontextprotocol.kotlin.sdk.shared.InMemoryTransport
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageResult
-import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.types.Implementation
-import io.modelcontextprotocol.kotlin.sdk.types.IncludeContext
-import io.modelcontextprotocol.kotlin.sdk.types.Method
-import io.modelcontextprotocol.kotlin.sdk.types.Role
-import io.modelcontextprotocol.kotlin.sdk.types.SamplingMessage
-import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.StopReason
-import io.modelcontextprotocol.kotlin.sdk.types.TextContent
-import io.modelcontextprotocol.kotlin.sdk.types.Tool
-import io.modelcontextprotocol.kotlin.sdk.types.ToolChoice
-import io.modelcontextprotocol.kotlin.sdk.types.ToolResultContent
-import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
-import io.modelcontextprotocol.kotlin.sdk.types.ToolUseContent
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import kotlinx.serialization.json.JsonPrimitive
-import kotlinx.serialization.json.buildJsonObject
-import org.junit.jupiter.api.assertDoesNotThrow
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-
-class SamplingTest {
-
- private val dummyTool = Tool(
- name = "t",
- inputSchema = ToolSchema(properties = buildJsonObject { }, required = emptyList()),
- )
-
- private val weatherTool = Tool(
- name = "get_weather",
- description = "Return the current temperature in Celsius.",
- inputSchema = ToolSchema(
- properties = buildJsonObject {
- put("location", buildJsonObject { put("type", JsonPrimitive("string")) })
- },
- required = listOf("location"),
- ),
- )
-
- private val minimalMessages = listOf(SamplingMessage(Role.User, TextContent("hi")))
-
- /**
- * Builds a connected [Server]+[Client] pair using [InMemoryTransport].
- *
- * @param clientCapabilities the capabilities the client advertises during initialize
- * @param samplingHandler the handler the client uses to respond to sampling requests
- * @return Pair of (server, sessionId) ready for [Server.createMessage] calls.
- */
- private fun buildPair(
- clientCapabilities: ClientCapabilities = ClientCapabilities(
- sampling = ClientCapabilities.Sampling(),
- ),
- samplingHandler: (CreateMessageRequest) -> CreateMessageResult = { _ ->
- CreateMessageResult(role = Role.Assistant, content = TextContent("ok"), model = "m")
- },
- ): Pair {
- val server = Server(
- serverInfo = Implementation(name = "srv", version = "1.0"),
- options = ServerOptions(capabilities = ServerCapabilities()),
- )
-
- val client = Client(
- clientInfo = Implementation(name = "cli", version = "1.0"),
- options = ClientOptions(capabilities = clientCapabilities),
- )
-
- client.setRequestHandler(Method.Defined.SamplingCreateMessage) { req, _ ->
- samplingHandler(req)
- }
-
- val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
-
- var sessionId: String? = null
- runBlocking {
- val sessionDeferred = CompletableDeferred()
- launch { client.connect(clientTransport) }
- launch {
- val session = server.createSession(serverTransport)
- sessionDeferred.complete(session.sessionId)
- }
- sessionId = sessionDeferred.await()
- }
-
- return Pair(server, checkNotNull(sessionId))
- }
-
- // ============================================================================
- // Server.createMessage — capability enforcement (SEP-1577)
- // ============================================================================
-
- @Test
- fun `tools field rejected when client has no sampling tools capability`() {
- val (server, sessionId) = buildPair()
- assertFailsWith {
- runBlocking {
- server.createMessage(
- sessionId = sessionId,
- params = CreateMessageRequest(
- params = CreateMessageRequestParams(
- maxTokens = 100,
- messages = minimalMessages,
- tools = listOf(dummyTool),
- ),
- ),
- )
- }
- }
- }
-
- @Test
- fun `toolChoice field rejected when client has no sampling tools capability`() {
- val (server, sessionId) = buildPair()
- assertFailsWith {
- runBlocking {
- server.createMessage(
- sessionId = sessionId,
- params = CreateMessageRequest(
- params = CreateMessageRequestParams(
- maxTokens = 100,
- messages = minimalMessages,
- toolChoice = ToolChoice(),
- ),
- ),
- )
- }
- }
- }
-
- @Test
- fun `includeContext with no sampling context capability succeeds with a warning`() {
- val (server, sessionId) = buildPair()
- assertDoesNotThrow {
- runBlocking {
- server.createMessage(
- sessionId = sessionId,
- params = CreateMessageRequest(
- params = CreateMessageRequestParams(
- maxTokens = 100,
- messages = minimalMessages,
- includeContext = IncludeContext.ThisServer,
- ),
- ),
- )
- }
- }
- }
-
- // ============================================================================
- // End-to-end tool-loop integration
- // ============================================================================
-
- @Test
- fun `server sends tools, client returns tool_use then final text`() {
- var turn = 0
- val (server, sessionId) = buildPair(
- clientCapabilities = ClientCapabilities(
- sampling = ClientCapabilities.Sampling(tools = EmptyJsonObject),
- ),
- samplingHandler = { _ ->
- turn++
- if (turn == 1) {
- CreateMessageResult(
- role = Role.Assistant,
- content = listOf(
- TextContent("Let me check."),
- ToolUseContent(
- id = "call_1",
- name = "get_weather",
- input = buildJsonObject { put("location", JsonPrimitive("London")) },
- ),
- ),
- model = "test",
- stopReason = StopReason.ToolUse,
- )
- } else {
- CreateMessageResult(
- role = Role.Assistant,
- content = TextContent("The temperature in London is 20°C."),
- model = "test",
- stopReason = StopReason.EndTurn,
- )
- }
- },
- )
-
- runBlocking {
- val messages = mutableListOf(
- SamplingMessage(Role.User, TextContent("What is the weather in London?")),
- )
-
- // — Turn 1: server sends tools, expects tool_use stop reason
- val first = server.createMessage(
- sessionId = sessionId,
- params = CreateMessageRequest(
- params = CreateMessageRequestParams(
- maxTokens = 256,
- messages = messages.toList(),
- tools = listOf(weatherTool),
- toolChoice = ToolChoice(mode = ToolChoice.Mode.Auto),
- ),
- ),
- )
- assertEquals(StopReason.ToolUse, first.stopReason)
- assertEquals(2, first.content.size)
-
- // — Append assistant turn and inject tool result
- messages.add(SamplingMessage(Role.Assistant, first.content))
- val toolUse = first.content.filterIsInstance().single()
- messages.add(
- SamplingMessage(
- Role.User,
- ToolResultContent(
- toolUseId = toolUse.id,
- content = listOf(TextContent("""{"tempC":20}""")),
- ),
- ),
- )
-
- // — Turn 2: server sends updated history, expects final text
- val second = server.createMessage(
- sessionId = sessionId,
- params = CreateMessageRequest(
- params = CreateMessageRequestParams(
- maxTokens = 256,
- messages = messages.toList(),
- tools = listOf(weatherTool),
- toolChoice = ToolChoice(mode = ToolChoice.Mode.Auto),
- ),
- ),
- )
- assertEquals(StopReason.EndTurn, second.stopReason)
- assertEquals(1, second.content.size)
- val text = second.content.single() as TextContent
- assertEquals("The temperature in London is 20°C.", text.text)
-
- server.close()
- }
- }
-}
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt
index 52f6908..1eb828f 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerBulkFeaturesTest.kt
@@ -1,6 +1,5 @@
package io.modelcontextprotocol.kotlin.sdk.server
-import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
import io.kotest.matchers.collections.shouldHaveSize
@@ -58,39 +57,6 @@ class ServerBulkFeaturesTest : AbstractServerFeaturesTest() {
tools.map { it.name } shouldContainExactlyInAnyOrder listOf("existing", "new-a", "new-b")
}
- @Test
- fun `addTools should throw and register nothing when a tool is already registered`() = runTest {
- server.addTool("existing", "Existing") { CallToolResult(emptyList()) }
-
- shouldThrow {
- server.addTools(
- listOf(
- RegisteredTool(Tool("new-a", ToolSchema())) { CallToolResult(emptyList()) },
- RegisteredTool(Tool("existing", ToolSchema())) { CallToolResult(emptyList()) },
- ),
- )
- }
-
- val tools = client.listTools().tools
-
- tools shouldHaveSize 1
- tools.map { it.name } shouldContainExactlyInAnyOrder listOf("existing")
- }
-
- @Test
- fun `addTools should throw when batch contains duplicate names`() = runTest {
- shouldThrow {
- server.addTools(
- listOf(
- RegisteredTool(Tool("dup", ToolSchema())) { CallToolResult(emptyList()) },
- RegisteredTool(Tool("dup", ToolSchema())) { CallToolResult(emptyList()) },
- ),
- )
- }
-
- client.listTools().tools.shouldBeEmpty()
- }
-
// ── addPrompts ─────────────────────────────────────────────────────────────
@Test
@@ -123,25 +89,6 @@ class ServerBulkFeaturesTest : AbstractServerFeaturesTest() {
prompts.map { it.name } shouldContainExactlyInAnyOrder listOf("existing-prompt", "new-p")
}
- @Test
- fun `addPrompts should throw and register nothing when a prompt is already registered`() = runTest {
- server.addPrompt("existing-prompt", "Existing") { GetPromptResult(messages = emptyList()) }
-
- shouldThrow {
- server.addPrompts(
- listOf(
- RegisteredPrompt(Prompt("new-p")) { GetPromptResult(messages = emptyList()) },
- RegisteredPrompt(Prompt("existing-prompt")) { GetPromptResult(messages = emptyList()) },
- ),
- )
- }
-
- val prompts = client.listPrompts().prompts
-
- prompts shouldHaveSize 1
- prompts.map { it.name } shouldContainExactlyInAnyOrder listOf("existing-prompt")
- }
-
// ── addResources ───────────────────────────────────────────────────────────
@Test
@@ -176,27 +123,6 @@ class ServerBulkFeaturesTest : AbstractServerFeaturesTest() {
resources.map { it.uri } shouldContainExactlyInAnyOrder listOf("test://existing-res", "test://new-res")
}
- @Test
- fun `addResources should throw and register nothing when a resource is already registered`() = runTest {
- server.addResource("test://existing-res", "Existing", "Existing resource") {
- ReadResourceResult(emptyList())
- }
-
- shouldThrow {
- server.addResources(
- listOf(
- RegisteredResource(Resource("test://new-res", "New")) { ReadResourceResult(emptyList()) },
- RegisteredResource(Resource("test://existing-res", "Dup")) { ReadResourceResult(emptyList()) },
- ),
- )
- }
-
- val resources = client.listResources().resources
-
- resources shouldHaveSize 1
- resources.map { it.uri } shouldContainExactlyInAnyOrder listOf("test://existing-res")
- }
-
// ── Partial-batch remove ───────────────────────────────────────────────────
@Test
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsTest.kt
index 44b1e42..d7ae46d 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerPromptsTest.kt
@@ -1,8 +1,5 @@
package io.modelcontextprotocol.kotlin.sdk.server
-import io.kotest.matchers.string.shouldContain
-import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
-import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptResult
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.Method
@@ -113,26 +110,6 @@ class ServerPromptsTest : AbstractServerFeaturesTest() {
assertFalse(promptListChangedNotificationReceived, "No notification should be sent when prompt doesn't exist")
}
- @Test
- fun `addPrompt should throw when prompt with same name is already registered`() = runTest {
- server.addPrompt(Prompt("test-prompt", "Original Prompt", null)) {
- GetPromptResult(description = "original", messages = emptyList())
- }
-
- val exception = assertThrows {
- server.addPrompt(Prompt("test-prompt", "Duplicate Prompt", null)) {
- GetPromptResult(description = "duplicate", messages = emptyList())
- }
- }
- exception.message.orEmpty() shouldContain "Prompt \"test-prompt\" is already registered"
-
- // The original registration is intact
- val prompts = client.listPrompts().prompts
- assertEquals(1, prompts.size, "Only the original prompt should be registered")
- val result = client.getPrompt(GetPromptRequest(GetPromptRequestParams("test-prompt")))
- assertEquals("original", result.description)
- }
-
@Test
fun `removePrompt should throw when prompts capability is not supported`() = runTest {
var promptListChangedNotificationReceived = false
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourceTemplateTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourceTemplateTest.kt
index 8405469..c8ace19 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourceTemplateTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourceTemplateTest.kt
@@ -5,7 +5,6 @@ import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.maps.shouldContainKey
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
-import io.kotest.matchers.string.shouldContain
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.ListResourceTemplatesRequest
import io.modelcontextprotocol.kotlin.sdk.types.McpException
@@ -163,24 +162,6 @@ class ServerResourceTemplateTest : AbstractServerFeaturesTest() {
removed shouldBe false
}
- @Test
- fun `addResourceTemplate should throw when template with same uriTemplate is already registered`() {
- server.addResourceTemplate("test://items/{id}", "Item") { _, _ ->
- ReadResourceResult(emptyList())
- }
-
- val exception = assertThrows {
- server.addResourceTemplate("test://items/{id}", "Duplicate Item") { _, _ ->
- ReadResourceResult(emptyList())
- }
- }
- exception.message.orEmpty() shouldContain "ResourceTemplate \"test://items/{id}\" is already registered"
-
- // The original registration is intact
- server.resourceTemplates shouldHaveSize 1
- server.resourceTemplates[0].name shouldBe "Item"
- }
-
@Test
fun `addResourceTemplate should throw when resources capability is not supported`() {
val noResourcesServer = Server(
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesTest.kt
index a830788..fafc3c8 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerResourcesTest.kt
@@ -1,10 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.server
-import io.kotest.matchers.string.shouldContain
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.Method
-import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceRequest
-import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceResult
import io.modelcontextprotocol.kotlin.sdk.types.ResourceListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
@@ -118,24 +115,6 @@ class ServerResourcesTest : AbstractServerFeaturesTest() {
)
}
- @Test
- fun `addResource should throw when resource with same uri is already registered`() = runTest {
- server.addResource("test://resource", "Original", "Original resource") {
- ReadResourceResult(listOf(TextResourceContents(text = "original", uri = "test://resource")))
- }
-
- val exception = assertThrows {
- server.addResource("test://resource", "Duplicate", "Duplicate resource") {
- ReadResourceResult(listOf(TextResourceContents(text = "duplicate", uri = "test://resource")))
- }
- }
- exception.message.orEmpty() shouldContain "Resource \"test://resource\" is already registered"
-
- // The original registration is intact
- val result = client.readResource(ReadResourceRequest(ReadResourceRequestParams("test://resource")))
- assertEquals("original", (result.contents.single() as TextResourceContents).text)
- }
-
@Test
fun `removeResource should throw when resources capability is not supported`() = runTest {
// Create server without resources capability
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSessionInitializeTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSessionInitializeTest.kt
index 38b169a..5031a9c 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSessionInitializeTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerSessionInitializeTest.kt
@@ -7,11 +7,9 @@ import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequest
import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCError
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCResponse
import io.modelcontextprotocol.kotlin.sdk.types.LATEST_PROTOCOL_VERSION
import io.modelcontextprotocol.kotlin.sdk.types.RPCError
-import io.modelcontextprotocol.kotlin.sdk.types.RequestId
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.toJSON
import kotlinx.coroutines.CompletableDeferred
@@ -20,13 +18,9 @@ import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withContext
-import kotlinx.serialization.json.buildJsonObject
-import kotlinx.serialization.json.put
-import kotlinx.serialization.json.putJsonObject
import org.junit.jupiter.api.Test
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.test.assertEquals
-import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
@@ -47,18 +41,6 @@ class ServerSessionInitializeTest {
),
)
- private fun createMalformedInitializeRequest(): JSONRPCRequest = JSONRPCRequest(
- id = RequestId(1),
- method = "initialize",
- params = buildJsonObject {
- putJsonObject("capabilities") {}
- putJsonObject("clientInfo") {
- put("name", "repro")
- put("version", "0.1.0")
- }
- },
- )
-
@Test
fun `should handle first initialize request successfully`() = runTest {
val session = createSession()
@@ -83,30 +65,6 @@ class ServerSessionInitializeTest {
assertEquals("test-client", session.clientVersion?.name)
}
- // Both initialize requests arrive before notifications/initialized, in the serial dispatch
- // phase, so processing and response order stay deterministic.
- @Test
- fun `should classify malformed initialize params as invalid params`() = runTest {
- val session = createSession()
- val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
-
- val responseDone = CompletableDeferred()
- clientTransport.onMessage { message ->
- if (message is JSONRPCError) {
- responseDone.complete(message)
- }
- }
-
- session.connect(serverTransport)
- clientTransport.send(createMalformedInitializeRequest())
-
- val response = responseDone.await()
- assertEquals(RPCError.ErrorCode.INVALID_PARAMS, response.error.code)
- assertFalse(response.error.message.contains("kotlinx.serialization"))
- assertNull(session.clientCapabilities)
- assertNull(session.clientVersion)
- }
-
@Test
fun `should reject duplicate initialize request`() = runTest {
val session = createSession()
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt
index 9ac744b..3572082 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsNotificationTest.kt
@@ -11,7 +11,6 @@ import kotlinx.coroutines.test.runTest
import org.awaitility.kotlin.await
import org.awaitility.kotlin.untilAsserted
import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@@ -82,27 +81,6 @@ class ServerToolsNotificationTest : AbstractServerFeaturesTest() {
}
}
- @Test
- fun `addTool should not send notification when duplicate is rejected`() = runTest {
- // Track notifications
- val notifications = mutableListOf()
- client.setNotificationHandler(
- Method.Defined.NotificationsToolsListChanged,
- ) { notification ->
- notifications.add(notification)
- CompletableDeferred(Unit)
- }
-
- server.addTool("test-tool", "Test Tool") { CallToolResult(emptyList()) }
- assertThrows {
- server.addTool("test-tool", "Duplicate Tool") { CallToolResult(emptyList()) }
- }
- // Close the server to stop processing further events and flush notifications
- server.close()
-
- assertEquals(1, notifications.size, "Only the first registration should send a notification")
- }
-
@Test
fun `notification should not be send when removed tool does not exists`() = runTest {
// Track notifications
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt
index 1b3df91..c2e8201 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ServerToolsTest.kt
@@ -1,8 +1,5 @@
package io.modelcontextprotocol.kotlin.sdk.server
-import io.kotest.matchers.string.shouldContain
-import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
-import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.Method
@@ -101,52 +98,6 @@ class ServerToolsTest : AbstractServerFeaturesTest() {
assertFalse(toolListChangedNotificationReceived, "No notification should be sent when tool doesn't exist")
}
- @Test
- fun `addTool should succeed with non-conforming tool name`() = runTest {
- server.addTool("my invalid tool!", "Tool with non-conforming name", ToolSchema()) {
- CallToolResult(listOf(TextContent("It works")))
- }
-
- val result = server.removeTool("my invalid tool!")
- assertTrue(result, "Tool with non-conforming name should have been registered and removable")
- }
-
- @Test
- fun `addTool should throw when tool with same name is already registered`() = runTest {
- server.addTool("test-tool", "Original Tool") {
- CallToolResult(listOf(TextContent("Original result")))
- }
-
- val exception = assertThrows {
- server.addTool("test-tool", "Duplicate Tool") {
- CallToolResult(listOf(TextContent("Duplicate result")))
- }
- }
- exception.message.orEmpty() shouldContain "Tool \"test-tool\" is already registered"
-
- // The original registration is intact
- val tools = client.listTools().tools
- assertEquals(1, tools.size, "Only the original tool should be registered")
- val result = client.callTool(CallToolRequest(CallToolRequestParams("test-tool")))
- assertEquals("Original result", (result.content.single() as TextContent).text)
- }
-
- @Test
- fun `addTool should succeed after removing existing tool`() = runTest {
- server.addTool("test-tool", "Original Tool") {
- CallToolResult(listOf(TextContent("Original result")))
- }
-
- server.removeTool("test-tool")
- server.addTool("test-tool", "Replacement Tool") {
- CallToolResult(listOf(TextContent("Replacement result")))
- }
-
- assertEquals(1, client.listTools().tools.size, "Re-adding after removal should not duplicate the tool")
- val result = client.callTool(CallToolRequest(CallToolRequestParams("test-tool")))
- assertEquals("Replacement result", (result.content.single() as TextContent).text)
- }
-
@Test
fun `removeTool should throw when tools capability is not supported`() = runTest {
var toolListChangedNotificationReceived = false
diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt
index 4d798ce..ea246db 100644
--- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt
+++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt
@@ -6,6 +6,7 @@ import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
import io.modelcontextprotocol.kotlin.sdk.client.Client
import io.modelcontextprotocol.kotlin.sdk.server.Server
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
+import io.modelcontextprotocol.kotlin.sdk.server.ServerSession
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequest
import io.modelcontextprotocol.kotlin.sdk.types.InitializeResult
@@ -15,6 +16,9 @@ import io.modelcontextprotocol.kotlin.sdk.types.ListResourcesResult
import io.modelcontextprotocol.kotlin.sdk.types.Method
import io.modelcontextprotocol.kotlin.sdk.types.Resource
import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.joinAll
+import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.test.Test
@@ -39,9 +43,18 @@ class ChannelTransportTest {
clientInfo = Implementation(name = "test client", version = "1.0"),
)
- val serverSession = server.createSession(serverTransport)
- client.connect(clientTransport)
+ val serverSessionResult = CompletableDeferred()
+ listOf(
+ launch {
+ client.connect(clientTransport)
+ },
+ launch {
+ serverSessionResult.complete(server.createSession(serverTransport))
+ },
+ ).joinAll()
+
+ val serverSession = serverSessionResult.await()
serverSession.setRequestHandler(Method.Defined.Initialize) { _, _ ->
InitializeResult(
protocolVersion = LATEST_PROTOCOL_VERSION,
diff --git a/integration-test/src/jvmTest/typescript/package-lock.json b/integration-test/src/jvmTest/typescript/package-lock.json
index ffb53fc..ca30f65 100644
--- a/integration-test/src/jvmTest/typescript/package-lock.json
+++ b/integration-test/src/jvmTest/typescript/package-lock.json
@@ -8,23 +8,23 @@
"name": "mcp-typescript-test-sdk",
"version": "1.0.0",
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.30.0",
+ "@modelcontextprotocol/sdk": "^1.28.0",
"express": "^5.2.1",
- "zod": "^4.6.2"
+ "zod": "^4.3.6"
},
"devDependencies": {
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
- "@types/node": "^26.5.1",
+ "@types/node": "^25.5.0",
"cors": "^2.8.6",
- "tsx": "^4.23.13",
- "typescript": "^7.0.2"
+ "tsx": "^4.21.0",
+ "typescript": "^6.0.2"
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
- "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
+ "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
@@ -39,9 +39,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
- "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
+ "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
@@ -56,9 +56,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
- "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
+ "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
@@ -73,9 +73,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
- "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
+ "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
@@ -90,9 +90,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
- "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
+ "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
@@ -107,9 +107,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
- "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
+ "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
@@ -124,9 +124,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
- "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
@@ -141,9 +141,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
- "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
+ "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
@@ -158,9 +158,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
- "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
+ "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
@@ -175,9 +175,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
- "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
+ "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
@@ -192,9 +192,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
- "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
+ "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
@@ -209,9 +209,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
- "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
+ "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
@@ -226,9 +226,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
- "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
+ "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
@@ -243,9 +243,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
- "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
+ "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
@@ -260,9 +260,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
- "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
+ "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
@@ -277,9 +277,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
- "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
+ "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
@@ -294,9 +294,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
- "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
+ "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
@@ -311,9 +311,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
- "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"cpu": [
"arm64"
],
@@ -328,9 +328,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
- "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
@@ -345,9 +345,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
- "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
+ "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"cpu": [
"arm64"
],
@@ -362,9 +362,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
- "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
+ "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
@@ -379,9 +379,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
- "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
+ "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
"cpu": [
"arm64"
],
@@ -396,9 +396,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
- "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
+ "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
@@ -413,9 +413,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
- "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
+ "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
@@ -430,9 +430,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
- "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
+ "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
@@ -447,9 +447,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
- "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
+ "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
@@ -464,24 +464,24 @@
}
},
"node_modules/@hono/node-server": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.0.tgz",
- "integrity": "sha512-XovyyCCnBzW+zKu+z/zq8hwNs4KOR5rEMAOxo2f40Q5xoOI37IMm6MIg2COOUtUApo0i6850MTBKH2u4QLGIqg==",
+ "version": "1.19.11",
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz",
+ "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==",
"license": "MIT",
"engines": {
- "node": ">=20"
+ "node": ">=18.14.1"
},
"peerDependencies": {
"hono": "^4"
}
},
"node_modules/@modelcontextprotocol/sdk": {
- "version": "1.30.0",
- "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz",
- "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==",
+ "version": "1.28.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.28.0.tgz",
+ "integrity": "sha512-gmloF+i+flI8ouQK7MWW4mOwuMh4RePBuPFAEPC6+pdqyWOUMDOixb6qZ69owLJpz6XmyllCouc4t8YWO+E2Nw==",
"license": "MIT",
"dependencies": {
- "@hono/node-server": "^1.19.9 || ^2.0.5",
+ "@hono/node-server": "^1.19.9",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"content-type": "^1.0.5",
@@ -579,13 +579,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "26.5.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-26.5.1.tgz",
- "integrity": "sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==",
+ "version": "25.5.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
+ "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~8.9.0"
+ "undici-types": "~7.18.0"
}
},
"node_modules/@types/qs": {
@@ -623,346 +623,6 @@
"@types/node": "*"
}
},
- "node_modules/@typescript/typescript-aix-ppc64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
- "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-darwin-arm64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
- "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-darwin-x64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
- "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-freebsd-arm64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
- "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-freebsd-x64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
- "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-arm": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
- "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-arm64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
- "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-loong64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
- "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-mips64el": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
- "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-ppc64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
- "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-riscv64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
- "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-s390x": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
- "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-linux-x64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
- "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-netbsd-arm64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
- "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-netbsd-x64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
- "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-openbsd-arm64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
- "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-openbsd-x64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
- "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-sunos-x64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
- "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-win32-arm64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
- "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
- "node_modules/@typescript/typescript-win32-x64": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
- "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=16.20.0"
- }
- },
"node_modules/accepts": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
@@ -1010,20 +670,20 @@
}
},
"node_modules/body-parser": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
- "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
+ "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
"license": "MIT",
"dependencies": {
"bytes": "^3.1.2",
- "content-type": "^2.0.0",
+ "content-type": "^1.0.5",
"debug": "^4.4.3",
- "http-errors": "^2.0.1",
- "iconv-lite": "^0.7.2",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.7.0",
"on-finished": "^2.4.1",
- "qs": "^6.15.2",
- "raw-body": "^3.0.2",
- "type-is": "^2.1.0"
+ "qs": "^6.14.1",
+ "raw-body": "^3.0.1",
+ "type-is": "^2.0.1"
},
"engines": {
"node": ">=18"
@@ -1033,19 +693,6 @@
"url": "https://opencollective.com/express"
}
},
- "node_modules/body-parser/node_modules/content-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
- "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -1229,9 +876,9 @@
}
},
"node_modules/es-object-atoms": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
- "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -1241,9 +888,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
- "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
+ "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1254,32 +901,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.28.1",
- "@esbuild/android-arm": "0.28.1",
- "@esbuild/android-arm64": "0.28.1",
- "@esbuild/android-x64": "0.28.1",
- "@esbuild/darwin-arm64": "0.28.1",
- "@esbuild/darwin-x64": "0.28.1",
- "@esbuild/freebsd-arm64": "0.28.1",
- "@esbuild/freebsd-x64": "0.28.1",
- "@esbuild/linux-arm": "0.28.1",
- "@esbuild/linux-arm64": "0.28.1",
- "@esbuild/linux-ia32": "0.28.1",
- "@esbuild/linux-loong64": "0.28.1",
- "@esbuild/linux-mips64el": "0.28.1",
- "@esbuild/linux-ppc64": "0.28.1",
- "@esbuild/linux-riscv64": "0.28.1",
- "@esbuild/linux-s390x": "0.28.1",
- "@esbuild/linux-x64": "0.28.1",
- "@esbuild/netbsd-arm64": "0.28.1",
- "@esbuild/netbsd-x64": "0.28.1",
- "@esbuild/openbsd-arm64": "0.28.1",
- "@esbuild/openbsd-x64": "0.28.1",
- "@esbuild/openharmony-arm64": "0.28.1",
- "@esbuild/sunos-x64": "0.28.1",
- "@esbuild/win32-arm64": "0.28.1",
- "@esbuild/win32-ia32": "0.28.1",
- "@esbuild/win32-x64": "0.28.1"
+ "@esbuild/aix-ppc64": "0.27.2",
+ "@esbuild/android-arm": "0.27.2",
+ "@esbuild/android-arm64": "0.27.2",
+ "@esbuild/android-x64": "0.27.2",
+ "@esbuild/darwin-arm64": "0.27.2",
+ "@esbuild/darwin-x64": "0.27.2",
+ "@esbuild/freebsd-arm64": "0.27.2",
+ "@esbuild/freebsd-x64": "0.27.2",
+ "@esbuild/linux-arm": "0.27.2",
+ "@esbuild/linux-arm64": "0.27.2",
+ "@esbuild/linux-ia32": "0.27.2",
+ "@esbuild/linux-loong64": "0.27.2",
+ "@esbuild/linux-mips64el": "0.27.2",
+ "@esbuild/linux-ppc64": "0.27.2",
+ "@esbuild/linux-riscv64": "0.27.2",
+ "@esbuild/linux-s390x": "0.27.2",
+ "@esbuild/linux-x64": "0.27.2",
+ "@esbuild/netbsd-arm64": "0.27.2",
+ "@esbuild/netbsd-x64": "0.27.2",
+ "@esbuild/openbsd-arm64": "0.27.2",
+ "@esbuild/openbsd-x64": "0.27.2",
+ "@esbuild/openharmony-arm64": "0.27.2",
+ "@esbuild/sunos-x64": "0.27.2",
+ "@esbuild/win32-arm64": "0.27.2",
+ "@esbuild/win32-ia32": "0.27.2",
+ "@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/escape-html": {
@@ -1362,13 +1009,12 @@
}
},
"node_modules/express-rate-limit": {
- "version": "8.6.0",
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.0.tgz",
- "integrity": "sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA==",
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz",
+ "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==",
"license": "MIT",
"dependencies": {
- "debug": "^4.4.3",
- "ip-address": "^10.2.0"
+ "ip-address": "10.1.0"
},
"engines": {
"node": ">= 16"
@@ -1387,9 +1033,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
- "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"funding": [
{
"type": "github",
@@ -1501,6 +1147,19 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-tsconfig": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -1526,9 +1185,9 @@
}
},
"node_modules/hasown": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
- "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -1538,9 +1197,9 @@
}
},
"node_modules/hono": {
- "version": "4.13.7",
- "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz",
- "integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==",
+ "version": "4.12.9",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz",
+ "integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==",
"license": "MIT",
"engines": {
"node": ">=16.9.0"
@@ -1589,9 +1248,9 @@
"license": "ISC"
},
"node_modules/ip-address": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz",
- "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+ "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"engines": {
"node": ">= 12"
@@ -1802,13 +1461,12 @@
}
},
"node_modules/qs": {
- "version": "6.16.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
- "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
- "es-define-property": "^1.0.1",
- "side-channel": "^1.1.1"
+ "side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
@@ -1850,6 +1508,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
@@ -1945,14 +1613,14 @@
}
},
"node_modules/side-channel": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
- "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.4",
- "side-channel-list": "^1.0.1",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
@@ -1964,13 +1632,13 @@
}
},
"node_modules/side-channel-list": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
- "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.4"
+ "object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
@@ -2035,13 +1703,14 @@
}
},
"node_modules/tsx": {
- "version": "4.23.13",
- "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.13.tgz",
- "integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==",
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "esbuild": "~0.28.0"
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
},
"bin": {
"tsx": "dist/cli.mjs"
@@ -2054,75 +1723,37 @@
}
},
"node_modules/type-is": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
- "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"license": "MIT",
"dependencies": {
- "content-type": "^2.0.0",
+ "content-type": "^1.0.5",
"media-typer": "^1.1.0",
"mime-types": "^3.0.0"
},
"engines": {
- "node": ">= 18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/type-is/node_modules/content-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
- "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
+ "node": ">= 0.6"
}
},
"node_modules/typescript": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
- "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
+ "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
- "tsc": "bin/tsc"
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
},
"engines": {
- "node": ">=16.20.0"
- },
- "optionalDependencies": {
- "@typescript/typescript-aix-ppc64": "7.0.2",
- "@typescript/typescript-darwin-arm64": "7.0.2",
- "@typescript/typescript-darwin-x64": "7.0.2",
- "@typescript/typescript-freebsd-arm64": "7.0.2",
- "@typescript/typescript-freebsd-x64": "7.0.2",
- "@typescript/typescript-linux-arm": "7.0.2",
- "@typescript/typescript-linux-arm64": "7.0.2",
- "@typescript/typescript-linux-loong64": "7.0.2",
- "@typescript/typescript-linux-mips64el": "7.0.2",
- "@typescript/typescript-linux-ppc64": "7.0.2",
- "@typescript/typescript-linux-riscv64": "7.0.2",
- "@typescript/typescript-linux-s390x": "7.0.2",
- "@typescript/typescript-linux-x64": "7.0.2",
- "@typescript/typescript-netbsd-arm64": "7.0.2",
- "@typescript/typescript-netbsd-x64": "7.0.2",
- "@typescript/typescript-openbsd-arm64": "7.0.2",
- "@typescript/typescript-openbsd-x64": "7.0.2",
- "@typescript/typescript-sunos-x64": "7.0.2",
- "@typescript/typescript-win32-arm64": "7.0.2",
- "@typescript/typescript-win32-x64": "7.0.2"
+ "node": ">=14.17"
}
},
"node_modules/undici-types": {
- "version": "8.9.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz",
- "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==",
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"license": "MIT"
},
@@ -2166,9 +1797,9 @@
"license": "ISC"
},
"node_modules/zod": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.2.tgz",
- "integrity": "sha512-lh5RCAGFa1Cm2hjtNwLQhSs/AsqdWnTQaBER9fEwN/88pSh7KOtJavtBx/0VlkN/uFd61SwYmljLMDAsHlvzBQ==",
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
diff --git a/integration-test/src/jvmTest/typescript/package.json b/integration-test/src/jvmTest/typescript/package.json
index b0147e4..0a43cd2 100644
--- a/integration-test/src/jvmTest/typescript/package.json
+++ b/integration-test/src/jvmTest/typescript/package.json
@@ -9,16 +9,16 @@
"server:http": "tsx server/http-server.ts"
},
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.30.0",
+ "@modelcontextprotocol/sdk": "^1.28.0",
"express": "^5.2.1",
- "zod": "^4.6.2"
+ "zod": "^4.3.6"
},
"devDependencies": {
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
- "@types/node": "^26.5.1",
+ "@types/node": "^25.5.0",
"cors": "^2.8.6",
- "tsx": "^4.23.13",
- "typescript": "^7.0.2"
+ "tsx": "^4.21.0",
+ "typescript": "^6.0.2"
}
}
diff --git a/kotlin-sdk-client/api/kotlin-sdk-client.api b/kotlin-sdk-client/api/kotlin-sdk-client.api
index d856f5b..da0e752 100644
--- a/kotlin-sdk-client/api/kotlin-sdk-client.api
+++ b/kotlin-sdk-client/api/kotlin-sdk-client.api
@@ -34,7 +34,6 @@ public class io/modelcontextprotocol/kotlin/sdk/client/Client : io/modelcontextp
public final fun removeRoot (Ljava/lang/String;)Z
public final fun removeRoots (Ljava/util/List;)I
public final fun sendRootsListChanged (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public final fun setElicitationCompleteHandler (Lkotlin/jvm/functions/Function1;)V
public final fun setElicitationHandler (Lkotlin/jvm/functions/Function1;)V
public final fun setLoggingLevel (Lio/modelcontextprotocol/kotlin/sdk/types/LoggingLevel;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun setLoggingLevel$default (Lio/modelcontextprotocol/kotlin/sdk/client/Client;Lio/modelcontextprotocol/kotlin/sdk/types/LoggingLevel;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
@@ -42,7 +41,6 @@ public class io/modelcontextprotocol/kotlin/sdk/client/Client : io/modelcontextp
public static synthetic fun subscribeResource$default (Lio/modelcontextprotocol/kotlin/sdk/client/Client;Lio/modelcontextprotocol/kotlin/sdk/types/SubscribeRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun unsubscribeResource (Lio/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun unsubscribeResource$default (Lio/modelcontextprotocol/kotlin/sdk/client/Client;Lio/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
- protected fun wrapRequestHandler (Lio/modelcontextprotocol/kotlin/sdk/types/Method;Lkotlin/jvm/functions/Function3;)Lkotlin/jvm/functions/Function3;
}
public final class io/modelcontextprotocol/kotlin/sdk/client/ClientKt {
@@ -52,8 +50,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/client/ClientKt {
public final class io/modelcontextprotocol/kotlin/sdk/client/ClientOptions : io/modelcontextprotocol/kotlin/sdk/shared/ProtocolOptions {
public fun ()V
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;ZLkotlin/coroutines/CoroutineContext;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;ZLkotlin/coroutines/CoroutineContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;Z)V
+ public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
}
@@ -102,8 +100,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/client/StdioClientTranspor
}
public final class io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTransport : io/modelcontextprotocol/kotlin/sdk/shared/AbstractClientTransport {
- public fun (Lio/ktor/client/HttpClient;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/client/ReconnectionOptions;ILkotlin/jvm/functions/Function1;)V
- public synthetic fun (Lio/ktor/client/HttpClient;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/client/ReconnectionOptions;ILkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Lio/ktor/client/HttpClient;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/client/ReconnectionOptions;Lkotlin/jvm/functions/Function1;)V
+ public synthetic fun (Lio/ktor/client/HttpClient;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/client/ReconnectionOptions;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getProtocolVersion ()Ljava/lang/String;
diff --git a/kotlin-sdk-client/build.gradle.kts b/kotlin-sdk-client/build.gradle.kts
index fbb1300..f50266c 100644
--- a/kotlin-sdk-client/build.gradle.kts
+++ b/kotlin-sdk-client/build.gradle.kts
@@ -4,7 +4,6 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
id("mcp.multiplatform")
- id("mcp.detekt")
id("mcp.publishing")
id("mcp.dokka")
alias(libs.plugins.kotlinx.binary.compatibility.validator)
@@ -15,8 +14,10 @@ kotlin {
iosArm64()
iosX64()
iosSimulatorArm64()
+ watchosX64()
watchosArm64()
watchosSimulatorArm64()
+ tvosX64()
tvosArm64()
tvosSimulatorArm64()
js {
@@ -55,6 +56,7 @@ kotlin {
implementation(libs.ktor.client.apache5)
implementation(libs.mockk)
implementation(libs.junit.jupiter.params)
+ implementation(libs.mokksy)
implementation(dependencies.platform(libs.netty.bom))
runtimeOnly(libs.slf4j.simple)
}
diff --git a/kotlin-sdk-client/detekt-baseline-commonMainSourceSet.xml b/kotlin-sdk-client/detekt-baseline-commonMainSourceSet.xml
new file mode 100644
index 0000000..5cddbe1
--- /dev/null
+++ b/kotlin-sdk-client/detekt-baseline-commonMainSourceSet.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ CyclomaticComplexMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize
+ LongMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize
+ MaxLineLength:StdioClientTransport.kt:StdioClientTransport$*
+ ThrowsCount:SseClientTransport.kt:SseClientTransport$private suspend fun CoroutineScope.collectMessages
+ ThrowsCount:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$@Suppress("ReturnCount", "CyclomaticComplexMethod") override suspend fun performSend
+ TooGenericExceptionCaught:Client.kt:Client$error: Throwable
+ TooGenericExceptionCaught:SseClientTransport.kt:SseClientTransport$e: Throwable
+ TooGenericExceptionCaught:StdioClientTransport.kt:StdioClientTransport$e: Throwable
+
+
diff --git a/kotlin-sdk-client/detekt-baseline-commonTestSourceSet.xml b/kotlin-sdk-client/detekt-baseline-commonTestSourceSet.xml
new file mode 100644
index 0000000..cab4322
--- /dev/null
+++ b/kotlin-sdk-client/detekt-baseline-commonTestSourceSet.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}"""
+ MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/resumed","params":{"fromToken":"$lastEventIdSent"}}"""
+
+
diff --git a/kotlin-sdk-client/detekt-baseline-main.xml b/kotlin-sdk-client/detekt-baseline-main.xml
index c34e75c..8411ab0 100644
--- a/kotlin-sdk-client/detekt-baseline-main.xml
+++ b/kotlin-sdk-client/detekt-baseline-main.xml
@@ -2,6 +2,18 @@
- LoopWithTooManyJumpStatements:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$while
+ CyclomaticComplexMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize
+ InjectDispatcher:StdioClientTransport.kt:StdioClientTransport$Default
+ InjectDispatcher:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$Default
+ LongMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize
+ MaxLineLength:StdioClientTransport.kt:StdioClientTransport$*
+ NoNameShadowing:StdioClientTransport.kt:StdioClientTransport$source
+ ThrowsCount:SseClientTransport.kt:SseClientTransport$private suspend fun CoroutineScope.collectMessages
+ ThrowsCount:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$@Suppress("ReturnCount", "CyclomaticComplexMethod") override suspend fun performSend
+ TooGenericExceptionCaught:Client.kt:Client$error: Throwable
+ TooGenericExceptionCaught:SseClientTransport.kt:SseClientTransport$e: Throwable
+ TooGenericExceptionCaught:StdioClientTransport.kt:StdioClientTransport$e: Throwable
+ UseCheckOrError:Client.kt:Client$throw IllegalStateException( "Server does not support resource subscriptions (required for $method)", )
+ UseCheckOrError:Client.kt:Client$throw IllegalStateException("Client does not support roots capability.")
diff --git a/kotlin-sdk-client/detekt-baseline-test.xml b/kotlin-sdk-client/detekt-baseline-test.xml
new file mode 100644
index 0000000..dd6adfa
--- /dev/null
+++ b/kotlin-sdk-client/detekt-baseline-test.xml
@@ -0,0 +1,14 @@
+
+
+
+
+ AbstractClassCanBeConcreteClass:AbstractStreamableHttpClientTest.kt:AbstractStreamableHttpClientTest$AbstractStreamableHttpClientTest
+ ForbiddenComment:StreamableHttpClientTest.kt:StreamableHttpClientTest$// TODO: how to get notifications via Client API?
+ LongParameterList:MockMcp.kt:MockMcp$fun handleJSONRPCRequest
+ LongParameterList:MockMcp.kt:MockMcp$fun handleWithResult
+ LongParameterList:MockMcp.kt:MockMcp$fun onInitialize
+ MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}"""
+ MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/resumed","params":{"fromToken":"$lastEventIdSent"}}"""
+ UseOrEmpty:ClientMetaParameterTest.kt:ClientMetaParameterTest$exception.message ?: ""
+
+
diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt
index 3352104..34b9a5b 100644
--- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt
+++ b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt
@@ -4,7 +4,6 @@ import io.github.oshai.kotlinlogging.KotlinLogging
import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi
import io.modelcontextprotocol.kotlin.sdk.shared.Protocol
import io.modelcontextprotocol.kotlin.sdk.shared.ProtocolOptions
-import io.modelcontextprotocol.kotlin.sdk.shared.RequestHandlerExtra
import io.modelcontextprotocol.kotlin.sdk.shared.RequestOptions
import io.modelcontextprotocol.kotlin.sdk.shared.Transport
import io.modelcontextprotocol.kotlin.sdk.types.BooleanSchema
@@ -14,12 +13,10 @@ import io.modelcontextprotocol.kotlin.sdk.types.CallToolResult
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
import io.modelcontextprotocol.kotlin.sdk.types.CompleteRequest
import io.modelcontextprotocol.kotlin.sdk.types.CompleteResult
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
import io.modelcontextprotocol.kotlin.sdk.types.DoubleSchema
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequest
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestFormParams
import io.modelcontextprotocol.kotlin.sdk.types.ElicitResult
-import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotification
import io.modelcontextprotocol.kotlin.sdk.types.EmptyResult
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptResult
@@ -48,9 +45,7 @@ import io.modelcontextprotocol.kotlin.sdk.types.PingRequest
import io.modelcontextprotocol.kotlin.sdk.types.PrimitiveSchemaDefinition
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceRequest
import io.modelcontextprotocol.kotlin.sdk.types.ReadResourceResult
-import io.modelcontextprotocol.kotlin.sdk.types.Request
import io.modelcontextprotocol.kotlin.sdk.types.RequestMeta
-import io.modelcontextprotocol.kotlin.sdk.types.RequestResult
import io.modelcontextprotocol.kotlin.sdk.types.Root
import io.modelcontextprotocol.kotlin.sdk.types.RootsListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.types.SUPPORTED_PROTOCOL_VERSIONS
@@ -64,7 +59,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.TitledSingleSelectEnumSchema
import io.modelcontextprotocol.kotlin.sdk.types.UnsubscribeRequest
import io.modelcontextprotocol.kotlin.sdk.types.UntitledMultiSelectEnumSchema
import io.modelcontextprotocol.kotlin.sdk.types.UntitledSingleSelectEnumSchema
-import io.modelcontextprotocol.kotlin.sdk.types.supportsUrl
import io.modelcontextprotocol.kotlin.sdk.types.toJson
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.getAndUpdate
@@ -72,14 +66,11 @@ import kotlinx.atomicfu.update
import kotlinx.collections.immutable.minus
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toPersistentSet
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonArray
-import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.cancellation.CancellationException
private val logger = KotlinLogging.logger {}
@@ -88,17 +79,12 @@ private val logger = KotlinLogging.logger {}
* Options for configuring the MCP client.
*
* @property capabilities The capabilities this client supports.
- * @param enforceStrictCapabilities Whether to strictly enforce capabilities when interacting with the server.
- * @param handlerCoroutineContext Coroutine context for inbound handlers. See [ProtocolOptions.handlerCoroutineContext].
+ * @property enforceStrictCapabilities Whether to strictly enforce capabilities when interacting with the server.
*/
public class ClientOptions(
public val capabilities: ClientCapabilities = ClientCapabilities(),
enforceStrictCapabilities: Boolean = true,
- handlerCoroutineContext: CoroutineContext = Dispatchers.Default,
-) : ProtocolOptions(
- enforceStrictCapabilities = enforceStrictCapabilities,
- handlerCoroutineContext = handlerCoroutineContext,
-)
+) : ProtocolOptions(enforceStrictCapabilities = enforceStrictCapabilities)
/**
* Initializes and connects an MCP client using the provided clientInfo [Implementation], client options,
@@ -135,6 +121,7 @@ public suspend fun mcpClient(
* @param clientInfo Information about the client implementation (name, version).
* @param options Configuration options for this client.
*/
+@Suppress("TooManyFunctions")
public open class Client(private val clientInfo: Implementation, options: ClientOptions = ClientOptions()) :
Protocol(options) {
@@ -184,7 +171,6 @@ public open class Client(private val clientInfo: Implementation, options: Client
"prompts" -> caps?.prompts != null
"resources" -> caps?.resources != null
"tools" -> caps?.tools != null
- "tasks" -> caps?.tasks != null
else -> true
}
@@ -223,7 +209,6 @@ public open class Client(private val clientInfo: Implementation, options: Client
serverInstructions = result.instructions
notification(InitializedNotification())
- enableConcurrentDispatch()
} catch (error: Throwable) {
logger.error(error) { "Failed to initialize client: ${error.message}" }
close()
@@ -250,15 +235,10 @@ public open class Client(private val clientInfo: Implementation, options: Client
Method.Defined.PromptsGet,
Method.Defined.PromptsList,
+ Method.Defined.CompletionComplete,
-> {
checkNotNull(serverCapabilities?.prompts) {
- "Server does not support prompts (required for $method)"
- }
- }
-
- Method.Defined.CompletionComplete -> {
- checkNotNull(serverCapabilities?.completions) {
- "Server does not support completions (required for $method)"
+ throw IllegalStateException("Server does not support prompts (required for $method)")
}
}
@@ -271,10 +251,10 @@ public open class Client(private val clientInfo: Implementation, options: Client
val resCaps = serverCapabilities?.resources
?: error("Server does not support resources (required for $method)")
- if (method == Method.Defined.ResourcesSubscribe) {
- check(resCaps.subscribe == true) {
- "Server does not support resource subscriptions (required for $method)"
- }
+ if (method == Method.Defined.ResourcesSubscribe && resCaps.subscribe != true) {
+ throw IllegalStateException(
+ "Server does not support resource subscriptions (required for $method)",
+ )
}
}
@@ -284,12 +264,6 @@ public open class Client(private val clientInfo: Implementation, options: Client
}
}
- Method.Defined.TasksGet,
- Method.Defined.TasksResult,
- Method.Defined.TasksList,
- Method.Defined.TasksCancel,
- -> assertTasksCapabilityForMethod(method)
-
Method.Defined.Initialize, Method.Defined.Ping -> {
// No specific capability required
}
@@ -300,24 +274,6 @@ public open class Client(private val clientInfo: Implementation, options: Client
}
}
- private fun assertTasksCapabilityForMethod(method: Method) {
- val tasks = serverCapabilities?.tasks
- ?: error("Server does not support tasks (required for $method)")
- when (method) {
- Method.Defined.TasksList -> checkNotNull(tasks.list) {
- "Server does not support listing tasks (required for $method)"
- }
-
- Method.Defined.TasksCancel -> checkNotNull(tasks.cancel) {
- "Server does not support cancelling tasks (required for $method)"
- }
-
- else -> {
- // TasksGet, TasksResult: base tasks capability suffices.
- }
- }
- }
-
override fun assertNotificationCapability(method: Method) {
when (method) {
Method.Defined.NotificationsRootsListChanged -> {
@@ -326,12 +282,6 @@ public open class Client(private val clientInfo: Implementation, options: Client
}
}
- Method.Defined.NotificationsTasksStatus -> {
- checkNotNull(capabilities.tasks) {
- "Client does not support tasks (required for $method)"
- }
- }
-
Method.Defined.NotificationsInitialized,
Method.Defined.NotificationsCancelled,
Method.Defined.NotificationsProgress,
@@ -373,26 +323,6 @@ public open class Client(private val clientInfo: Implementation, options: Client
}
}
- /**
- * Wraps incoming-request handlers with SEP-1577 client-side enforcement.
- *
- * For `sampling/createMessage`: if the incoming request carries `tools` or
- * `toolChoice` but this client did not advertise [ClientCapabilities.Sampling.tools],
- * the wrapper throws an [McpException] with JSON-RPC error code `InvalidParams`
- * before the user-supplied handler runs. Matches the TypeScript SDK wrapper in
- * `Client.setRequestHandler`.
- */
- override fun wrapRequestHandler(
- method: Method,
- block: suspend (T, RequestHandlerExtra) -> RequestResult?,
- ): suspend (T, RequestHandlerExtra) -> RequestResult? {
- if (method != Method.Defined.SamplingCreateMessage) return block
- return { request, extra ->
- (request as? CreateMessageRequest)?.let { validateSamplingToolsCapability(it, capabilities) }
- block(request, extra)
- }
- }
-
/**
* Sends a ping request to the server to check connectivity.
*
@@ -407,7 +337,7 @@ public open class Client(private val clientInfo: Implementation, options: Client
* @param params The completion request parameters.
* @param options Optional request options.
* @return The completion result returned by the server, or `null` if none.
- * @throws IllegalStateException If the server does not support completions.
+ * @throws IllegalStateException If the server does not support prompts or completion.
*/
public suspend fun complete(params: CompleteRequest, options: RequestOptions? = null): CompleteResult =
request(params, options)
@@ -602,8 +532,9 @@ public open class Client(private val clientInfo: Implementation, options: Client
* @throws IllegalStateException If the client does not support roots.
*/
public fun removeRoot(uri: String): Boolean {
- checkNotNull(capabilities.roots) {
- "Client does not support roots capability."
+ if (capabilities.roots == null) {
+ logger.error { "Failed to remove root '$uri': Client does not support roots capability" }
+ throw IllegalStateException("Client does not support roots capability.")
}
logger.info { "Removing root: $uri" }
val oldMap = roots.getAndUpdate { current -> current.remove(uri) }
@@ -659,18 +590,9 @@ public open class Client(private val clientInfo: Implementation, options: Client
/**
* Sets the elicitation handler.
*
- * The handler receives both form-mode ([ElicitRequestFormParams]) and URL-mode
- * ([io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestURLParams]) requests;
- * branch on `request.params` to tell them apart. For URL mode,
- * the host application must obtain explicit user consent and display the target domain before
- * navigating — the SDK never opens or validates the URL — and should return
- * [ElicitResult.Action.Decline] or [ElicitResult.Action.Cancel] when it cannot or will not proceed.
- * A URL-mode [ElicitResult.Action.Accept] only signals consent; the outcome arrives out-of-band via
- * [setElicitationCompleteHandler].
- *
- * When a form-mode handler returns [ElicitResult.Action.Accept], any properties missing from
+ * When the handler returns [ElicitResult.Action.Accept], any properties missing from
* [ElicitResult.content] are automatically populated with default values defined in the
- * elicitation schema. URL-mode responses carry no content.
+ * elicitation schema.
*
* @param handler The elicitation handler.
* @throws IllegalStateException if the client does not support elicitation.
@@ -688,37 +610,9 @@ public open class Client(private val clientInfo: Implementation, options: Client
}
}
- /**
- * Sets the handler invoked when the server reports that a URL-mode elicitation has completed.
- *
- * The handler is called for every `notifications/elicitation/complete` notification. Because the
- * server only sends this for an out-of-band (URL-mode) interaction, the client must support url-mode
- * elicitation. The client is responsible for correlating the notification's `elicitationId` with a
- * pending elicitation, ignoring unknown or already-completed identifiers, and providing a manual way
- * to continue if a notification never arrives.
- *
- * @param handler Invoked with each completion notification.
- * @throws IllegalStateException if the client does not support url-mode elicitation.
- */
- public fun setElicitationCompleteHandler(handler: (ElicitationCompleteNotification) -> Unit) {
- check(capabilities.elicitation.supportsUrl) {
- logger.error {
- "Failed to set elicitation-complete handler: client does not support url-mode elicitation"
- }
- "Client does not support url-mode elicitation."
- }
- logger.info { "Setting the elicitation-complete handler" }
-
- setNotificationHandler(
- Method.Defined.NotificationsElicitationComplete,
- ) { notification ->
- handler(notification)
- CompletableDeferred(Unit)
- }
- }
-
// --- Internal Handlers ---
+ @Suppress("ReturnCount")
private fun applyElicitationDefaults(request: ElicitRequest, result: ElicitResult): ElicitResult {
if (result.action != ElicitResult.Action.Accept) return result
val formParams = request.params as? ElicitRequestFormParams ?: return result
diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SamplingValidation.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SamplingValidation.kt
deleted file mode 100644
index 1290cb0..0000000
--- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SamplingValidation.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.client
-
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
-import io.modelcontextprotocol.kotlin.sdk.types.McpException
-import io.modelcontextprotocol.kotlin.sdk.types.RPCError
-
-/**
- * Validates SEP-1577 client-side enforcement: an incoming `sampling/createMessage`
- * request that carries `tools` or `toolChoice` requires the client to have advertised
- * the `sampling.tools` sub-capability. When the capability is missing, throws an
- * [McpException] with JSON-RPC error code `InvalidParams`, matching TypeScript SDK
- * client enforcement.
- */
-internal fun validateSamplingToolsCapability(request: CreateMessageRequest, capabilities: ClientCapabilities) {
- val params = request.params
- if (params.tools == null && params.toolChoice == null) return
- if (capabilities.sampling?.tools != null) return
- val field = if (params.tools != null) "tools" else "toolChoice"
- throw McpException(
- code = RPCError.ErrorCode.INVALID_PARAMS,
- message = "Client does not support sampling with tools but request contains $field parameter",
- )
-}
diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SseClientTransport.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SseClientTransport.kt
index 0295eb9..3c9d88b 100644
--- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SseClientTransport.kt
+++ b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SseClientTransport.kt
@@ -24,12 +24,11 @@ import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
-import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import kotlinx.serialization.SerializationException
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.time.Duration
@@ -136,7 +135,6 @@ public class SseClientTransport(
throw e
} finally {
closeResources()
- invokeOnCloseCallback()
}
}
@@ -173,15 +171,13 @@ public class SseClientTransport(
}
override suspend fun closeResources() {
- withContext(NonCancellable) {
- job?.cancel()
- try {
- if (::session.isInitialized) session.cancel()
- if (::scope.isInitialized) scope.cancel()
- endpoint.cancel()
- } catch (e: Throwable) {
- _onError(e)
- }
+ job?.cancelAndJoin()
+ try {
+ if (::session.isInitialized) session.cancel()
+ if (::scope.isInitialized) scope.cancel()
+ endpoint.cancel()
+ } catch (e: Throwable) {
+ _onError(e)
}
}
}
diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransport.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransport.kt
index 5e990ee..12529cc 100644
--- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransport.kt
+++ b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StdioClientTransport.kt
@@ -21,7 +21,6 @@ import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
-import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelAndJoin
@@ -32,7 +31,6 @@ import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import kotlinx.io.Buffer
import kotlinx.io.IOException
@@ -227,6 +225,7 @@ public class StdioClientTransport @JvmOverloads public constructor(
}
override suspend fun performSend(message: JSONRPCMessage, options: TransportSendOptions?) {
+ @Suppress("SwallowedException")
try {
sendChannel.send(message)
} catch (e: CancellationException) {
@@ -242,20 +241,8 @@ public class StdioClientTransport @JvmOverloads public constructor(
}
override suspend fun closeResources() {
- withContext(NonCancellable) {
- scope.stopProcessing("Closed")
- closeReadSources()
- scope.coroutineContext[Job]?.join() // Wait for all coroutines to complete
- }
- }
-
- private fun closeReadSources() {
- runCatching { input.close() }
- .onFailure { logger.debug(it) { "Error closing stdin source" } }
- error?.let { source ->
- runCatching { source.close() }
- .onFailure { logger.debug(it) { "Error closing stderr source" } }
- }
+ scope.stopProcessing("Closed")
+ scope.coroutineContext[Job]?.join() // Wait for all coroutines to complete
}
private fun sendOutboundMessage(message: JSONRPCMessage, sink: Sink, mainScope: CoroutineScope) {
@@ -277,8 +264,6 @@ public class StdioClientTransport @JvmOverloads public constructor(
private suspend fun handleJSONRPCMessage(msg: JSONRPCMessage) {
try {
_onMessage.invoke(msg)
- } catch (e: CancellationException) {
- throw e
} catch (e: Throwable) {
logger.error(e) { "Error processing message." }
runCatching { _onError.invoke(e) }
@@ -299,7 +284,7 @@ public class StdioClientTransport @JvmOverloads public constructor(
) {
val buffer = Buffer()
try {
- source.use {
+ source.use { source ->
while (isActive) {
val bytesRead = source.readAtMostTo(buffer, BUFFER_SIZE)
if (bytesRead == -1L) {
diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTransport.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTransport.kt
index 6df352a..999e25a 100644
--- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTransport.kt
+++ b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTransport.kt
@@ -21,10 +21,8 @@ import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import io.ktor.http.isSuccess
-import io.ktor.utils.io.charsets.TooLongLineException
import io.ktor.utils.io.readUTF8Line
import io.modelcontextprotocol.kotlin.sdk.shared.AbstractClientTransport
-import io.modelcontextprotocol.kotlin.sdk.shared.TooLongFrameException
import io.modelcontextprotocol.kotlin.sdk.shared.TransportSendOptions
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCNotification
@@ -43,10 +41,6 @@ import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
-import kotlinx.serialization.json.JsonObject
-import kotlinx.serialization.json.JsonPrimitive
-import kotlin.io.encoding.Base64
-import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.math.pow
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
@@ -55,24 +49,11 @@ import kotlin.time.Duration.Companion.seconds
private const val MCP_SESSION_ID_HEADER = "mcp-session-id"
private const val MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version"
private const val MCP_RESUMPTION_TOKEN_HEADER = "Last-Event-ID"
-private const val MCP_METHOD_HEADER = "Mcp-Method"
-private const val MCP_NAME_HEADER = "Mcp-Name"
-private const val MCP_BASE64_PREFIX = "=?base64?"
-private const val MCP_BASE64_SUFFIX = "?="
-
-/**
- * Default maximum size, in characters, of a single inline SSE event assembled from a POST response.
- *
- * Mirrors the stdio transport's 16 MiB frame cap: a server that streams `data:` lines without ever
- * terminating the event cannot grow the client's buffer without bound.
- */
-private const val DEFAULT_MAX_INLINE_SSE_EVENT_SIZE: Int = 16 * 1024 * 1024
/**
* Represents an error from the Streamable HTTP transport.
*
* @property code HTTP status code associated with the error, or `null` if unavailable
- * @param message detailed error description appended to the exception message
*/
public class StreamableHttpError(public val code: Int? = null, message: String? = null) :
Exception("Streamable HTTP error: $message")
@@ -84,32 +65,21 @@ private sealed interface ConnectResult {
}
/**
- * Client transport implementing the MCP Streamable HTTP transport specification.
+ * Client transport for Streamable HTTP: this implements the MCP Streamable HTTP transport specification.
+ * It will connect to a server using HTTP POST for sending messages and HTTP GET with Server-Sent Events
+ * for receiving messages.
*
- * Sends messages via HTTP POST and receives messages via HTTP GET with Server-Sent Events.
- * Supports automatic SSE reconnection with exponential backoff, stream resumption via the
- * `Last-Event-ID` header, and explicit session termination.
- *
- * @param client Ktor HTTP client used for all requests
- * @param url MCP endpoint URL
- * @param reconnectionOptions reconnection backoff and retry-limit settings for the SSE stream
- * @param maxInlineSseEventSize maximum size, in characters, of a single inline SSE event parsed from a
- * POST response; a server that exceeds it (including by never terminating an event) fails the send
- * with [io.modelcontextprotocol.kotlin.sdk.shared.TooLongFrameException]. Defaults to 16 MiB.
- * @param requestBuilder builder applied to every outgoing HTTP request, e.g. for adding auth headers
+ * @property sessionId session identifier assigned by the server after initialization, or `null` before connection
+ * @property protocolVersion MCP protocol version negotiated with the server, or `null` before connection
*/
+@Suppress("TooManyFunctions")
public class StreamableHttpClientTransport(
private val client: HttpClient,
private val url: String,
private val reconnectionOptions: ReconnectionOptions = ReconnectionOptions(),
- private val maxInlineSseEventSize: Int = DEFAULT_MAX_INLINE_SSE_EVENT_SIZE,
private val requestBuilder: HttpRequestBuilder.() -> Unit = {},
) : AbstractClientTransport() {
- init {
- require(maxInlineSseEventSize > 0) { "maxInlineSseEventSize must be greater than 0" }
- }
-
@Deprecated(
"Use constructor with ReconnectionOptions",
replaceWith = ReplaceWith(
@@ -124,20 +94,12 @@ public class StreamableHttpClientTransport(
url: String,
reconnectionTime: Duration?,
requestBuilder: HttpRequestBuilder.() -> Unit = {},
- ) : this(
- client,
- url,
- ReconnectionOptions(initialReconnectionDelay = reconnectionTime ?: 1.seconds),
- requestBuilder = requestBuilder,
- )
+ ) : this(client, url, ReconnectionOptions(initialReconnectionDelay = reconnectionTime ?: 1.seconds), requestBuilder)
override val logger: KLogger = KotlinLogging.logger {}
- /** Session identifier assigned by the server after initialization, or `null` before connection. */
public var sessionId: String? = null
private set
-
- /** MCP protocol version negotiated with the server, or `null` before connection. */
public var protocolVersion: String? = null
private var sseJob: Job? = null
@@ -159,6 +121,7 @@ public class StreamableHttpClientTransport(
/**
* Sends a single message with optional resumption support
*/
+ @Suppress("ReturnCount", "CyclomaticComplexMethod", "LongMethod", "TooGenericExceptionCaught", "ThrowsCount")
override suspend fun performSend(message: JSONRPCMessage, options: TransportSendOptions?) {
logger.debug { "Client sending message via POST to $url: ${McpJson.encodeToString(message)}" }
@@ -175,7 +138,6 @@ public class StreamableHttpClientTransport(
val jsonBody = McpJson.encodeToString(message)
val response = client.post(url) {
applyCommonHeaders(this)
- applyStandardPostHeaders(this, message)
headers.append(HttpHeaders.Accept, "${ContentType.Application.Json}, ${ContentType.Text.EventStream}")
contentType(ContentType.Application.Json)
setBody(jsonBody)
@@ -298,6 +260,7 @@ public class StreamableHttpClientTransport(
var attempt = 0
var needsDelay = initialServerRetryDelay != null
+ @Suppress("LoopWithTooManyJumpStatements")
while (isActive) {
// Delay before (re)connection: skip only for first fresh SSE connection
if (needsDelay) {
@@ -333,6 +296,7 @@ public class StreamableHttpClientTransport(
}
}
+ @Suppress("TooGenericExceptionCaught")
private suspend fun connectSse(lastEventId: String?): ConnectResult {
logger.debug { "Client attempting to start SSE session at url: $url" }
return try {
@@ -399,36 +363,7 @@ public class StreamableHttpClientTransport(
}
}
- private fun applyStandardPostHeaders(builder: HttpRequestBuilder, message: JSONRPCMessage) {
- val (method, params) = when (message) {
- is JSONRPCRequest -> message.method to message.params
- is JSONRPCNotification -> message.method to message.params
- else -> return
- }
-
- builder.headers {
- append(MCP_METHOD_HEADER, method)
-
- val paramsObject = params as? JsonObject ?: return@headers
- val mcpName = paramsObject.stringValue("name") ?: paramsObject.stringValue("uri")
- mcpName?.let { append(MCP_NAME_HEADER, it.encodeMcpHeaderValue()) }
- }
- }
-
- private fun JsonObject.stringValue(key: String): String? =
- (get(key) as? JsonPrimitive)?.takeIf { it.isString }?.content
-
- @OptIn(ExperimentalEncodingApi::class)
- private fun String.encodeMcpHeaderValue(): String {
- val containsUnsafeCharacters = any { it != '\t' && it.code !in 0x20..0x7e }
- val hasEdgeWhitespace = firstOrNull()?.isWhitespace() == true || lastOrNull()?.isWhitespace() == true
- val matchesBase64Sentinel = startsWith(MCP_BASE64_PREFIX) && endsWith(MCP_BASE64_SUFFIX)
-
- if (!containsUnsafeCharacters && !hasEdgeWhitespace && !matchesBase64Sentinel) return this
-
- return "$MCP_BASE64_PREFIX${Base64.Default.encode(encodeToByteArray())}$MCP_BASE64_SUFFIX"
- }
-
+ @Suppress("TooGenericExceptionCaught")
private suspend fun collectSse(
session: ClientSSESession,
replayMessageId: RequestId?,
@@ -473,6 +408,7 @@ public class StreamableHttpClientTransport(
return SseStreamResult(hasPrimingEvent, receivedResponse, localLastEventId, localServerRetryDelay)
}
+ @Suppress("CyclomaticComplexMethod")
private suspend fun handleInlineSse(
response: HttpResponse,
replayMessageId: RequestId?,
@@ -519,15 +455,9 @@ public class StreamableHttpClientTransport(
}
}
+ @Suppress("LoopWithTooManyJumpStatements")
while (!channel.isClosedForRead) {
- // Bound each line so a server that streams a line without ever terminating it cannot
- // exhaust client memory; readUTF8Line returns null at the end of the stream.
- val line = try {
- channel.readUTF8Line(maxInlineSseEventSize)
- } catch (_: TooLongLineException) {
- throw TooLongFrameException(maxInlineSseEventSize.toLong() + 1, maxInlineSseEventSize)
- }
- if (line == null) break
+ val line = channel.readUTF8Line() ?: break
if (line.isEmpty()) {
dispatch(id = id, eventName = eventName, data = sb.toString())
// reset
@@ -541,13 +471,7 @@ public class StreamableHttpClientTransport(
line.startsWith("event:") -> eventName = line.substringAfter("event:").trim()
- line.startsWith("data:") -> {
- sb.append(line.substringAfter("data:").trim())
- // Cap an event assembled from many data: lines that never sees a terminating blank line.
- if (sb.length > maxInlineSseEventSize) {
- throw TooLongFrameException(sb.length.toLong(), maxInlineSseEventSize)
- }
- }
+ line.startsWith("data:") -> sb.append(line.substringAfter("data:").trim())
line.startsWith("retry:") -> line.substringAfter("retry:").trim().toLongOrNull()?.let {
localServerRetryDelay = it.milliseconds
diff --git a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientAssertCapabilityTest.kt b/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientAssertCapabilityTest.kt
deleted file mode 100644
index e9379a7..0000000
--- a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientAssertCapabilityTest.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.client
-
-import io.kotest.matchers.string.shouldContain
-import io.modelcontextprotocol.kotlin.sdk.shared.Transport
-import io.modelcontextprotocol.kotlin.sdk.shared.TransportSendOptions
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.Implementation
-import io.modelcontextprotocol.kotlin.sdk.types.InitializeResult
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
-import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCResponse
-import io.modelcontextprotocol.kotlin.sdk.types.LATEST_PROTOCOL_VERSION
-import io.modelcontextprotocol.kotlin.sdk.types.Method
-import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities
-import kotlinx.coroutines.test.runTest
-import kotlin.test.Test
-import kotlin.test.assertFailsWith
-
-/**
- * Tests for the protected helper [Client.assertCapability].
- *
- * `assertCapability(capability, method)` is `protected`, so this suite uses
- * a small subclass [TestClient] that re-exports it via [TestClient.exposedAssertCapability].
- * Server capabilities are seeded by completing the initialization handshake through
- * [CapabilitiesTransport], which replays a configurable [ServerCapabilities] in its
- * `initialize` response.
- */
-class ClientAssertCapabilityTest {
-
- @Test
- fun `assertCapability tasks throws when server has no tasks capability`() = runTest {
- val client = newTestClient(serverCapabilities = ServerCapabilities())
- val ex = assertFailsWith {
- client.exposedAssertCapability("tasks", "tasks/list")
- }
- ex.message.orEmpty() shouldContain "Server does not support tasks"
- }
-
- @Test
- fun `TasksGet throws when server has no tasks capability`() = runTest {
- val client = newTestClient(serverCapabilities = ServerCapabilities())
- val ex = assertFailsWith {
- client.exposedAssertCapabilityForMethod(Method.Defined.TasksGet)
- }
- ex.message.orEmpty() shouldContain "Server does not support tasks"
- }
-
- @Test
- fun `TasksGet does not throw when server declared tasks`() = runTest {
- val client = newTestClient(
- serverCapabilities = ServerCapabilities(tasks = ServerCapabilities.Tasks()),
- )
- client.exposedAssertCapabilityForMethod(Method.Defined.TasksGet)
- }
-
- @Test
- fun `TasksList throws when server tasks list is null`() = runTest {
- val client = newTestClient(
- serverCapabilities = ServerCapabilities(tasks = ServerCapabilities.Tasks()),
- )
- val ex = assertFailsWith {
- client.exposedAssertCapabilityForMethod(Method.Defined.TasksList)
- }
- ex.message.orEmpty() shouldContain "Server does not support listing tasks"
- }
-
- @Test
- fun `TasksCancel throws when server tasks cancel is null`() = runTest {
- val client = newTestClient(
- serverCapabilities = ServerCapabilities(tasks = ServerCapabilities.Tasks()),
- )
- val ex = assertFailsWith {
- client.exposedAssertCapabilityForMethod(Method.Defined.TasksCancel)
- }
- ex.message.orEmpty() shouldContain "Server does not support cancelling tasks"
- }
-
- @Test
- fun `NotificationsTasksStatus throws when client has no tasks capability`() = runTest {
- val client = newTestClient(clientCapabilities = ClientCapabilities())
- val ex = assertFailsWith {
- client.exposedAssertNotificationCapability(Method.Defined.NotificationsTasksStatus)
- }
- ex.message.orEmpty() shouldContain "Client does not support tasks"
- }
-
- @Test
- fun `CompletionComplete does not throw when server declared completions`() = runTest {
- val client = newTestClient(
- serverCapabilities = ServerCapabilities(completions = ServerCapabilities.Completions),
- )
- client.exposedAssertCapabilityForMethod(Method.Defined.CompletionComplete)
- }
-
- @Test
- fun `CompletionComplete throws when server has no completions capability`() = runTest {
- val client = newTestClient(
- serverCapabilities = ServerCapabilities(prompts = ServerCapabilities.Prompts()),
- )
- val ex = assertFailsWith {
- client.exposedAssertCapabilityForMethod(Method.Defined.CompletionComplete)
- }
- ex.message.orEmpty() shouldContain "Server does not support completions"
- }
-
- private suspend fun newTestClient(
- serverCapabilities: ServerCapabilities = ServerCapabilities(),
- clientCapabilities: ClientCapabilities = ClientCapabilities(),
- ): TestClient {
- val client = TestClient(
- Implementation("test-client", "1.0.0"),
- ClientOptions(capabilities = clientCapabilities),
- )
- val transport = CapabilitiesTransport(serverCapabilities)
- client.connect(transport)
- return client
- }
-
- /**
- * Test-only [Client] subclass that exposes the protected
- * [Client.assertCapability], [Client.assertCapabilityForMethod], and
- * [Client.assertNotificationCapability] helpers to the test code.
- */
- private class TestClient(clientInfo: Implementation, options: ClientOptions = ClientOptions()) :
- Client(clientInfo, options) {
- fun exposedAssertCapability(capability: String, method: String): Unit = assertCapability(capability, method)
- fun exposedAssertCapabilityForMethod(method: Method): Unit = assertCapabilityForMethod(method)
- fun exposedAssertNotificationCapability(method: Method): Unit = assertNotificationCapability(method)
- }
-
- /**
- * Minimal in-memory [Transport] that responds to `initialize` with a configurable
- * [ServerCapabilities]. Other JSON-RPC traffic (e.g. the `notifications/initialized`
- * sent by [Client.connect]) is silently consumed.
- */
- private class CapabilitiesTransport(private val serverCapabilities: ServerCapabilities) : Transport {
- private var onMessageBlock: (suspend (JSONRPCMessage) -> Unit)? = null
- private var onCloseBlock: (() -> Unit)? = null
-
- override suspend fun start() = Unit
-
- override suspend fun send(message: JSONRPCMessage, options: TransportSendOptions?) {
- if (message is JSONRPCRequest && message.method == "initialize") {
- onMessageBlock?.invoke(
- JSONRPCResponse(
- id = message.id,
- result = InitializeResult(
- protocolVersion = LATEST_PROTOCOL_VERSION,
- capabilities = serverCapabilities,
- serverInfo = Implementation("mock-server", "1.0.0"),
- ),
- ),
- )
- }
- }
-
- override suspend fun close() {
- onCloseBlock?.invoke()
- }
-
- override fun onMessage(block: suspend (JSONRPCMessage) -> Unit) {
- onMessageBlock = block
- }
-
- override fun onClose(block: () -> Unit) {
- onCloseBlock = block
- }
-
- override fun onError(block: (Throwable) -> Unit) = Unit
- }
-}
diff --git a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/MockSseClientEngine.kt b/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/MockSseClientEngine.kt
index 14af546..3627b6c 100644
--- a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/MockSseClientEngine.kt
+++ b/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/MockSseClientEngine.kt
@@ -53,14 +53,6 @@ internal open class MockSseClientEngine(
job.cancel()
}
- /**
- * Closes the SSE byte channel without disposing the engine, simulating a server-side
- * disconnect of the SSE stream.
- */
- fun disconnectSseStream() {
- channel.close()
- }
-
private fun responseContext(): CoroutineContext = dispatcher + Job()
private suspend fun respondWithSseEndpointEvent(data: HttpRequestData): HttpResponseData {
diff --git a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/SseClientTransportTest.kt b/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/SseClientTransportTest.kt
index 319ba2a..6cc7f6e 100644
--- a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/SseClientTransportTest.kt
+++ b/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/sse/SseClientTransportTest.kt
@@ -1,6 +1,5 @@
package io.modelcontextprotocol.kotlin.sdk.client.sse
-import io.kotest.assertions.nondeterministic.eventually
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import io.ktor.client.HttpClient
@@ -10,7 +9,6 @@ import io.modelcontextprotocol.kotlin.sdk.client.SseClientTransport
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCNotification
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
-import kotlin.time.Duration.Companion.seconds
class SseClientTransportTest {
@@ -92,31 +90,6 @@ class SseClientTransportTest {
engine.close()
}
- @Test
- fun `onClose callback fires when the SSE stream is disconnected by the server`() = runTest {
- // Given
- val sseUrl = "http://example.com/api/mcp/sse"
-
- // And
- val engine = CapturingSseClientEngine(endpoint = "/messages?sessionId=abc")
- val transport = sseTransport(sseUrl, engine)
- var onCloseFired = false
- transport.onClose { onCloseFired = true }
-
- // When
- transport.start()
- engine.disconnectSseStream()
-
- // Then
- eventually(2.seconds) {
- onCloseFired shouldBe true
- }
-
- // Cleanup
- transport.close()
- engine.close()
- }
-
private fun sseTransport(sseUrl: String, engine: CapturingSseClientEngine) =
SseClientTransport(HttpClient(engine) { install(SSE) }, sseUrl)
diff --git a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/streamable/http/StreamableHttpClientTransportTest.kt b/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/streamable/http/StreamableHttpClientTransportTest.kt
index d9aaf7a..67b2f07 100644
--- a/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/streamable/http/StreamableHttpClientTransportTest.kt
+++ b/kotlin-sdk-client/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/streamable/http/StreamableHttpClientTransportTest.kt
@@ -19,7 +19,6 @@ import io.ktor.http.headersOf
import io.ktor.utils.io.ByteReadChannel
import io.modelcontextprotocol.kotlin.sdk.client.Client
import io.modelcontextprotocol.kotlin.sdk.client.StreamableHttpClientTransport
-import io.modelcontextprotocol.kotlin.sdk.shared.TooLongFrameException
import io.modelcontextprotocol.kotlin.sdk.types.Implementation
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCNotification
@@ -44,15 +43,11 @@ import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
import kotlin.test.assertTrue
-import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
class StreamableHttpClientTransportTest {
- private fun createTransport(
- maxInlineSseEventSize: Int = 16 * 1024 * 1024,
- handler: MockRequestHandler,
- ): StreamableHttpClientTransport {
+ private fun createTransport(handler: MockRequestHandler): StreamableHttpClientTransport {
val mockEngine = MockEngine(handler)
val httpClient = HttpClient(mockEngine) {
install(SSE) {
@@ -60,11 +55,7 @@ class StreamableHttpClientTransportTest {
}
}
- return StreamableHttpClientTransport(
- httpClient,
- url = "http://localhost:8080/mcp",
- maxInlineSseEventSize = maxInlineSseEventSize,
- )
+ return StreamableHttpClientTransport(httpClient, url = "http://localhost:8080/mcp")
}
private fun buildSseMessage(id: String, method: String, params: String): String = buildString {
@@ -246,100 +237,6 @@ class StreamableHttpClientTransportTest {
transport.close()
}
- @Test
- fun `should send MCP method and name headers from name`() = runTest {
- val transport = createTransport { request ->
- assertEquals("tools/call", request.headers["Mcp-Method"])
- assertEquals("read_file", request.headers["Mcp-Name"])
- respond(content = "", status = HttpStatusCode.Accepted)
- }
-
- transport.start()
- transport.send(
- JSONRPCRequest(
- id = "test-id",
- method = "tools/call",
- params = buildJsonObject {
- put("name", JsonPrimitive("read_file"))
- put("arguments", buildJsonObject { put("path", JsonPrimitive("README.md")) })
- },
- ),
- )
- transport.close()
- }
-
- @Test
- fun `should send MCP name header from URI`() = runTest {
- val transport = createTransport { request ->
- assertEquals("resources/read", request.headers["Mcp-Method"])
- assertEquals("file:///README.md", request.headers["Mcp-Name"])
- respond(content = "", status = HttpStatusCode.Accepted)
- }
-
- transport.start()
- transport.send(
- JSONRPCRequest(
- id = "test-id",
- method = "resources/read",
- params = buildJsonObject { put("uri", JsonPrimitive("file:///README.md")) },
- ),
- )
- transport.close()
- }
-
- @Test
- fun `should encode MCP name header values when required`() = runTest {
- val cases = listOf(
- "Hello, 世界" to "=?base64?SGVsbG8sIOS4lueVjA==?=",
- " padded " to "=?base64?IHBhZGRlZCA=?=",
- "line1\nline2" to "=?base64?bGluZTEKbGluZTI=?=",
- "=?base64?literal?=" to "=?base64?PT9iYXNlNjQ/bGl0ZXJhbD89?=",
- )
-
- cases.forEachIndexed { index, (name, expectedHeader) ->
- val transport = createTransport { request ->
- assertEquals(expectedHeader, request.headers["Mcp-Name"])
- respond(content = "", status = HttpStatusCode.Accepted)
- }
-
- transport.start()
- transport.send(
- JSONRPCRequest(
- id = "test-id-$index",
- method = "tools/call",
- params = buildJsonObject { put("name", JsonPrimitive(name)) },
- ),
- )
- transport.close()
- }
- }
-
- @Test
- fun `should send MCP method header for notifications`() = runTest {
- val transport = createTransport { request ->
- assertEquals("notifications/tools/list_changed", request.headers["Mcp-Method"])
- assertNull(request.headers["Mcp-Name"])
- respond(content = "", status = HttpStatusCode.Accepted)
- }
-
- transport.start()
- transport.send(JSONRPCNotification(method = "notifications/tools/list_changed"))
- transport.close()
- }
-
- @Test
- fun `should omit MCP standard headers for responses`() = runTest {
- val transport = createTransport { request ->
- assertNull(request.headers["Mcp-Method"])
- assertNull(request.headers["Mcp-Name"])
- respond(content = "", status = HttpStatusCode.Accepted)
- }
-
- transport.start()
- transport.send(JSONRPCResponse(id = RequestId.StringId("test-id")))
- transport.close()
- }
-
// Engine doesn't support SSECapability: https://youtrack.jetbrains.com/issue/KTOR-8177/MockEngine-Add-SSE-support
@Ignore
@Test
@@ -581,170 +478,6 @@ class StreamableHttpClientTransportTest {
}
}
- @Test
- fun testSkipEmptySSE() = runTest {
- val transport = createTransport { request ->
- if (request.method == HttpMethod.Post) {
- val sseContent = buildString {
- // Event 1: empty data — must be skipped without error
- appendLine("id: a")
- appendLine("data:")
- appendLine()
- // Event 2: whitespace-only data — must be skipped without error
- appendLine("id: b")
- appendLine("data: \t ")
- appendLine()
- // Event 3: multi-line data lines that together form a valid JSON-RPC response
- appendLine("id: c")
- appendLine("event: message")
- appendLine("data: {")
- appendLine("""data: "jsonrpc":"2.0",""")
- appendLine("""data: "id":"test-1",""")
- appendLine("""data: "result":{""")
- appendLine("""data: "protocolVersion":"2025-06-18",""")
- appendLine("""data: "capabilities":{},""")
- appendLine("""data: "serverInfo":{"name":"server","version":"1.0.0"}""")
- appendLine("data: }")
- appendLine("data: }")
- appendLine()
- }
-
- respond(
- content = ByteReadChannel(sseContent),
- status = HttpStatusCode.OK,
- headers = headersOf(
- HttpHeaders.ContentType,
- ContentType.Text.EventStream.toString(),
- ),
- )
- } else {
- respond("", HttpStatusCode.OK)
- }
- }
-
- val receivedMessages = mutableListOf()
- val messageReceived = CompletableDeferred()
-
- transport.onMessage { message ->
- receivedMessages.add(message)
- if (!messageReceived.isCompleted) {
- messageReceived.complete(Unit)
- }
- }
-
- transport.start()
-
- transport.send(
- JSONRPCRequest(
- id = "test-1",
- method = "initialize",
- params = buildJsonObject { },
- ),
- )
-
- eventually {
- messageReceived.await()
- }
-
- receivedMessages shouldHaveSize 1
- val response = receivedMessages[0]
- response.shouldBeInstanceOf()
- response.id shouldBe RequestId.StringId("test-1")
-
- transport.close()
- }
-
- @Test
- fun testInlineSseRejectsEventExceedingMaxSize() = runTest {
- // A malicious server streams an endless single event: many `data:` lines that accumulate
- // past the cap and never send the blank-line terminator that would flush the buffer.
- val maxInlineSseEventSize = 64
- val transport = createTransport(maxInlineSseEventSize) { request ->
- if (request.method == HttpMethod.Post) {
- val sseContent = buildString {
- appendLine("event: message")
- // 20 lines × 16 chars = 320 chars of accumulated data, no terminating blank line.
- repeat(20) { appendLine("data: ${"A".repeat(16)}") }
- }
- respond(
- content = ByteReadChannel(sseContent),
- status = HttpStatusCode.OK,
- headers = headersOf(HttpHeaders.ContentType, ContentType.Text.EventStream.toString()),
- )
- } else {
- respond("", HttpStatusCode.OK)
- }
- }
-
- val receivedMessages = mutableListOf()
- val receivedErrors = mutableListOf()
- transport.onMessage { receivedMessages.add(it) }
- transport.onError { receivedErrors.add(it) }
- transport.start()
-
- val error = assertFailsWith {
- transport.send(JSONRPCRequest(id = "req-1", method = "test", params = buildJsonObject { }))
- }
-
- error.cause.shouldBeInstanceOf()
- receivedErrors.filterIsInstance() shouldHaveSize 1
- receivedMessages shouldHaveSize 0
- transport.close()
- }
-
- @Test
- fun testInlineSseEventExactlyAtMaxSizeIsAccepted() = runTest {
- // An event whose assembled data length equals the cap must still be accepted and dispatched:
- // the guard rejects only sizes strictly greater than the cap (parity with ReadBuffer).
- val part1 = """{"jsonrpc":"2.0","""
- val part2 = """"method":"notifications/tools/list_changed"}"""
- val maxInlineSseEventSize = (part1 + part2).length
-
- val transport = createTransport(maxInlineSseEventSize) { request ->
- if (request.method == HttpMethod.Post) {
- val sseContent = buildString {
- appendLine("event: message")
- appendLine("data: $part1")
- appendLine("data: $part2")
- appendLine()
- }
- respond(
- content = ByteReadChannel(sseContent),
- status = HttpStatusCode.OK,
- headers = headersOf(HttpHeaders.ContentType, ContentType.Text.EventStream.toString()),
- )
- } else {
- respond("", HttpStatusCode.OK)
- }
- }
-
- val receivedMessages = mutableListOf()
- val receivedErrors = mutableListOf()
- val messageReceived = CompletableDeferred()
- transport.onMessage {
- receivedMessages.add(it)
- if (!messageReceived.isCompleted) messageReceived.complete(Unit)
- }
- transport.onError { receivedErrors.add(it) }
- transport.start()
-
- transport.send(JSONRPCRequest(id = "req-1", method = "test", params = buildJsonObject { }))
-
- eventually { messageReceived.await() }
-
- receivedMessages shouldHaveSize 1
- (receivedMessages[0] as JSONRPCNotification).method shouldBe "notifications/tools/list_changed"
- receivedErrors shouldHaveSize 0
- transport.close()
- }
-
- @Test
- fun testNonPositiveMaxInlineSseEventSizeThrows() {
- assertFailsWith {
- createTransport(maxInlineSseEventSize = 0) { respond("", HttpStatusCode.OK) }
- }
- }
-
@Test
fun testInlineSSEInResponse() = runTest {
val transport = createTransport { request ->
@@ -910,7 +643,7 @@ class StreamableHttpClientTransportTest {
eventually {
while (receivedMessages.isEmpty()) {
- delay(10.milliseconds)
+ delay(10)
}
}
@@ -1101,6 +834,7 @@ class StreamableHttpClientTransportTest {
transport.close()
}
+ @Suppress("DEPRECATION")
@Test
fun testDeprecatedConstructorStillWorks() = runTest {
val mockEngine = MockEngine { _ ->
diff --git a/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/AbstractStreamableHttpClientTest.kt b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/AbstractStreamableHttpClientTest.kt
new file mode 100644
index 0000000..4a04e81
--- /dev/null
+++ b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/AbstractStreamableHttpClientTest.kt
@@ -0,0 +1,42 @@
+package io.modelcontextprotocol.kotlin.sdk.client
+
+import io.ktor.client.HttpClient
+import io.ktor.client.engine.apache5.Apache5
+import io.ktor.client.plugins.logging.LogLevel
+import io.ktor.client.plugins.logging.Logging
+import io.ktor.client.plugins.sse.SSE
+import org.junit.jupiter.api.AfterAll
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.TestInstance
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+internal abstract class AbstractStreamableHttpClientTest {
+
+ // start mokksy on random port
+ protected val mockMcp: MockMcp = MockMcp(verbose = true)
+
+ @AfterEach
+ fun afterEach() {
+ mockMcp.verifyNoUnexpectedRequests()
+ }
+
+ @AfterAll
+ fun afterAll() {
+ mockMcp.verifyNoUnmatchedStubs()
+ mockMcp.close()
+ }
+
+ protected suspend fun connect(client: Client) {
+ client.connect(
+ StreamableHttpClientTransport(
+ url = mockMcp.url,
+ client = HttpClient(Apache5) {
+ install(SSE)
+ install(Logging) {
+ level = LogLevel.ALL
+ }
+ },
+ ),
+ )
+ }
+}
diff --git a/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientSamplingValidationTest.kt b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientSamplingValidationTest.kt
deleted file mode 100644
index 7775a08..0000000
--- a/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientSamplingValidationTest.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-package io.modelcontextprotocol.kotlin.sdk.client
-
-import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
-import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequestParams
-import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
-import io.modelcontextprotocol.kotlin.sdk.types.McpException
-import io.modelcontextprotocol.kotlin.sdk.types.RPCError
-import io.modelcontextprotocol.kotlin.sdk.types.Role
-import io.modelcontextprotocol.kotlin.sdk.types.SamplingMessage
-import io.modelcontextprotocol.kotlin.sdk.types.TextContent
-import io.modelcontextprotocol.kotlin.sdk.types.Tool
-import io.modelcontextprotocol.kotlin.sdk.types.ToolChoice
-import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
-import kotlinx.serialization.json.buildJsonObject
-import org.junit.jupiter.api.assertDoesNotThrow
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-
-class ClientSamplingValidationTest {
-
- private val dummyTool = Tool(
- name = "t",
- inputSchema = ToolSchema(properties = buildJsonObject { }, required = emptyList()),
- )
-
- private val minimalMessages = listOf(SamplingMessage(Role.User, TextContent("hi")))
-
- private val noToolsCaps = ClientCapabilities(sampling = ClientCapabilities.sampling)
- private val withToolsCaps = ClientCapabilities(
- sampling = ClientCapabilities.Sampling(tools = EmptyJsonObject),
- )
-
- @Test
- fun `request without tools or toolChoice is always accepted`() {
- val request = CreateMessageRequest(
- CreateMessageRequestParams(maxTokens = 10, messages = minimalMessages),
- )
- assertDoesNotThrow { validateSamplingToolsCapability(request, noToolsCaps) }
- }
-
- @Test
- fun `tools without sampling tools capability throws InvalidParams`() {
- val request = CreateMessageRequest(
- CreateMessageRequestParams(
- maxTokens = 10,
- messages = minimalMessages,
- tools = listOf(dummyTool),
- ),
- )
- val exception = assertFailsWith {
- validateSamplingToolsCapability(request, noToolsCaps)
- }
- assertEquals(RPCError.ErrorCode.INVALID_PARAMS, exception.code)
- check(exception.message?.contains("tools") == true)
- }
-
- @Test
- fun `toolChoice without sampling tools capability throws InvalidParams`() {
- val request = CreateMessageRequest(
- CreateMessageRequestParams(
- maxTokens = 10,
- messages = minimalMessages,
- toolChoice = ToolChoice(mode = ToolChoice.Mode.Required),
- ),
- )
- val exception = assertFailsWith {
- validateSamplingToolsCapability(request, noToolsCaps)
- }
- assertEquals(RPCError.ErrorCode.INVALID_PARAMS, exception.code)
- check(exception.message?.contains("toolChoice") == true)
- }
-
- @Test
- fun `tools with sampling tools capability is accepted`() {
- val request = CreateMessageRequest(
- CreateMessageRequestParams(
- maxTokens = 10,
- messages = minimalMessages,
- tools = listOf(dummyTool),
- ),
- )
- assertDoesNotThrow { validateSamplingToolsCapability(request, withToolsCaps) }
- }
-
- @Test
- fun `toolChoice with sampling tools capability is accepted`() {
- val request = CreateMessageRequest(
- CreateMessageRequestParams(
- maxTokens = 10,
- messages = minimalMessages,
- toolChoice = ToolChoice(mode = ToolChoice.Mode.Auto),
- ),
- )
- assertDoesNotThrow { validateSamplingToolsCapability(request, withToolsCaps) }
- }
-}
diff --git a/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/MockMcp.kt b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/MockMcp.kt
new file mode 100644
index 0000000..fb71904
--- /dev/null
+++ b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/MockMcp.kt
@@ -0,0 +1,241 @@
+package io.modelcontextprotocol.kotlin.sdk.client
+
+import dev.mokksy.mokksy.BuildingStep
+import dev.mokksy.mokksy.Mokksy
+import dev.mokksy.mokksy.StubConfiguration
+import dev.mokksy.mokksy.shutdown
+import dev.mokksy.mokksy.start
+import io.ktor.http.ContentType
+import io.ktor.http.HttpMethod
+import io.ktor.http.HttpStatusCode
+import io.ktor.sse.ServerSentEvent
+import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCRequest
+import io.modelcontextprotocol.kotlin.sdk.types.RequestId
+import kotlinx.coroutines.flow.Flow
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.JsonObject
+import kotlinx.serialization.json.JsonPrimitive
+import kotlinx.serialization.json.buildJsonObject
+import kotlinx.serialization.json.contentOrNull
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
+import kotlinx.serialization.json.putJsonObject
+
+const val MCP_SESSION_ID_HEADER = "MCP-Session-Id"
+
+internal class MockMcp(verbose: Boolean = false) : AutoCloseable {
+
+ private val mokksy: Mokksy = Mokksy(verbose = verbose).apply {
+ start()
+ }
+
+ fun verifyNoUnexpectedRequests() {
+ mokksy.verifyNoUnexpectedRequests()
+ }
+
+ fun verifyNoUnmatchedStubs() {
+ mokksy.verifyNoUnmatchedStubs()
+ }
+
+ val url = "${mokksy.baseUrl()}/mcp"
+
+ @Suppress("LongParameterList")
+ fun onInitialize(
+ clientName: String? = null,
+ sessionId: String,
+ protocolVersion: String = "2025-03-26",
+ serverName: String = "Mock MCP Server",
+ serverVersion: String = "1.0.0",
+ capabilities: JsonObject = buildJsonObject {
+ putJsonObject("tools") {
+ put("listChanged", JsonPrimitive(false))
+ }
+ },
+ ) {
+ val predicates = if (clientName != null) {
+ arrayOf<(JSONRPCRequest?) -> Boolean>({
+ it?.params?.jsonObject
+ ?.get("clientInfo")?.jsonObject
+ ?.get("name")?.jsonPrimitive
+ ?.contentOrNull == clientName
+ })
+ } else {
+ emptyArray()
+ }
+
+ handleWithResult(
+ jsonRpcMethod = "initialize",
+ sessionId = sessionId,
+ bodyPredicates = predicates,
+ // language=json
+ result = """
+ {
+ "capabilities": $capabilities,
+ "protocolVersion": "$protocolVersion",
+ "serverInfo": {
+ "name": "$serverName",
+ "version": "$serverVersion"
+ },
+ "_meta": {
+ "foo": "bar"
+ }
+ }
+ """.trimIndent(),
+ )
+ }
+
+ fun onJSONRPCRequest(
+ httpMethod: HttpMethod = HttpMethod.Post,
+ jsonRpcMethod: String,
+ expectedSessionId: String? = null,
+ vararg bodyPredicates: (JSONRPCRequest) -> Boolean,
+ ): BuildingStep = mokksy.method(
+ configuration = StubConfiguration(removeAfterMatch = true),
+ httpMethod = httpMethod,
+ requestType = JSONRPCRequest::class,
+ ) {
+ path("/mcp")
+ expectedSessionId?.let {
+ containsHeader(MCP_SESSION_ID_HEADER, it)
+ }
+ bodyMatchesPredicate(
+ description = "JSON-RPC version is '2.0'",
+ predicate =
+ {
+ it!!.jsonrpc == "2.0"
+ },
+ )
+ bodyMatchesPredicate(
+ description = "JSON-RPC Method should be '$jsonRpcMethod'",
+ predicate =
+ {
+ it!!.method == jsonRpcMethod
+ },
+ )
+ bodyPredicates.forEach { predicate ->
+ bodyMatchesPredicate(predicate = { predicate.invoke(it!!) })
+ }
+ }
+
+ @Suppress("LongParameterList")
+ fun handleWithResult(
+ httpMethod: HttpMethod = HttpMethod.Post,
+ jsonRpcMethod: String,
+ expectedSessionId: String? = null,
+ sessionId: String,
+ contentType: ContentType = ContentType.Application.Json,
+ statusCode: HttpStatusCode = HttpStatusCode.OK,
+ vararg bodyPredicates: (JSONRPCRequest) -> Boolean,
+ result: () -> JsonObject,
+ ) {
+ onJSONRPCRequest(
+ httpMethod = httpMethod,
+ jsonRpcMethod = jsonRpcMethod,
+ expectedSessionId = expectedSessionId,
+ bodyPredicates = bodyPredicates,
+ ) respondsWith {
+ val requestId = when (val id = request.body().id) {
+ is RequestId.NumberId -> id.value.toString()
+ is RequestId.StringId -> "\"${id.value}\""
+ }
+ val resultObject = result.invoke()
+ // language=json
+ body = """
+ {
+ "jsonrpc": "2.0",
+ "id": $requestId,
+ "result": $resultObject
+ }
+ """.trimIndent()
+ this.contentType = contentType
+ headers += MCP_SESSION_ID_HEADER to sessionId
+ httpStatus = statusCode
+ }
+ }
+
+ @Suppress("LongParameterList")
+ fun handleWithResult(
+ httpMethod: HttpMethod = HttpMethod.Post,
+ jsonRpcMethod: String,
+ expectedSessionId: String? = null,
+ sessionId: String,
+ contentType: ContentType = ContentType.Application.Json,
+ statusCode: HttpStatusCode = HttpStatusCode.OK,
+ vararg bodyPredicates: (JSONRPCRequest) -> Boolean,
+ result: String,
+ ) {
+ handleWithResult(
+ httpMethod = httpMethod,
+ jsonRpcMethod = jsonRpcMethod,
+ expectedSessionId = expectedSessionId,
+ sessionId = sessionId,
+ contentType = contentType,
+ statusCode = statusCode,
+ bodyPredicates = bodyPredicates,
+ result = {
+ Json.parseToJsonElement(result).jsonObject
+ },
+ )
+ }
+
+ @Suppress("LongParameterList")
+ fun handleJSONRPCRequest(
+ httpMethod: HttpMethod = HttpMethod.Post,
+ jsonRpcMethod: String,
+ expectedSessionId: String? = null,
+ sessionId: String,
+ contentType: ContentType = ContentType.Application.Json,
+ statusCode: HttpStatusCode = HttpStatusCode.OK,
+ vararg bodyPredicates: (JSONRPCRequest?) -> Boolean,
+ bodyBuilder: () -> String = { "" },
+ ) {
+ onJSONRPCRequest(
+ httpMethod = httpMethod,
+ jsonRpcMethod = jsonRpcMethod,
+ expectedSessionId = expectedSessionId,
+ bodyPredicates = bodyPredicates,
+ ) respondsWith {
+ body = bodyBuilder.invoke()
+ this.contentType = contentType
+ headers += MCP_SESSION_ID_HEADER to sessionId
+ httpStatus = statusCode
+ }
+ }
+
+ fun onSubscribe(httpMethod: HttpMethod = HttpMethod.Post, sessionId: String): BuildingStep = mokksy.method(
+ httpMethod = httpMethod,
+ name = "MCP GETs",
+ requestType = Any::class,
+ ) {
+ path("/mcp")
+ containsHeader(MCP_SESSION_ID_HEADER, sessionId)
+ containsHeader("Accept", "application/json,text/event-stream")
+ containsHeader("Cache-Control", "no-store")
+ }
+
+ fun handleSubscribeWithGet(sessionId: String, block: () -> Flow) {
+ onSubscribe(
+ httpMethod = HttpMethod.Get,
+ sessionId = sessionId,
+ ) respondsWithSseStream {
+ headers += MCP_SESSION_ID_HEADER to sessionId
+ this.flow = block.invoke()
+ }
+ }
+
+ fun mockUnsubscribeRequest(sessionId: String) {
+ mokksy.delete(
+ configuration = StubConfiguration(removeAfterMatch = true),
+ requestType = JSONRPCRequest::class,
+ ) {
+ path("/mcp")
+ containsHeader(MCP_SESSION_ID_HEADER, sessionId)
+ } respondsWith {
+ body = null
+ }
+ }
+
+ override fun close() {
+ mokksy.shutdown()
+ }
+}
diff --git a/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTest.kt b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTest.kt
new file mode 100644
index 0000000..a1fe89e
--- /dev/null
+++ b/kotlin-sdk-client/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/StreamableHttpClientTest.kt
@@ -0,0 +1,293 @@
+package io.modelcontextprotocol.kotlin.sdk.client
+
+import io.kotest.matchers.collections.shouldContain
+import io.kotest.matchers.shouldBe
+import io.ktor.http.ContentType
+import io.ktor.http.HttpMethod
+import io.ktor.http.HttpStatusCode
+import io.ktor.sse.ServerSentEvent
+import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
+import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
+import io.modelcontextprotocol.kotlin.sdk.types.Implementation
+import io.modelcontextprotocol.kotlin.sdk.types.Tool
+import io.modelcontextprotocol.kotlin.sdk.types.ToolSchema
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import kotlinx.serialization.json.buildJsonObject
+import kotlinx.serialization.json.put
+import kotlinx.serialization.json.putJsonObject
+import org.junit.jupiter.api.TestInstance
+import kotlin.test.Test
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import kotlin.uuid.ExperimentalUuidApi
+import kotlin.uuid.Uuid
+
+@OptIn(ExperimentalUuidApi::class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+internal class StreamableHttpClientTest : AbstractStreamableHttpClientTest() {
+
+ @Test
+ fun `Should skip empty SSE`(): Unit = runBlocking {
+ val client = Client(
+ clientInfo = Implementation(
+ name = "client1",
+ version = "1.0.0",
+ ),
+ options = ClientOptions(
+ capabilities = ClientCapabilities(),
+ ),
+ )
+ val sessionId = Uuid.random().toString()
+
+ mockMcp.onJSONRPCRequest(
+ httpMethod = HttpMethod.Post,
+ jsonRpcMethod = "initialize",
+ ).respondsWithStream {
+ headers += MCP_SESSION_ID_HEADER to sessionId
+ flow = flowOf(
+ "id: ${Uuid.random()}\n",
+ "data:\n", // empty data
+ "\n",
+ "id: ${Uuid.random()}\n",
+ "data: \t \n", // tabs and spaces
+ "\n",
+ "id: ${Uuid.random()}\n",
+ "event: message\n",
+ // multiline data
+ "data: {\n",
+ "data: \"result\":{\n" +
+ "data: \"protocolVersion\":\"2025-06-18\",\n" +
+ "data: \"capabilities\":{},\n" +
+ "data: \"serverInfo\":{\"name\":\"simple-streamable-http-server\",\"version\":\"1.0.0\"}\n" +
+ "data: },\n" +
+ "data: \"jsonrpc\":\"2.0\",\n" +
+ "data: \"id\":\"7ce065b0678f49e5b04ce5a0fcc7d518\"\n" +
+ "data: }\n",
+ "\n",
+ )
+ }
+
+ mockMcp.handleJSONRPCRequest(
+ jsonRpcMethod = "notifications/initialized",
+ expectedSessionId = sessionId,
+ sessionId = sessionId,
+ statusCode = HttpStatusCode.Accepted,
+ )
+
+ mockMcp.handleSubscribeWithGet(sessionId) {
+ emptyFlow()
+ }
+
+ connect(client)
+ }
+
+ @Test
+ fun `test streamableHttpClient`() = runBlocking {
+ val client = Client(
+ clientInfo = Implementation(
+ name = "client1",
+ version = "1.0.0",
+ ),
+ options = ClientOptions(
+ capabilities = ClientCapabilities(),
+ ),
+ )
+
+ val sessionId = Uuid.random().toString()
+
+ mockMcp.onInitialize(
+ clientName = "client1",
+ sessionId = sessionId,
+ )
+
+ mockMcp.handleJSONRPCRequest(
+ jsonRpcMethod = "notifications/initialized",
+ expectedSessionId = sessionId,
+ sessionId = sessionId,
+ statusCode = HttpStatusCode.Accepted,
+ )
+
+ mockMcp.handleSubscribeWithGet(sessionId) {
+ flow {
+ delay(500.milliseconds)
+ emit(
+ ServerSentEvent(
+ event = "message",
+ id = "1",
+ data = @Suppress("MaxLineLength")
+ //language=json
+ """{"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}""",
+ ),
+ )
+ delay(200.milliseconds)
+ emit(
+ ServerSentEvent(
+ data = @Suppress("MaxLineLength")
+ //language=json
+ """{"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}""",
+ ),
+ )
+ }
+ }
+
+ // TODO: how to get notifications via Client API?
+
+ mockMcp.handleWithResult(
+ jsonRpcMethod = "tools/list",
+ sessionId = sessionId,
+ // language=json
+ result = """
+ {
+ "tools": [
+ {
+ "name": "get_weather",
+ "title": "Weather Information Provider",
+ "description": "Get current weather information for a location",
+ "inputSchema": {
+ "type": "object",
+ "properties": {
+ "location": {
+ "type": "string",
+ "description": "City name or zip code"
+ }
+ },
+ "required": ["location"]
+ },
+ "outputSchema": {
+ "type": "object",
+ "properties": {
+ "temperature": {
+ "type": "number",
+ "description": "Temperature, Celsius"
+ }
+ },
+ "required": ["temperature"]
+ },
+ "_meta": {}
+ }
+ ]
+ }
+ """.trimIndent(),
+ )
+
+ connect(client)
+
+ val listToolsResult = client.listTools()
+
+ listToolsResult.tools shouldContain Tool(
+ name = "get_weather",
+ title = "Weather Information Provider",
+ description = "Get current weather information for a location",
+ inputSchema = ToolSchema(
+ properties = buildJsonObject {
+ putJsonObject("location") {
+ put("type", "string")
+ put("description", "City name or zip code")
+ }
+ },
+ required = listOf("location"),
+ ),
+ outputSchema = ToolSchema(
+ properties = buildJsonObject {
+ putJsonObject("temperature") {
+ put("type", "number")
+ put("description", "Temperature, Celsius")
+ }
+ },
+ required = listOf("temperature"),
+ ),
+ annotations = null,
+ meta = EmptyJsonObject,
+ )
+
+ client.close()
+ }
+
+ @Test
+ fun `terminateSession sends DELETE request`() = runBlocking {
+ val client = Client(
+ clientInfo = Implementation(name = "client1", version = "1.0.0"),
+ options = ClientOptions(capabilities = ClientCapabilities()),
+ )
+ val sessionId = Uuid.random().toString()
+
+ mockMcp.onInitialize(clientName = "client1", sessionId = sessionId)
+ mockMcp.handleJSONRPCRequest(
+ jsonRpcMethod = "notifications/initialized",
+ expectedSessionId = sessionId,
+ sessionId = sessionId,
+ statusCode = HttpStatusCode.Accepted,
+ )
+ mockMcp.handleSubscribeWithGet(sessionId) { emptyFlow() }
+
+ connect(client)
+
+ mockMcp.mockUnsubscribeRequest(sessionId = sessionId)
+ (client.transport as StreamableHttpClientTransport).terminateSession()
+ (client.transport as StreamableHttpClientTransport).sessionId shouldBe null
+
+ client.close()
+ }
+
+ @Test
+ fun `handle MethodNotAllowed`() = runBlocking {
+ checkSupportNonStreamingResponse(
+ ContentType.Text.EventStream,
+ HttpStatusCode.MethodNotAllowed,
+ )
+ }
+
+ @Test
+ fun `handle non-streaming response`() = runBlocking {
+ checkSupportNonStreamingResponse(
+ ContentType.Application.Json,
+ HttpStatusCode.OK,
+ )
+ }
+
+ private suspend fun checkSupportNonStreamingResponse(contentType: ContentType, statusCode: HttpStatusCode) {
+ val sessionId = "SID_${Uuid.random().toHexString()}"
+ val clientName = "client-${Uuid.random().toHexString()}"
+ val client = Client(
+ clientInfo = Implementation(name = clientName, version = "1.0.0"),
+ options = ClientOptions(
+ capabilities = ClientCapabilities(),
+ ),
+ )
+
+ mockMcp.onInitialize(clientName = clientName, sessionId = sessionId)
+
+ mockMcp.handleJSONRPCRequest(
+ jsonRpcMethod = "notifications/initialized",
+ expectedSessionId = sessionId,
+ sessionId = sessionId,
+ statusCode = HttpStatusCode.Accepted,
+ )
+
+ mockMcp.onSubscribe(
+ httpMethod = HttpMethod.Get,
+ sessionId = sessionId,
+ ) respondsWith {
+ headers += MCP_SESSION_ID_HEADER to sessionId
+ body = null
+ httpStatus = statusCode
+ this.contentType = contentType
+ }
+
+ mockMcp.handleWithResult(jsonRpcMethod = "ping", sessionId = sessionId) {
+ buildJsonObject {}
+ }
+
+ connect(client)
+
+ delay(1.seconds)
+
+ client.ping() // connection is still alive
+
+ client.close()
+ }
+}
diff --git a/kotlin-sdk-core/.openapi-generator-ignore b/kotlin-sdk-core/.openapi-generator-ignore
new file mode 100644
index 0000000..dbdf930
--- /dev/null
+++ b/kotlin-sdk-core/.openapi-generator-ignore
@@ -0,0 +1,29 @@
+# OpenAPI Generator Ignore
+# Generated by openapi-generator https://github.com/openapitools/openapi-generator
+
+# Use this file to prevent files from being overwritten by the generator.
+# The patterns follow closely to .gitignore or .dockerignore.
+
+# As an example, the C# client generator defines ApiClient.cs.
+# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
+**/auth/**
+**/infrastructure/**
+!**/Base64ByteArray.kt
+!**/Bytes.kt
+
+
+## Exclude Models which are causing problems
+**/JSONRPCResponse.kt
+**/Result.kt
+**/ListToolsResult.kt
+**/InitializeRequest.kt
+**/InitializeResult.kt
+**/ServerCapabilities.kt
+**/InitializeRequestParams.kt
+**/CreateMessageRequest.kt
+**/CreateMessageRequestParams.kt
+**/ClientCapabilities.kt
+**/Tool.kt
+**/ToolInputSchema.kt
+**/ToolOutputSchema.kt
+
diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api
index b3e88d8..d4f88ba 100644
--- a/kotlin-sdk-core/api/kotlin-sdk-core.api
+++ b/kotlin-sdk-core/api/kotlin-sdk-core.api
@@ -59,7 +59,6 @@ public abstract class io/modelcontextprotocol/kotlin/sdk/shared/Protocol {
public abstract fun assertRequestHandlerCapability (Lio/modelcontextprotocol/kotlin/sdk/types/Method;)V
public final fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun connect (Lio/modelcontextprotocol/kotlin/sdk/shared/Transport;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- protected final fun enableConcurrentDispatch ()V
public final fun getFallbackNotificationHandler ()Lkotlin/jvm/functions/Function2;
public final fun getFallbackRequestHandler ()Lkotlin/jvm/functions/Function3;
public final fun getNotificationHandlers ()Ljava/util/Map;
@@ -72,7 +71,6 @@ public abstract class io/modelcontextprotocol/kotlin/sdk/shared/Protocol {
public static synthetic fun notification$default (Lio/modelcontextprotocol/kotlin/sdk/shared/Protocol;Lio/modelcontextprotocol/kotlin/sdk/types/Notification;Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public fun onClose ()V
public fun onError (Ljava/lang/Throwable;)V
- protected fun onInitializedNotification ()V
public final fun removeNotificationHandler (Lio/modelcontextprotocol/kotlin/sdk/types/Method;)V
public final fun removeRequestHandler (Lio/modelcontextprotocol/kotlin/sdk/types/Method;)V
public final fun request (Lio/modelcontextprotocol/kotlin/sdk/types/Request;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -81,23 +79,19 @@ public abstract class io/modelcontextprotocol/kotlin/sdk/shared/Protocol {
public final fun setFallbackRequestHandler (Lkotlin/jvm/functions/Function3;)V
public final fun setNotificationHandler (Lio/modelcontextprotocol/kotlin/sdk/types/Method;Lkotlin/jvm/functions/Function1;)V
public final fun setRequestHandlerInternal (Lio/modelcontextprotocol/kotlin/sdk/types/Method;Lkotlin/jvm/functions/Function3;)V
- protected fun wrapRequestHandler (Lio/modelcontextprotocol/kotlin/sdk/types/Method;Lkotlin/jvm/functions/Function3;)Lkotlin/jvm/functions/Function3;
}
public final class io/modelcontextprotocol/kotlin/sdk/shared/ProtocolKt {
public static final field IMPLEMENTATION_NAME Ljava/lang/String;
- public static final fun currentRequestHandlerExtra (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getDEFAULT_REQUEST_TIMEOUT ()J
}
public class io/modelcontextprotocol/kotlin/sdk/shared/ProtocolOptions {
- public synthetic fun (ZJLkotlin/coroutines/CoroutineContext;IIILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (ZJLkotlin/coroutines/CoroutineContext;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (ZJILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (ZJLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getEnforceStrictCapabilities ()Z
- public final fun getHandlerCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public final fun getTimeout-UwyO8pc ()J
public final fun setEnforceStrictCapabilities (Z)V
- public final fun setHandlerCoroutineContext (Lkotlin/coroutines/CoroutineContext;)V
public final fun setTimeout-LRDsOJo (J)V
}
@@ -112,16 +106,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/shared/ReadBufferKt {
public static final fun serializeMessage (Lio/modelcontextprotocol/kotlin/sdk/types/JSONRPCMessage;)Ljava/lang/String;
}
-public final class io/modelcontextprotocol/kotlin/sdk/shared/RequestHandlerExtra : kotlin/coroutines/AbstractCoroutineContextElement {
- public static final field Key Lio/modelcontextprotocol/kotlin/sdk/shared/RequestHandlerExtra$Key;
- public final fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/types/Method;
- public final fun getRequestId ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;
- public final fun sendNotification (Lio/modelcontextprotocol/kotlin/sdk/types/Notification;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public final fun sendRequest (Lio/modelcontextprotocol/kotlin/sdk/types/Request;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
- public static synthetic fun sendRequest$default (Lio/modelcontextprotocol/kotlin/sdk/shared/RequestHandlerExtra;Lio/modelcontextprotocol/kotlin/sdk/types/Request;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/shared/RequestHandlerExtra$Key : kotlin/coroutines/CoroutineContext$Key {
+public final class io/modelcontextprotocol/kotlin/sdk/shared/RequestHandlerExtra {
+ public fun ()V
}
public final class io/modelcontextprotocol/kotlin/sdk/shared/RequestOptions : io/modelcontextprotocol/kotlin/sdk/shared/TransportSendOptions {
@@ -138,12 +124,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/shared/RequestOptions : io
public fun toString ()Ljava/lang/String;
}
-public final class io/modelcontextprotocol/kotlin/sdk/shared/TooLongFrameException : java/io/IOException {
- public fun (JI)V
- public final fun getFrameSize ()J
- public final fun getMaxFrameSize ()I
-}
-
public abstract interface class io/modelcontextprotocol/kotlin/sdk/shared/Transport {
public abstract fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun onClose (Lkotlin/jvm/functions/Function0;)V
@@ -390,19 +370,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/BooleanSchema$Compan
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/CacheScope : java/lang/Enum {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/CacheScope$Companion;
- public static final field Private Lio/modelcontextprotocol/kotlin/sdk/types/CacheScope;
- public static final field Public Lio/modelcontextprotocol/kotlin/sdk/types/CacheScope;
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
- public static fun valueOf (Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/types/CacheScope;
- public static fun values ()[Lio/modelcontextprotocol/kotlin/sdk/types/CacheScope;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/CacheScope$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/CallToolRequest : io/modelcontextprotocol/kotlin/sdk/types/ClientRequest {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequest$Companion;
public fun (Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;)V
@@ -416,7 +383,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CallToolRequest : io
public final fun getName ()Ljava/lang/String;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestParams;
- public final fun getTask ()Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@@ -441,26 +407,24 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CallToolRequestBuild
public final fun arguments (Lkotlin/jvm/functions/Function1;)V
public final fun arguments (Lkotlinx/serialization/json/JsonObject;)V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getName ()Ljava/lang/String;
public final fun setName (Ljava/lang/String;)V
}
public final class io/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams : io/modelcontextprotocol/kotlin/sdk/types/RequestParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams$Companion;
- public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;
- public final fun component4-VI-3G7E ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy-6R815Ik (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;
- public static synthetic fun copy-6R815Ik$default (Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;
+ public final fun component3-VI-3G7E ()Lkotlinx/serialization/json/JsonObject;
+ public final fun copy-EDEVuBg (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;
+ public static synthetic fun copy-EDEVuBg$default (Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolRequestParams;
public fun equals (Ljava/lang/Object;)Z
public final fun getArguments ()Lkotlinx/serialization/json/JsonObject;
public fun getMeta-VI-3G7E ()Lkotlinx/serialization/json/JsonObject;
public final fun getName ()Ljava/lang/String;
- public final fun getTask ()Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@@ -639,32 +603,22 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CancelledNotificatio
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/CapabilitiesKt {
- public static final fun getSupportsUrl (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;)Z
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Companion;
public fun ()V
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;
+ public fun (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
+ public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;
- public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;
- public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;
- public final fun component5 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component6 ()Ljava/util/Map;
- public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
+ public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
+ public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
+ public final fun copy (Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
+ public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
public fun equals (Ljava/lang/Object;)Z
- public final fun getElicitation ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;
+ public final fun getElicitation ()Lkotlinx/serialization/json/JsonObject;
public final fun getExperimental ()Lkotlinx/serialization/json/JsonObject;
- public final fun getExtensions ()Ljava/util/Map;
public final fun getRoots ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Roots;
- public final fun getSampling ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;
- public final fun getTasks ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;
+ public final fun getSampling ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@@ -681,39 +635,8 @@ public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ClientCapa
}
public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Companion {
- public final fun getElicitation ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;
- public final fun getSampling ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation$Companion;
- public fun ()V
- public fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getForm ()Lkotlinx/serialization/json/JsonObject;
- public final fun getUrl ()Lkotlinx/serialization/json/JsonObject;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation$Companion {
+ public final fun getElicitation ()Lkotlinx/serialization/json/JsonObject;
+ public final fun getSampling ()Lkotlinx/serialization/json/JsonObject;
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
@@ -746,169 +669,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$R
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling$Companion;
- public fun ()V
- public fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getContext ()Lkotlinx/serialization/json/JsonObject;
- public final fun getTools ()Lkotlinx/serialization/json/JsonObject;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Companion;
- public fun ()V
- public fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;
- public final fun copy (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getCancel ()Lkotlinx/serialization/json/JsonObject;
- public final fun getList ()Lkotlinx/serialization/json/JsonObject;
- public final fun getRequests ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Companion;
- public fun ()V
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;
- public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;
- public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getElicitation ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;
- public final fun getSampling ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation$Companion;
- public fun ()V
- public fun (Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getCreate ()Lkotlinx/serialization/json/JsonObject;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Elicitation$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling$Companion;
- public fun ()V
- public fun (Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getCreateMessage ()Lkotlinx/serialization/json/JsonObject;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Tasks$Requests$Sampling$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/ClientCapabilitiesBuilder {
public fun ()V
public final fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
- public final fun elicitation (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Elicitation;)V
+ public final fun elicitation (Lkotlin/jvm/functions/Function1;)V
+ public final fun elicitation (Lkotlinx/serialization/json/JsonObject;)V
public final fun experimental (Lkotlin/jvm/functions/Function1;)V
public final fun experimental (Lkotlinx/serialization/json/JsonObject;)V
- public final fun extensions (Ljava/util/Map;)V
public final fun roots (Ljava/lang/Boolean;)V
public static synthetic fun roots$default (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilitiesBuilder;Ljava/lang/Boolean;ILjava/lang/Object;)V
- public final fun sampling (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities$Sampling;)V
+ public final fun sampling (Lkotlin/jvm/functions/Function1;)V
+ public final fun sampling (Lkotlinx/serialization/json/JsonObject;)V
}
public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/ClientNotification : io/modelcontextprotocol/kotlin/sdk/types/Notification {
@@ -980,7 +751,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CompleteRequestBuild
public fun ()V
public final fun argument (Ljava/lang/String;Ljava/lang/String;)V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/CompleteRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun context (Ljava/util/Map;)V
public final fun context (Lkotlin/jvm/functions/Function1;)V
public final fun ref (Lio/modelcontextprotocol/kotlin/sdk/types/Reference;)V
@@ -1160,8 +931,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ContentTypes : java/
public static final field IMAGE Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
public static final field RESOURCE_LINK Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
public static final field TEXT Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
- public static final field TOOL_RESULT Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
- public static final field TOOL_USE Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public final fun getValue ()Ljava/lang/String;
public static fun valueOf (Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
@@ -1190,7 +959,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestParams;
public final fun getStopSequences ()Ljava/util/List;
public final fun getSystemPrompt ()Ljava/lang/String;
- public final fun getTask ()Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;
public final fun getTemperature ()Ljava/lang/Double;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
@@ -1214,7 +982,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest
public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getContext ()Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;
public final fun getMaxTokens ()Ljava/lang/Integer;
public final fun getStopSequences ()Ljava/util/List;
@@ -1235,12 +1003,9 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest
public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams : io/modelcontextprotocol/kotlin/sdk/types/RequestParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams$Companion;
- public synthetic fun (ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()I
- public final fun component10 ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;
- public final fun component11 ()Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;
- public final fun component12-VI-3G7E ()Lkotlinx/serialization/json/JsonObject;
public final fun component2 ()Ljava/util/List;
public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;
public final fun component4 ()Ljava/lang/String;
@@ -1248,9 +1013,9 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest
public final fun component6 ()Ljava/lang/Double;
public final fun component7 ()Ljava/util/List;
public final fun component8 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component9 ()Ljava/util/List;
- public final fun copy-7OuPiQU (ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams;
- public static synthetic fun copy-7OuPiQU$default (Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams;ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams;
+ public final fun component9-VI-3G7E ()Lkotlinx/serialization/json/JsonObject;
+ public final fun copy-qtvSJzw (ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams;
+ public static synthetic fun copy-qtvSJzw$default (Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams;ILjava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;Ljava/lang/Double;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequestParams;
public fun equals (Ljava/lang/Object;)Z
public final fun getIncludeContext ()Lio/modelcontextprotocol/kotlin/sdk/types/IncludeContext;
public final fun getMaxTokens ()I
@@ -1260,10 +1025,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest
public final fun getModelPreferences ()Lio/modelcontextprotocol/kotlin/sdk/types/ModelPreferences;
public final fun getStopSequences ()Ljava/util/List;
public final fun getSystemPrompt ()Ljava/lang/String;
- public final fun getTask ()Lio/modelcontextprotocol/kotlin/sdk/types/TaskMetadata;
public final fun getTemperature ()Ljava/lang/Double;
- public final fun getToolChoice ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;
- public final fun getTools ()Ljava/util/List;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@@ -1285,19 +1047,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageRequest
public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult : io/modelcontextprotocol/kotlin/sdk/types/ClientResult {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult$Companion;
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/Role;
- public final fun component2 ()Ljava/util/List;
+ public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;
public final fun component3 ()Ljava/lang/String;
public final fun component4-6olV-UY ()Ljava/lang/String;
public final fun component5 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy-ctVX1tw (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult;
- public static synthetic fun copy-ctVX1tw$default (Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult;Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult;
+ public final fun copy-ctVX1tw (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult;
+ public static synthetic fun copy-ctVX1tw$default (Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult;Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult;
public fun equals (Ljava/lang/Object;)Z
- public final fun getContent ()Ljava/util/List;
+ public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;
public fun getMeta ()Lkotlinx/serialization/json/JsonObject;
public final fun getModel ()Ljava/lang/String;
public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/types/Role;
@@ -1408,104 +1168,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CustomRequest$Compan
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest : io/modelcontextprotocol/kotlin/sdk/types/ClientRequest {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest$Companion;
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;)V
- public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;
- public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest;Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getMeta--Khlwt8 ()Lkotlinx/serialization/json/JsonObject;
- public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/types/Method;
- public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;
- public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestParams;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/DiscoverRequest$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams : io/modelcontextprotocol/kotlin/sdk/types/RequestParams {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams$Companion;
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1--Khlwt8 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy-hVVZdjo (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;
- public static synthetic fun copy-hVVZdjo$default (Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;
- public fun equals (Ljava/lang/Object;)Z
- public fun getMeta--Khlwt8 ()Lkotlinx/serialization/json/JsonObject;
- public synthetic fun getMeta-VI-3G7E ()Lkotlinx/serialization/json/JsonObject;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/DiscoverRequestParams$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/DiscoverResult : io/modelcontextprotocol/kotlin/sdk/types/ServerResult {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverResult$Companion;
- public fun (Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Ljava/lang/String;Ljava/lang/String;JLio/modelcontextprotocol/kotlin/sdk/types/CacheScope;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Ljava/lang/String;Ljava/lang/String;JLio/modelcontextprotocol/kotlin/sdk/types/CacheScope;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Ljava/util/List;
- public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
- public final fun component3 ()Ljava/lang/String;
- public final fun component4 ()Ljava/lang/String;
- public final fun component5 ()J
- public final fun component6 ()Lio/modelcontextprotocol/kotlin/sdk/types/CacheScope;
- public final fun component7 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Ljava/lang/String;Ljava/lang/String;JLio/modelcontextprotocol/kotlin/sdk/types/CacheScope;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverResult;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverResult;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Ljava/lang/String;Ljava/lang/String;JLio/modelcontextprotocol/kotlin/sdk/types/CacheScope;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverResult;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getCacheScope ()Lio/modelcontextprotocol/kotlin/sdk/types/CacheScope;
- public final fun getCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
- public final fun getInstructions ()Ljava/lang/String;
- public fun getMeta ()Lkotlinx/serialization/json/JsonObject;
- public final fun getResultType ()Ljava/lang/String;
- public final fun getSupportedVersions ()Ljava/util/List;
- public final fun getTtlMs ()J
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/DiscoverResult$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverResult$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverResult;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/DiscoverResult;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/DiscoverResult$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/DoubleSchema : io/modelcontextprotocol/kotlin/sdk/types/NumberSchemaDefinition {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/DoubleSchema$Companion;
public fun ()V
@@ -1579,7 +1241,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitRequest$Compan
public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ElicitRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getMessage ()Ljava/lang/String;
public final fun requestedSchema (Lio/modelcontextprotocol/kotlin/sdk/types/ElicitRequestParams$RequestedSchema;)V
public final fun requestedSchema (Lkotlin/jvm/functions/Function1;)V
@@ -1957,7 +1619,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest$Com
public final class io/modelcontextprotocol/kotlin/sdk/types/GetPromptRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getArguments ()Ljava/util/Map;
public final fun getName ()Ljava/lang/String;
public final fun setArguments (Ljava/util/Map;)V
@@ -2402,7 +2064,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/InitializeRequest$Co
public final class io/modelcontextprotocol/kotlin/sdk/types/InitializeRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/InitializeRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun capabilities (Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;)V
public final fun capabilities (Lkotlin/jvm/functions/Function1;)V
public final fun getProtocolVersion ()Ljava/lang/String;
@@ -2792,7 +2454,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListPromptsRequest$C
public final class io/modelcontextprotocol/kotlin/sdk/types/ListPromptsRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/PaginatedRequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ListPromptsRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
}
public final class io/modelcontextprotocol/kotlin/sdk/types/ListPromptsResult : io/modelcontextprotocol/kotlin/sdk/types/PaginatedResult, io/modelcontextprotocol/kotlin/sdk/types/ServerResult {
@@ -2863,7 +2525,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourceTemplate
public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourceTemplatesRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/PaginatedRequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ListResourceTemplatesRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
}
public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourceTemplatesResult : io/modelcontextprotocol/kotlin/sdk/types/PaginatedResult, io/modelcontextprotocol/kotlin/sdk/types/ServerResult {
@@ -2934,7 +2596,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourcesRequest
public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourcesRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/PaginatedRequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ListResourcesRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
}
public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourcesResult : io/modelcontextprotocol/kotlin/sdk/types/PaginatedResult, io/modelcontextprotocol/kotlin/sdk/types/ServerResult {
@@ -3004,7 +2666,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListRootsRequest$Com
public final class io/modelcontextprotocol/kotlin/sdk/types/ListRootsRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ListRootsRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
}
public final class io/modelcontextprotocol/kotlin/sdk/types/ListRootsResult : io/modelcontextprotocol/kotlin/sdk/types/ClientResult {
@@ -3138,7 +2800,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListToolsRequest$Com
public final class io/modelcontextprotocol/kotlin/sdk/types/ListToolsRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/PaginatedRequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ListToolsRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
}
public final class io/modelcontextprotocol/kotlin/sdk/types/ListToolsResult : io/modelcontextprotocol/kotlin/sdk/types/PaginatedResult, io/modelcontextprotocol/kotlin/sdk/types/ServerResult {
@@ -3262,7 +2924,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Logging_dslKt {
public abstract interface annotation class io/modelcontextprotocol/kotlin/sdk/types/McpDsl : java/lang/annotation/Annotation {
}
-public class io/modelcontextprotocol/kotlin/sdk/types/McpException : java/lang/Exception {
+public final class io/modelcontextprotocol/kotlin/sdk/types/McpException : java/lang/Exception {
public fun (I)V
public fun (ILjava/lang/String;)V
public fun (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
@@ -3272,7 +2934,7 @@ public class io/modelcontextprotocol/kotlin/sdk/types/McpException : java/lang/E
public final fun getData ()Lkotlinx/serialization/json/JsonElement;
}
-public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/MediaContent : io/modelcontextprotocol/kotlin/sdk/types/ContentBlock, io/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent {
+public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/MediaContent : io/modelcontextprotocol/kotlin/sdk/types/ContentBlock {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent$Companion;
}
@@ -3356,7 +3018,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Method$Defined : jav
public static final field ResourcesUnsubscribe Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
public static final field RootsList Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
public static final field SamplingCreateMessage Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
- public static final field ServerDiscover Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
public static final field TasksCancel Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
public static final field TasksGet Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
public static final field TasksList Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
@@ -3565,7 +3226,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/PingRequest$Companio
public final class io/modelcontextprotocol/kotlin/sdk/types/PingRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/PingRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
}
public final class io/modelcontextprotocol/kotlin/sdk/types/PingRequest_dslKt {
@@ -3879,8 +3540,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/RPCError$ErrorCode {
public static final field PARSE_ERROR I
public static final field REQUEST_TIMEOUT I
public static final field RESOURCE_NOT_FOUND I
- public static final field UNSUPPORTED_PROTOCOL_VERSION I
- public static final field URL_ELICITATION_REQUIRED I
}
public final class io/modelcontextprotocol/kotlin/sdk/types/ReadResourceRequest : io/modelcontextprotocol/kotlin/sdk/types/ClientRequest {
@@ -3917,7 +3576,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ReadResourceRequest$
public final class io/modelcontextprotocol/kotlin/sdk/types/ReadResourceRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/ReadResourceRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getUri ()Ljava/lang/String;
public final fun setUri (Ljava/lang/String;)V
}
@@ -4125,12 +3784,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/RequestMeta {
public static fun equals-impl (Lkotlinx/serialization/json/JsonObject;Ljava/lang/Object;)Z
public static final fun equals-impl0 (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Z
public static final fun get-impl (Lkotlinx/serialization/json/JsonObject;Ljava/lang/String;)Lkotlinx/serialization/json/JsonElement;
- public static final fun getClientCapabilities-impl (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ClientCapabilities;
- public static final fun getClientInfo-impl (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/Implementation;
public final fun getJson ()Lkotlinx/serialization/json/JsonObject;
- public static final fun getLogLevel-impl (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/LoggingLevel;
public static final fun getProgressToken-impl (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;
- public static final fun getProtocolVersion-impl (Lkotlinx/serialization/json/JsonObject;)Ljava/lang/String;
public static final fun getRelatedTask-impl (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/RelatedTaskMetadata;
public fun hashCode ()I
public static fun hashCode-impl (Lkotlinx/serialization/json/JsonObject;)I
@@ -4541,18 +4196,13 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Roots_dslKt {
public final class io/modelcontextprotocol/kotlin/sdk/types/SamplingMessage {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage$Companion;
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/Role;
- public final fun component2 ()Ljava/util/List;
- public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage;Lio/modelcontextprotocol/kotlin/sdk/types/Role;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage;
+ public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;
+ public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;)Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage;
+ public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage;Lio/modelcontextprotocol/kotlin/sdk/types/Role;Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessage;
public fun equals (Ljava/lang/Object;)Z
- public final fun getContent ()Ljava/util/List;
- public final fun getMeta ()Lkotlinx/serialization/json/JsonObject;
+ public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;
public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/types/Role;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
@@ -4580,15 +4230,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuild
public final fun user (Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;)V
}
-public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent : io/modelcontextprotocol/kotlin/sdk/types/WithMeta {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent$Companion;
- public abstract fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/Sampling_dslKt {
public static final fun assistant (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function0;)V
public static final fun assistantAudio (Lio/modelcontextprotocol/kotlin/sdk/types/SamplingMessageBuilder;Lkotlin/jvm/functions/Function1;)V
@@ -4604,26 +4245,22 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Sampling_dslKt {
public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Companion;
public fun ()V
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
+ public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;
public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;
public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;
public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
public final fun component5 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component6 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;
- public final fun component7 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component8 ()Ljava/util/Map;
- public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Ljava/util/Map;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
+ public final fun component6 ()Lkotlinx/serialization/json/JsonObject;
+ public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
+ public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;
public fun equals (Ljava/lang/Object;)Z
public final fun getCompletions ()Lkotlinx/serialization/json/JsonObject;
public final fun getExperimental ()Lkotlinx/serialization/json/JsonObject;
- public final fun getExtensions ()Ljava/util/Map;
public final fun getLogging ()Lkotlinx/serialization/json/JsonObject;
public final fun getPrompts ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Prompts;
public final fun getResources ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Resources;
- public final fun getTasks ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;
public final fun getTools ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
@@ -4706,97 +4343,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$R
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Companion;
- public fun ()V
- public fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;
- public final fun copy (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getCancel ()Lkotlinx/serialization/json/JsonObject;
- public final fun getList ()Lkotlinx/serialization/json/JsonObject;
- public final fun getRequests ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Companion;
- public fun ()V
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;
- public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getTools ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools$Companion;
- public fun ()V
- public fun (Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getCall ()Lkotlinx/serialization/json/JsonObject;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tasks$Requests$Tools$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities$Tools$Companion;
public fun ()V
@@ -4884,7 +4430,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/SetLevelRequest$Comp
public final class io/modelcontextprotocol/kotlin/sdk/types/SetLevelRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/SetLevelRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getLoggingLevel ()Lio/modelcontextprotocol/kotlin/sdk/types/LoggingLevel;
public final fun setLoggingLevel (Lio/modelcontextprotocol/kotlin/sdk/types/LoggingLevel;)V
}
@@ -4957,7 +4503,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/StopReason$Companion
public final fun getEndTurn--MeDoko ()Ljava/lang/String;
public final fun getMaxTokens--MeDoko ()Ljava/lang/String;
public final fun getStopSequence--MeDoko ()Ljava/lang/String;
- public final fun getToolUse--MeDoko ()Ljava/lang/String;
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
@@ -5050,7 +4595,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/SubscribeRequest$Com
public final class io/modelcontextprotocol/kotlin/sdk/types/SubscribeRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/SubscribeRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getUri ()Ljava/lang/String;
public final fun setUri (Ljava/lang/String;)V
}
@@ -5527,49 +5072,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ToolAnnotations$Comp
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolChoice {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Companion;
- public fun ()V
- public fun (Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;)V
- public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;
- public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getMode ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ToolChoice$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode : java/lang/Enum {
- public static final field Auto Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode$Companion;
- public static final field None Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;
- public static final field Required Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;
- public static fun getEntries ()Lkotlin/enums/EnumEntries;
- public static fun valueOf (Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;
- public static fun values ()[Lio/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolChoice$Mode$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/ToolExecution {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolExecution$Companion;
public fun ()V
@@ -5630,59 +5132,20 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ToolListChangedNotif
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolResultContent : io/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolResultContent$Companion;
- public fun (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Boolean;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Boolean;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Ljava/lang/String;
- public final fun component2 ()Ljava/util/List;
- public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component4 ()Ljava/lang/Boolean;
- public final fun component5 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Boolean;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolResultContent;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolResultContent;Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Boolean;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolResultContent;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getContent ()Ljava/util/List;
- public fun getMeta ()Lkotlinx/serialization/json/JsonObject;
- public final fun getStructuredContent ()Lkotlinx/serialization/json/JsonObject;
- public final fun getToolUseId ()Ljava/lang/String;
- public fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
- public fun hashCode ()I
- public final fun isError ()Ljava/lang/Boolean;
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ToolResultContent$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ToolResultContent$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolResultContent;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ToolResultContent;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolResultContent$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/ToolSchema {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema$Companion;
public fun ()V
- public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Ljava/lang/String;
- public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component3 ()Ljava/util/List;
- public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;
+ public fun (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V
+ public synthetic fun (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
+ public final fun component2 ()Ljava/util/List;
+ public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
+ public final fun copy (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;
+ public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;
public fun equals (Ljava/lang/Object;)Z
public final fun getDefs ()Lkotlinx/serialization/json/JsonObject;
public final fun getProperties ()Lkotlinx/serialization/json/JsonObject;
public final fun getRequired ()Ljava/util/List;
- public final fun getSchema ()Ljava/lang/String;
public final fun getType ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
@@ -5703,41 +5166,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ToolSchema$Companion
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolUseContent : io/modelcontextprotocol/kotlin/sdk/types/SamplingMessageContent {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent$Companion;
- public fun (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
- public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun component1 ()Ljava/lang/String;
- public final fun component2 ()Ljava/lang/String;
- public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
- public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
- public final fun copy (Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getId ()Ljava/lang/String;
- public final fun getInput ()Lkotlinx/serialization/json/JsonObject;
- public fun getMeta ()Lkotlinx/serialization/json/JsonObject;
- public final fun getName ()Ljava/lang/String;
- public fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ToolUseContent$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ToolUseContent;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/ToolUseContent$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/ToolsKt {
public static final fun error (Lio/modelcontextprotocol/kotlin/sdk/types/CallToolResult$Companion;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolResult;
public static synthetic fun error$default (Lio/modelcontextprotocol/kotlin/sdk/types/CallToolResult$Companion;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/CallToolResult;
@@ -5818,7 +5246,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequest$C
public final class io/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequestBuilder : io/modelcontextprotocol/kotlin/sdk/types/RequestBuilder {
public fun ()V
public fun build ()Lio/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequest;
- public synthetic fun build$io_modelcontextprotocol_kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
+ public synthetic fun build$kotlin_sdk_core ()Lio/modelcontextprotocol/kotlin/sdk/types/Request;
public final fun getUri ()Ljava/lang/String;
public final fun setUri (Ljava/lang/String;)V
}
@@ -5853,35 +5281,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequestPa
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData$Companion;
- public fun (Ljava/util/List;Ljava/lang/String;)V
- public final fun component1 ()Ljava/util/List;
- public final fun component2 ()Ljava/lang/String;
- public final fun copy (Ljava/util/List;Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getRequested ()Ljava/lang/String;
- public final fun getSupported ()Ljava/util/List;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/UnsupportedProtocolVersionData$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
public final class io/modelcontextprotocol/kotlin/sdk/types/UntitledMultiSelectEnumSchema : io/modelcontextprotocol/kotlin/sdk/types/MultiSelectEnumSchema {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/UntitledMultiSelectEnumSchema$Companion;
public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Lio/modelcontextprotocol/kotlin/sdk/types/UntitledMultiSelectEnumSchema$Items;Ljava/util/List;)V
@@ -5984,39 +5383,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/UntitledSingleSelect
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
-public final class io/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData {
- public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData$Companion;
- public fun (Ljava/util/List;)V
- public final fun component1 ()Ljava/util/List;
- public final fun copy (Ljava/util/List;)Lio/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData;
- public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData;Ljava/util/List;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData;
- public fun equals (Ljava/lang/Object;)Z
- public final fun getElicitations ()Ljava/util/List;
- public fun hashCode ()I
- public fun toString ()Ljava/lang/String;
-}
-
-public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
- public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData$$serializer;
- public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
- public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData;
- public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
- public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
- public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData;)V
- public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
- public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredData$Companion {
- public final fun serializer ()Lkotlinx/serialization/KSerializer;
-}
-
-public final class io/modelcontextprotocol/kotlin/sdk/types/UrlElicitationRequiredException : io/modelcontextprotocol/kotlin/sdk/types/McpException {
- public fun (Ljava/util/List;Ljava/lang/String;)V
- public synthetic fun (Ljava/util/List;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public final fun getElicitations ()Ljava/util/List;
-}
-
public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/WithMeta {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/WithMeta$Companion;
public abstract fun getMeta ()Lkotlinx/serialization/json/JsonObject;
diff --git a/kotlin-sdk-core/build.gradle.kts b/kotlin-sdk-core/build.gradle.kts
index 1d19f5d..0e702c4 100644
--- a/kotlin-sdk-core/build.gradle.kts
+++ b/kotlin-sdk-core/build.gradle.kts
@@ -4,10 +4,65 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
id("mcp.multiplatform")
- id("mcp.detekt")
id("mcp.publishing")
id("mcp.dokka")
alias(libs.plugins.kotlinx.binary.compatibility.validator)
+ alias(libs.plugins.openapi.generator)
+}
+
+/*
+tasks.withType>().configureEach {
+ dependsOn(tasks.openApiGenerate)
+}
+
+tasks.named("runKtlintCheckOverCommonMainSourceSet") {
+ dependsOn(tasks.openApiGenerate)
+}
+
+// Also ensure it runs before other relevant tasks
+tasks.withType().configureEach {
+ dependsOn(tasks.openApiGenerate)
+}
+
+tasks.withType().configureEach {
+ if (name.lowercase().contains("sourcesjar")) {
+ dependsOn(tasks.openApiGenerate)
+ }
+}
+ */
+
+openApiGenerate {
+ val schemaVersion = "2025-03-26" // or "2025-06-18" or "draft"
+ val schemaUrl =
+// "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/refs/heads/main/schema/$schemaVersion/schema.json"
+ "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/refs/tags/$schemaVersion/schema/$schemaVersion/schema.json"
+ generatorName = "kotlin"
+ remoteInputSpec = schemaUrl
+ outputDir = layout.buildDirectory.dir("generated-sources/openapi").get().asFile.absolutePath
+ packageName = "io.modelcontextprotocol.kotlin.sdk.models"
+ modelPackage = "io.modelcontextprotocol.kotlin.sdk.models"
+ apiPackage = "io.modelcontextprotocol.kotlin.sdk.api"
+ generateModelTests = false
+ generateModelDocumentation = false
+ cleanupOutput = false
+ skipValidateSpec = true // do not validate spec
+ library.set("multiplatform")
+ ignoreFileOverride.set("${layout.projectDirectory}/.openapi-generator-ignore")
+ globalProperties.set(
+ mapOf(
+ "supportingFiles" to "",
+ "models" to "", // or generate all models
+ ),
+ )
+ configOptions.set(
+ mapOf(
+ "omitGradleWrapper" to "true",
+ "enumPropertyNaming" to "UPPERCASE",
+ "dateLibrary" to "kotlinx-datetime",
+ "explicitApi" to "true",
+ "modelMutable" to "false",
+ ),
+ )
}
// Generation library versions
@@ -36,8 +91,10 @@ kotlin {
iosArm64()
iosX64()
iosSimulatorArm64()
+ watchosX64()
watchosArm64()
watchosSimulatorArm64()
+ tvosX64()
tvosArm64()
tvosSimulatorArm64()
js {
diff --git a/kotlin-sdk-core/detekt-baseline-commonMainSourceSet.xml b/kotlin-sdk-core/detekt-baseline-commonMainSourceSet.xml
new file mode 100644
index 0000000..d774a5b
--- /dev/null
+++ b/kotlin-sdk-core/detekt-baseline-commonMainSourceSet.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ CyclomaticComplexMethod:jsonUtils.kt:@OptIn(ExperimentalUnsignedTypes::class, ExperimentalSerializationApi::class) private fun convertToJsonElement: JsonElement
+ LongMethod:Protocol.kt:Protocol$public suspend fun <T : RequestResult> request: T
+ MagicNumber:completion.kt:CompleteResult.Completion$100
+ MatchingDeclarationName:capabilities.dsl.kt:ClientCapabilitiesBuilder
+ MatchingDeclarationName:methods.kt:Method
+ MaxLineLength:AbstractClientTransport.kt:AbstractClientTransport$*
+ MaxLineLength:ClientTransportState.kt:ClientTransportState.Initializing$*
+ MaxLineLength:Protocol.kt:Protocol$"Received progress notification: token=${notification.params.progressToken}, progress=${notification.params.progress}/${notification.params.total}"
+ MaxLineLength:Protocol.kt:Protocol$*
+ MaxLineLength:Protocol.kt:RequestOptions$*
+ MaxLineLength:Transport.kt:Transport$*
+ MaxLineLength:TransportSendOptions.kt:TransportSendOptions$*
+ ReturnCount:ReadBuffer.kt:ReadBuffer$public fun readMessage: JSONRPCMessage?
+ ThrowsCount:AbstractClientTransport.kt:AbstractClientTransport$public override suspend fun send
+ TooGenericExceptionCaught:AbstractClientTransport.kt:AbstractClientTransport$e: Exception
+ TooGenericExceptionCaught:Protocol.kt:Protocol$cause: Throwable
+ TooGenericExceptionCaught:Protocol.kt:Protocol$error: Throwable
+ TooGenericExceptionCaught:Protocol.kt:Protocol$sendError: Throwable
+ TooGenericExceptionCaught:ReadBuffer.kt:ReadBuffer$e: Exception
+ TooGenericExceptionCaught:WebSocketMcpTransport.kt:WebSocketMcpTransport$e: Exception
+ TooManyFunctions:Protocol.kt:Protocol
+
+
diff --git a/kotlin-sdk-core/detekt-baseline-main.xml b/kotlin-sdk-core/detekt-baseline-main.xml
index d483d7e..a22fbcd 100644
--- a/kotlin-sdk-core/detekt-baseline-main.xml
+++ b/kotlin-sdk-core/detekt-baseline-main.xml
@@ -2,6 +2,31 @@
+ CyclomaticComplexMethod:jsonUtils.kt:@OptIn(ExperimentalUnsignedTypes::class, ExperimentalSerializationApi::class) private fun convertToJsonElement: JsonElement
+ InjectDispatcher:utils.jvm.kt:IO
+ InstanceOfCheckForException:AbstractClientTransport.kt:AbstractClientTransport$e is McpException
+ LongMethod:Protocol.kt:Protocol$public suspend fun <T : RequestResult> request: T
+ MagicNumber:completion.kt:CompleteResult.Completion$100
+ MatchingDeclarationName:capabilities.dsl.kt:ClientCapabilitiesBuilder
+ MatchingDeclarationName:methods.kt:Method
+ MaxLineLength:AbstractClientTransport.kt:AbstractClientTransport$*
+ MaxLineLength:ClientTransportState.kt:ClientTransportState.Initializing$*
+ MaxLineLength:Protocol.kt:Protocol$"Received progress notification: token=${notification.params.progressToken}, progress=${notification.params.progress}/${notification.params.total}"
+ MaxLineLength:Protocol.kt:Protocol$*
+ MaxLineLength:Protocol.kt:RequestOptions$*
+ MaxLineLength:Transport.kt:Transport$*
+ MaxLineLength:TransportSendOptions.kt:TransportSendOptions$*
+ NoNameShadowing:Protocol.kt:Protocol$error
+ ReturnCount:ReadBuffer.kt:ReadBuffer$public fun readMessage: JSONRPCMessage?
+ ThrowsCount:AbstractClientTransport.kt:AbstractClientTransport$public override suspend fun send
+ TooGenericExceptionCaught:AbstractClientTransport.kt:AbstractClientTransport$e: Exception
+ TooGenericExceptionCaught:Protocol.kt:Protocol$cause: Throwable
+ TooGenericExceptionCaught:Protocol.kt:Protocol$error: Throwable
+ TooGenericExceptionCaught:Protocol.kt:Protocol$sendError: Throwable
+ TooGenericExceptionCaught:ReadBuffer.kt:ReadBuffer$e: Exception
+ TooGenericExceptionCaught:WebSocketMcpTransport.kt:WebSocketMcpTransport$e: Exception
+ TooManyFunctions:Protocol.kt:Protocol
UnsafeCallOnNullableType:Protocol.kt:Protocol$response!!
+ UseCheckNotNull:Protocol.kt:Protocol$check(error != null)
diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractClientTransport.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractClientTransport.kt
index 27013d3..5f67999 100644
--- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractClientTransport.kt
+++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractClientTransport.kt
@@ -99,10 +99,8 @@ public abstract class AbstractClientTransport : AbstractTransport() {
* proceeding with further operations. It is often used to enforce state invariants within the transport's
* lifecycle mechanics, enabling robust error handling and debugging in case of unexpected state transitions.
*
- * @param expected The expected transport state.
- * An exception will be thrown if the actual state does not match this value.
- * @param lazyMessage Function that builds the exception message from the actual state when the check fails.
- * Defaults to a message that compares the expected state against the observed one.
+ * @param expected The expected transport state. An exception will be thrown if the actual state does not match this value.
+ * @throws IllegalStateException If the current state does not match the expected state.
*/
protected fun checkState(
expected: ClientTransportState,
diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt
index a738289..7b88100 100644
--- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt
+++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt
@@ -6,13 +6,11 @@ import kotlin.concurrent.atomics.AtomicBoolean
import kotlin.concurrent.atomics.ExperimentalAtomicApi
/**
- * Base class for [Transport] implementations that handles the bookkeeping for [onClose], [onError]
- * and [onMessage] callback registration, exposing the composed callbacks to subclasses for invocation.
- *
- * The callbacks are composed: each call to [onClose], [onError], or [onMessage] chains the new block
- * after any previously registered ones.
+ * Implements [onClose], [onError] and [onMessage] functions of [Transport] providing
+ * corresponding [_onClose], [_onError] and [_onMessage] properties to use for an implementation.
*/
@OptIn(ExperimentalAtomicApi::class)
+@Suppress("PropertyName")
public abstract class AbstractTransport : Transport {
private val onCloseCalled = AtomicBoolean(false)
private var _onClose: (() -> Unit) = {}
diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Protocol.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Protocol.kt
index 908e524..43c0ae5 100644
--- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Protocol.kt
+++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Protocol.kt
@@ -27,34 +27,15 @@ import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.getAndUpdate
import kotlinx.atomicfu.update
-import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.PersistentMap
-import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.NonCancellable
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.currentCoroutineContext
-import kotlinx.coroutines.job
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.Semaphore
-import kotlinx.coroutines.sync.withPermit
-import kotlinx.coroutines.withContext
-import kotlinx.coroutines.withTimeoutOrNull
-import kotlinx.serialization.SerializationException
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.withTimeout
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.encodeToJsonElement
-import kotlin.coroutines.AbstractCoroutineContextElement
-import kotlin.coroutines.ContinuationInterceptor
-import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.cancellation.CancellationException
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
@@ -72,26 +53,23 @@ public typealias ProgressCallback = (Progress) -> Unit
/**
* Additional initialization options.
*
- * [handlerCoroutineContext] is read once when [Protocol.connect] attaches a transport and stays
- * fixed until the next [Protocol.connect].
- *
- * @property enforceStrictCapabilities whether to restrict emitted requests to only those the remote
- * side advertised support for. Local-side capability checks are unaffected. Defaults to `false` for
- * backward compatibility with SDK versions that did not advertise capabilities correctly.
* @property timeout default timeout for outgoing requests
- * @property handlerCoroutineContext coroutine context used to run inbound request and notification
- * handlers once initialization has completed. Must contain a real dispatching
- * [kotlin.coroutines.ContinuationInterceptor]. `Dispatchers.Unconfined` and unconfined test
- * dispatchers are unsupported, since handler resumptions would then run on the transport read loop
- * and reintroduce head-of-line blocking. Any [kotlinx.coroutines.Job] in this context is ignored.
*/
public open class ProtocolOptions(
+ /**
+ * Whether to restrict emitted requests to only those that the remote side has indicated
+ * that they can handle, through their advertised capabilities.
+ *
+ * Note that this DOES NOT affect checking of _local_ side capabilities, as it is
+ * considered a logic error to mis-specify those.
+ *
+ * Currently, this defaults to false, for backwards compatibility with SDK versions
+ * that did not advertise capabilities correctly.
+ * In the future, this will default to true.
+ */
public var enforceStrictCapabilities: Boolean = false,
+
public var timeout: Duration = DEFAULT_REQUEST_TIMEOUT,
- public var handlerCoroutineContext: CoroutineContext = Dispatchers.Default,
- // Internal handler-concurrency bounds, read once at connect and not part of the public surface.
- internal var maxConcurrentHandlers: Int = DEFAULT_MAX_CONCURRENT_HANDLERS,
- internal var maxInFlightHandlers: Int = DEFAULT_MAX_IN_FLIGHT_HANDLERS,
)
/**
@@ -99,27 +77,21 @@ public open class ProtocolOptions(
*/
public val DEFAULT_REQUEST_TIMEOUT: Duration = 60.seconds
-/** Default cap on concurrently executing inbound handlers per connection. */
-internal const val DEFAULT_MAX_CONCURRENT_HANDLERS: Int = 64
-
-/** Default cap on launched-but-not-completed inbound handlers per connection. */
-internal const val DEFAULT_MAX_IN_FLIGHT_HANDLERS: Int = 256
-
/**
* Options that can be given per request.
*
- * @param relatedRequestId if present, used to indicate to the transport which incoming request to
- * associate this outgoing message with.
- * @param resumptionToken the resumption token used to continue long-running requests that were interrupted.
- * Allows clients to reconnect and continue from where they left off, if supported by the transport.
- * @param onResumptionToken callback invoked when the resumption token changes, if supported by the transport.
- * Allows clients to persist the latest token for potential reconnection.
+ * @property relatedRequestId if present,
+ * `relatedRequestId` is used to indicate to the transport which incoming request to associate this outgoing message with.
+ * @property resumptionToken the resumption token used to continue long-running requests that were interrupted.
+ * This allows clients to reconnect and continue from where they left off, if supported by the transport.
+ * @property onResumptionToken a callback that is invoked when the resumption token changes, if supported by the transport.
+ * This allows clients to persist the latest token for potential reconnection.
* @property onProgress callback for progress notifications.
- * If set, requests progress notifications from the remote end (if supported);
- * when progress notifications are received, this callback is invoked.
+ * If set, requests progress notifications from the remote end (if supported).
+ * When progress notifications are received, this callback will be invoked.
* @property timeout a timeout for this request.
- * If exceeded, a [McpException] with code `RequestTimeout` is raised from [Protocol.request].
- * If not specified, [DEFAULT_REQUEST_TIMEOUT] is used.
+ * If exceeded, a McpException with code `RequestTimeout` will be raised from request().
+ * If not specified, `DEFAULT_REQUEST_TIMEOUT` will be used as the timeout.
*/
public class RequestOptions(
relatedRequestId: RequestId? = null,
@@ -155,7 +127,7 @@ public class RequestOptions(
override fun hashCode(): Int {
var result = super.hashCode()
- result = 31 * result + onProgress.hashCode()
+ result = 31 * result + (onProgress?.hashCode() ?: 0)
result = 31 * result + timeout.hashCode()
return result
}
@@ -166,88 +138,28 @@ public class RequestOptions(
/**
* Extra data given to request handlers.
- *
- * One instance exists per in-flight inbound request. It is passed as the second parameter to
- * low-level handlers registered via [Protocol.setRequestHandler] **and** installed into the
- * handler coroutine's [CoroutineContext], so code nested anywhere under the handler can reach it
- * via [currentRequestHandlerExtra] without parameter plumbing.
- *
- * @property requestId the JSON-RPC id of the request being handled
- * @property method the request's method
*/
-public class RequestHandlerExtra internal constructor(
- public val requestId: RequestId,
- public val method: Method,
- private val protocol: Protocol,
- internal val capturedTransport: Transport,
-) : AbstractCoroutineContextElement(Key) {
-
- /** Context key for retrieving the extra from a handler coroutine's context. */
- public companion object Key : CoroutineContext.Key
-
- /**
- * Sends a notification tagged with `relatedRequestId = `[requestId], letting transports
- * associate it with the request being handled (e.g. route it onto the request's SSE stream).
- */
- public suspend fun sendNotification(notification: Notification) {
- protocol.notification(notification, relatedRequestId = requestId)
- }
-
- /**
- * Sends a request to the peer tagged with `relatedRequestId = `[requestId].
- *
- * Mirrors [Protocol.request]; cancellation of the handler coroutine propagates into this call.
- */
- public suspend fun sendRequest(request: Request, options: RequestOptions? = null): T {
- val base = options ?: RequestOptions()
- return protocol.request(
- request,
- RequestOptions(
- relatedRequestId = requestId,
- resumptionToken = base.resumptionToken,
- onResumptionToken = base.onResumptionToken,
- onProgress = base.onProgress,
- timeout = base.timeout,
- ),
- )
- }
-}
-
-/** The [RequestHandlerExtra] of the MCP request being handled by the current coroutine, or `null` outside a handler. */
-public suspend fun currentRequestHandlerExtra(): RequestHandlerExtra? = currentCoroutineContext()[RequestHandlerExtra]
+public class RequestHandlerExtra
internal val COMPLETED = CompletableDeferred(Unit).also { it.complete(Unit) }
-/** Cap on how many recently-cancelled outbound request ids are remembered to quiet late responses/progress. */
-private const val CANCELLED_REQUEST_IDS_REMEMBERED = 256
-
-// Bypass set: exempt from both bound tiers so the connection cannot self-block.
-// Responses are not listed — they are handled inline before dispatch.
-private val CONTROL_METHODS: Set = setOf(
- Method.Defined.Ping.value,
- Method.Defined.NotificationsCancelled.value,
- Method.Defined.NotificationsProgress.value,
- Method.Defined.NotificationsInitialized.value,
-)
-
/**
* Implements MCP protocol framing on top of a pluggable transport, including
* features like request/response linking, notifications, and progress.
*
- * @property options protocol-level configuration; `null` falls back to defaults
+ * @property transport the active transport, or `null` if not connected
+ * @property requestHandlers registered request handlers keyed by method name
+ * @property notificationHandlers registered notification handlers keyed by method name
+ * @property responseHandlers pending response handlers keyed by request ID
+ * @property progressHandlers registered progress callbacks keyed by progress token
*/
public abstract class Protocol(@PublishedApi internal val options: ProtocolOptions?) {
- private val connectionRef: AtomicRef = atomic(null)
-
- /** The active transport, or `null` if not connected. */
- public val transport: Transport?
- get() = connectionRef.value?.transport
+ public var transport: Transport? = null
+ private set
private val _requestHandlers:
AtomicRef RequestResult?>> =
atomic(persistentMapOf())
-
- /** Registered request handlers keyed by method name. */
public val requestHandlers: Map<
String,
suspend (
@@ -259,41 +171,20 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
private val _notificationHandlers =
atomic(persistentMapOf Unit>())
-
- /** Registered notification handlers keyed by method name. */
public val notificationHandlers: Map Unit>
get() = _notificationHandlers.value
private val _responseHandlers:
AtomicRef Unit>> =
atomic(persistentMapOf())
-
- /** Pending response handlers keyed by request ID. */
public val responseHandlers: Map Unit>
get() = _responseHandlers.value
private val _progressHandlers: AtomicRef> =
atomic(persistentMapOf())
-
- /** Registered progress callbacks keyed by progress token. */
public val progressHandlers: Map
get() = _progressHandlers.value
- private val recentlyCancelledRequestIds: AtomicRef> =
- atomic(persistentListOf())
-
- /** Remembers a locally-cancelled outbound request id so its late response/progress is quieted. */
- private fun rememberCancelledRequestId(id: RequestId) {
- recentlyCancelledRequestIds.update { current ->
- val appended = current.adding(id)
- if (appended.size > CANCELLED_REQUEST_IDS_REMEMBERED) appended.removingAt(0) else appended
- }
- }
-
- /** Whether [id] was recently cancelled locally (see [rememberCancelledRequestId]). */
- private fun isRecentlyCancelled(id: RequestId?): Boolean =
- id != null && recentlyCancelledRequestIds.value.contains(id)
-
/**
* Callback for when the connection is closed for any reason.
*
@@ -323,11 +214,6 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
public var fallbackNotificationHandler: (suspend (notification: JSONRPCNotification) -> Unit)? = null
init {
- setNotificationHandler(Method.Defined.NotificationsCancelled) { notification ->
- handleCancelledNotification(notification)
- COMPLETED
- }
-
setNotificationHandler(Method.Defined.NotificationsProgress) { notification ->
onProgress(notification)
COMPLETED
@@ -338,64 +224,15 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
}
}
- /**
- * Enables concurrent dispatch of inbound messages on the current connection.
- *
- * Called by subclasses when the MCP initialization phase completes (the client after the
- * initialize exchange; the server session on receiving `notifications/initialized`). Until
- * then, inbound messages are handled inline in arrival order. Reset by [connect].
- */
- protected fun enableConcurrentDispatch() {
- val connection = connectionRef.value ?: return
- connection.concurrentDispatchEnabled.value = true
- }
-
- /**
- * Router hook invoked whenever `notifications/initialized` is received, before handler lookup.
- *
- * Runs regardless of which notification handler is registered for the method, so replacing the
- * handler via [setNotificationHandler] cannot disable subclass initialization logic.
- */
- protected open fun onInitializedNotification() {}
-
/**
* Attaches to the given transport, starts it, and starts listening for messages.
*
- * The Protocol object assumes ownership of the Transport, replacing any callbacks that have
- * already been set, and expects that it is the only user of the Transport instance going forward.
- *
- * @throws IllegalStateException if this Protocol is already connected; call [close] first.
+ * The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
*/
public open suspend fun connect(transport: Transport) {
- val maxConcurrent = options?.maxConcurrentHandlers ?: DEFAULT_MAX_CONCURRENT_HANDLERS
- val maxInFlight = options?.maxInFlightHandlers ?: DEFAULT_MAX_IN_FLIGHT_HANDLERS
- require(maxConcurrent > 0) { "maxConcurrentHandlers must be positive, but was $maxConcurrent" }
- require(maxInFlight >= maxConcurrent) {
- "maxInFlightHandlers ($maxInFlight) must be >= maxConcurrentHandlers ($maxConcurrent)"
- }
- val handlerContext = (options?.handlerCoroutineContext ?: Dispatchers.Default).minusKey(Job)
- val interceptor = handlerContext[ContinuationInterceptor]
- if (interceptor === Dispatchers.Unconfined ||
- interceptor?.let { it::class.simpleName?.contains("Unconfined") } == true
- ) {
- logger.warn {
- "handlerCoroutineContext uses an unconfined dispatcher ($interceptor); " +
- "handler resumptions will run on the transport read loop and may block unrelated messages"
- }
- }
- val connection = Connection(
- transport = transport,
- handlerScope = CoroutineScope(SupervisorJob() + handlerContext + CoroutineName("McpProtocol")),
- executionSemaphore = Semaphore(maxConcurrent),
- maxInFlightHandlers = maxInFlight,
- )
- if (!connectionRef.compareAndSet(null, connection)) {
- connection.handlerScope.cancel()
- error("Protocol is already connected; close() the current connection before connecting a new transport")
- }
-
+ this.transport = transport
transport.onClose {
- doClose(connection)
+ doClose()
}
transport.onError {
@@ -403,43 +240,24 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
}
transport.onMessage { message ->
- if (connectionRef.value !== connection) {
- logger.trace { "Dropping message received after close: ${message::class.simpleName}" }
- return@onMessage
- }
when (message) {
is JSONRPCResponse -> onResponse(message, null)
- is JSONRPCRequest -> dispatchRequest(connection, message)
- is JSONRPCNotification -> dispatchNotification(connection, message)
+ is JSONRPCRequest -> onRequest(message)
+ is JSONRPCNotification -> onNotification(message)
is JSONRPCError -> onResponse(null, message)
is JSONRPCEmptyMessage -> Unit
}
}
logger.info { "Starting transport" }
- try {
- transport.start()
- } catch (cause: Throwable) {
- // Roll back so the Protocol can be reconnected after a failed start; idempotent with
- // a transport-initiated doClose (both CAS on the same connection).
- if (connectionRef.compareAndSet(connection, null)) {
- connection.handlerScope.cancel()
- }
- throw cause
- }
+ transport.start()
}
- private fun doClose(connection: Connection) {
- // A stale onClose from a previous transport must not tear down the successor connection.
- if (!connectionRef.compareAndSet(connection, null)) {
- logger.trace { "Ignoring close signal from a stale transport" }
- return
- }
- val handlersToNotify = _responseHandlers.getAndSet(persistentMapOf()).values.toList()
+ private fun doClose() {
+ val handlersToNotify = _responseHandlers.value.values.toList()
+ _responseHandlers.getAndSet(persistentMapOf())
_progressHandlers.getAndSet(persistentMapOf())
- recentlyCancelledRequestIds.getAndSet(persistentListOf())
- connection.inFlightRequestJobs.getAndSet(persistentMapOf())
- connection.handlerScope.cancel()
+ transport = null
onClose()
val error = McpException(RPCError.ErrorCode.CONNECTION_CLOSED, "Connection closed")
@@ -448,110 +266,6 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
}
}
- private suspend fun dispatchRequest(connection: Connection, request: JSONRPCRequest) {
- if (!connection.concurrentDispatchEnabled.value) {
- // Serial phase (before the peer completes MCP initialization): inline, in arrival
- // order — the delivering coroutine awaits the handler.
- onRequest(request, connection, trackCancellation = false)
- return
- }
- val bypass = request.method in CONTROL_METHODS
- if (!bypass && !tryAdmit(connection)) {
- rejectOverloadedRequest(connection, request)
- return
- }
- // UNDISPATCHED starts the handler synchronously in the delivering coroutine, preserving
- // arrival order, then moves to handlerCoroutineContext at its first suspension.
- connection.handlerScope.launch(start = CoroutineStart.UNDISPATCHED) {
- // Register before any suspension point so a following notifications/cancelled can find
- // and cancel even a handler still parked on admission.
- registerInFlight(connection, request.id, coroutineContext.job)
- try {
- if (bypass) {
- onRequest(request, connection, trackCancellation = true)
- } else {
- connection.executionSemaphore.withPermit {
- onRequest(request, connection, trackCancellation = true)
- }
- }
- } finally {
- if (!bypass) connection.inFlightCount.decrementAndGet()
- }
- }
- }
-
- private suspend fun dispatchNotification(connection: Connection, notification: JSONRPCNotification) {
- if (notification.method == Method.Defined.NotificationsInitialized.value) {
- onInitializedNotification()
- }
- if (!connection.concurrentDispatchEnabled.value) {
- onNotification(notification)
- return
- }
- val bypass = notification.method in CONTROL_METHODS
- if (!bypass && !tryAdmit(connection)) {
- val dropped = IllegalStateException(
- "Dropped notification ${notification.method}: too many in-flight messages",
- )
- logger.warn { dropped.message }
- onError(dropped)
- return
- }
- connection.handlerScope.launch(start = CoroutineStart.UNDISPATCHED) {
- try {
- if (bypass) {
- onNotification(notification)
- } else {
- connection.executionSemaphore.withPermit { onNotification(notification) }
- }
- } finally {
- if (!bypass) connection.inFlightCount.decrementAndGet()
- }
- }
- }
-
- private fun tryAdmit(connection: Connection): Boolean {
- while (true) {
- val current = connection.inFlightCount.value
- if (current >= connection.maxInFlightHandlers) return false
- if (connection.inFlightCount.compareAndSet(current, current + 1)) return true
- }
- }
-
- private suspend fun rejectOverloadedRequest(connection: Connection, request: JSONRPCRequest) {
- logger.warn {
- "Rejecting request ${request.method} (id: ${request.id}): " +
- "in-flight handler limit (${connection.maxInFlightHandlers}) reached"
- }
- try {
- connection.transport.send(
- JSONRPCError(
- id = request.id,
- error = RPCError(
- code = RPCError.ErrorCode.INTERNAL_ERROR,
- message = "Server is busy: too many in-flight messages",
- ),
- ),
- )
- } catch (e: CancellationException) {
- throw e
- } catch (cause: Throwable) {
- logger.error(cause) { "Failed to send busy rejection for request id: ${request.id}" }
- onError(cause)
- }
- }
-
- private fun handleCancelledNotification(notification: CancelledNotification) {
- val connection = connectionRef.value ?: return
- val requestId = notification.params.requestId
- val job = connection.inFlightRequestJobs.value[requestId]
- if (job == null) {
- logger.trace { "Ignoring cancellation for unknown or already-completed request: $requestId" }
- return
- }
- job.cancel("Cancelled by peer: ${notification.params.reason ?: "unknown"}")
- }
-
private suspend fun onNotification(notification: JSONRPCNotification) {
logger.trace { "Received notification: ${notification.method}" }
@@ -563,24 +277,21 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
}
try {
handler(notification)
- } catch (e: CancellationException) {
- throw e
} catch (cause: Throwable) {
logger.error(cause) { "Error handling notification: ${notification.method}" }
onError(cause)
}
}
- private suspend fun onRequest(request: JSONRPCRequest, connection: Connection, trackCancellation: Boolean) {
+ private suspend fun onRequest(request: JSONRPCRequest) {
logger.trace { "Received request: ${request.method} (id: ${request.id})" }
val handler = requestHandlers[request.method] ?: fallbackRequestHandler
- val capturedTransport = connection.transport
if (handler === null) {
logger.trace { "No handler found for request: ${request.method}" }
try {
- capturedTransport.send(
+ transport?.send(
JSONRPCError(
id = request.id,
error = RPCError(
@@ -589,8 +300,6 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
),
),
)
- } catch (e: CancellationException) {
- throw e
} catch (cause: Throwable) {
logger.error(cause) { "Error sending method not found response" }
onError(cause)
@@ -598,77 +307,37 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
return
}
- val extra = RequestHandlerExtra(
- requestId = request.id,
- method = Method.from(request.method),
- protocol = this,
- capturedTransport = capturedTransport,
- )
-
- // Registration in the in-flight registry already happened in dispatchRequest's launch
- // block (synchronously, pre-suspension). Here we only need the job for suppression checks.
- val handlerJob = if (trackCancellation) currentCoroutineContext().job else null
-
- withContext(extra) {
- try {
- val result = handler(request, extra)
- logger.trace { "Request handled successfully: ${request.method} (id: ${request.id})" }
- if (handlerJob?.isCancelled == true) {
- logger.trace { "Suppressing response for request cancelled by peer (id: ${request.id})" }
- return@withContext
- }
- capturedTransport.send(
- JSONRPCResponse(
- id = request.id,
- result = result ?: EmptyResult(),
- ),
- )
- } catch (e: CancellationException) {
- // Intentional exception to the always-rethrow-CE convention, only at this boundary.
- if (handlerJob == null) throw e // serial phase: inline dispatch is not response-suppressed
- if (handlerJob.isCancelled) throw e // genuine peer or close cancel: suppress the response
- // CE escaped a live handler job (e.g. a leaked inner withTimeout). Answer
- // INTERNAL_ERROR so the peer does not hang until its own timeout.
- respondWithError(capturedTransport, request, e)
- } catch (cause: Throwable) {
- if (handlerJob?.isCancelled == true) {
- logger.trace { "Suppressing error response for request cancelled by peer (id: ${request.id})" }
- return@withContext
- }
- respondWithError(capturedTransport, request, cause)
- }
- }
- }
-
- private fun registerInFlight(connection: Connection, id: RequestId, job: Job) {
- val previous = connection.inFlightRequestJobs.getAndUpdate { current -> current.putting(id, job) }[id]
- if (previous != null) {
- logger.warn { "Duplicate in-flight request id $id; replacing the previous handler job" }
- }
- job.invokeOnCompletion {
- // Identity guard: never evict a successor job registered under a reused id.
- connection.inFlightRequestJobs.update { current ->
- if (current[id] === job) current.removing(id) else current
- }
- }
- }
-
- private suspend fun respondWithError(transport: Transport, request: JSONRPCRequest, cause: Throwable) {
- logger.error(cause) { "Error handling request: ${request.method} (id: ${request.id})" }
+ @Suppress("TooGenericExceptionCaught", "InstanceOfCheckForException")
try {
- val rpcError = if (cause is McpException) {
- RPCError(code = cause.code, message = cause.message.orEmpty(), data = cause.data)
- } else {
- RPCError(code = RPCError.ErrorCode.INTERNAL_ERROR, message = cause.message ?: "Internal error")
- }
- transport.send(JSONRPCError(id = request.id, error = rpcError))
+ val result = handler(request, RequestHandlerExtra())
+ logger.trace { "Request handled successfully: ${request.method} (id: ${request.id})" }
+
+ transport?.send(
+ JSONRPCResponse(
+ id = request.id,
+ result = result ?: EmptyResult(),
+ ),
+ )
} catch (e: CancellationException) {
throw e
- } catch (sendError: Throwable) {
- logger.error(sendError) {
- "Failed to send error response for request: ${request.method} (id: ${request.id})"
+ } catch (cause: Throwable) {
+ logger.error(cause) { "Error handling request: ${request.method} (id: ${request.id})" }
+
+ try {
+ val rpcError = if (cause is McpException) {
+ RPCError(code = cause.code, message = cause.message.orEmpty(), data = cause.data)
+ } else {
+ RPCError(code = RPCError.ErrorCode.INTERNAL_ERROR, message = cause.message ?: "Internal error")
+ }
+ transport?.send(JSONRPCError(id = request.id, error = rpcError))
+ } catch (e: CancellationException) {
+ throw e
+ } catch (sendError: Throwable) {
+ logger.error(sendError) {
+ "Failed to send error response for request: ${request.method} (id: ${request.id})"
+ }
+ // Optionally implement fallback behavior here
}
- onError(sendError)
}
}
@@ -683,10 +352,6 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
val handler = _progressHandlers.value[progressToken]
if (handler == null) {
- if (isRecentlyCancelled(progressToken)) {
- logger.trace { "Ignoring progress for a locally cancelled request: $progressToken" }
- return
- }
val error = Error(
"Received a progress notification for an unknown token: ${McpJson.encodeToString(notification)}",
)
@@ -703,7 +368,7 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
val oldResponseHandlers = _responseHandlers.getAndUpdate { current ->
if (messageId != null && messageId in current) {
- current.removing(messageId)
+ current.remove(messageId)
} else {
current
}
@@ -712,12 +377,8 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
val handler = oldResponseHandlers[messageId]
if (handler != null) {
- messageId?.let { msg -> _progressHandlers.update { it.removing(msg) } }
+ messageId?.let { msg -> _progressHandlers.update { it.remove(msg) } }
} else {
- if (isRecentlyCancelled(messageId)) {
- logger.trace { "Ignoring response for a locally cancelled request: $messageId" }
- return
- }
onError(
IllegalStateException(
"Received a response for an unknown message ID: ${McpJson.encodeToString(error ?: response)}",
@@ -729,8 +390,8 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
if (response != null) {
handler(response, null)
} else {
- checkNotNull(error)
- val mcpException = McpException.fromError(
+ check(error != null)
+ val mcpException = McpException(
code = error.error.code,
message = error.error.message,
data = error.error.data,
@@ -775,8 +436,7 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
public suspend fun request(request: Request, options: RequestOptions? = null): T {
logger.trace { "Sending request: ${request.method}" }
val result = CompletableDeferred()
- val connection = connectionRef.value ?: error("Not connected")
- val transport = connection.transport
+ val transport = transport ?: error("Not connected")
if (this@Protocol.options?.enforceStrictCapabilities == true) {
assertCapabilityForMethod(request.method)
@@ -786,7 +446,7 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
options?.onProgress?.let { progressHandler ->
logger.trace { "Registering progress handler for request id: $id" }
_progressHandlers.update { current ->
- current.putting(id, progressHandler)
+ current.put(id, progressHandler)
}
val paramsObject = (this.params as? JsonObject) ?: JsonObject(emptyMap())
@@ -805,99 +465,57 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
val jsonRpcRequestId = jsonRpcRequest.id
_responseHandlers.update { current ->
- current.putting(jsonRpcRequestId) { response, error ->
+ current.put(jsonRpcRequestId) { response, error ->
if (error != null) {
result.completeExceptionally(error)
- return@putting
+ return@put
}
try {
@Suppress("UNCHECKED_CAST")
result.complete(response!!.result as T)
- } catch (e: Throwable) {
- result.completeExceptionally(e)
+ } catch (error: Throwable) {
+ result.completeExceptionally(error)
}
}
}
- if (connectionRef.value !== connection) {
- // Connection closed or replaced during registration; nothing would complete this handler.
- _responseHandlers.update { it.removing(jsonRpcRequestId) }
- _progressHandlers.update { it.removing(jsonRpcRequestId) }
- throw McpException(RPCError.ErrorCode.CONNECTION_CLOSED, "Connection closed")
- }
+ val cancel: suspend (Throwable) -> Unit = { reason: Throwable ->
+ _responseHandlers.update { current -> current.remove(jsonRpcRequestId) }
+ _progressHandlers.update { current -> current.remove(jsonRpcRequestId) }
- val cancelPending: suspend (reason: Throwable, notifyPeer: Boolean) -> Unit = { reason, notifyPeer ->
- // Remember before removing: a racing onResponse/onProgress that sees the handler gone
- // must also see the id, or it reports a late message as unknown. Order is load-bearing.
- rememberCancelledRequestId(jsonRpcRequestId)
- _responseHandlers.update { current -> current.removing(jsonRpcRequestId) }
- _progressHandlers.update { current -> current.removing(jsonRpcRequestId) }
+ val notification = CancelledNotification(
+ params = CancelledNotificationParams(
+ requestId = jsonRpcRequestId,
+ reason = reason.message ?: "Unknown",
+ ),
+ )
- if (notifyPeer) {
- val notification = CancelledNotification(
- params = CancelledNotificationParams(
- requestId = jsonRpcRequestId,
- reason = reason.message ?: "Unknown",
- ),
- )
- transport.send(notification.toJSON(), options)
- }
+ val jsonRpcNotification = notification.toJSON()
+
+ transport.send(jsonRpcNotification, options)
result.completeExceptionally(reason)
}
- // The MCP spec forbids cancelling `initialize`; local cleanup still runs,
- // but no notifications/cancelled goes on the wire (timeout and cancel paths alike).
- val notifyPeerOnCancel = request.method != Method.Defined.Initialize
-
val timeout = options?.timeout ?: DEFAULT_REQUEST_TIMEOUT
try {
- val response = withTimeoutOrNull(timeout) {
+ withTimeout(timeout) {
logger.trace { "Sending request message with id: $jsonRpcRequestId" }
- transport.send(jsonRpcRequest, options)
- result.await()
+ this@Protocol.transport?.send(jsonRpcRequest, options)
}
- if (response == null) {
- // Our own request timeout expired. An outer withTimeout's cancellation propagates
- // through withTimeoutOrNull instead of returning null and is handled by the catch below.
- logger.error { "Request timed out after ${timeout.inWholeMilliseconds}ms: ${request.method}" }
- val timeoutError = McpException(
+ return result.await()
+ } catch (cause: TimeoutCancellationException) {
+ logger.error { "Request timed out after ${timeout.inWholeMilliseconds}ms: ${request.method}" }
+ cancel(
+ McpException(
code = RPCError.ErrorCode.REQUEST_TIMEOUT,
message = "Request timed out",
data = JsonObject(mutableMapOf("timeout" to JsonPrimitive(timeout.inWholeMilliseconds))),
- )
- withContext(NonCancellable) {
- try {
- cancelPending(timeoutError, notifyPeerOnCancel)
- } catch (e: Throwable) {
- logger.warn(e) { "Failed to notify peer about timed-out request" }
- onError(e)
- }
- }
- result.cancel()
- throw timeoutError
- }
- return response
- } catch (cause: CancellationException) {
- // Plain caller cancellation, or an outer withTimeout that propagated through
- // withTimeoutOrNull (only our own deadline returns null there). NonCancellable so the
- // cancellation notification still goes out even though the caller's job is cancelled.
- // A secondary send failure must never replace the original CancellationException.
- withContext(NonCancellable) {
- try {
- cancelPending(cause, notifyPeerOnCancel)
- } catch (e: Throwable) {
- logger.warn(e) { "Failed to notify peer about cancelled request" }
- onError(e)
- }
- }
+ ),
+ )
+ result.cancel(cause)
throw cause
- } finally {
- // Backstop for the failed-send path. The success, timeout and cancellation paths already
- // removed these handlers, so this only bites when the outbound send failed.
- _responseHandlers.update { it.removing(jsonRpcRequestId) }
- _progressHandlers.update { it.removing(jsonRpcRequestId) }
}
}
@@ -918,11 +536,6 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
* Registers a handler to invoke when this protocol object receives a request with the given method.
*
* Note that this will replace any previous request handler for the same method.
- *
- * Handlers may run concurrently after initialization and are cancelled cooperatively, so always
- * re-throw [CancellationException]. Replacing a built-in control-method handler (`ping`,
- * `notifications/cancelled`, `notifications/progress`, `notifications/initialized`) with slow
- * code degrades connection liveness.
*/
public inline fun setRequestHandler(
method: Method,
@@ -938,60 +551,31 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
block: suspend (T, RequestHandlerExtra) -> RequestResult?,
) {
assertRequestHandlerCapability(method)
- val wrapped = wrapRequestHandler(method, block)
_requestHandlers.update { current ->
- current.putting(method.value) { jSONRPCRequest, extraHandler ->
- val request = try {
- jSONRPCRequest.fromJSON()
- } catch (cause: SerializationException) {
- throw McpException(
- code = RPCError.ErrorCode.INVALID_PARAMS,
- message = "Invalid params",
- cause = cause,
- )
- }
- val response = wrapped(request as T, extraHandler)
+ current.put(method.value) { jSONRPCRequest, extraHandler ->
+ val request = jSONRPCRequest.fromJSON()
+ val response = block(request as T, extraHandler)
response
}
}
}
- /**
- * Subclass hook to wrap an incoming-request handler before it is registered.
- *
- * Called once by [setRequestHandler] during registration. Subclasses may return a
- * new function that, when invoked, performs additional checks (capability gates,
- * schema validation, etc.) before delegating to [block]. The default implementation
- * is the identity.
- */
- @Suppress("UNUSED_PARAMETER")
- protected open fun