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).
// CosmoDev polyglot showcase port from src/lib/tool-schema.ts. C# 12, System.Text.Json only.
using System.Text.Json;
using System.Text.Json.Nodes;
static class ToolSchema
{
static readonly string[] Supported = ["string", "number", "integer", "boolean", "object", "array"];
static string? Str(JsonNode? n) => n is JsonValue v && v.TryGetValue<string>(out var s) ? s : null;
static bool? Bool(JsonNode? n) => n is JsonValue v && v.TryGetValue<bool>(out var b) ? b : null;
static string Kind(JsonNode? v) => v is JsonValue j ? (j.TryGetValue<string>(out _) ? "string"
: j.TryGetValue<decimal>(out _) ? "number"
: j.TryGetValue<bool>(out _) ? "bool" : "other") : "other";
// The recursive strict-mode walk — every object nests the same rules.
static void CheckObject(string path, JsonObject obj, List<(string Rule, string Path)> issues)
{
if (Bool(obj["additionalProperties"]) != false) issues.Add(("no-additional-properties", path));
if (obj["properties"] is not JsonObject props) return;
var required = obj["required"] as JsonArray;
var missing = props.Where(kv => kv.Value is JsonObject
&& (required?.All(r => Str(r) != kv.Key) ?? true))
.Select(kv => kv.Key).ToList();
if (missing.Count > 0)
issues.Add(("all-required", $"{path}: required missing {string.Join(", ", missing)}"));
foreach (var kv in props)
{
if (kv.Value is not JsonObject prop) continue;
var p = $"{path}.properties.{kv.Key}";
if (prop.ContainsKey("default")) issues.Add(("no-defaults", p));
if (string.IsNullOrWhiteSpace(Str(prop["description"])))
issues.Add(("description-present", p));
var ty = Str(prop["type"]);
if (ty == null || !Supported.Contains(ty))
issues.Add(("typed-properties", $"{p}: must be one of {string.Join(" | ", Supported)}"));
if (prop["enum"] is JsonArray en)
{
var kinds = en.Select(v => Kind(v)).ToHashSet();
if (en.Count == 0 || kinds.Count > 1 || kinds.Contains("other")) issues.Add(("enum-values", p));
}
if (ty == "array" && prop["items"] is not JsonObject) issues.Add(("array-items", p));
if (ty == "object" && prop["properties"] is JsonObject) CheckObject(p, prop, issues);
}
}
// Validate a JSON tool definition string.
static List<(string Rule, string Path)> ValidateToolSchema(string json)
{
JsonNode? root;
try { root = JsonNode.Parse(json); }
catch (JsonException e) { return [("json-parseable", $"$: {e.Message}")]; }
if (root is not JsonObject o) return [("json-parseable", "$: input must be a JSON object")];
var issues = new List<(string, string)>();
var name = Str(o["name"]) ?? "";
var nameOk = name.Length is >= 1 and <= 64
&& name.All(c => c is ((>= 'a' and <= 'z') or (>= '0' and <= '9') or '_' or '-'));
if (!nameOk) issues.Add(("non-empty-name", "name: must be 1-64 chars of [a-z0-9_-]"));
if (string.IsNullOrWhiteSpace(Str(o["description"])))
issues.Add(("description-present", "description: the tool needs a description"));
if (o["input_schema"] is JsonObject schema && Str(schema["type"]) == "object")
CheckObject("input_schema", schema, issues);
else
issues.Add(("json-parseable", "input_schema: must be an object with type: \"object\""));
return issues;
}
static void Main()
{
// Demo: validate a small broken definition and print the issues.
var broken = """
{"name": "Get_Weather", "input_schema": {"type": "object", "properties": {
"city": {"type": "string", "default": "Paris"}, "unit": {"type": "string", "description": "celsius or fahrenheit", "enum": ["c", 2]},
"tags": {"type": "array"}}, "required": ["city"]}}
""";
foreach (var (rule, where) in ValidateToolSchema(broken)) Console.WriteLine($"{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 →