Skip to content

Tool Schema Builder — C++ 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 C++ 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: C++ (20 — designated initializers keep the fixture readable).
//           C++ has no stdlib JSON, so the definition is modeled as native
// Source:   structs; the rule walk mirrors checkObject() in
//           src/lib/tool-schema.ts (the canonical TypeScript lib).

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

struct Prop {
    std::string key, type, description;
    bool has_default = false;   // a "default" key is present
    bool has_enum = false;
    std::string enum_kinds;     // one char per value: s/n/b ("" + has_enum = empty enum)
    bool has_items = false;     // arrays: items object present
    std::vector<Prop> nested;   // objects: nested properties
    bool nested_strict = false; // nested object sets additionalProperties:false
};

struct ToolDef {
    std::string name, description;
    bool strict = false;        // additionalProperties === false
    std::vector<std::string> required;
    std::vector<Prop> props;
};

void check(const std::string& rule, const std::string& path) {
    std::cout << rule << "  " << path << "\n";
}
const std::vector<std::string> TYPES = {"string", "number", "integer", "boolean", "object", "array"};

// The recursive strict-mode walk — every object nests the same rules.
void checkObject(const std::string& path, const std::vector<Prop>& props,
                 const std::vector<std::string>& required, bool strict) {
    if (!strict) check("no-additional-properties", path);
    int missing = 0;
    for (const auto& p : props)
        if (std::find(required.begin(), required.end(), p.key) == required.end()) missing++;
    if (missing > 0) check("all-required", path);
    for (const auto& p : props) {
        std::string pp = path + ".properties." + p.key;
        if (p.has_default) check("no-defaults", pp);
        if (p.description.empty()) check("description-present", pp);
        if (std::find(TYPES.begin(), TYPES.end(), p.type) == TYPES.end()) check("typed-properties", pp);
        if (p.has_enum) {
            bool mixed = false;
            for (size_t i = 1; i < p.enum_kinds.size(); i++)
                if (p.enum_kinds[i] != p.enum_kinds[0]) mixed = true;
            if (p.enum_kinds.empty() || mixed) check("enum-values", pp);
        }
        if (p.type == "array" && !p.has_items) check("array-items", pp);
        if (!p.nested.empty()) checkObject(pp, p.nested, {}, p.nested_strict);
    }
}

int main() {
    // Demo: validate a small broken definition and print the issues.
    ToolDef broken{
        .name = "Get_Weather",  // uppercase fails [a-z0-9_-]
        .props = {
            {.key = "city", .type = "string", .has_default = true},
            {.key = "unit", .type = "string", .description = "celsius or fahrenheit",
             .has_enum = true, .enum_kinds = "sn"},  // one string + one number
            {.key = "tags", .type = "array"},
        },
        .required = {"city"},
    };
    const std::string& n = broken.name;
    bool nameOk = !n.empty() && n.size() <= 64 && std::all_of(n.begin(), n.end(), [](char c) {
        return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-';
    });
    if (!nameOk) check("non-empty-name", "name: must be 1-64 chars of [a-z0-9_-]");
    if (broken.description.empty())
        check("description-present", "description: the tool needs a description");
    checkObject("input_schema", broken.props, broken.required, broken.strict);
}

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 →