JSON queries with a JMESPath subset.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:omdsh-dev/dsh-tool-json
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 query tool plugin — JMESPath-inspired path queries (custom subset) with a zero-dependency recursive-descent parser.
Why
Handling JSON is a high-frequency operation for agents — API return values, config files, and tool outputs are JSON everywhere. The current approach spawns a bash subprocess to run node -e or jq, incurring subprocess overhead and string serialization costs every time.
DSH's built-in grep can do regex matching but cannot understand JSON structure. For {"items":[{"id":1}]}:
greponly does string-level search, easily false-matching values, keys, or same-named keys in nested sub-objectsjsonfollows structured paths, matching only the specified path without confusing keys and values
Security model
Hand-written recursive-descent parser, no eval/new Function; Object.hasOwn prevents prototype-chain pollution (reading constructor/__proto__ does not trigger the prototype chain). Resource limits (uniformly enforced across both the object and string input paths):
- Query expression length ≤ 200 characters, parse depth ≤ 20 levels, array indices must be safe integers
- String input ≤ 1,000,000 bytes (UTF-8); input nesting depth ≤ 100
- Single wildcard projection ≤ 100,000 elements
- Only accepts JSON-compatible values (null/boolean/finite number/string/array/plain object; rejects undefined/BigInt/functions/Date/non-finite numbers)
Error classification (JsonQueryError): MISSING_PROPERTY (skipped inside projections), TYPE_MISMATCH/INDEX_OUT_OF_BOUNDS/INVALID_QUERY (thrown as-is), all with a unified json: prefix.
Cost model (AUDIT-JSON-03): input undergoes full validation (type/depth/bytes/cycles/enumerability) before every query — this is an intentional security cost; even querying a small field fully scans the input;
timeoutMscannot interrupt the synchronous validation.
Architecture
DSH Agent
│ ctx.tools.register()
▼
src/index.ts(Cordis 插件入口 + action 分发)
│
▼
src/query.ts
├── parseQuery() — 递归下降解析器(strict 语法 + 转义 + 上限)
├── executeQuery() — 执行器(错误分类 + 投影上限)
└── normalizeInput() — 双形态输入 + assertJsonCompatible 校验
Tool declaration
ctx.tools.register(defineTool({
name: 'json',
parameters: {
input: { type: 'json', required: true, description: 'JSON value or JSON string to query.' },
query: { type: 'string', required: true, description: 'Path expression, e.g. "data.items[0].name".' },
},
output: { schema: { type: 'json' }, render: (_a, v) => [{ type: 'text', text: JSON.stringify(v) }] },
execute: (args) => Promise.resolve(executeAction(args) as JsonValue),
timeoutMs: 1000,
}))
input has two forms: an object passed directly (the model generates the argument directly, zero escaping) or a string (raw passthrough from bash/read); normalizeInput unifies normalization and validation.
Query syntax (JMESPath-inspired subset)
| expression | example | description |
|---|---|---|
| Dot access | foo.bar |
Nested object property (identifier charset [A-Za-z0-9_$ + BMP non-ASCII) |
| Bracket index | items[0] |
Array index (safe integer) |
| Bracket property | items['key'] / items["key"] |
Property names containing special characters |
| Wildcard projection | items[*].name |
Arrays only; extracts element properties |
| Composition | a.b[0].c.d |
Any combination of the above |
Semantic boundaries (intentionally incompatible with standard JMESPath, locked):
- Multi-level wildcards like
items[*].tags[*]return nested arrays ([['a','b'],['c']]) without standard projection flattening - Wildcards apply to arrays only; object field enumeration is not supported; non-object elements are skipped per projection semantics; legal
nullresults are preserved - Quoted properties support three escapes
\\\'\"(usable under any quoting form); illegal escapes error - Only
MISSING_PROPERTYis skipped inside projections; type/index/internal errors are thrown as-is
Not supported (low-frequency scenarios; fall back to bash + node): filters [?downloads > 1000], pipes |, function calls.
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 verified in an isolated consumer of @deepseek-ai/dsh@0.1.0-rc.6:
- Types/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 are self-contained: typescript/vitest/@types/node) →npm run typecheck→npm test→npm run build→npm pack - Consumption verification: tarball installed into the 0.1.0-rc.6 consumer →
dsh --profile compat --dump-configshows this plugin's row → the tool actually registers and executes - Startup:
npx -p @deepseek-ai/dsh@0.1.0-rc.6 dsh web(lib production mode; do notinstall -gglobally)
Version adaptation
- DSH version adapted: DSH 0.1.0-rc.6 (npm)
- Bundle declaration:
dsh.bundleinpackage.json(patch points tocordis.patch.yml) +exportsfields - Patch format:
cordis.patch.ymluses the- insert:list (patches are id-targeted; a bare- id:entry reportsentry not found) - files: the published tarball contains
lib/,src/,cordis.patch.yml
Installation
Plugin source repository: https://github.com/omdsh-dev/dsh-tool-json (public).
Profile Bundle (recommended)
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-json
# 一次性任务(headless)profile —— dsh run 默认使用 headless
dsh plugin --profile headless add github:omdsh-dev/dsh-tool-json
The dsh.bundle.patch inside the package (pointing to cordis.patch.yml) automatically adds the plugin to the profile's layer stack after installation; the plugin's cordis.patch.yml inserts the tool-json entry via - insert:.
⚠️ web and headless are different profiles: installing into web does not automatically cover headless;
dsh runuses the headless profile by default.
Install via npm pack tarball
npm pack # generates dsh-tool-json-*.tgz
dsh plugin --profile web add ./dsh-tool-json-*.tgz
dsh plugin --profile headless add ./dsh-tool-json-*.tgz
Verify installation
dsh --profile web --dump-config | grep tool-json
Run verification
dsh run "使用 json 工具查询 {"a":{"b":1}} 的 a.b"
Manual installation and legacy compatibility
Only for legacy snapshots that do not support Profile Bundle, or plugin development/debugging environments:
- Place into the monorepo:
cp -r json ~/.dsh/source/master/packages/tools/json(development/debugging) - Add
"@deepseek-ai/dsh-tool-json": "workspace:^"toapps/cli/package.json; add{ "path": "./packages/tools/json" }to thereferencesoftsconfig.host.json pnpm install && pnpm run build- Insert the plugin in the profile's user-layer patch (
~/.dsh/profiles/<name>/cordis.patch.yml):
- insert:
- id: tool-json
name: '@deepseek-ai/dsh-tool-json'
- Verify:
dsh --profile <name> --dump-config | grep tool-json
Note: patches are id-targeted — a bare
- id:entry reportsentry "xxx" not found; it must be wrapped in an- insert:list.
Usage
json { input: <JSON>, query: "items[0].name" } → "hello"
json { input: <JSON>, query: "items[*].name" } → ["a", "b"](合法 null 保留)
json { input: <JSON>, query: "items['complex-key']" } → "ok"
Known limitations
- Read-only: cannot modify JSON fields (use
str_replace_editor/writefor in-place edits; asetmode may be considered for v2) - No filter expressions, no standard JMESPath projection flattening (see semantic boundaries)
- Object-form input relies on the DSH parameter pipeline to guarantee lossless JSON
Testing
pnpm test
54 test cases (functionality, errors, attack payloads, object/string dual-form inputs, resource limits, and escaping boundaries). See the locally maintained design document for the full list.
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.