Skip to content

fix(val,mcp): Standard Schema v1 compat + packed-ELF record drops (cutils#27) - #80

Merged
devin-ai-integration[bot] merged 3 commits into
mainfrom
devin/1792347973-mcp-val-std-compat
Oct 18, 2026
Merged

devin-ai-integration[bot] merged 3 commits into
mainfrom
devin/1792347973-mcp-val-std-compat

Conversation

@espetro

@espetro espetro commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes sigilco/cutils#27 — the @chttp/mcp × @chttp/val pairing failed to type-check under the published tarballs, and the packed ELF dropped response fields. Note: the issue is filed on cutils, but @chttp/val now lives in packages/val here and @chttp/mcp only exists on this branch — so the fix lands stacked on the move-auth-mcp branch.

Type compatibility (val):

  • StdProps.version widened to number made every val schema refuse StandardSchemaV1 (TS2345/TS2739). It is now the literal 1.
  • v.toJsonSchema/v.toOpenApiSchema required a VSchema, so a consumer's emitJsonSchema adapter receiving StandardSchemaV1 rejected it. They now take the {"~standard": unknown} structural minimum and validate instanceof VSchema at runtime.
  • tool() handler input stayed unknown (TS18046) because InferInput/InferOutput found no ~standard.types slot and factories erased concrete classes to VSchema. Each schema class now merges a StdTyped<I, O> interface (type-level only — the runtime field stays StdProps, keeping scriptc lowering at 100%), and factories return the concrete classes (string(): VStr, …).

Packed-ELF record drops (mcp):

  • scriptc drops member writes that expand a non-empty record literal when the package is compiled from its tsc dist JS — packed tarballs returned bare {"jsonrpc":"2.0","id":N} for every method. resultResponse, errorResponse, progressNotification params, and the initialize/tools/resources/prompts listing records now use single full literals or {} open records.

Smoke coverage:

  • pnpm smoke:pack now packs @chttp/mcp, type-checks tests/pack-smoke/mcp-app.ts (the issue's repro: val schema into tool() + emitJsonSchema arrow + inferred handler input) under tsc, builds it to an ELF, and serves tools/list + tools/call (valid + invalid args) through Server.handleRequest.

Known limitation documented in the script: an error-member response crossing the package call boundary still loses the member under scriptc's stringify — invalid-args returns the envelope {"jsonrpc","id":3} (dispatch reaches errorResponse correctly; JS runtimes emit the full INVALID_PARAMS body). This looks like a compiler-side tagged-union/serialization gap worth its own issue.

Test plan

  • pnpm test — all workspace tests incl. packages/mcp elf.test: pass
  • pnpm -r build: pass
  • pnpm smoke:pack: pass (incl. new mcp+val section)
  • bash scripts/scriptc-gate.sh: 100% on all eight gates
  • pnpm lint: warnings only (pre-existing)
  • pnpm docs:build: pass

Link to Devin session: https://app.devin.ai/sessions/3f418b516b064c8ea1c69ecc54752ba9
Open in Devin Desktop: https://app.devin.ai/desktop/session/3f418b516b064c8ea1c69ecc54752ba9?variant=devin
Requested by: @espetro

@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Deploying chttpjs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 846487c
Status: ✅  Deploy successful!
Preview URL: https://00be6f1d.chttpjs.pages.dev
Branch Preview URL: https://devin-1792347973-mcp-val-std.chttpjs.pages.dev

View logs

… (cutils#27)

StdProps.version is the literal 1 so val schemas assign to StandardSchemaV1
instead of failing TS2345/TS2739; each concrete schema class merges a
StdTyped<I, O> interface so InferInput/InferOutput resolve real input/output
types in consumers like @chttp/mcp tool(); toJsonSchema/toOpenApiSchema accept
the {"~standard": unknown} structural minimum so a consumer's emitJsonSchema
adapter can feed any standard schema; factories return concrete class types
so inference reaches the merged interfaces.
…owering

Scriptc drops member writes that expand a non-empty record literal once the
package is lowered from its tsc dist, so resultResponse/errorResponse and the
tools/resources/prompts/initialize listing records now start from {} open
records or single full literals — packed tarballs returned bare {jsonrpc,id}
envelopes before this (cutils#27).
Packs @chttp/mcp alongside router+val, type-checks a consumer app mounting a
val schema through tool()/emitJsonSchema under tsc, builds it to an ELF, and
serves tools/list + tools/call (valid and invalid args) through
Server.handleRequest.
@devin-ai-integration
devin-ai-integration Bot changed the base branch from devin/1792135585-move-auth-mcp to main October 18, 2026 18:29
@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1792347973-mcp-val-std-compat branch from f09f4b5 to 846487c Compare October 18, 2026 18:29
@devin-ai-integration
devin-ai-integration Bot merged commit 265f0f7 into main Oct 18, 2026
3 checks passed
@devin-ai-integration
devin-ai-integration Bot deleted the devin/1792347973-mcp-val-std-compat branch October 18, 2026 18:32
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Follow-up on the known gap documented in scripts/pack-smoke.sh (response record's error member dropped when crossing the package call boundary into the consumer's stringify):

Reproduced with a minimal standalone scriptc program (no chttp deps — a package class whose handleRequest returns a Resp | null union built from {jsonrpc,id,result} and {jsonrpc,id,error} literals). Under 0.3.4 the error member is physically stripped at the boundary (Object.keys-equivalent probe reads ABSENT, not just skipped by JSON.stringify); under 0.3.5 and 0.3.6 it survives — this is the upstream fix "Checked JSON records keep keys they do not declare" from the v0.3.5 changelog.

Upstream issue (retroactive report + regression coverage): vercel-labs/scriptc#1314

So the pack-smoke workaround can be retired once the repo's scriptc floor is >=0.3.5.

Written by Devin

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.

@chttp/mcp 0.2.0 x @chttp/val 0.3.0: val schema fails type-checking in tool() and breaks the scriptc build

1 participant