Skip to content

Tool Schema Builder — Kotlin source

Build function-calling and MCP tool schemas that pass strict mode on the first try, and lint pasted ones against the strict-mode contract — additionalProperties, required-sync, defaults, enums — with one-click autofix for every mechanical violation. Runs entirely in your browser.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Tool Schema Builder — strict-mode validation of function-calling / MCP
// tool definitions (OpenAI strict mode / MCP inputSchema contract).
//
// Language: Kotlin (2.0, standard library only)
// Source:   CosmoDev polyglot showcase port, from src/lib/tool-schema.ts
//           (the canonical TypeScript implementation). Kotlin has no
//           stdlib JSON, so the definition arrives as the Map<String, Any?>
//           a kotlinx.serialization parse would produce; only the parse
//           differs.

@Suppress("UNCHECKED_CAST")
fun obj(v: Any?): Map<String, Any?>? = v as? Map<String, Any?>

val SUPPORTED = setOf("string", "number", "integer", "boolean", "object", "array")

// The recursive strict-mode walk — every object nests the same rules.
fun checkObject(path: String, o: Map<String, Any?>, issues: MutableList<Pair<String, String>>) {
    if (o["additionalProperties"] != false) issues += "no-additional-properties" to path
    val props = obj(o["properties"]) ?: return
    val required = o["required"] as? List<String> ?: emptyList()
    val missing = props.keys.filter { it !in required }
    if (missing.isNotEmpty()) issues += "all-required" to "$path: required missing ${missing.joinToString(", ")}"
    for ((key, raw) in props) {
        val prop = obj(raw) ?: continue
        val p = "$path.properties.$key"
        if ("default" in prop) issues += "no-defaults" to p
        val desc = prop["description"] as? String ?: ""
        if (desc.isBlank()) issues += "description-present" to p
        val ty = prop["type"] as? String
        if (ty == null || ty !in SUPPORTED) {
            issues += "typed-properties" to "$p: must be one of ${SUPPORTED.joinToString(" | ")}"
        }
        (prop["enum"] as? List<Any?>)?.let { en ->
            val kinds = en.map { it?.javaClass?.simpleName ?: "null" }.toSet()
            val primitive = kinds.all { it in setOf("String", "Integer", "Long", "Double", "Boolean") }
            if (en.isEmpty() || kinds.size > 1 || !primitive) issues += "enum-values" to p
        }
        if (ty == "array" && obj(prop["items"]) == null) issues += "array-items" to p
        if (ty == "object" && obj(prop["properties"]) != null) checkObject(p, prop, issues)
    }
}

// Validate a parsed tool definition ({name, description, input_schema}).
fun validate(root: Map<String, Any?>): List<Pair<String, String>> {
    val issues = mutableListOf<Pair<String, String>>()
    val name = root["name"] as? String ?: ""
    if (name.isEmpty() || name.length > 64 || name.any { it !in 'a'..'z' && it !in '0'..'9' && it != '_' && it != '-' }) {
        issues += "non-empty-name" to "name: must be 1-64 chars of [a-z0-9_-]"
    }
    if ((root["description"] as? String ?: "").isBlank()) {
        issues += "description-present" to "description: the tool needs a description"
    }
    val schema = obj(root["input_schema"])
    if (schema != null && schema["type"] == "object") {
        checkObject("input_schema", schema, issues)
    } else {
        issues += "json-parseable" to "input_schema: must be an object with type: \"object\""
    }
    return issues
}

// Demo: validate a small broken definition and print the issues.
fun main() {
    val broken = mapOf(
        "name" to "Get_Weather",
        "input_schema" to mapOf(
            "type" to "object",
            "properties" to mapOf(
                "city" to mapOf("type" to "string", "default" to "Paris"),
                "unit" to mapOf("type" to "string", "description" to "celsius or fahrenheit",
                                "enum" to listOf("c", 2)),
                "tags" to mapOf("type" to "array")),
            "required" to listOf("city")))
    validate(broken).forEach { (rule, where) -> println("$rule  $where") }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →