dsh-mcp-live-status
Live MCP server status in the DeepSeek Harness conversation composer — see which MCP servers are actually connected before you hit send.
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 28, 2026
- Updated
- Aug 28, 2026
Introduction
dsh-mcp-live-status
English | 中文
See which MCP servers are actually connected — in the composer, before you hit send.
A DeepSeek Harness plugin that puts a live MCP status pill in the conversation composer's tool row, next to the access-mode and model controls.

Why
DSH's settings page can tell you an MCP plugin mounted. It cannot tell you the server answered.
@deepseek-ai/dsh-mcp-client defaults to failOnStartupError: false, so a
server whose transport never connected still reaches Cordis fiber state
ACTIVE. It looks healthy everywhere in the UI. You find out it is dead when
the agent tries to call one of its tools, mid-task.
This plugin closes that gap by checking the one thing that actually proves a
handshake happened: tool registration. mcp-client only registers
mcp__<serverName>__<toolName> on ctx.tools after connect() and
listTools() have both succeeded. So the status is a join of two sources —
what the Loader says is configured, against what the tool registry says arrived.
| Loader entry | Registered tools | Shown as |
|---|---|---|
fiber active | > 0 | 🟢 Connected |
fiber active | 0 | 🟡 Up, not connected ← the one nothing else surfaces |
fiber pending / loading | — | 🔵 Starting |
fiber failed | — | 🔴 Mount failed |
| disabled | — | ⚪ Disabled (hidden by default) |
Install
dsh plugin --profile web add dsh-mcp-live-status
Or straight from source, if you prefer not to go through npm:
dsh plugin --profile web add github:felix-lj-ct/dsh-mcp-live-status
Then restart the profile:
dsh --profile web
The pill appears in the composer tool row. If no MCP servers are configured, it renders nothing at all and costs no layout.
What it looks like
Healthy — no denominator, because a denominator would carry no information
[+] [⏱ Full access ⌄] [● MCP 4] [model ⌄] [↑]
Degraded — the denominator appears exactly when something is wrong
[+] [⏱ Full access ⌄] [● MCP 3/4] [model ⌄] [↑]
Clicking the pill opens a read-only panel listing every server with its transport, tool count, and — when something is off — the reason:
┌──────────────────────────────────┐
│ MCP servers ⟳ │
├──────────────────────────────────┤
│ ● atlassian stdio 31 tools │
│ ● mongodb-qa stdio 20 tools │
│ ● broken-probe stdio Up, not connected │
├──────────────────────────────────┤
│ 3 configured · 2 connected │
└──────────────────────────────────┘
Configuration
Optional. Defaults are fine for most people.
- id: dsh-mcp-live-status
name: dsh-mcp-live-status
config:
pollIntervalMs: 10000 # 0 disables polling (mount + manual refresh only)
showDisabled: false # include Loader entries that are disabled
Polling only runs while the browser tab is visible.
Permissions and risk
Read-only. This plugin has no way to start, stop, reload, or reconfigure an MCP server — managing servers stays with the settings page.
| Surface | What it does |
|---|---|
ctx.loader | Reads the configured plugin tree (read-only iteration) |
ctx.tools | Reads registered tool names only; never calls a tool |
ctx.webServer | Serves one JSON route, GET /dsh-mcp-live-status/status |
| Network | None outbound. The browser half fetches only that local route. |
| Storage | None. No cache, no history, no files written. |
On secrets. MCP servers are routinely launched with credentials on the
command line (--connectionString mongodb+srv://user:pass@host, --token …).
Because the status payload is fetched by a browser, argument values are
filtered before they leave the host: anything containing a URI scheme or
user:pass@ userinfo, anything following a flag named like a secret
(pass/token/key/secret/auth/conn/dsn/uri/url), any opaque blob
over 24 characters, and anything over 40 characters is dropped. env is never
read at all. HTTP transports keep only origin + pathname, so query strings and
userinfo never ship.
The result is recognisable but not exploitable — npx -y mongodb-mcp-server --readOnly rather than the connection string.
This filter is heuristic. If you run a server whose plain arguments are
themselves sensitive, set showDisabled: false and review what the route
returns before exposing DSH beyond localhost:
curl 127.0.0.1:3080/dsh-mcp-live-status/status
Compatibility
- DeepSeek Harness
0.1.0-rc.7(developed and verified against this version) - Profile:
web(the plugin targets the browser UI;platform: web) - Requires
webServer.loaderandtoolsare read opportunistically throughctx.reflect.get()— a profile missing either still boots, it just reports less. - No dependency on
dsh-typert-loaderordsh-api-gateway.
Known limitations
- Needs a session.
conversation.input.leftis session-scoped and the shell passes it no zone until a session exists. In practice the pill is visible from the new-session screen onward, because picking a workspace creates the session — but on a truly session-less screen it renders nothing. - Polling, not push. mcp-client emits no status events, so there is nothing to subscribe to. The readout can be up to one interval stale.
no-toolsis inference, not a probe. A server that genuinely publishes zero tools is indistinguishable from one that never connected. Both are amber. The plugin never opens its own MCP connection to check.- Counts tools, not health. A server that connected and then went silent keeps its last registered tool generation until mcp-client's reconnect budget runs out.
Development
npm install
npm run build # tsc — host half only; the browser half is plain JS
dsh plugin --profile web add ./dsh-mcp-live-status
dsh --profile web --dump-config | grep -A2 dsh-mcp-live-status
dist/ is committed on purpose. pnpm blocks a git dependency's prepare
script by default, and the allowBuilds key it asks for is pinned to the commit
hash — so building on install would make every user paste a new key on every
release. Run npm run build before committing a change to src/.
The two halves are independent:
src/index.ts→dist/index.js— Node side; collects status, serves the route.lib/client.js— browser side; hand-written CJS factory, no build step, no JSX. The module loader discovers it fromexports["./client"]+dsh.clientand serves it at/plugins/dsh-mcp-live-status/client.js.
To reproduce the amber state, add a server pointing at a command that does not
exist and leave failOnStartupError: false.
License
MIT