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.

Frontend Tools Bridge — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins

dsh-frontend-tools-bridge

Frontend Tools Bridge

DSH plugin: loopback WebSocket bridge that mirrors application frontend tools onto the agent's tool registry and forwards model calls back to the application — one bridge, many apps, key-based namespace isolation

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

npx -y @deepseek-ai/dsh plugin --profile web add dsh-frontend-tools-bridge@0.2.0
READMECompatibilityVersions

Compatibility and provenance

Frontend Tools Bridge is published as dsh-frontend-tools-bridge and currently resolves to version 0.2.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

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

Versions

0.2.0stable
8/16/2026
0.1.0stable
8/15/2026

Related plugins

Loading related plugins…

Latest
0.2.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
205.2 kB
Files
22
Surface
any
License
MIT
Source
npm
GitHub
★ 1
Weekly downloads
31
Last push
8/16/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 integrations-communication.

Acp App@deepseek-ai/dsh-acp-appThe dsh ACP profile bundle: automation-only JSON-RPC stdio and process lifecycle over dsh-baseRemote Web Ui@linxin666/dsh-remote-web-uiScan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and rPocketdsh-pocketPut DeepSeek Harness in your pocket: one package, one settings page, and scan a QR code on your phone to access DSH on your computer in sync (LAN + public network, real-time screen mirroring).DSCODE@toddzheng024/dscode-bundleA complete DeepSeek coding agent with persistent shell, Ultra collaboration and automatic permission review.

README

dsh-frontend-tools-bridge

English | 中文

The dsh half of the frontend tools bridge: a loopback-only WebSocket server that lets any number of connected applications mirror their own tools onto ctx.tools, each under its own namespace. Real implementations stay in the applications (browser renderers, web apps, Node processes) — the bridge carries no business knowledge; it forwards model calls to the owning application and returns its results. The application-side counterpart is dsh-frontend-tools-client.

What it does

The plugin starts one WebSocketServer bound to 127.0.0.1 (a non-loopback bind would expose the model's tool access to the local network; the handshake key is a client identity, not a network boundary). Authentication is a client roster: applications generate their own DSH KEYs (64 hex chars via the client SDK) and a user registers each key against a namespace through a conversation, so the key doubles as the client's identity and the application owns its credential lifecycle. A connection completes a hello handshake (protocol version, key); the server answers welcome echoing the bound namespace. One session may hold a namespace at a time — a second connection presenting the same namespace is rejected with duplicate_connection; once the owner disconnects, its mirrored tools are unregistered, in-flight calls are rejected, and a replacement connection for that namespace is accepted immediately. Clients under different namespaces connect concurrently and never see each other's tools.

Registration is per-register-batch and all-or-nothing: if any tool in a batch fails validation or name conflict, the whole batch is refused with a non-fatal invalid_tool error and the client may fix its definitions and send another register. Each accepted tool appears on ctx.tools under the public name <namespace>__<rawName> (the mcp-client contract): normalized to the model function-name alphabet [A-Za-z0-9_-] and 64-character budget, with a 12-hex-character SHA-256 identity hash appended whenever normalization is lossy so distinct client names never collapse. A batch that would push the whole bridge past maxTools is refused with a non-fatal too_many_tools error. An unregister batch removes tools by raw name and is answered with the raw names actually removed (unknown names are ignored), freeing those public names for later re-registration on the same connection.

A model invocation is forwarded to the owning connection as a call frame carrying the client's raw tool name (the public name never travels back); the promise settles when the matching callResult arrives, when the caller aborts through exec.signal, or — rejection — when the connection drops or callTimeoutMs elapses without an answer. A late callResult for an abandoned or timed-out call is dropped without failing the session. Results render as pretty-printed JSON text content; the output schema defaults to any JSON when the client does not declare one.

Write approval

Tools are read/write classified by the application's own declaration: a tool registered with readOnly: true only reads state; every other tool (the fail-safe default) is a WRITE operation. The bridge installs one tools/pre-execute listener covering its mirrored tools plus its own admin tools: read-only tools and tools owned by other plugins pass through untouched, while each write call returns { kind: 'ask' } — DSH's official human-approval channel. Only an allowed-once grant forwards the call to the application; a rejection, a cancellation, or a deployment with no approval channel mounted (for example a headless run) denies the call automatically — the pipeline's fail-closed contract, which also produces the paired approval/asked / approval/decided audit events on the session log. The approval card the user sees carries the reason 前端工具写操作 "<namespace>.<rawName>" plus the already-streamed call arguments; the grant is one-shot, so every write call asks again.

The admin tools are classified the same way: frontend_tools_list_clients reads; frontend_tools_register_client and frontend_tools_revoke_client write — register deliberately, because a model could otherwise swap the user-pasted KEY for another one and quietly hand the namespace to a different application.

Liveness is probed every 15 seconds with a ping frame; a probe still unanswered when the next one is due terminates the session (the close path owns the cleanup chain: tools unregistered, in-flight calls rejected, replacement connections accepted).

Admin tools

The everyday way to onboard an application is a conversation, not a config edit. Credentials are application-owned: the application generates a DSH KEY (client SDK generateClientKey), shows it to the user, and the user hands it to the model, which registers it through these model-facing tools on ctx.tools.

  • frontend_tools_register_client(namespace, key) — registers the application-provided DSH KEY against the namespace and persists it, returning { namespace, url, replaced }; the output never echoes the key (the user's message already carries it, and a second copy would only widen session-log exposure). Re-registering for the same namespace replaces the credential immediately (the old key stops authenticating); a key bound to another namespace is rejected as an identity conflict, as is anything that does not match the 64-hex DSH KEY pattern.
  • frontend_tools_list_clients() — lists every roster entry (static and registered) as { namespace, connected, toolCount }; keys never appear in the output.
  • frontend_tools_revoke_client(namespace) — removes the registered credential and drops its live connection in one step, so a revoked client cannot keep serving tools.

Registered credentials persist in frontend-tools-clients.json (mode 600 on POSIX) under the state directory, surviving bridge restarts. Statically configured clients (see staticClients below) are owned by cordis.yml: they authenticate like registered ones (their keys are free-form strings owned by the configuration) but refuse register and revoke, which name the configuration as the place to edit.

Configuration

Five Config fields, validated at load:

  • port — loopback port to listen on (default 31870).
  • callTimeoutMs — deadline for one forwarded call (default 30000); a callResult arriving later rejects the model-facing promise with a timeout error and is dropped.
  • maxTools — maximum number of tools mirrored across every connected client (default 200, an abuse guard rather than a semantic limit). One global ceiling keeps concurrent applications from crowding the model's tool list.
  • staticClients — optional statically configured { namespace, key } entries, merged with the persisted roster at load; intended for tests and declarative setups. A namespace or key colliding with the persisted roster fails load loud.
  • stateDir — directory holding the registered-client roster file; defaults to the harness state home (~/.dsh, overridable through DSH_HOME).

An empty roster at load is valid: register the application-generated DSH KEY once (frontend_tools_register_client) to onboard the first application. Bind failures (for example a taken port) and a malformed roster file reject plugin load instead of leaving a dead server behind. Disposal closes the listener, terminates every session, unregisters every mirrored tool (admin tools included), and rejects in-flight calls.

Protocol

Version 5, text frames only, JSON-encoded. Client → server: hello, register, unregister, callResult, pong. Server → client: welcome, registered, unregistered, call, ping, error. The hello frame carries the key only — the namespace comes from the roster binding and is echoed in welcome. v5 adds the optional readOnly flag to each registered tool spec (the write-approval classification above); v4 and older peers are rejected at the handshake. Malformed frames and phase violations (for example register before the handshake) answer invalid_message and close the socket. The authoritative message and error-code definitions live in dsh-frontend-tools-client (src/protocol.ts there); this package consumes them as a dependency, so the two sides cannot drift.

Export shape

A namespace plugin: it exports name / inject / Config / apply and no default. BridgeServer, MirrorRegistry, CallDispatcher, ClientRoster, and registerAdminTools are also exported for reuse and testing; the wire protocol itself is exported by the client package.

Model Experience

Indirectly, through the application-advertised tool schemas this package mirrors onto ctx.tools; the connected application owns every schema, description, and result it registers.

KV Cache effect

Between registration events the mirrored schema set is stable and follows the reusable request prefix like any ctx.tools registration. Client registration, additional register or unregister batches, and a disconnect that unregisters the tools change the visible tool set and therefore replace the request prefix from that point; individual forwarded results append as ordinary tool-call results and do not invalidate earlier entries.

Known Limitations and Deferred Work

  • One session per namespace — a second connection presenting a key bound to an occupied namespace is rejected with duplicate_connection; there is no multiplexing several connections onto one namespace.
  • No reconnection lease — the server neither buffers nor replays registrations for a reconnecting client; the client SDK's automatic reconnection re-sends its tool list instead.
  • Results render as JSON text only — forwarded values always render as pretty-printed JSON; presentation intents (terminal, diff, locations) are deferred.