JSON Schema validation: validate/paths/explain/normalize.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:omdsh-dev/dsh-tool-schema
GitHub-sourced plugins run build scripts on your machine at install time. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
DSH JSON Schema validation tool plugin — validate data, list failing paths, explain schema constraints, and safely apply defaults. Zero network, zero dynamic code execution.
Motivation
Agents need to validate arbitrary JSON data against a schema (API response structures, plugin manifests, config files, session events) and locate failing paths. Existing paths lack this capability:
- The
defineToolparameter DSL is an author DSL — it targets plugin authors declaring tool parameters, not a general-purpose validation service for arbitrary user schemas dsh-tool-jsononly offers querying — it can fetch paths and filter, but it does not validate structure or provide RFC 6901 failure location- Model "eyeball" validation is unreliable — with complex nested schemas (allOf/oneOf/
$ref/pattern) combined, hand-computing pass/fail is highly error-prone and cannot show a verifiable process
This plugin provides an independent pure-function JSON Schema validation kernel: one function call returns the verdict, path-based errors, and schema issues. It executes no code, accesses no network, and never silently ignores unsupported schema keywords.
Security Model
- Zero dynamic execution: the validation kernel is a pure data traversal — it does not construct
RegExp(patterns run inside a separate worker), noeval, no network access, no file reads - Unsupported keywords are never silently ignored: reports an
unsupported-keywordschema issue; withstrictSchema=true(default) it fails outright (valid:false/complete:false), withstrictSchema=falseit validates the supported subset (valid:null/complete:false/supportedSubsetValid) - ReDoS defense: all
patternchecks share a 1,000 ms hard budget inside a terminable worker thread; on timeout it callsterminate()and reports an error — catastrophic backtracking cannot block the host process; patterns ≤ 16 KiB, ≤ 100 per schema - Prototype pollution protection: all object access uses
Object.hasOwn;__proto__/constructor/prototypeare treated only as ordinary JSON keys $refsafety: only local references are supported (#and#/$defs/<token>, RFC 6901 escaping); the target must exist; cycle detection (schema-check statically reportsref-cycle+ a dynamic(schemaNode, instance)stack fallback at validation time)- Budgets:
- data / schema each ≤ 256 KiB (over the limit reports an error directly)
- nesting depth ≤ 64, schema nodes ≤ 10,000, traversal nodes ≤ 100,000
- errors 100 (default) / 1,000 (max);
$refchains ≤ 64 - canonical output ≤ 1 MiB (on overflow, errors/schemaIssues etc. are truncated and
truncatedis set)
- Tool parameters are recorded in the session log — do not pass sensitive data
Tool Declaration
Registers the schema tool (@deepseek-ai/dsh-tool-schema, row id tool-schema), uniformly emitting JSON text strings.
| action | Purpose | Output |
|---|---|---|
validate |
Validate whether the instance conforms to the schema | verdict + RFC 6901 instancePath/schemaPath errors (stable ordering) + schemaIssues + checkedNodes + truncated |
paths |
Return only failing paths | paths (path + keyword summary) + errorCount + truncated |
explain |
Statically explain schema constraints | a sequence of constraint-tree nodes (nodes, a finite list rather than long-form natural language) + schemaIssues + truncated |
normalize |
Deep copy + apply explicit defaults, then validate |
appliedDefaults (path + value) + warnings (default-invalid / normalize-skip-branch) + the full validate result |
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | ✅ | validate / paths / explain / normalize |
data |
json | The instance to validate (required for validate/paths/normalize; null is valid data) |
|
schema |
json | ✅ | JSON Schema (boolean or object; draft 2020-12 subset) |
strictSchema |
boolean | Fail on unsupported keywords. Default true |
|
maxErrors |
integer | Maximum number of errors to report. Default 100, range 1..1,000 |
Supported keywords: type/enum/const, objects (required/properties/additionalProperties/minProperties/maxProperties), arrays (items/minItems/maxItems/uniqueItems), strings (minLength/maxLength/pattern), numbers (minimum/maximum/exclusive*/multipleOf), combinators (allOf/anyOf/oneOf/not), local $ref.
Example Output
{"action":"validate","complete":true,"valid":true,"supportedSubsetValid":true,
"errors":[],"schemaIssues":[],"checkedNodes":3,"truncated":false}
{"action":"paths","valid":false,"paths":[{"path":"/a","keywords":["type"]}],
"errorCount":1,"truncated":false}
{"action":"normalize","valid":true,
"appliedDefaults":[{"path":"/b","value":5}],"warnings":[]}
Design Points
- Error format:
instancePath/schemaPath(RFC 6901 JSON Pointer),keyword, stablecode,message(+expected/actual); stable ordering: instancePath → schemaPath → keyword lexicographic - Combinator keywords: when all anyOf/oneOf branches fail, the top-level error plus a bounded
branchessummary (≤ 3 per branch) is returned; oneOf with 0 / multiple matching branches reportsone-of/one-of-multiplerespectively;notfails when its sub-schema validates - Number semantics: JSON numbers must be finite; integers use
Number.isInteger;multipleOfuses a scaling/tolerance strategy (relative tolerance1e-9) rather than% === 0, with no arbitrary-precision promise - String length: counted by Unicode code points; patterns run in a terminable worker sharing a 1,000 ms total budget
$refsemantics: pure$refcycles are statically reported by schema-check;$refwith sibling keywords takes effect together per draft 2020-12 (no 2019-09 sibling-ignoring behavior for$ref)- normalize does not overstep: it never mutates input (all new objects use
Object.create(null)); it only applies explicitdefaults for fields missing underproperties; the default must be JSON-compatible and pass the corresponding sub-schema (otherwise adefault-invalidwarning and skip); no type coercion, no deletion of additional properties; oneOf/anyOf is entered only when exactly one branch already matches without applying defaults (otherwise anormalize-skip-branchwarning) - explain is not silent: the output carries
schemaIssues; unsupported keywords are reported under explain as well - Reproducible output: errors and issues are stably ordered; overflow truncation sets
truncated; canonical output ≤ 1 MiB (contract assertion)
Build & Test
# 构建(零依赖,仅需 monorepo 的 tsc)
node <monorepo>/node_modules/typescript/bin/tsc -p tsconfig.json
# 测试(vitest,125 个用例:scalar/object/array/combinators/ref/pattern/normalize/limits/register)
node <monorepo>/node_modules/vitest/vitest.mjs run tests
npm 0.1.0-rc.6 Compatibility (Verified)
This plugin has been migrated to the npm 0.1.0-rc.6 dependency line and fully validated end-to-end in an isolated consumer of @deepseek-ai/dsh@0.1.0-rc.6:
- Type/runtime:
@deepseek-ai/cordis@^4.0.1+@deepseek-ai/dsh-tools@>=0.0.1-rc.1 <0.2.0+@deepseek-ai/dsh-invariants@>=0.0.1-rc.1 <0.2.0(peer); no longer depends on unscopedcordis - Standalone build:
npm install(devDependencies self-contained: typescript/vitest/@types/node) →npm run typecheck→npm test→npm run build→npm pack - Consumption validation: the tarball is installed into a 0.1.0-rc.6 consumer →
dsh --profile compat --dump-configshows this plugin's row → the tool actually registers and executes successfully - Launch method:
npx -p @deepseek-ai/dsh@0.1.0-rc.6 dsh web(lib production mode; do notinstall -gglobally)
Installation
Profile Bundle (Recommended)
The repository lives at omdsh-dev/dsh-tool-schema (public). Install this plugin as a standalone bundle into a profile (DSH 0.1.0-rc.6 (npm)):
# 交互式(web)profile
dsh plugin --profile web add github:omdsh-dev/dsh-tool-schema
# 一次性任务(headless)profile —— dsh run 默认使用 headless
dsh plugin --profile headless add github:omdsh-dev/dsh-tool-schema
The dsh.bundle.patch inside the package automatically adds the plugin to the profile's layer stack after installation (row id: tool-schema). Missing peer dependencies of the plugin (cordis, @deepseek-ai/dsh-tools) are provided by the profile's healed profiles/node_modules fallback install.
⚠️ web and headless are different profiles: installing to web does not automatically cover headless;
dsh runuses the headless profile by default. Windows paths use forward slashes (C:/...).
Installing from an npm pack Tarball
Build locally and install from the tarball path (no GitHub dependency):
# tarball method (web shown; headless same)
npm pack
dsh plugin --profile web add <path to the npm pack tarball>
Verify Installation
dsh --profile web --dump-config | grep tool-schema
Runtime Verification
dsh run "用 schema 工具验证 {name: 'x', age: 3} 是否符合给定 JSON Schema"
Manual Installation & Legacy Compatibility
Legacy scenarios: monorepo integration, legacy snapshots that do not support Profile Bundle, or plugin development/debugging environments (local junction/symlink, manually editing profile layers).
License
MIT
Links
More in this category
liustack/modlens★ 1199
Vision bridge for text-only models: paste an image, get structured JSON evidence (OCR, layout, semantics).
Anionex/dsh-vision-toolkit★ 308
Vision tasks for text-only models: intent-aware image Q&A, long-screenshot OCR, UI reproduction, grounding, and pixel diff.
zhaoolee/notes★ 138
Export DSH conversations as Smartisan Notes-style PNGs, or create and update Markdown notes in a configured account-scoped workspace.
liustack/modsearch★ 85
Web search bridge for text-only agents: ask the web or X, get structured JSON evidence (search, fetch, citations).
Lum1104/dsh-browser★ 80
Chrome sidebar extension that lets DSH operate your browser directly, no vision capabilities required.
taxueseek/argo★ 69
Search built for agents: multilingual coverage across web, academic, code, shopping, finance, news, and encyclopedias.