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
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: 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
DSH 0.1.5-rc.1 Compatibility (Verified)
This plugin has been migrated to the DSH 0.1.5-rc.1 harness and fully validated end-to-end in an isolated consumer of local harness 0.1.5-rc.1:
- 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.5-rc.1 consumer →
dsh --profile compat --dump-configshows this plugin's row → the tool actually registers and executes successfully - Launch method:
npx -p @deepseek-ai/dsh@next 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.5-rc.1):
# 交互式(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
Tencent/WeKnora#dsh-weknora★ 30675
Four read-only tools over a WeKnora knowledge base: list knowledge bases, hybrid passage search, reassemble one document's chunks in order, and WeKnora's own cited RAG or ReAct-agent answer with a resumable session id.
superdesigndev/treg★ 3596
Tool catalog for agents: search ~2,600 external endpoints (SEO and SERP, backlinks, social, people and company enrichment, ad libraries, scraping) by the task you want done, read each one's parameters and per-call price, then call it with the credential injected server-side. Ships the skill plus an MCP row that stays disabled until TREG_TOKEN is set.
TencentCloudBase/CloudBase-AI-Toolkit#dsh-plugin★ 1126
Tencent CloudBase backend for DeepSeek Harness — scaffold and deploy full-stack apps from chat, render query results as table cards with paging, sorting and CSV export, preview a deployment on its domain, and call the CloudBase MCP toolset (`mcp__cloudbase__*`) with device-code login.
gitroomhq/postiz-agent#dsh-postiz★ 496
Connects DeepSeek Harness to Postiz over MCP: list connected social media channels, fetch per-platform posting rules, and schedule, draft, or publish posts to X, LinkedIn, Instagram, Facebook, Threads, TikTok, YouTube, Reddit, Bluesky, Mastodon, Discord, Slack, Telegram and more; adds a postiz workflow skill.
EthanYoQ/Invoice-Downloader#dsh-invoice-downloader★ 446
Local IMAP invoice download, OCR, archive, and Excel reimbursement summaries for DeepSeek Harness.
anysearch-team/anysearch-dsh★ 430
AnySearch-powered real-time web and vertical search provider for DeepSeek Harness.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.