Skip to content

Tool Schema Builder — JavaScript 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 JavaScript 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 implementation). ES2022, stdlib only.
 */

export const SUPPORTED_TYPES = ['string', 'number', 'integer', 'boolean', 'object', 'array'];

const isObj = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);

// The recursive strict-mode walk — every object nests the same rules.
function checkObject(path, obj, issues) {
  if (obj.additionalProperties !== false) {
    issues.push({ rule: 'no-additional-properties', path });
  }
  const props = isObj(obj.properties) ? obj.properties : {};
  const keys = Object.keys(props).filter((k) => isObj(props[k]));
  const required = Array.isArray(obj.required) ? obj.required : [];
  const missing = keys.filter((k) => !required.includes(k));
  if (missing.length > 0) {
    issues.push({ rule: 'all-required', path: `${path}: required missing ${missing.join(', ')}` });
  }
  for (const key of keys) {
    const prop = props[key];
    const p = `${path}.properties.${key}`;
    if ('default' in prop) issues.push({ rule: 'no-defaults', path: p });
    if (typeof prop.description !== 'string' || !prop.description.trim()) {
      issues.push({ rule: 'description-present', path: p });
    }
    if (!SUPPORTED_TYPES.includes(prop.type)) {
      issues.push({ rule: 'typed-properties', path: `${p}: must be one of ${SUPPORTED_TYPES.join(' | ')}` });
    }
    if (Array.isArray(prop.enum)) {
      const kinds = new Set(prop.enum.map((v) => typeof v));
      if (prop.enum.length === 0 || kinds.size > 1 || kinds.has('object') || kinds.has('undefined')) {
        issues.push({ rule: 'enum-values', path: p });
      }
    }
    if (prop.type === 'array' && !isObj(prop.items)) issues.push({ rule: 'array-items', path: p });
    if (prop.type === 'object' && isObj(prop.properties)) checkObject(p, prop, issues);
  }
}

/** Validate a JSON tool definition ({name, description, input_schema}). */
export function validateToolSchema(input) {
  let root;
  try {
    root = JSON.parse(input);
  } catch (e) {
    return { parseError: String(e), issues: [] };
  }
  if (!isObj(root)) {
    return { issues: [{ rule: 'json-parseable', path: '$: input must be a JSON object' }] };
  }
  const issues = [];
  if (!/^[a-z0-9_-]{1,64}$/.test(root.name ?? '')) {
    issues.push({ rule: 'non-empty-name', path: 'name: must be 1-64 chars of [a-z0-9_-]' });
  }
  if (typeof root.description !== 'string' || !root.description.trim()) {
    issues.push({ rule: 'description-present', path: 'description: the tool needs a description' });
  }
  const schema = isObj(root.input_schema) ? root.input_schema : null;
  if (schema && schema.type === 'object') checkObject('input_schema', schema, issues);
  else issues.push({ rule: 'json-parseable', path: 'input_schema: must be an object with type: "object"' });
  return { issues };
}

// Demo: validate a small broken definition and print the issues.
const broken = JSON.stringify({ 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'] } });
for (const { rule, path } of validateToolSchema(broken).issues) console.log(`${rule}  ${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 →