fix: classify malformed request params as invalid params (#886)

## Summary

Classify typed request deserialization failures as JSON-RPC `Invalid
Params`
instead of `Internal error`.

Fixes #830.

## Changes

- translate `SerializationException` from typed request decoding to
`-32602`
- return a stable `Invalid params` message without serialization
internals
- cover malformed `initialize` requests through session and Streamable
HTTP paths

## Scope and risk

The catch is limited to request parameter deserialization after the
JSON-RPC
method has been resolved. Envelope parsing, unknown methods, handler
failures,
and successful requests keep their existing behavior.

## Verification

```bash
./gradlew :integration-test:jvmTest --tests 'io.modelcontextprotocol.kotlin.sdk.server.ServerSessionInitializeTest'
./gradlew :kotlin-sdk-server:jvmTest --tests 'io.modelcontextprotocol.kotlin.sdk.server.StreamableHttpServerTransportTest'
./gradlew :kotlin-sdk-core:jvmTest --tests 'io.modelcontextprotocol.kotlin.sdk.shared.ProtocolTest'
./gradlew ktlintCheck apiCheck
```

Co-authored-by: Pavel Gorgulov <devcrocod@gmail.com>
This commit is contained in:
King Star 2026-07-23 00:12:12 +08:00 • committed by GitHub
commit cab669b108
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -48,6 +48,7 @@ import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.encodeToJsonElement
@ -941,7 +942,15 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
_requestHandlers.update { current ->
current.putting(method.value) { jSONRPCRequest, extraHandler ->
val request = jSONRPCRequest.fromJSON()
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)
response
}