Skip to content

tool schemas generated from specs/t27mcp/tool_schema.t27: self-contained inputs, catalog identity = seal hash - #24

Open
gHashTag wants to merge 1 commit into
masterfrom
claude/tool-schema-table
Open

gHashTag wants to merge 1 commit into
masterfrom
claude/tool-schema-table

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Refs gHashTag/t27#10444. Companion of gHashTag/t27#10446 (the spec specs/t27mcp/tool_schema.t27, which must merge first; this PR carries its generated table and seal).

What

Every tool's inputSchema is now built from rows of wasm/tool_schema_table.mjs, the t27c gen-js output of specs/t27mcp/tool_schema.t27 (27 tool rows, 270 field rows). schema.mjs (39 lines) turns the rows into JSON Schema and decides nothing about a field. The ~20 inline schema blocks in server.mjs and the tgTools schemas in telegram.mjs are gone.

  • Inputs are self-contained: no $ref, $defs or definitions; every input object sets additionalProperties: false.
  • A tool with no row stops the server at start (tool X has no row in specs/t27mcp/tool_schema.t27).
  • tools/list returns _meta["t27/catalog"] = <seal spec_hash>/p<profile>: the catalog identity is the spec's seal hash plus the output profile. Seal copied unchanged to wasm/tool_schema.seal.json.
  • Output schemas are opt-in: T27_MCP_OUTPUT_PROFILE=1 advertises protocol 2025-06-18, an outputSchema on the 15 object-returning tools, and structuredContent on their results. The default profile (0) is protocol 2024-11-05 with inputs only, as before.
  • Tuple, array, boolean and nested-object constraints are kept in both profiles (prefixItems + minItems/maxItems, items, type: boolean, nested properties).

Before and after

See the table in gHashTag/t27#10446 (measured on this repo's master 792c0e4). In short: 0 of 26 tools declared an output shape, now 15 of 27 do (opt-in; the other 12 return text or arbitrary JSON); 23 integer fields were unbounded number, now integer with bounds; 6 closed sets were free strings, now enums; 6 fields gain patterns; t27_game rules.cycle is a five-step tuple; 8 tools stop copying one filter block.

Tests

smoke.mjs (live stdio, as before) gains 9 checks, read back off the wire: every advertised tool has a row; no $ref/$defs/definitions; every input refuses unknown keys; the catalog identity equals the seal hash plus profile; t27f_view status array (max 27, items 0..255); boolean/enum/integer bound/required on t27_grep and t27_specs; the t27_game tuple; the hits.line bound four levels down; the profile's output-schema rule (and, under profile 1, structuredContent equals the text result). Both profiles pass (node smoke.mjs, T27_MCP_OUTPUT_PROFILE=1 node smoke.mjs). npm run check:schema checks the table and seal against wasm/tool_schema.build.

Spec side: 12 of 12 tests pass, 0 vacuous, a mutated tuple row fails 2 (see #10446).

Language budget

Hand-written JS (glue): +64 / -137 lines, net -73. Per file: schema.mjs +39 (new), server.mjs +5/-130, smoke.mjs +18/-1, telegram.mjs +2/-6. No file exceeds 40 added lines; the PR adds 64 (limit 80). The generated table and seal are data from the spec.

Note for #23 (t27_paper_claims): the spec already has a row for it, so its inline inputSchema should be dropped when it rebases; schemas() supplies it.

🤖 Generated with Claude Code

…elf-contained, catalog identity is the spec seal hash (Refs gHashTag/t27#10444)

schema.mjs builds each tool's JSON Schema from the rows of wasm/tool_schema_table.mjs
(t27c gen-js of the spec). The inline inputSchema blocks in server.mjs and telegram.mjs
are gone: 137 lines deleted, 64 added. A tool with no row stops the server at start.
tools/list carries _meta["t27/catalog"] = <seal spec_hash>/p<profile>. Output schemas are
opt-in (T27_MCP_OUTPUT_PROFILE=1, protocol 2025-06-18, structuredContent). smoke.mjs reads
tuple, array, boolean and nested-object constraints back off the wire. npm run check:schema.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tri-claw

tri-claw Bot commented Oct 18, 2026

Copy link
Copy Markdown

Summary by t27-bees

This PR replaces ~20 inline JSON Schema blocks in server.mjs and telegram.mjs with schemas generated from specs/t27mcp/tool_schema.t27 (via wasm/tool_schema_table.mjs). The new schema.mjs builds self-contained input schemas (no $ref/$defs, additionalProperties: false) and opt-in output schemas controlled by T27_MCP_OUTPUT_PROFILE. Catalog identity is now the spec's seal hash plus profile. Smoke tests validate both profiles.

Worth a look

  • schema.mjs:33 — T.TOOLS[t].filters is used without optional chaining; if a tool row lacks a filters property (or it's not a boolean), the shared filter fields (G_SPEC_FILTERS) may not be included for that tool. The spec should guarantee this field exists, but the code assumes it.
  • schema.mjs:13 — readFileSync for the seal file runs at module load; if wasm/tool_schema.seal.json is missing or malformed, the server crashes on import before any request. The build step should guarantee the file, but there's no fallback.
  • server.mjs:749 — Object.assign(t, schemas(t.name)) mutates tool objects after definition, overwriting any remaining inline inputSchema (e.g., t27_check, t27_examples, t27_find still have them in the diff shown). This works but leaves dead code; the diff is truncated so they may be removed elsewhere.
  • smoke.mjs:55 — The out helper calls T.TOOLS.findIndex inside a filter for each field row, which is O(n²) over fields. Harmless for a test, but fragile if tool names aren't unique in T.TOOLS.
  • telegram.mjs:34 — tgTools now exports only {name, description, handler}; schemas(t.name) in server.mjs must provide the input schema. If a tg_* tool name is missing from the schema spec, the server throws at startup (by design), but there's no explicit test that all TG_TOOLS have rows.

Read: 9 files, +224 -139, the whole diff up to 60000 characters (the rest was not read). Head 8f4dcb1. Model: openai-compatible/nvidia/nemotron-3-ultra-550b-a55b. Commands: @t27-bees review | summary | help | pause | resume. Free for every repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant