Repository navigation
Releases: katticot/dsh-mcp-apps
Release list
v0.2.5
dsh-mcp-apps 0.2.5 — DSH 0.2.1-rc.5 baseline + the app-view persistence fix.
Verified before release: typecheck, 194 unit tests, artifact tests, and the 3-policy packaged smoke test (allow / deny / approve) against the real @deepseek-ai/dsh@0.2.1-rc.5 runtime.
Provenance: signed and published to the sigstore transparency log (logIndex 3692636223).
[0.2.5] - 2026-11-05
Changed
- DSH
0.2.1-rc.5compatibility: all@deepseek-ai/dsh-*peer ranges bumped from^0.1.7-rc.2to^0.2.1-rc.5, and the matching devDependencies from the exact0.1.7-rc.2to the exact0.2.1-rc.5. DSH'sevaluatePluginCompatibilitychecker rejects plugins whose dsh peers don't satisfy the running version, so0.2.4and earlier refuse to load on the0.2.1-rc.5runtime (the version now shipped inside the DeepSeek Harness desktop application). - Cordis devDependency repinned to the exact
4.0.5the0.2.1-rc.5runtime ships; the^4.0.2peer range is unchanged (cordis is not covered by the DSH checker). scripts/smoke-dsh.mjsnow installs and runs@deepseek-ai/dsh@0.2.1-rc.5for the packaged smoke test.docs/dsh-compatibility.mdand the README compatibility table updated to the new baseline.
Removed
@deepseek-ai/dsh-client-ui-slotspeer: the package no longer exists in the DSH0.2.1-rc.5runtime; the browserslotsservice is now provided by@deepseek-ai/dsh-client-ui-renderer. This plugin never imported the package (the client code injects theslotsservice by name only), so the peer was dropped rather than retargeted. Thetool.call.toolviewandplugins.detail.sectionslot names this plugin registers against are unchanged.
Fixed
- Client: app views no longer silently disappear when the turn folds. The
McpAppToolViewreveal loop bound its MutationObserver target — and the user-gesture listeners that make a manual collapse stick — eagerly, on the first React commit where the iframe exists. The chat host commits the turn-process disclosure around the tool block on a later commit, so that eager binding watched a node too deep to ever observe the host's fold mutations; only the one-shot 300–2500 ms reveal timers defended the view, and a fold landing after they expired was final. The binding is now self-healing: every reveal pass re-resolves the target from the live iframe and re-binds the observer and listeners when it changes, and the effect cleanup removes listeners from the currently-bound node. Addstests/client-plugin-race.spec.ts, which reproduces the real host commit order (the view mounts outside the disclosure, which is committed around the block afterwards) and fails against the old eager binding. tests/dsh-host-contract.spec.tsnow passes an ownerPeerScopetoConnectionFetchHandler.fetch: under DSH0.2.1-rc.5the shared-channel fetch handler takes a requiredpeerargument and answers403for exact routes when the dispatching peer's principal is notowner(the plugin's own registered routes are unaffected — the dispatcher invokes them with the request only).
v0.2.4
Added
- Remote MCP Server OAuth 2.0 / PKCE Support:
- Secure file-backed OAuth token store (
~/.dsh/mcp-apps/oauth/<server>.json) with restricted filesystem permissions (0700directory,0600token file) persisting access tokens, refresh tokens, and dynamic client registration records across DSH restarts. - Full OAuth 2.0 authorization code flow with PKCE (RFC 7636) and metadata discovery (RFC 8414 / RFC 9728).
- Dynamic client registration (RFC 7591) support alongside pre-configured static client credentials.
- Dedicated
/api/mcp-apps/oauth/callbackweb route with popup authentication window coordination viawindow.opener.postMessage. - Disconnect / logout endpoint (
/api/mcp-apps/oauth/disconnect) and "Disconnect" button in the Plugins UI status section to cleanly revoke tokens and unregister tools. externalUrlconfiguration setting for deployments behind reverse proxies or external URLs to ensure accurate OAuth redirect URIs.
- Secure file-backed OAuth token store (
- Resilient Remote Server Lifecycle & Error Handling:
- Concise single-line network error reporting: unwraps nested Node.js/Undici causes (
ConnectTimeoutError,UND_ERR_CONNECT_TIMEOUT,ECONNREFUSED) instead of printing raw multi-line stack traces to stdout/stderr. - Exponential backoff with jitter for automatic server reconnection on connection drop or startup failure.
- Manual retry RPC endpoint (
/api/mcp-apps/servers/retry) and interactive "Retry" button on disconnected server rows in the Plugins UI. - Immediate lifecycle shutdown: forwards
lifecycleController.signalinto remote transports so pressing^Cor shutting down DSH aborts in-flight requests immediately without waiting for HTTP socket timeouts.
- Concise single-line network error reporting: unwraps nested Node.js/Undici causes (
- UI & Client Status Enhancements:
- Detailed server connection states: "Needs Authorization", "Authenticated", "Connected", and "Disconnected".
- Interactive "Connect Account", "Disconnect", and "Retry" buttons on server cards with accessible ARIA semantics and responsive design.
v0.2.3
Added
- Config schema metadata: every field in
Confignow carries a human-readable.description(), timeout fields carry.role('ms'), andenv/headersdict values carry.role('secret')— DSH's schema-driven settings forms now show documentation and redact secrets before they reach the browser, without changing any validation, default, or type. - A read-only
servers/statusendpoint and a matching status section on the plugin's page in DSH's Plugins UI: per-server connection state, tool counts, and an expandable, filterable list of individual tool names grouped by visibility (model-visible / app-only / both). Never exposescommand,args,env,headers, orurl. - The package now self-activates as a DSH bundle:
dsh plugin add dsh-mcp-appsinserts itsmcp-appsrow automatically (viadsh/cordis.patch.yml), giving it a card and detail page — with the official MCP mark as its icon — in DSH's Plugins UI. Previously it had to be wired in by hand with aninsert:patch entry and had no presence in that UI at all.
Changed
- README's Install and Configure sections rewritten for the bundle flow: install via the DSH Harness UI or CLI, then configure by patching (not
insert:-ing) themcp-appsrow in your profile'scordis.patch.yml. Added a "Migrating from 0.2.x" section for existinginsert:-based configs. - Peer ranges bumped to
^0.1.7-rc.2to match the latest DSH; added@deepseek-ai/dsh-client-ui-plugin-manageras an optional peer, deliberately left out ofdsh.client.injectso a deployment without the plugin manager doesn't gate this plugin's own tool-view loading on it. scripts/smoke-dsh.mjsnow patches the bundle's ownmcp-appsrow instead of inserting a second one.- Common transitive dependencies routed through
@socketregistrydrop-in replacements via pnpmoverrides.
Security
- Connection-error messages surfaced through
servers/statusare sanitized before storage: URL userinfo/query/fragment are stripped, any configured secret value (plusBearer <token>-style sub-tokens and percent-encoded variants of it) is redacted, and the result is truncated to 300 characters. Configured secrets under 3 characters are not redacted, to avoid collateral redaction of common short substrings — documented as a known limitation.
v0.2.2
v0.2.2
Summary
Restores compatibility with DSH 0.1.7-rc.2 (0.2.1 was rejected as incompatible on that runtime), fixes a bug that prevented tools from registering on headless DSH hosts, and merges in a batch of previously-unpublished lifecycle, approval, and client-reliability fixes. This release also fixes two bugs in the release pipeline itself that blocked publishing 0.2.2 on the first two attempts.
DSH 0.1.7-rc.2 compatibility
DSH 0.1.7-rc.2 refused to load the plugin (Plugin dsh-mcp-apps@0.2.1 is incompatible with dsh 0.1.7-rc.2: peerDependencies {...}) because the @deepseek-ai/dsh-* peer ranges were pinned to an exact 0.1.5-rc.3 string. DSH's compatibility checker (evaluatePluginCompatibility in @deepseek-ai/dsh-app-boot) only inspects peerDependencies keys named @deepseek-ai/dsh or prefixed @deepseek-ai/dsh-, and calls semver.satisfies(runtimeVersion, requirement, { includePrerelease: true }); a bare version with no range operator is treated as exact-equality, which is why the old pin failed outright against 0.1.7-rc.2.
Every @deepseek-ai/dsh-* peer is now a caret range, ^0.1.7-rc.2, verified against the checker's own logic: 0.1.7-rc.3 and later 0.1.x prereleases keep satisfying it (because of includePrerelease: true), while a 0.2.0 DSH runtime correctly still trips it. Matching devDependencies (and @deepseek-ai/cordis, pinned to 4.0.4) are pinned exactly so CI builds/tests against the same runtime the peer range targets; the cordis peerDependency itself stays a permissive ^4.0.2 since it isn't covered by DSH's checker. Full writeup: docs/dsh-compatibility.md.
Fixes
- Tools failed to register on headless hosts:
src/index.tspreviously injected'webServer'alongside'tools'and'connection', but this plugin's routes go through the publicctx.connection.fetch.registercontract (HostConnectionFetch), which never touchesctx.webServer. Since every Cordis 4.xinjectentry is a hard requirement, requiringwebServerleft the plugin's fiber permanentlyPENDINGon a headless DSH host, so none of its host-side MCP tools would register.webServeris no longer ininject(now just['tools', 'connection']); a regression test builds a real Cordis tree withtools+connectionbut nowebServerprovider and asserts the fiber still activates. - Public DSH host connection lifecycle: the plugin registers its
/api/mcp-apps/*endpoints through the publicHostConnectionFetch.register(route)contract instead of a fabricatedContext.connectionshape and runtime prototype-probing, returning the publicConnectionRpcResultresponse envelope on each route. Shutdown now awaits every route's unregister call (via each route's own disposer) before independently tearing down the tool pool and session store. - Scoped optional approval services with a stale-service check:
approvalandagentsare only injected when a server config actually setsallowAppToolCalls: 'approve'(Object.values(config.servers).some(server => server.allowAppToolCalls === 'approve')), instead of being unconditional hard requirements. The approval/agent service references are captured viactx.inject's disposer pattern and cleared if the service is swapped or torn down — an in-flight approval call re-checks the captured reference after the request resolves and fails withunavailableinstead of using a stale reference if the service changed underneath it. - Reconnect-aware client tool discovery: the client (
src/client/index.tsx,McpAppToolView.tsx) re-discovers MCP app tools after a host reconnect instead of only on initial load. - Browser-safe client artifact: the
./clientbundle is now built and verified to run without Node-only globals, with a dedicatedtests/client-bundle.spec.tsandvitest.artifact.config.tschecking the packaged bundle. - Headless DSH smoke test in CI:
scripts/smoke-dsh.mjsinstalls a real DSH runtime into a throwawayDSH_HOME, packs and loads the plugin, and drives a livetools/callround trip end-to-end acrossallow/deny/approvepolicies — now wired into bothci.ymlandrelease.ymlagainst@deepseek-ai/dsh@0.1.7-rc.2, headless. - Architecture diagram docs: added
docs/diagrams/plugin-architecture.html/.jsondocumenting the plugin's Cordis service/lifecycle wiring.
Release pipeline fixes
Two bugs in .github/workflows/release.yml blocked the first two attempts to publish this version (both fixed before this release went out, see #5 and #6):
- Tarball path misparse (#5):
npm publish "$PACKAGE_TARBALL"withPACKAGE_TARBALL="dist/dsh-mcp-apps-0.2.2.tgz"(no./prefix) madenpm-package-argtreat the bare relative path as a hosted-git shorthand (owner/repo) instead of a local file, so npm triedgit ls-remote ssh://git@github.com/dist/dsh-mcp-apps-0.2.2.tgz.gitand failed with a permission-denied error before ever reaching the registry. Fixed by definingPACKAGE_TARBALLwith a leading./so every consumer gets an unambiguous local path, plus a no-authnpm publish --dry-runguard added toci.ymlto catch this class of regression on every PR. - npm version too old for OIDC trusted publishing (#6): once the path bug was fixed,
npm publish --provenancefailed withENEEDAUTHbecause the npm CLI bundled withactions/setup-node's Node 22 (10.9.8) doesn't support OIDC-based trusted publishing, which requires npm >= 11.5.1. Fixed by pinningnpm@11.20.0immediately before the publish step (and before the CI dry-run guard).
Upgrade notes
No source-level API break for consumers: pnpm typecheck passed against the bumped devDependencies with no usage changes needed (HostConnectionFetch, SubprocessRuntime, AgentRegistry, ApprovalService/ApprovalOutcome all held across the version bump). webServer is no longer a required inject, so tools now register correctly on headless DSH hosts (no webServer service) — 0.2.1 silently failed to register any tools there.
This release requires DSH >= 0.1.7-rc.2. A host running DSH 0.1.5-rc.x will now refuse to load this plugin (its runtime version doesn't satisfy the ^0.1.7-rc.2 peer range). Hosts on 0.1.7-rc.2 or a later 0.1.x prerelease can load it as before; a future DSH 0.2.x runtime will need a new plugin release. No configuration changes are required for existing installs.
Links
- npm: https://www.npmjs.com/package/dsh-mcp-apps/v/0.2.2
- Compare: v0.2.1...v0.2.2
- PR: #4
- Release pipeline fixes: #5, #6
v0.2.1
v0.2.1
Summary
Security fixes for two unbounded-memory paths (oversized stdio messages, unchecked raw resource reads) plus CI hardening. No breaking changes.
Fixes (security)
- Stdio memory exhaustion: a stdio MCP server could send an arbitrarily large unterminated JSON-RPC line, and the SDK's
ReadBufferwould buffer it without limit while waiting for a newline.maxMessageBytesis now passed through as the SDK's ownmaxBufferSizeoption onStdioClientTransport, so an oversized line now throws and closes the connection (handled by the existing reconnect/backoff logic) instead of growing memory unbounded. - Unchecked raw resource reads:
readResourceRawreturnedclient.readResource()without any size check, whilereadResourceenforcedMAX_RESOURCE_SIZE_BYTESbut measured length in UTF-16 code units and fully base64-decoded blobs just to measure them. Both paths now sum content sizes in UTF-8 bytes for text, and in bytes derived algebraically from the base64 length for blobs (no decoding needed), summed across all content items.
Changes
maxMessageBytesnow also applies tostdioservers (previously only remote transports), sharing the sameDEFAULT_MAX_MESSAGE_BYTESdefault of 16MB with remote transports.- Internal cleanup: removed the unreachable no-
serverNamefallback branches inServerPool.listResources/readResourceRaw/readResource, makingserverNamea required parameter.ServerPoolis internal and not exported, so this has no user-facing effect.
CI
- Pinned
actions/checkout,pnpm/action-setup, andactions/setup-nodeinci.ymlto the exact commit SHAsrelease.ymlalready used.
Upgrade notes
No breaking changes. A stdio server that sends a single message larger than 16MB will now fail where it previously could grow memory unbounded (or hit the SDK's undocumented 10MB default) — raise maxMessageBytes for that server if you expect larger single messages.
Links
- npm: https://www.npmjs.com/package/dsh-mcp-apps/v/0.2.1
- Full changelog: v0.2.0...v0.2.1
v0.2.0
v0.2.0 Release Notes
Highlights
- Security hardening: Session tokens now required for reverse tool calls and RPC operations
- Transport compliance: Only MCP-spec transports (
stdio,streamable-http,sse) are now supported; customipcandwebsocketremoved - Improved security defaults:
allowAppToolCallsnow defaults todenyfor safer by-default configuration - Session isolation: Bounded session store with least-recently-used eviction prevents unbounded memory growth
- Release infrastructure: Published via npm trusted publishing with provenance verification
Breaking changes
-
Transport removal: The
ipcandwebsockettransports have been removed. To migrate anipcserver, run it asstdioor put it behind an HTTP endpoint (streamable-httporsse). Replacewebsocketwithstreamable-httporsse. Remoteurls must now behttp(s)://(a config withtransport: 'ipc'or'websocket'now fails validation at startup). -
App tool call authorization:
allowAppToolCallsdefaults todeny, so reverse calls from apps are blocked until explicitly configured. Each server must now declare one of:'deny'(blocked),'approve'(routed through approval handler), or'allow'(permitted). -
Environment variable allowlisting:
${VAR}expansion (inenvandheadersonly) no longer readsDSH_*, secret-like names (*KEY*,*TOKEN*,*SECRET*,*PASSWORD*),SSH_AUTH_SOCKorGPG_AGENT_INFOunless the server lists them inallowedVars, e.g.allowedVars: ['MCP_TOKEN']. Otherwise they expand to an empty string (or the:-default). -
Node engine version:
engines.nodeis now>=22. Older Node versions are no longer supported. -
Client types export: The
./clientexport no longer declares types; it is loaded only by the DSH module loader. -
Configuration validation: Null servers (
servers.<id>: null), non-positive timeouts, and malformed remote URLs are now rejected at startup.
Security
-
Session-bound RPC:
tools/call,resources/read,resources/read-raw, andresources/listnow require a valid session token bound to the server that issued it. The unauthenticated fallback has been removed. -
CSP hardening: CSP meta tag is placed first using DOMParser, with a safe fallback. Private IP ranges, link-local addresses, and the blanket
img-src https:rule have been removed for stronger isolation. -
Loopback-only HTTP: Remote plain
http://is allowed only to loopback addresses (127.0.0.1,[::1]). Incoming messages are capped per server withmaxMessageBytesto prevent memory exhaustion.
Packaging/Release
- Published via npm trusted publishing using OIDC, eliminating the need for static tokens and providing cryptographic provenance verification.
- Release workflow enforces minimal permissions, runs on a dedicated
releaseenvironment, and pins all actions to commit SHAs. - Package contents are verified: no dangling source maps,
.npmignoreremoved,packageManagerfield added.
Upgrade notes
servers:
# Before (0.1.0): ipc
# daemon:
# transport: ipc
# socketPath: /tmp/mcp-daemon.sock
# After (0.2.0): spawn it as stdio (or expose it over streamable-http / sse)
daemon:
transport: stdio
command: my-mcp-server
allowAppToolCalls: approve # default is deny; 'approve' asks the user during a turn
remote:
transport: streamable-http
url: https://mcp.example.com/mcp
headers:
Authorization: 'Bearer ${MCP_TOKEN}'
allowedVars: [MCP_TOKEN] # secret-like vars are blocked unless listedRequires Node.js 22 or later. Full details: #1.
v0.1.0 - Initial Release
Universal MCP Apps (SEP-1865) Host & UI Plugin for DeepSeek Harness
- Multi-transport server pool (stdio, unix domain sockets/ipc, remote sse & streamable-http)
- Dynamic tool synchronization and LLM API schema sanitization (^[a-zA-Z0-9_-]+$)
- Sandboxed iframe container with dynamically synthesized Content Security Policy (CSP)
- Resilient PostMessage transport with early handshake buffering
- Bidirectional AppBridge reverse tool calling and live data refresh fallback
- Auto-reveal lifecycle coordinator preventing premature DSH turn process collapse
- Full unit test suite (17 vitest tests) and automated CI/CD workflows