Preface

DeepSeek Harness (dsh) is an open-source agent runtime developed by DeepSeek, officially positioned as a developer preview release, with the slogan “Everything is a plugin”: model adaptation, tool registration, session logging, and Agent loops can all be replaced via plugins without modifying the runtime source code. The official entry point for launching the Web UI is:

npx @deepseek-ai/dsh web

A more common pain point in daily development is the flip side: mainstream conversational models like DeepSeek and GLM are purely text-based and cannot see screenshots. Error interfaces, design drafts, PDF pages, frontend rendering results often have to be described verbally first, forcing the model to guess them. The community directory deepseek-harness-plugin.com is an independent site with no official affiliation to DeepSeek / Fang (HyperMind; it categorizes and catalogs such extensions, with a featured plugin under “Tools and Capabilitiesmodlens.

This article is organized after cross-verifying against the directory’s detail page, GitHub repository README / INSTALL.md / host access documentation / output contract, and DeepSeek Harness official instructions: what it is, what it can do after installation, how to write commands, and what the image recognition results look like.

What it is

modlens is a vision plugin maintained by liustack (authored by Leon Liu), with the npm package name @liustack/modlens, licensed under MIT, and mainly written in TypeScript. At the time of writing, the version in the repository’s package.json is 3.18.1 (2026-08-17), requiring Node.js >= 22.19. The community directory categorized it under “Tools and Capabilities”, with an inclusion date of 2026-08-14; the GitHub repository page showed 2384** stars on 2026-08-17 (the directory page snapshot showed 1548 stars; use the repository page star count shall prevail).

The positioning of both the directory page and repository README is consistent: it is a vision plugin on DeepSeek Harness, and a visual bridge for pure text coding agents — handing images to an external vision engine, returning structured JSON with OCR, layout, and semantics, which is then passed to the pure text model in the current session for reasoning.

In dsh, it is not a Skill triggered by prompt words. The repository’s INSTALL.md clearly states: only copy the skills/modlens folder, users will not get the modlens_read_image tool, nor will they see the (modlens vision) model entry (see issue #32. The correct form is a native plugin (dsh bundle): register tools, wrap pure text routes, and take over pasting in the Web UI.

The same image recognition engine also appears as a Skill on Claude Code, Codex, Pi, and OpenCode, with all configurations written in ~/.modlens/config.json. This article takes dsh as the main subject.

Core Features

1. Native Tool modlens_read_image

After being installed into dsh, the plugin registers modlens_read_image. The tool’s schema is sent to the model with every request, without relying on keyword heuristics. The model calls it when it sees an image path or attachment, the plugin runs its own CLI within the package, and returns structured evidence as the tool output.

The repository’s CHANGELOG 3.16.3 explained the naming reason: if dsh already has the host’s own read_image, layered registration will not cause a conflict, and the model will still hit the host tool, which rejects pure text models. So the plugin uses its own name to avoid preemption.

2. Two Pasting Paths

The repository README divides pasting images on dsh into two paths, determined by the host based on the model’s metadata (inputModalities), not guessed by name:

  1. **Direct Pasting (paste-to-path)
    When the current model is confirmed to be pure text, the browser sends the image to the local dsh web server’s /modlens/paste (only loopback, magic byte verification, max 25 MB limit), saves it as a private temporary file, and the file path appears in the input box. The message does not include image attachments, so dsh's image access check will not block it. This is close to the form Pi, OpenCode, and Claude Code pass to the model, and is also the primary trigger condition formodlens_read_image.

  2. **Paste after Switching to (modlens vision) Variant
    The plugin will automatically add wrapper entries to each provider route carrying pure text DeepSeek / GLM. Common ones by default are DeepSeek-V4-Flash (modlens vision) and DeepSeek-V4-Pro (modlens vision); additional routes (such as opencode-go, zai) will each have an extra set. The official vision models of the two vendors will be excluded. This path retains thumbnails, and converts image blocks into evidence text when sending requests. The selector has memory, just select once.

If the metadata cannot be confirmed, or the model declares support for image input, pasting remains native, and the vision model continues to read images by itself. Set pasteToPath: false in the plugin configuration line can turn off the first path.

3. Structured Evidence, Not a Paragraph of Prose

Each recognition outputs a single JSON to stdout (output contract v2). The outer structure is roughly:

{
  "image": "/abs/path/or/url",
  "provider": "gemini-api",
  "result": { },
  "meta": {
    "generatedAt": "2026-08-01T12:00:00.000Z",
    "model": "gemini-3.6-flash-low",
    "durationSeconds": 25.4,
    "attempts": [],
    "warnings": []
  }
}

The required top-level fields of result are summary, ocr, layout, semantics, visual, uncertainty:
- ocr: Full text transcription, and text split by line
- layout.regions: Blocks divided in reading order, type is a free string (title, paragraph, table, chart, code, link, nav, etc. are just common terms in documents, not a closed enum
- semantics: Scenes, entities, relationships
- visual: Supplementary information such as main colors, style
- uncertainty: List unclear parts truthfully, instead of filling in fabricated content

Compared to v1, v2 removes pixel-level bbox and numerical confidence. The documentation’s reason is that vision models are most likely to fabricate these two items. Structurally不合格 results will undergo fail over, instead of being handed directly to the upstream model. meta.attempts records each attempt on the chain; when reusing the quota of a local CLI, meta.warnings will indicate whose quota was used.

4. Multiple Engines, One Failover Chain

modlens is not tied to a single vision service. The repository documentation lists six built-in providers, configure one of them to use:

Provider Requirements Single time consumption given in documentation
gemini-api Gemini API key 5–10 seconds (recommended default by the repository)
openai OpenAI-compatible endpoint (key + baseUrl + model) 5–10 seconds
anthropic Anthropic API key 5–10 seconds
antigravity-cli Free agy CLI, log in once in the browser 15–45 seconds
claude-cli Signed-in Claude Code 20–45 seconds
kimi-cli Signed-in Kimi Code, needs explicit naming 20–45 seconds

When not pinning the provider, the configured engines form a failover chain: API fast lanes are tried first, agent CLI as fallback, and the first available result wins. openai here is a protocol socket, not “only connect to OpenAI: DashScope’s qwen-vl, GLM open platform, SiliconFlow, OpenRouter, self-hosted vLLM / Ollama, as long as they follow chat-completions and support image input, can use the same set of keys.

Locally signed-in Codex, OpenCode, Pi, Grok CLI need to config set reuse.<name> true before entering the chain, and will not deduct other people’s subscriptions by default. kimi-cli also needs explicit naming to run, as it consumes Kimi Code subscriptions.

Installation and Activation

Commands given by the community directory

The installation command on the plugin detail page is:

dsh plugin add github:liustack/modlens

For reproducible installation, the directory page requires fixing the commit hash:

dsh plugin add github:liustack/modlens#<commit>

The directory page also reminds: the plugin runs with the permissions of the current dsh process, and may execute code during installation; you should check the source code repository and license before installation.

Version-pinning syntax for dsh from the repository

The commands given to dsh users in INSTALL.md and docs/harness-setup.md is another one. At the time of writing, pinned to 3.18.1:

npx -y @deepseek-ai/dsh plugin --profile web add @liustack/modlens@3.18.1

The repository deliberately does not use @latest: pnpm 11 enables minimumReleaseAge (24 hours) by default, the dist-tag is only resolved in versions that have passed the cooling-off period, and @latest may install an older version from a day ago. Specifying the version number is explicit. Updates also use add instead of update: update only moves within the recorded semver range, and the caret range installed 2.x cannot reach 3.x.

You can query the current version with the following command, then replace the version number in the command with the output value:

npm view @liustack/modlens version

Restart dsh after installation, and look for entries with the (modlens vision) suffix in the model selector. You can confirm the list with:

npx -y @deepseek-ai/dsh plugin --profile web list

If declares no dsh.bundle appears, the repository’s judgment is that an old package was installed during the cooling-off period, follow the “Stay Updated section of the host access documentation, do not switch to copying the Skill directory.

web is just an example profile in the documentation. The actual profile name is subject to your local machine, just replace --profile with your own.

Configure a Vision Engine

The plugin can register the tool, but the actual image recognition is done by the engine. The configuration file is ~/.modlens/config.json, shared by dsh and other harnesses. Web UI users can open the card in Settings → Plugins → Plugin Configuration: select the engine, fill in the key / address / model, and authorize which local logins can be borrowed. The card reads and writes the same file via the loopback route, and will not send saved secrets to the browser; leaving the secret field empty means keeping the original value.

The repository recommends starting with the Gemini API (apply at Google AI Studio, terms and quotas are subject to Google):

modlens config set gemini-api.apiKey
modlens config set provider gemini-api

When no parameter is given, apiKey will hide the echo prompt for input, to avoid the key entering argv and shell history. For completely skip registration, the repository suggests Antigravity CLI:

curl -fsSL https://antigravity.google/cli/install.sh | bash
agy

Complete the login in the browser and then exit. For headless environments or SSH without a desktop, the documentation advises against this path, and recommends using an API key instead.

For OpenAI-compatible vision models (examples from the repository README, endpoints are subject to the current documentation of each platform):

modlens config set openai.baseUrl https://dashscope.aliyuncs.com/compatible-mode/v1
modlens config set openai.apiKey
modlens config set openai.model qwen3-vl-plus
modlens config set provider openai

All three fields are required, and the model must accept image input. The same set of keys can be replaced with other compatible gateways. Since 3.17.0 onwards, once a provider is written into the configuration file, the credentials shall prevail, and will no longer be mixed with environment variables such as OPENAI_API_KEY by field, avoiding cases where the address comes from the file and the key comes from the environment, to piece together a credential that does not exist on either side.

Health Check

modlens doctor

On success, look for two lines: the name under Selected provider, and whether it is marked [ok] in the Providers list. It is normal for the remaining providers to show [!!]. Common issues are listed in the documentation: Node version lower than 22.19, Gemini apiKey not written, agy not in PATH. Add --json to get a machine-readable report.

Test end-to-end once (will consume one recognition quota):

modlens -i /path/to/image.png

Typical Usage

After installation, you can chat normally in dsh. The following examples below are from the repository documentation and the directory page, not fabricated cases.

1. Paste screenshots directly with pure text models

Select regular pure text entries such as DeepSeek-V4-Flash / DeepSeek-V4-Pro, paste the error interface or UI exception into the input box. The browser saves the image as a temporary file, the path enters the composer, the model calls modlens_read_image, and then answers based on the OCR and layout in the JSON. The directory page summarizes this change as: from “Help me describe this screenshot” to “What exactly is on the screen”.

2. Switch to the vision variant to retain thumbnails

Select DeepSeek-V4-Flash (modlens vision) (or the corresponding entry automatically generated locally) in the model selector, then paste. The thumbnail remains in the message, and you can see in the trace that the image has been transcribed when sending the request. The repository README demonstrated this path with a DeepSeek Harness paste, driven by the pure text DeepSeek-V4-Flash.

Note the restriction stated in repository issue #40: once there is an image attachment in the session, dsh will refuse to switch back to regular pure text entries that do not declare image modalities. The (modlens vision) variant can switch over because it converts image blocks into evidence text when sending requests. The first “paste-to-path` path will not generate attachments, and the selector will not be locked.

3. Document pages, design drafts, frontend restoration

Three types of scenarios listed on the directory page:
- Document extraction: Long PDF pages, slides, screenshots become queryable structured data
- Frontend work: Let the agent sees design drafts or rendered pages, write or modify code according to layout semantics
- Debug with screenshots: Paste errors and UI exceptions directly into the conversation

For large-scale long screenshot OCR, the directory page suggests matching dsh-vision-toolkit (the corresponding entry in the relevant list is agent-vision-toolkit. modlens’s own positioning is “paste an image, get back a piece of evidence JSON”, not a long image pipeline.

4. Actual test records in the repository README

These are the original records marked in the README, driven by the pure text DeepSeek-V4-Flash, used to illustrate the output granularity, not third-party evaluations:
- Read a tweet screenshot in Codex Desktop: author, caption, photo details, time and interaction numbers
- Three images at one time: read each one individually, and determine if they belong to the same visual family
- 128-model comparison scatter plot: coordinate axes, logarithmic scale, vendor color matching, highlighted areas and dashed-marked DeepSeek models
- Paste slides into Claude Code terminal: title, layout, background; truncated file names are written into uncertainty instead of fabricating a complete file name

Applicable Scenarios and Notes

Suitable for:
- Using DeepSeek / GLM pure text models for coding in dsh, but often need to view screenshots, design drafts, error interfaces
- Hope the model references specific text and blocks on the graph, instead of describing out of thin air
- Already have Gemini / compatible vision APIs, or have locally signed-in Claude Code, Codex, etc., and hope to reuse them instead of building another set of multimodal gateway

Before use, please note the following points, all from the directory page or repository documentation:
1. Permissions and License. The plugin runs with the permissions of the current dsh process, and may execute code during installation. Check the source code and MIT license before installation. The community directory is not an official app store.
2.
dsh is still in developer preview. Plugin interfaces may change. modlens claims its interface is very small (tool registration, llm adaptation layer used for vision variants, attachment reading, a pre-execution hook for agents), and moving the interface will throw an error instead of failing silently.
3. **Treat image content as untrusted input. Instructions that can be viewed by the model can be included in screenshots. The security documentation requires: only analyze images you are willing to open; for untrusted images, first pin -p gemini-api (modlens downloads bytes instead of running local agents). Who fetches remote URLs varies by provider: gemini-api downloads locally and performs private address / magic-by