feat(rpc)!: support Standard Schema for RPC definitions with runtime validation - #157
Merged
Conversation
…te at runtime Widen `args`/`returns` from valibot's `GenericSchema` to the `StandardSchemaV1` interface so valibot, zod, arktype, and any other Standard Schema validator work interchangeably, and infer handler types from the schema's Standard Schema input types. Every invocation path (local, over-the-wire, agent/MCP) now validates declared `args`/`returns` at the boundary via `getRpcHandler`, throwing coded diagnostics DF0043 / DF0044 on failure. Validation is guard-only: payloads are never rewritten, so schemas describing a subset of an object don't strip the sender's extra fields. The MCP JSON-schema surface keeps valibot's converter for precise schemas and degrades non-valibot vendors to a permissive object schema.
✅ Deploy Preview for devfra ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Reconcile MCP SDK v2 migration (#156) with Standard Schema support: keep both the new @modelcontextprotocol/server Tool import and the @standard-schema/spec import in build-server, and add @standard-schema/spec alongside the v2 MCP catalog entries.
…t dep) Remove valibot and @valibot/to-json-schema from devframe's runtime dependencies so nothing forces a specific Standard Schema validator on users. Consumers bring their own (valibot, zod, arktype) or use the new built-in zero-dependency builder. - Add devframe/utils/schema: a tiny zero-dep Standard Schema builder (s) exposing string/number/boolean/void/null/picklist/array/object/ optional/nullable, used by the recipes. - Convert recipes (common-rpc-functions, interactive-auth) off valibot to the built-in builder. - Make the CLI-flags parser validator-neutral: validate via the ~standard contract and duck-type the schema kind (works for the builder and valibot; other vendors degrade to value-flags). - Drop @valibot/to-json-schema; the agent/MCP surface advertises a permissive object schema for any declared args/returns. - valibot stays a devDependency for tests only. BREAKING CHANGE: the exported recipe definitions (openInEditor, openInFinder, interactive-auth functions) now type their args/returns as the built-in DevframeSchema instead of valibot schema types, and @valibot/to-json-schema is no longer a devframe dependency. Agent tool JSON schemas are now permissive objects rather than precise valibot conversions.
…ins; docs use valibot - Rename devframe/utils/schema -> devframe/utils/simple-schema and rename the exported type DevframeSchema -> SimpleSchema. The builder is now explicitly documented as discouraged for app code (a minimal, best-effort validator for devframe's own first-party packages). - Extend the builder with record/union/literal and make object() infer optional keys for optional() fields (matching valibot/zod). - Migrate the built-in plugins (assets, og, terminals) off valibot onto devframe/utils/simple-schema, and drop valibot from their dependencies. - Docs: never reference the built-in builder; all schema examples use valibot with an explicit install hint (npm i valibot). BREAKING CHANGE: devframe/utils/schema is renamed to devframe/utils/simple-schema and its exported type DevframeSchema is renamed to SimpleSchema.
…d-json Adopt @standard-community/standard-json (dispatches on the schema's ~standard vendor, per-vendor converters are optional peers) so the agent/MCP surface and the inspect plugin produce precise JSON schema for whatever validator a user brings — valibot, zod, arktype — while forcing none. Missing/unknown converters degrade to a permissive object schema. - Core MCP to-json-schema + build-server tool projection are now async (lazy vendor-converter import). - inspect's schema converter switches from @valibot/to-json-schema to standard-json (async), gaining any-vendor support. - Docs: recommend valibot as the lightest validator, but note that json-render and the MCP server already use zod, so users pulling zod via those integrations should prefer zod to reuse the dependency. - AGENTS.md: first-party packages (devframe, @devframes/*) must stay validator-neutral (no preferred validator dependency); docs guide users to valibot or zod for their own integrations.
…-json + quansync Replace @standard-community/standard-json with the schema's own Standard JSON Schema converter (~standard.jsonSchema from @standard-schema/spec, implemented by e.g. zod 4). This keeps JSON-schema generation vendor-neutral and precise for validators that ship a converter, while removing the @standard-community/standard-json + quansync dependencies and reverting the async conversion back to synchronous. Validators without a native converter degrade to a permissive object schema. The inspect plugin additionally keeps @valibot/to-json-schema as a fallback so it still converts valibot schemas precisely. Technique adapted from PR #155.
6 tasks
The snapshot was previously updated from an incremental dist that rendered multi-entry types via subpath specifiers (devframe/rpc, devframe/types); a clean build (as CI does) canonicalizes them to the bare devframe entry. Regenerated to match CI.
antfubot
added a commit
that referenced
this pull request
Aug 3, 2026
main landed two breaking changes this branch didn't know about: - deps!: migrate MCP adapter to @modelcontextprotocol/sdk v2 (#156) — the monolithic package split into @modelcontextprotocol/server + @modelcontextprotocol/client; setRequestHandler moved from imported schema constants to method-string form. - feat(rpc)!: support Standard Schema for RPC definitions (#157) — RpcArgsSchema/RpcReturnSchema key off StandardSchemaV1 instead of valibot's GenericSchema; @valibot/to-json-schema dropped; single-arg JSON-Schema unwrapping removed (always arg0/arg1 now). Adaptations: - connect.ts: import from @modelcontextprotocol/server(+/stdio) and @modelcontextprotocol/client (dynamic, still peer-optional); handler registration uses 'tools/list'/'tools/call' method strings. - devframe's package.json: @modelcontextprotocol/client added as an optional peer (connect.ts uses it to dial discovered instances) and bundled correctly via tsdown's onlyBundle (client pulls in @modelcontextprotocol/core, pkce-challenge, eventsource[-parser], jose — all now declared). - Renumbered DF0042 (registry write failure) and DF0043 (missing MCP SDK) to DF0045/DF0046 — both collided with codes main allocated to unrelated diagnostics (capabilities.build:false; RPC arg/return validation) while this branch was in flight. - AgentTool/AgentToolInput.args and DevframeCommandAgentOptions.args retyped from valibot's GenericSchema[] to StandardSchemaV1[]. host-agent.ts no longer eagerly converts args to inputSchema (that module is gone); a kind: 'tool' entry now carries args raw, mirroring how an RPC-backed tool defers to ctx.rpc.definitions — the MCP adapter's computeInputSchema converts either on demand. - hub's commands→agent bridge: coercePositionalArgs no longer detects a single-object schema to unwrap (that convention is gone project- wide); always maps arg0/arg1/... positionally, matching RPC-backed tool coercion. - Tests updated for the new args-carried-raw contract, plus a new end-to-end MCP-adapter test proving Standard Schema args convert to JSON Schema over the real wire (arg0-keyed, not unwrapped). - Removed the now-fully-redundant devframe/utils/valibot-json-schema module and its registrations (superseded by the upstream to-json-schema.ts, which already covers every validator via ~standard.jsonSchema, degrading to a permissive object schema for validators without one — e.g. valibot). Verified: 1020 unit tests, typecheck (21/21), lint, full build, and 17/17 Playwright e2e (incl. both connector round-trips through the real stdio/HTTP MCP v2 pipeline) all green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
RPC function definitions accept any Standard Schema validator for
args/returns— valibot, zod, arktype, or devframe's built-in builder — and declared schemas are enforced at runtime. Devframe's core no longer depends on any specific validator.Why
Schemas used to be valibot-only and inert (type inference + JSON-schema only). Standing on the Standard Schema interface lets tools bring whichever validator they already use, makes declared schemas a real boundary guarantee, and removes valibot as a forced dependency of devframe.
How
RPC layer (validator-neutral + enforced)
RpcArgsSchema/RpcReturnSchemaand arg/return inference key offStandardSchemaV1(new zero-runtime@standard-schema/specdep).getRpcHandlerwraps the resolved handler so local, over-the-wire, and agent/MCP calls all validate declaredargs/returnsviaschema['~standard'].validate(). Failures raise DF0043 (arg) / DF0044 (return). Validation is guard-only — payloads are never rewritten, so extra object fields survive.Fully validator-neutral core (no forced valibot)
devframe/utils/schema: a tiny zero-dependency Standard Schema builder (s) — string/number/boolean/void/null/picklist/array/object/optional/nullable.~standardcontract and duck-types the schema kind (works for the builder and valibot; other vendors degrade to value-flags).@valibot/to-json-schemadropped; the agent/MCP surface advertises a permissive object schema for any declared args/returns.valibotis now only a devDependency (tests). Verified: no valibot in devframe runtime deps, no valibot import in shippeddist.BREAKING CHANGE
args/returnsschemas are now enforced at runtime; a payload that doesn't match its schema throwsDF0043/DF0044where it previously ran. Guard-only validation still allows extra object fields, but a schema stricter than reality will reject calls that used to pass.args/returnsas the built-inDevframeSchemainstead of valibot schema types, and@valibot/to-json-schemais no longer a devframe dependency. Agent tool JSON schemas are now permissive objects rather than precise valibot conversions.Docs:
docs/guide/rpc.md(validator-neutral + runtime-enforcement warning + built-ins),docs/guide/standalone-cli.md,docs/helpers/common-rpc-functions.md, and newdocs/errors/DF0043.md/DF0044.md.This PR was created with the help of an agent.