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 →