Fix: Handle logging/setLevel request for server (#358)

Fix #293 

Also fixed a bug with initialization: properties must be declared before
the init block; otherwise, the initialization order is violated and an
NPE will occur in the init block.

## Motivation and Context
After initialization, MCP clients send a `logging/setLevel` request if
the logging capability is enabled. This request was previously not
handled, leading to an error that blocked the use of the MCP server in
the inspector and other clients.


## How Has This Been Tested?
Added integration test

## 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)
- [ ] 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 2025-11-03 19:45:09 +01:00 • committed by GitHub
commit ab9ba54b33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -886,6 +886,86 @@ class ClientTest {
)
}
@Test
fun `should handle logging setLevel request`() = runTest {
val server = Server(
Implementation(name = "test server", version = "1.0"),
ServerOptions(
capabilities = ServerCapabilities(
logging = EmptyJsonObject,
),
),
)
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(
capabilities = ClientCapabilities(),
),
)
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val receivedMessages = mutableListOf<LoggingMessageNotification>()
client.setNotificationHandler<LoggingMessageNotification>(Method.Defined.NotificationsMessage) { notification ->
receivedMessages.add(notification)
CompletableDeferred(Unit)
}
val serverSessionResult = CompletableDeferred<ServerSession>()
listOf(
launch {
client.connect(clientTransport)
println("Client connected")
},
launch {
serverSessionResult.complete(server.connect(serverTransport))
println("Server connected")
},
).joinAll()
val serverSession = serverSessionResult.await()
// Set logging level to warning
val minLevel = LoggingLevel.warning
val result = client.setLoggingLevel(minLevel)
assertEquals(EmptyJsonObject, result._meta)
// Send messages of different levels
val testMessages = listOf(
LoggingLevel.debug to "Debug - should be filtered",
LoggingLevel.info to "Info - should be filtered",
LoggingLevel.warning to "Warning - should pass",
LoggingLevel.error to "Error - should pass",
)
testMessages.forEach { (level, message) ->
serverSession.sendLoggingMessage(
LoggingMessageNotification(
params = LoggingMessageNotification.Params(
level = level,
data = buildJsonObject { put("message", message) },
),
),
)
}
delay(100)
// Only warning and error should be received
assertEquals(2, receivedMessages.size, "Should receive only 2 messages (warning and error)")
// Verify all received messages have severity >= minLevel
receivedMessages.forEach { message ->
val messageSeverity = message.params.level.ordinal
assertTrue(
messageSeverity >= minLevel.ordinal,
"Received message with level ${message.params.level} should have severity >= $minLevel",
)
}
}
@Test
fun `should handle server elicitation`() = runTest {
val client = Client(