DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Screenshot Feedback Hook Mcp — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins

dsh-screenshot-feedback-hook-mcp

Screenshot Feedback Hook Mcp

Screenshot feedback for DeepSeek Harness — let your coding agent SEE what it builds

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-screenshot-feedback-hook-mcp@0.2.1
READMECompatibilityVersions

Compatibility and provenance

Screenshot Feedback Hook Mcp is published as dsh-screenshot-feedback-hook-mcp and currently resolves to version 0.2.1. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
9/20/2026

Versions

0.2.1stable
8/28/2026
0.2.0stable
8/25/2026
0.1.1stable
8/22/2026
Show 1 more versionCollapse versions
0.1.0stable
8/21/2026

Related plugins

Loading related plugins…

Latest
0.2.1
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
212.1 kB
Files
30
Surface
web
License
MIT
Source
npm
GitHub
★ 3
Weekly downloads
64
Last push
8/31/2026
View source ↗Project homepage ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in vision-media.

Tool Describe Image@linxin666/dsh-tool-describe-imageModel-facing describe_image tool for the dsh web GUI: gives a text-only model image understanding by asking a vision-language model at an OpenAI-compatible endpoint to describe one image (local path, http(s) URL, or attachment reference). Hot-pluggable — Modlens@liustack/modlensPlug-in vision for text-only LLMs, powered by the free Antigravity CLIDeepseek Ivideodeepseek-ivideoiPolloWork HyperFrames Video Studio and 27 editable video templates as a native DeepSeek Harness conversation view.Codexdsh-codexChatGPT OAuth, Codex models, search, read_image URL support, and gpt-image-2 generation for DeepSeek Harness

README

dsh-screenshot-feedback-hook-mcp

English | 中文

A DeepSeek Harness plugin that lets your agent see the screen it just produced — a web page, an EasyEDA/CAD drawing, any desktop app — and correct itself from what it sees.

It is the dsh half of screenshot-feedback-hook-mcp; the Python package still owns capture and compression (mss + Pillow, Windows/Linux/macOS), and this plugin owns the dsh wiring.

The agent calls take_screenshot and the image arrives in the conversation

Why a native plugin instead of the Claude Code hook bridge

A Claude Code hook can only return text, so the best it can do is hand back a file path and hope the agent reads it. dsh has a durable image-attachment service, so a native plugin can commit the screenshot and put a real image block into the conversation — the agent does not have to call anything.

PathHow the image arrives
take_screenshot toolthe tool result carries the image block
after a tool runs (opt-in)the screenshot rides the tool result as additional context
at the end of a turn (opt-in)the screenshot is steered/injected into the next step

Requirements

  • dsh v0.1.0-rc.8 or later — every API this plugin uses was verified against that tag.
  • pnpm on PATH — dsh plugin forwards to it.
  • A screenshot-feedback-hook-mcp >= 0.3.0 that can run capture --json — it owns capture and compression. Three ways to get one are below. Older releases lack that subcommand; the plugin detects it and names the upgrade.
  • On macOS, screen-recording permission — and note that a missing one is not reliably detected. See Platform notes before you trust a screenshot.
  • A model that accepts image input — see Image-capable models. Without one the plugin refuses to capture and explains how to switch.

Getting the screenshot CLI onto the machine

The plugin's only contact with this CLI is the command / args pair in its config (see Configuration): it looks command up on PATH once, and that is all. So pick any of the three below — uv is not a hard dependency, just the default route.

A. Install uv — the default config then works as shipped (recommended)

# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

Nothing else to change: the defaults are command: uvx and args: ['screenshot-feedback-hook-mcp'], and uvx fetches the package from PyPI on demand (the first run needs network).

uv needs no Python of its own — it is a standalone binary that downloads an interpreter when it needs one. That makes this the lowest bar for a machine with no usable Python yet; route B requires you to already have one.

B. You already have Python and want a resident install

pipx install screenshot-feedback-hook-mcp
# or: uv tool install screenshot-feedback-hook-mcp

Then point the config at it. A later layer replaces the whole config, so restate every key you want:

# ~/.dsh/profiles/<name>/cordis.patch.yml
- id: screenshot-feedback
  name: dsh-screenshot-feedback-hook-mcp
  config:
    command: screenshot-feedback-hook-mcp
    args: []
    monitor: 0

C. Inside a venv, or you would rather not touch PATH at all

Give command an absolute path; the rest is as in B:

    command: C:\Users\you\proj\.venv\Scripts\screenshot-feedback-hook-mcp.exe
    args: []

command / args / cwd are not on the settings card — they decide where the executable lives, which is a deployment fact rather than a user preference. So routes B and C mean editing the YAML above; there is no control for them in the settings page.

The failure messages are actionable, at least: when the command cannot be found the plugin tells you to install uv, or to set command to screenshot-feedback-hook-mcp with an empty args after a pipx install; when the Python package is too old it names the upgrade instead of dumping an argparse usage string at you.

Install

This plugin is listed on awesome-dsh-plugin under vision, so you can install it straight from the dsh plugin market. The market card currently hands out the GitHub spec below — the npm spec is waiting on that site's own probe to link the npm package, which is outside this repo. Typing the npm command below yourself has always worked.

dsh plugin --profile web add dsh-screenshot-feedback-hook-mcp
dsh web

Ask the agent to "take a screenshot and tell me what is on screen". Use --profile <name> for whichever profile you boot.

Installing from GitHub, and why not from a working tree

The plugin registry lists a git spec, and it works: pnpm clones the repo, runs this package's prepare script to build lib/, then packs the result and installs that.

dsh plugin --profile web add github:lkh081231/screenshot-feedback-hook-mcp#path:/dsh-plugin

pnpm 10 and later gate build scripts. The first install — before the built package is in pnpm's content-addressable store — can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED. Allow it in ~/.dsh/profiles/<name>/pnpm-workspace.yaml under onlyBuiltDependencies, copying the exact entry pnpm's error prints, then run the command again. Once the built package is cached the gate no longer applies.

Do not point dsh plugin add at a working tree. dsh plugin --profile web add ./screenshot-feedback-hook-mcp/dsh-plugin installs the directory as a pnpm link:, and that fails two different ways:

  • lib/ is a build product and is not in git. pnpm does not run prepare for linked packages, so main: "lib/index.js" points at nothing and dsh dies at boot:

    dsh: plugin tree failed to load: ... Cannot find module
    '~/.dsh/profiles/web/node_modules/dsh-screenshot-feedback-hook-mcp/lib/index.js'
    
  • Building in place fixes that and causes something worse. Node resolves a linked package from its real path, so after npm install the @deepseek-ai/dsh-* copies sitting in dsh-plugin/node_modules/ shadow the host's — the duplicate-instance failure described in Upgrading from 0.1.0, which breaks every tool call in that profile, not just screenshots.

To install from a checkout, pack it first so only the files allowlist reaches the profile:

git clone https://github.com/lkh081231/screenshot-feedback-hook-mcp.git
cd screenshot-feedback-hook-mcp/dsh-plugin
npm install && npm run build && npm pack
dsh plugin --profile web add ./dsh-screenshot-feedback-hook-mcp-0.2.1.tgz

Whichever route you take, confirm ~/.dsh/profiles/<name>/node_modules/@deepseek-ai/ holds no dsh-* package — only schemastery and cosmokit belong there.

For what changed in each release, see the changelog.

Upgrading from 0.1.0 (read this)

0.1.0 installed dsh's runtime packages into the profile as ordinary dependencies, putting a second @deepseek-ai/dsh-tools in front of the one dsh itself loads. The damage is not limited to screenshots — every tool call in that profile dies with:

Cannot read properties of undefined (reading 'prepare')

0.1.1 declares them as peers, so nothing dsh-related is installed into the profile any more. If you already have 0.1.0, clear the polluted node_modules along with it:

dsh plugin --profile web remove dsh-screenshot-feedback-hook-mcp
rm -rf ~/.dsh/profiles/web/node_modules
dsh plugin --profile web add dsh-screenshot-feedback-hook-mcp

Then confirm ~/.dsh/profiles/<name>/node_modules/@deepseek-ai/ holds no dsh-* package at all — only schemastery and cosmokit belong there.

How it registers with dsh

This package is a dsh bundle — an npm package that carries a configuration layer, so you never hand-write a patch. Its package.json declares what it contributes:

{
  "name": "dsh-screenshot-feedback-hook-mcp",
  "main": "lib/index.js",
  "files": ["lib", "cordis.patch.yml"],
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}

That cordis.patch.yml is the layer. It references the plugin module by package name — not by relative path, or Node could not resolve the installed code:

- insert:
    - id: screenshot-feedback
      name: dsh-screenshot-feedback-hook-mcp
      config:
        command: uvx
        args: ['screenshot-feedback-hook-mcp']
        monitor: 0

dsh plugin --profile <name> add ... forwards to pnpm inside the profile directory, sees the dsh.bundle manifest, and appends the package to that profile's dsh.profile.bundles:

{
  "name": "dsh-profile-web",
  "dependencies": { "dsh-screenshot-feedback-hook-mcp": "..." },
  "dsh": { "profile": { "bundles": [
    "@deepseek-ai/dsh-base",
    "@deepseek-ai/dsh-web-app",
    "dsh-screenshot-feedback-hook-mcp"
  ] } }
}

Inspect the layer before booting anything:

dsh --profile web --dump-config   # shows a `# == dsh-screenshot-feedback-hook-mcp` layer with its id: screenshot-feedback row

To uninstall: dsh plugin --profile web remove dsh-screenshot-feedback-hook-mcp drops both the dependency and its layer.

Layers compose in this order: each bundle's patch in dsh.profile.bundles order (@deepseek-ai/dsh-base first) → the profile's own cordis.patch.yml → the home-level $DSH_HOME/cordis.patch.yml → every --patch overlay. So you can override this package's row from your own profile layer without touching the package.

Configuration

The plugin's card in Settings → Plugins → Plugin configuration

Day to day, tune it from the card at Settings → Plugins → Plugin configuration → Screenshot feedback. It writes $DSH_HOME/settings.yaml, layered over the composition entry, with no restart. Every field on the card says whether you have overridden it and resets back to the composed value in one click.

The bundle inserts one plugin row with id screenshot-feedback — the right place for fixed deployment facts. To change it, add a row with the same id to your profile's $DSH_HOME/profiles/<name>/cordis.patch.yml. A later layer replaces the whole config value, so restate every key you want:

- id: screenshot-feedback
  name: dsh-screenshot-feedback-hook-mcp
  config:
    command: uvx
    args: ['screenshot-feedback-hook-mcp']
    monitor: 1
    autoAfterTools: true
    autoAfterToolsDelayMs: 2000
FieldDefaultOn the cardMeaning
commanduvxThe screenshot executable. Installed with pipx/uv tool? Set it to screenshot-feedback-hook-mcp and clear args.
args['screenshot-feedback-hook-mcp']Fixed arguments placed before the subcommand.
cwd''Working directory for the child process; empty means the host's cwd.
monitor0✓0 stitches every monitor, 1..N picks one. list_monitors shows the indices.
delayMs0✓Wait before a manual capture, so a page or drawing finishes rendering.
maxEdge1568✓Longest edge in pixels. Do not exceed 2000 — the attachment store refuses larger images.
targetKb80✓Byte budget. dsh has no 25k-token MCP output cap, so raise it when you need more detail.
captureTimeoutMs30000✓Per-capture timeout, on top of the configured delay.
warnOnTextOnlyModeltrue✓Explain how to reach an image-capable model when the gate refuses. Once per reason, per session.
autoAfterToolsfalse✓Capture after a matching tool call.
autoAfterToolsMatcheredit|write|str_replace_editor✓Tool names. A plain `[A-Za-z0-9_
autoAfterToolsDelayMs1500✓

command / args / cwd are deliberately off the card: they decide where the executable is found, which is deployment composition rather than user preference. Editing config hot-swaps the plugin; editing the card needs not even that — every trigger re-reads the configuration.

How the settings card is wired

dsh's Plugin configuration tab renders the intersection of two ledgers: which settings namespaces the Host serves, and which cards the browser registered under those keys. So this package ships both halves, paired by the single namespace screenshot-feedback:

  • The Host half (src/index.ts) registers the namespace through installSettingsSection from @deepseek-ai/dsh-settings, with the cordis.yml row as the composition base, and points its configuration source at the resolved scope. With no settings service mounted it falls back to the composition entry and behaves exactly as before.
  • The browser half (src/client/) registers a React card into the settings.plugin.item keyed slot under that same namespace. It reads and writes through ctx.settingsScope, which fences each write with the revision it read, so a form that has drifted from the document is refused rather than overwriting a concurrent change.

The browser half is discovered through the dsh.client declaration in package.json, and its artifact is lib/client.js:

{
  "exports": { "./client": { "default": "./lib/client.js" } },
  "dsh": { "client": { "platform": "web", "inject": ["@deepseek-ai/dsh-client-ui-settings-plugins"] } }
}

[!NOTE] dsh's own clientBundle preset that produces this artifact is not published to npm (its README lists this as a known limitation), so tsdown.config.ts reproduces the contract here: the lazy-CJS closure factory, the window.__ModuleLoader__.load banner/footer, and keeping only module-table specifiers as require(). When upgrading dsh, re-check packages/client/tsdown.client.ts and packages/client/web/src/platform.ts against it.

About the automatic timings

Both are off by default: each screenshot is an image that rides every later request in the session until compaction.

  • autoAfterTools hooks tools/post-execute and attaches the screenshot to the tool result. It cannot loop, and it skips failed tool calls (their screen proves nothing).
  • autoOnTurnStop hooks agent/turn-stopping. Steering there forces another step, which reaches the same stop boundary again — dsh's Claude Code hook bridge pins stop_hook_active to false and has no consecutive-block cap, so a naive Stop hook keeps the agent running forever. This plugin dedupes on payload.turn: at most one capture per turn.

A failing screenshot never blocks anything — it is logged and the tool pipeline or turn continues unchanged.

Platform notes

Capture itself lives in the Python package, so these apply to every front end it has — this plugin included.

  • Windows: works out of the box.
  • Linux: X11 works out of the box. Under pure Wayland mss is limited; the CLI detects the session and every screenshot carries a warning about it, so the model is told. That warning fires whenever XDG_SESSION_TYPE=wayland, even when XWayland makes the capture come out fine.
  • macOS: grant Screen Recording to the terminal/IDE that runs dsh (System Settings → Privacy & Security → Screen Recording), then fully quit and relaunch it. Without permission you do not get a black screen — you get the desktop wallpaper plus the menu bar, with every application window missing.

[!WARNING] A missing macOS permission is not reliably detected. Verify it by hand once.

The current check is an after-the-fact heuristic: it shrinks the frame to 16x16 and looks at the grayscale range, so it only fires when the capture is near-uniform (pure black, a solid-colour wallpaper). A photographic wallpaper does not trip it — and that is what macOS ships with.

So the model can receive a screenshot that looks like a perfectly normal desktop, with no warning attached, while the window it wanted is simply not in it. That is worse than a black screen: the model concludes its own page failed to render and goes off debugging code that is fine.

Before relying on the automatic timings, run the CLI by hand once and look at the image:

uvx screenshot-feedback-hook-mcp capture --out shot.jpg

If your window is in it, the permission is granted and everything below works. A definitive check (CGPreflightScreenCaptureAccess(), independent of what the frame looks like) is planned but needs a macOS machine to verify.

Environment warnings that do fire reach the model: they ride in the screenshot's warnings and are rendered as a <warnings> line beside the image, so the model sees the reason rather than only a broken picture.

Image-capable models

dsh only puts an image into the conversation when the exact route declares image input (ctx.llm.resolveModelInfo(...).inputModalities) — the same gate as the built-in read_image tool. On dsh v0.1.0-rc.8, the built-in deepseek-official route publishes only deepseek-v4-flash and deepseek-v4-pro, and both are text-only.

[!IMPORTANT] The Settings page cannot declare modalities. The model card under Settings → Models only edits id / name / context window / max tokens — there is no modality field, so any model you add there is treated as text-only and this plugin refuses to capture. After adding a custom model, click Open config file on that page and add the modality to it by hand in settings.yaml: input: [text, image] (or inputModalities: [text, image] under llm-deepseek). Hand-written fields survive later edits made from the Settings page.

To get an image-capable route:

  1. Add an Anthropic/OpenAI-style catalog provider under Settings → Models and pick one of its vision models.

  2. For a custom provider, declare the modality in $DSH_HOME/settings.yaml:

    llm-pi-ai:
      providers:
        my-gateway:
          apiKeyEnv: GATEWAY_API_KEY
          api: openai-completions
          baseURL: https://gateway.example/v1
          models:
            - id: vision-model
              input: [text, image]
    

    Catalog providers use modelOverrides.<model-id>.input instead; a whole route can fall back with defaultInput: [text, image].

  3. If your DeepSeek endpoint really serves a vision model, list it under llm-deepseek.models with inputModalities: [text, image].

These fields assert what your endpoint supports; they do not add capability it lacks.

Alternatives without this plugin

  • Bridge the MCP server: @deepseek-ai/dsh-mcp-client can run the same Python package as an MCP server, exposing mcp__screenshot__take_screenshot. Same image gate, but no automatic capture.
  • Bridge your Claude Code hooks: @deepseek-ai/dsh-hooks-claude-code runs an existing hooks.json. Use PostToolUse only, with lowercase dsh tool names in the matcher and --image-tool read_image so the agent is pointed at a tool that exists. Do not use a Stop hook there — stop_hook_active is always false on dsh, so the CLI's loop guard cannot fire.

Development

npm install
npm run typecheck
npm test
npm run build

The real-capture integration test is skipped unless you point it at an installed CLI:

DSH_SCREENSHOT_CLI=../.venv/Scripts/screenshot-feedback-hook-mcp.exe npx vitest run

[!WARNING] Every @deepseek-ai/dsh-* package (and @deepseek-ai/cordis) is a peer dependency, never a dependency. Local development gets them from devDependencies; at runtime they must come from the host installation. Moving one back into dependencies makes pnpm materialize a second copy inside the profile, which shadows the symlinks dsh keeps in ~/.dsh/profiles/node_modules/. dsh-tools keys its scheduler with a module-local Symbol, so two copies mean ctx.tools[TOOL_RUNTIME_SCHEDULER] is undefined and every tool call in that profile — read, write, bash, all of them — dies with Cannot read properties of undefined (reading 'prepare'). tests/packaging.spec.ts guards this.

MIT License.

Wait before the automatic capture.
autoOnTurnStopfalse✓Capture as a turn is about to close.
autoOnTurnStopDelayMs1500✓Wait before the automatic capture.
autoOnTurnStopSteertrue✓true steers the model into one more step to look at it; false only injects it as context.