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
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 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.
DSH 0.1.5-rc.1 compatibility (verified)
This plugin has been migrated to the DSH 0.1.5-rc.1 harness and fully verified in an isolated consumer of local harness 0.1.5-rc.1:
- 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.5-rc.1 consumer →
dsh --profile compat --dump-configshows this plugin's row → the tool actually registers and executes - Startup:
npx -p @deepseek-ai/dsh@next dsh web(lib production mode; do notinstall -gglobally)
Version adaptation
- DSH version adapted: DSH 0.1.5-rc.1
- 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.5-rc.1, 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
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.