refactor: explicit locked detekt config (#729)

Migrate detekt to a convention plugin in `buildSrc`, switch the project
to a fully explicit YAML config (`buildUponDefaultConfig = false`), fix
the real findings that this stricter setup revealed, and remove
`@Suppress` annotations that became unnecessary after the rule changes.

## Motivation and Context
- Explicit visibility into the rule set
- Lock against detekt version upgrades

## Breaking Changes
None

## Types of changes
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update

## Checklist
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed
This commit is contained in:
Pavel Gorgulov 2026-04-29 12:31:49 +02:00 • committed by GitHub
commit 64804a05c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
80 changed files with 1106 additions and 507 deletions

View file

@ -5,40 +5,62 @@ description: "Add KDoc documentation to Kotlin public API. Use whenever the user
# KDoc Generator
Add KDoc comments to public Kotlin API declarations.
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.
## What to document
## Tag placement
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
| 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` |
Include `@Deprecated` elements.
## Style
## Context
- 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.
Read the implementation, not just the signature, to write accurate descriptions. Understanding what the code actually does prevents superficial or misleading documentation.
## Examples
## KDoc format
**Class/interface example:**
**Class with mixed primary-constructor parameters:**
````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
* @property timeout idle timeout before a session is evicted
* @param scope coroutine scope for session lifecycle tasks
* @param secret used internally for session token signing
*/
public class SessionManager(
public val maxSessions: Int, // public val → @property
scope: CoroutineScope, // no val/var → @param
private val secret: ByteArray, // private val → @param
)
````
**Function example:**
**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:**
````kotlin
/**
@ -46,10 +68,7 @@ Read the implementation, not just the signature, to write accurate descriptions.
*
* 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}")))
* }
* ```
@ -60,15 +79,3 @@ Read the implementation, not just the signature, to write accurate descriptions.
* @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

View file

@ -34,7 +34,7 @@ jobs:
include:
- os: ubuntu-latest
job-name: "Linux Tests"
gradle-tasks: "ktlintCheck build"
gradle-tasks: "build"
max-workers: 3
- os: macos-latest

View file

@ -1,8 +1,5 @@
import dev.detekt.gradle.extensions.FailOnSeverity
plugins {
id("mcp.dokka")
alias(libs.plugins.detekt)
alias(libs.plugins.ktlint)
alias(libs.plugins.kover)
}
@ -23,16 +20,6 @@ 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 {

View file

@ -8,6 +8,7 @@ repositories {
}
dependencies {
implementation(libs.detekt.gradle)
implementation(libs.kotlin.gradle)
implementation(libs.kotlin.serialization)
implementation(libs.kotlinx.atomicfu.gradle)

View file

@ -0,0 +1,23 @@
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<Detekt>().configureEach {
exclude("**/generated/**", "**/generated-sources/**", "**/build/**")
}
pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
tasks.named("detekt").configure {
dependsOn("detektMainJvm")
}
}

View file

@ -57,12 +57,3 @@ kotlin {
explicitApi = ExplicitApiMode.Strict
jvmToolchain(21)
}
tasks.named("detekt").configure {
dependsOn(
"detektMainJvm",
"detektCommonMainSourceSet",
"detektTestJvm",
"detektCommonTestSourceSet",
)
}

View file

@ -3,41 +3,840 @@ config:
warningsAsErrors: false
checkExhaustiveness: false
complexity:
LargeClass:
excludes: &testFolders
- '**/test/**'
- '**/commonTest/**'
- '**/jvmTest/**'
- '**/jsTest/**'
- '**/iosTest/**'
LongMethod:
excludes: *testFolders
processors:
active: false
coroutines:
InjectDispatcher:
excludes: *testFolders
empty-blocks:
EmptyFunctionBlock:
excludes: *testFolders
console-reports:
active: true
exclude:
- 'ProjectStatisticsReport'
- 'ComplexityReport'
- 'NotificationReport'
- 'IssuesReport'
- 'FileBasedIssuesReport'
# - 'LiteIssuesReport'
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
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
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: []
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
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
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']
excludeImportStatements: false
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.<init>(kotlin.Double)'
- reason: 'using `BigDecimal(String)` can result in a `NumberFormatException`. Use `String.toBigDecimalOrNull()`'
value: 'java.math.BigDecimal.<init>(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.*'

View file

@ -45,7 +45,6 @@ internal const val WAV_BASE64 = "UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABA
private val logger = KotlinLogging.logger {}
@Suppress("LongMethod")
fun Server.registerConformanceTools() {
// 1. Simple text
addTool(

View file

@ -2,7 +2,7 @@
# plugins version
atomicfu = "0.32.1"
binaryCompatibilityValidatorPlugin = "0.18.1"
detekt = "2.0.0-alpha.2"
detekt = "2.0.0-alpha.3"
dokka = "2.2.0"
kotlin = "2.3.21"
kover = "0.9.8"
@ -28,6 +28,7 @@ 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" }
@ -75,7 +76,6 @@ 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" }

View file

@ -1,5 +1,6 @@
plugins {
id("mcp.multiplatform")
id("mcp.detekt")
}
kotlin {

View file

@ -1,17 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>AbstractClassCanBeConcreteClass:AbstractPromptIntegrationTest.kt:AbstractPromptIntegrationTest$AbstractPromptIntegrationTest</ID>
<ID>AbstractClassCanBeConcreteClass:AbstractResourceIntegrationTest.kt:AbstractResourceIntegrationTest$AbstractResourceIntegrationTest</ID>
<ID>AbstractClassCanBeConcreteClass:AbstractToolIntegrationTest.kt:AbstractToolIntegrationTest$AbstractToolIntegrationTest</ID>
<ID>AbstractClassCanBeConcreteClass:BaseTransportTest.kt:BaseTransportTest$BaseTransportTest</ID>
<ID>CyclomaticComplexMethod:AbstractToolIntegrationTest.kt:AbstractToolIntegrationTest$private fun setupCalculatorTool</ID>
<ID>ForbiddenComment:StdioClientTransportTest.kt:StdioClientTransportTest$// TODO: fix running on windows</ID>
<ID>MatchingDeclarationName:PromptIntegrationTestSse.kt:SchemaPromptIntegrationTestSse : AbstractPromptIntegrationTest</ID>
<ID>ThrowsCount:AbstractPromptIntegrationTest.kt:AbstractPromptIntegrationTest$override fun configureServer</ID>
<ID>UseCheckOrError:ClientTest.kt:ClientTest.&lt;no name provided>$throw IllegalStateException("Test error")</ID>
<ID>UseOrEmpty:AbstractToolIntegrationTest.kt:AbstractToolIntegrationTest$(request.params.arguments?.get("tags") as? JsonArray)?.mapNotNull { (it as? JsonPrimitive)?.content } ?: emptyList()</ID>
<ID>UseRequire:AbstractPromptIntegrationTest.kt:AbstractPromptIntegrationTest$throw IllegalArgumentException("Missing required argument: $argName")</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -4,6 +4,7 @@ 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)

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize</ID>
<ID>LongMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize</ID>
<ID>MaxLineLength:StdioClientTransport.kt:StdioClientTransport$*</ID>
<ID>ThrowsCount:SseClientTransport.kt:SseClientTransport$private suspend fun CoroutineScope.collectMessages</ID>
<ID>ThrowsCount:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$@Suppress("ReturnCount", "CyclomaticComplexMethod") override suspend fun performSend</ID>
<ID>TooGenericExceptionCaught:Client.kt:Client$error: Throwable</ID>
<ID>TooGenericExceptionCaught:SseClientTransport.kt:SseClientTransport$e: Throwable</ID>
<ID>TooGenericExceptionCaught:StdioClientTransport.kt:StdioClientTransport$e: Throwable</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,8 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}"""</ID>
<ID>MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/resumed","params":{"fromToken":"$lastEventIdSent"}}"""</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -2,18 +2,6 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize</ID>
<ID>InjectDispatcher:StdioClientTransport.kt:StdioClientTransport$Default</ID>
<ID>InjectDispatcher:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$Default</ID>
<ID>LongMethod:StdioClientTransport.kt:StdioClientTransport$override suspend fun initialize</ID>
<ID>MaxLineLength:StdioClientTransport.kt:StdioClientTransport$*</ID>
<ID>NoNameShadowing:StdioClientTransport.kt:StdioClientTransport$source</ID>
<ID>ThrowsCount:SseClientTransport.kt:SseClientTransport$private suspend fun CoroutineScope.collectMessages</ID>
<ID>ThrowsCount:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$@Suppress("ReturnCount", "CyclomaticComplexMethod") override suspend fun performSend</ID>
<ID>TooGenericExceptionCaught:Client.kt:Client$error: Throwable</ID>
<ID>TooGenericExceptionCaught:SseClientTransport.kt:SseClientTransport$e: Throwable</ID>
<ID>TooGenericExceptionCaught:StdioClientTransport.kt:StdioClientTransport$e: Throwable</ID>
<ID>UseCheckOrError:Client.kt:Client$throw IllegalStateException( "Server does not support resource subscriptions (required for $method)", )</ID>
<ID>UseCheckOrError:Client.kt:Client$throw IllegalStateException("Client does not support roots capability.")</ID>
<ID>LoopWithTooManyJumpStatements:StreamableHttpClientTransport.kt:StreamableHttpClientTransport$while</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>AbstractClassCanBeConcreteClass:AbstractStreamableHttpClientTest.kt:AbstractStreamableHttpClientTest$AbstractStreamableHttpClientTest</ID>
<ID>ForbiddenComment:StreamableHttpClientTest.kt:StreamableHttpClientTest$// TODO: how to get notifications via Client API?</ID>
<ID>LongParameterList:MockMcp.kt:MockMcp$fun handleJSONRPCRequest</ID>
<ID>LongParameterList:MockMcp.kt:MockMcp$fun handleWithResult</ID>
<ID>LongParameterList:MockMcp.kt:MockMcp$fun onInitialize</ID>
<ID>MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}"""</ID>
<ID>MaxLineLength:StreamableHttpClientTransportTest.kt:StreamableHttpClientTransportTest$"""data: {"jsonrpc":"2.0","method":"notifications/resumed","params":{"fromToken":"$lastEventIdSent"}}"""</ID>
<ID>UseOrEmpty:ClientMetaParameterTest.kt:ClientMetaParameterTest$exception.message ?: ""</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -83,7 +83,7 @@ private val logger = KotlinLogging.logger {}
* Options for configuring the MCP client.
*
* @property capabilities The capabilities this client supports.
* @property enforceStrictCapabilities Whether to strictly enforce capabilities when interacting with the server.
* @param enforceStrictCapabilities Whether to strictly enforce capabilities when interacting with the server.
*/
public class ClientOptions(
public val capabilities: ClientCapabilities = ClientCapabilities(),
@ -125,7 +125,6 @@ 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) {
@ -242,7 +241,7 @@ public open class Client(private val clientInfo: Implementation, options: Client
Method.Defined.CompletionComplete,
-> {
checkNotNull(serverCapabilities?.prompts) {
throw IllegalStateException("Server does not support prompts (required for $method)")
"Server does not support prompts (required for $method)"
}
}
@ -255,10 +254,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 && resCaps.subscribe != true) {
throw IllegalStateException(
"Server does not support resource subscriptions (required for $method)",
)
if (method == Method.Defined.ResourcesSubscribe) {
check(resCaps.subscribe == true) {
"Server does not support resource subscriptions (required for $method)"
}
}
}
@ -556,9 +555,8 @@ 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 {
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.")
checkNotNull(capabilities.roots) {
"Client does not support roots capability."
}
logger.info { "Removing root: $uri" }
val oldMap = roots.getAndUpdate { current -> current.remove(uri) }
@ -636,7 +634,6 @@ public open class Client(private val clientInfo: Implementation, options: Client
// --- 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

View file

@ -227,7 +227,6 @@ public class StdioClientTransport @JvmOverloads public constructor(
}
override suspend fun performSend(message: JSONRPCMessage, options: TransportSendOptions?) {
@Suppress("SwallowedException")
try {
sendChannel.send(message)
} catch (e: CancellationException) {
@ -290,7 +289,7 @@ public class StdioClientTransport @JvmOverloads public constructor(
) {
val buffer = Buffer()
try {
source.use { source ->
source.use {
while (isActive) {
val bytesRead = source.readAtMostTo(buffer, BUFFER_SIZE)
if (bytesRead == -1L) {

View file

@ -54,6 +54,7 @@ private const val MCP_RESUMPTION_TOKEN_HEADER = "Last-Event-ID"
* 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")
@ -65,14 +66,17 @@ private sealed interface ConnectResult {
}
/**
* 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.
* Client transport implementing the MCP Streamable HTTP transport specification.
*
* @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
* 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 requestBuilder builder applied to every outgoing HTTP request, e.g. for adding auth headers
*/
@Suppress("TooManyFunctions")
public class StreamableHttpClientTransport(
private val client: HttpClient,
private val url: String,
@ -98,8 +102,11 @@ public class StreamableHttpClientTransport(
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
@ -121,7 +128,6 @@ 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)}" }
@ -260,7 +266,6 @@ 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) {
@ -296,7 +301,6 @@ 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 {
@ -363,7 +367,6 @@ public class StreamableHttpClientTransport(
}
}
@Suppress("TooGenericExceptionCaught")
private suspend fun collectSse(
session: ClientSSESession,
replayMessageId: RequestId?,
@ -408,7 +411,6 @@ public class StreamableHttpClientTransport(
return SseStreamResult(hasPrimingEvent, receivedResponse, localLastEventId, localServerRetryDelay)
}
@Suppress("CyclomaticComplexMethod")
private suspend fun handleInlineSse(
response: HttpResponse,
replayMessageId: RequestId?,
@ -455,7 +457,6 @@ public class StreamableHttpClientTransport(
}
}
@Suppress("LoopWithTooManyJumpStatements")
while (!channel.isClosedForRead) {
val line = channel.readUTF8Line() ?: break
if (line.isEmpty()) {

View file

@ -834,7 +834,6 @@ class StreamableHttpClientTransportTest {
transport.close()
}
@Suppress("DEPRECATION")
@Test
fun testDeprecatedConstructorStillWorks() = runTest {
val mockEngine = MockEngine { _ ->

Some files were not shown because too many files have changed in this diff Show more