Skip to content

Tool Schema Builder — Java 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 Java 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).
// CosmoDev polyglot showcase port, from src/lib/tool-schema.ts (the canonical
// TypeScript lib). Java 17; no stdlib JSON, so the definition arrives as the
// Map<String,Object> a Jackson/org.json parse would produce; only the parse differs.

import java.util.*;

public class ToolSchema {
    static final Set<String> SUPPORTED = Set.of("string", "number", "integer", "boolean", "object", "array");
    record Issue(String rule, String path) {}

    @SuppressWarnings("unchecked")
    static Map<String, Object> obj(Object v) { return v instanceof Map ? (Map<String, Object>) v : null; }

    static Map<String, Object> map(Object... kv) {
        Map<String, Object> m = new LinkedHashMap<>();
        for (int i = 0; i + 1 < kv.length; i += 2) m.put((String) kv[i], kv[i + 1]);
        return m;
    }

    // The recursive strict-mode walk — every object nests the same rules.
    static void checkObject(String path, Map<String, Object> o, List<Issue> issues) {
        if (!Boolean.FALSE.equals(o.get("additionalProperties"))) issues.add(new Issue("no-additional-properties", path));
        Map<String, Object> props = obj(o.get("properties"));
        if (props == null) return;
        List<String> required = o.get("required") instanceof List<?> l ? (List<String>) l : List.of();
        String missing = props.keySet().stream()
            .filter(k -> !required.contains(k)).reduce((a, b) -> a + ", " + b).orElse(null);
        if (missing != null) issues.add(new Issue("all-required", path + ": required missing " + missing));
        for (var e : props.entrySet()) {
            Map<String, Object> prop = obj(e.getValue());
            if (prop == null) continue;
            String p = path + ".properties." + e.getKey();
            if (prop.containsKey("default")) issues.add(new Issue("no-defaults", p));
            if (!(prop.get("description") instanceof String d) || d.isBlank())
                issues.add(new Issue("description-present", p));
            if (!(prop.get("type") instanceof String t) || !SUPPORTED.contains(t))
                issues.add(new Issue("typed-properties", p + ": must be one of " + String.join(" | ", SUPPORTED)));
            if (prop.get("enum") instanceof List<?> en) {
                Set<String> kinds = new HashSet<>();
                for (Object v : en) kinds.add(v == null ? "null" : v.getClass().getSimpleName());
                boolean primitive = kinds.stream().allMatch(k -> k.equals("String") || k.equals("Integer")
                    || k.equals("Long") || k.equals("Double") || k.equals("Boolean"));
                if (en.isEmpty() || kinds.size() > 1 || !primitive) issues.add(new Issue("enum-values", p));
            }
            if ("array".equals(prop.get("type")) && obj(prop.get("items")) == null)
                issues.add(new Issue("array-items", p));
            if ("object".equals(prop.get("type")) && obj(prop.get("properties")) != null)
                checkObject(p, prop, issues);
        }
    }

    // Validate a parsed tool definition ({name, description, input_schema}).
    static List<Issue> validate(Map<String, Object> root) {
        List<Issue> issues = new ArrayList<>();
        if (!(root.get("name") instanceof String n) || !n.matches("^[a-z0-9_-]{1,64}$"))
            issues.add(new Issue("non-empty-name", "name: must be 1-64 chars of [a-z0-9_-]"));
        if (!(root.get("description") instanceof String d) || d.isBlank())
            issues.add(new Issue("description-present", "description: the tool needs a description"));
        Map<String, Object> schema = obj(root.get("input_schema"));
        if (schema != null && "object".equals(schema.get("type"))) checkObject("input_schema", schema, issues);
        else issues.add(new Issue("json-parseable", "input_schema: must be an object with type: \"object\""));
        return issues;
    }

    public static void main(String[] args) {
        // Demo: validate a small broken definition and print the issues.
        Map<String, Object> broken = map(
            "name", "Get_Weather",
            "input_schema", map(
                "type", "object",
                "properties", map(
                    "city", map("type", "string", "default", "Paris"),
                    "unit", map("type", "string", "description", "celsius or fahrenheit", "enum", List.of("c", 2)),
                    "tags", map("type", "array")),
                "required", List.of("city")));
        for (Issue i : validate(broken)) System.out.println(i.rule() + "  " + i.path());
    }
}

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 →