Skip to content

refactor: Decouple MCP JSON serialization from global ContentNegotiation configuration #668

@kpavlov

Description

@kpavlov

Problem

Caused by #664.

MCP requires specific kotlinx.serialization.json.Json settings (explicitNulls = false, encodeDefaults = true, classDiscriminatorMode = NONE), currently bundled as McpJson. The Ktor server must install ContentNegotiation with this exact configuration:

install(ContentNegotiation) {
    json(McpJson)
}

This creates two problems:

  1. Easy to forget. Users who omit McpJson get silent serialization failures at runtime (e.g., explicit null fields in JSON-RPC responses). Partially addressed by auto-installation in feat(server): auto-install ContentNegotiation with McpJson (#664) #665.

  2. Conflicts with application-wide JSON configuration. Users may need different Json settings for non-MCP routes (e.g., encodeDefaults = false, prettyPrint = true, custom naming strategies). Because Ktor's ContentNegotiation applies a single Json instance globally, there is no way to use one configuration for MCP endpoints and another for the rest of the application.

Proposal

Inline the required serialization behavior into MCP schema classes (DTOs) using kotlinx.serialization annotations, so they serialize correctly regardless of the global Json configuration.

Class-level settings override global settings, so users would be free to configure ContentNegotiation however they need:

install(ContentNegotiation) {                                                                                                                                                                                  
    json() // any configuration — MCP classes carry their own settings
}                                                                                                                                                                                                                

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesapiPublic API changesenhancementNew feature or requestready for workHas enough information to startrefactoringCode improvements and refactoring

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions