dsh-mcp-live-status
开发工具 活跃维护

dsh-mcp-live-status

felix-lj-ct/dsh-mcp-live-status

在MCP工具行新增状态胶囊组件,直观展示已配置MCP服务器的可达数量、各服务器传输类型、可用工具数与故障原因,配置后自动同步实时状态,无需手动排查连通性。

0
Stars 标星
0
Forks 分支
0
Watchers 关注
0
Open Issues
JavaScript
主要语言
MIT
开源协议
85 KB
仓库大小
22 天前
最后推送
一键安装扩展 / 插件指令
dsh plugin --profile web add github:felix-lj-ct/dsh-mcp-live-status
git clone https://github.com/felix-lj-ct/dsh-mcp-live-status.git
git clone git@github.com:felix-lj-ct/dsh-mcp-live-status.git
README.md main

dsh-mcp-live-status

English | 中文

Listed on dsh-plugin.org
npm
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.

The MCP status pill in the composer tool row

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

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 — a running instance keeps the old code in memory:

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:

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.

- 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. 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

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.tsdist/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