@@ -56,6 +56,8 @@ import kotlinx.serialization.json.JsonElement
5656import  kotlinx.serialization.json.JsonNull 
5757import  kotlinx.serialization.json.JsonObject 
5858import  kotlinx.serialization.json.JsonPrimitive 
59+ import  kotlinx.serialization.json.buildJsonArray 
60+ import  kotlinx.serialization.json.buildJsonObject 
5961import  kotlin.coroutines.cancellation.CancellationException 
6062
6163private  val  logger =  KotlinLogging .logger {}
@@ -600,61 +602,38 @@ public open class Client(private val clientInfo: Implementation, options: Client
600602     * - Name: alphanumeric start/end, may contain hyphens, underscores, dots (empty allowed) 
601603     */  
602604    private  fun  validateMetaKeys (keys :  Set <String >) {
603-         for  (key in  keys) {
604-             if  (! isValidMetaKey(key)) {
605-                 throw  Error (" Invalid _meta key '$key '. Must follow format [prefix/]name with valid labels." 
605+         val  labelPattern =  Regex (" [a-zA-Z]([a-zA-Z0-9-]*[a-zA-Z0-9])?" 
606+         val  namePattern =  Regex (" [a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?" 
607+         
608+         keys.forEach { key -> 
609+             require(key.isNotEmpty()) { " Meta key cannot be empty" 
610+             
611+             val  (prefix, name) =  key.split(' /' =  2 ).let  { parts -> 
612+                 when  (parts.size) {
613+                     1  ->  null  to parts[0 ]
614+                     else  ->  parts[0 ] to parts[1 ]
615+                 }
606616            }
607-         }
608-     }
609- 
610-     private  fun  isValidMetaKey (key :  String ): Boolean  {
611-         if  (key.isEmpty()) return  false 
612-         val  parts =  key.split(' /' =  2 )
613-         return  when  (parts.size) {
614-             1  ->  {
615-                 //  No prefix, just validate name
616-                 isValidMetaName(parts[0 ])
617+             
618+             //  Validate prefix if present
619+             prefix?.let  {
620+                 require(it.isNotEmpty()) { " Invalid _meta key '$key ': prefix cannot be empty" 
621+                 
622+                 val  labels =  it.split(' .' 
623+                 require(labels.all { label ->  label.matches(labelPattern) }) {
624+                     " Invalid _meta key '$key ': prefix labels must start with a letter, end with letter/digit, and contain only letters, digits, or hyphens" 
625+                 }
626+                 
627+                 require(labels.none { label ->  label.equals(" modelcontextprotocol" =  true ) ||  label.equals(" mcp" =  true ) }) {
628+                     " Invalid _meta key '$key ': prefix cannot contain reserved labels 'modelcontextprotocol' or 'mcp'" 
629+                 }
617630            }
618- 
619-             2   ->  { 
620-                  val  (prefix,  name)  =  parts 
621-                 isValidMetaPrefix(prefix)  &&  isValidMetaName( name) 
631+              
632+             //  Validate name (empty allowed) 
633+             require( name.isEmpty()  ||  name.matches(namePattern)) { 
634+                 " Invalid _meta key ' $key ':  name must start and end with alphanumeric characters, and contain only alphanumerics, hyphens, underscores, or dots " 
622635            }
623- 
624-             else  ->  false 
625-         }
626-     }
627- 
628-     private  fun  isValidMetaPrefix (prefix :  String ): Boolean  {
629-         if  (prefix.isEmpty()) return  false 
630-         val  labels =  prefix.split(' .' 
631- 
632-         if  (! labels.all { isValidLabel(it) }) {
633-             return  false 
634-         }
635- 
636-         return  ! labels.any { label -> 
637-             label.equals(" modelcontextprotocol" =  true ) || 
638-                 label.equals(" mcp" =  true )
639-         }
640-     }
641- 
642-     private  fun  isValidLabel (label :  String ): Boolean  {
643-         if  (label.isEmpty()) return  false 
644-         if  (! label.first().isLetter() ||  ! label.last().let  { it.isLetter() ||  it.isDigit() }) {
645-             return  false 
646636        }
647-         return  label.all { it.isLetter() ||  it.isDigit() ||  it ==  ' -' 
648-     }
649- 
650-     private  fun  isValidMetaName (name :  String ): Boolean  {
651-         //  Empty names are allowed per MCP specification
652-         if  (name.isEmpty()) return  true 
653- 
654-         if  (! name.first().isLetterOrDigit() ||  ! name.last().isLetterOrDigit()) {
655-             return  false 
656-         }
657-         return  name.all { it.isLetterOrDigit() ||  it in  setOf (' -' ' _' ' .' 
658637    }
659638
660639    private  fun  convertToJsonMap (map :  Map <String , Any ?>): Map <String , JsonElement > =  map.mapValues { (key, value) -> 
@@ -669,54 +648,42 @@ public open class Client(private val clientInfo: Implementation, options: Client
669648    @OptIn(ExperimentalUnsignedTypes ::class , ExperimentalSerializationApi ::class )
670649    private  fun  convertToJsonElement (value :  Any? ): JsonElement  =  when  (value) {
671650        null  ->  JsonNull 
672- 
673-         is  Map <* , * > ->  {
674-             val  jsonMap =  value.entries.associate { (k, v) -> 
675-                 k.toString() to convertToJsonElement(v)
676-             }
677-             JsonObject (jsonMap)
678-         }
679- 
680651        is  JsonElement  ->  value
681- 
682652        is  String  ->  JsonPrimitive (value)
683- 
684653        is  Number  ->  JsonPrimitive (value)
685- 
686654        is  Boolean  ->  JsonPrimitive (value)
687- 
688655        is  Char  ->  JsonPrimitive (value.toString())
689- 
690656        is  Enum <* > ->  JsonPrimitive (value.name)
691657
692-         is  Collection <* > ->  JsonArray (value.map { convertToJsonElement(it) })
693- 
694-         is  Array <* > ->  JsonArray (value.map { convertToJsonElement(it) })
695- 
696-         is  IntArray  ->  JsonArray (value.map { JsonPrimitive (it) })
697- 
698-         is  LongArray  ->  JsonArray (value.map { JsonPrimitive (it) })
699- 
700-         is  FloatArray  ->  JsonArray (value.map { JsonPrimitive (it) })
701- 
702-         is  DoubleArray  ->  JsonArray (value.map { JsonPrimitive (it) })
703- 
704-         is  BooleanArray  ->  JsonArray (value.map { JsonPrimitive (it) })
658+         is  Map <* , * > ->  buildJsonObject {
659+             value.forEach { (k, v) -> 
660+                 put(k.toString(), convertToJsonElement(v))
661+             }
662+         }
705663
706-         is  ShortArray  ->  JsonArray (value.map { JsonPrimitive (it) })
664+         is  Collection <* > ->  buildJsonArray {
665+             value.forEach { add(convertToJsonElement(it)) }
666+         }
707667
708-         is  ByteArray  ->  JsonArray (value.map { JsonPrimitive (it) })
668+         is  Array <* > ->  buildJsonArray {
669+             value.forEach { add(convertToJsonElement(it)) }
670+         }
709671
710-         is  CharArray  ->  JsonArray (value.map { JsonPrimitive (it.toString()) })
672+         //  Primitive arrays - use iterator for unified handling
673+         is  IntArray  ->  buildJsonArray { value.forEach { add(it) } }
674+         is  LongArray  ->  buildJsonArray { value.forEach { add(it) } }
675+         is  FloatArray  ->  buildJsonArray { value.forEach { add(it) } }
676+         is  DoubleArray  ->  buildJsonArray { value.forEach { add(it) } }
677+         is  BooleanArray  ->  buildJsonArray { value.forEach { add(it) } }
678+         is  ShortArray  ->  buildJsonArray { value.forEach { add(it) } }
679+         is  ByteArray  ->  buildJsonArray { value.forEach { add(it) } }
680+         is  CharArray  ->  buildJsonArray { value.forEach { add(it.toString()) } }
711681
712682        //  ExperimentalUnsignedTypes
713-         is  UIntArray  ->  JsonArray (value.map { JsonPrimitive (it) })
714- 
715-         is  ULongArray  ->  JsonArray (value.map { JsonPrimitive (it) })
716- 
717-         is  UShortArray  ->  JsonArray (value.map { JsonPrimitive (it) })
718- 
719-         is  UByteArray  ->  JsonArray (value.map { JsonPrimitive (it) })
683+         is  UIntArray  ->  buildJsonArray { value.forEach { add(it) } }
684+         is  ULongArray  ->  buildJsonArray { value.forEach { add(it) } }
685+         is  UShortArray  ->  buildJsonArray { value.forEach { add(it) } }
686+         is  UByteArray  ->  buildJsonArray { value.forEach { add(it) } }
720687
721688        else  ->  {
722689            logger.debug { " Converting unknown type ${value::class .simpleName}  to string: $value " 
0 commit comments