Bundle
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.
- Source
- felix-lj-ct
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-mcp-live-status
English | [中文](README.zh.md)
[](https://dsh-plugin.org/plugins/felix-lj-ct/dsh-mcp-live-status)
[](https://www.npmjs.com/package/dsh-mcp-live-status)
[](LICENSE)
**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.

## Features
- MCP connection status at a glance, inside the composer
- Catches the false-healthy case: mounted but never connected
- Click through for per-server transport, tool count, and failure reason
- Read-only and zero-config; command-line credentials are redacted
## 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
```bash
dsh plugin --profile web add dsh-mcp-live-status
```
Or straight from source, if you prefer not to go through npm:
```bash
dsh plugin --profile web add github:felix-lj-ct/dsh-mcp-live-status
```
Then restart the profile — a running instance keeps the old code in memory:
```bash
dsh --profile web
```
That is the whole setup. There is nothing to configure and no command to run:
open a session and the pill is in the composer tool row, just right of the
access-mode control.
**Two prerequisites**, both easy to miss:
1. **The `web` profile.** The plugin is browser UI; it does nothing in
`headless` or `tui`.
2. **At least one configured MCP server.** With none, the pill renders nothing
at all — by design, so an empty setup costs no layout. That is also the most
common reason for "I installed it and see nothing".
### Not seeing the pill?
Check the host half first — it answers independently of the browser:
```bash
curl 127.0.0.1:3080/dsh-mcp-live-status/status
```
- **Connection refused / 404** — the host half is not loaded. Confirm the patch
row landed: `dsh --profile web --dump-config | grep -A2 dsh-mcp-live-status`
- **`"configured": 0`** — no MCP servers are configured, or they are all
disabled. Set `showDisabled: true` to see the disabled ones.
- **Valid JSON with servers, but no pill** — you are on a screen with no
session. Open or start a conversation.
## 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.
```yaml
- 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:
```bash
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`. `loader` and `tools` are read opportunistically through
`ctx.reflect.get()` — a profile missing either still boots, it just reports
less.
- No dependency on `dsh-typert-loader` or `dsh-api-gateway`.
## Known limitations
- **Needs a session.** `conversation.input.left` is 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-tools` is 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
```bash
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 from `exports["./client"]` +
`dsh.client` and 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
Install
dsh plugin --profile web add github:felix-lj-ct/dsh-mcp-live-status#4023f341ff6d5af8744fe91a2673bd9cf99022e6
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-mcp-live-status from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.