Skip to main content
Glama
README.md
# claude-to-dsh

Let **Claude Desktop** (or any MCP client) drive **DeepSeek Harness** — and watch the work happen in the Harness sidebar as it runs.

```
Claude Desktop ──MCP/stdio──▶ claude-to-dsh ──loopback HTTP──▶ Harness window
                                                                   └─▶ new Session
                                                                       (sidebar visible)
```

## Why this exists

DeepSeek Harness ships two ways to drive it from outside a process, and neither fits an MCP client:

| Official path | Protocol | Works with Claude Desktop? |
|---|---|---|
| `dsh --profile acp` ([`@deepseek-ai/dsh-acp`](https://www.npmjs.com/package/@deepseek-ai/dsh-acp)) | ACP | ❌ Claude speaks MCP |
| `dsh --profile headless` | CLI + JSON events | ✅ but invisible |
| `dsh-sdk-jsonrpc-server` | JSON-RPC | ❌ |

The headless route works, but its sessions land in `~/.dsh/sessions/--<cwd>--/`, which the GUI never scans. The sidebar reads the **workspace registry held in the running process's memory**, and no outside process can write to it — the browser API is fenced by a per-process launch token.

So this project has two halves:

1. **A Host plugin** that runs *inside* the Harness process and creates the session through that process's own `sessionController`, which attaches it to a workspace. Result: a real session, in the sidebar, with history.
2. **An MCP server** that Claude Desktop launches, which forwards tasks to that plugin over loopback.

If the plugin is not running, the MCP server falls back to `dsh --profile headless`, so the tool still works — the answer just says `ran headless (not shown in the Harness GUI)`.

## Install

### 1. Add the plugin to your Harness profile

From npm (after it is published):

```bash
dsh plugin --profile desktop add claude-to-dsh
```

Straight from GitHub:

```bash
dsh plugin --profile desktop add github:wang53333/claude-to-dsh
```

From a local checkout:

```bash
dsh plugin --profile desktop add file:C:\path\to\claude-to-dsh
```

Use `--profile web` instead of `desktop` if you run the browser profile. There is nothing else to edit by hand: the command appends the package to the profile's `dsh.profile.bundles`, and the bundle's own `cordis.patch.yml` supplies the mount row.

```json
{ "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "claude-to-dsh"] } } }
```

### 2. Point Claude Desktop at the MCP server

Edit `claude_desktop_config.json` (`%APPDATA%\Claude\` on Windows, `~/Library/Application Support/Claude/` on macOS; Microsoft Store builds redirect this into `%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\`):

```json
{
  "mcpServers": {
    "dsh": {
      "command": "npx",
      "args": ["-y", "claude-to-dsh"]
    }
  }
}
```

Without npm, point at the checkout instead:

```json
{
  "mcpServers": {
    "dsh": {
      "command": "node",
      "args": ["C:\\path\\to\\claude-to-dsh\\bin\\mcp-server.js"]
    }
  }
}
```

### 3. Restart DeepSeek Harness, then Claude Desktop

The plugin mounts at Harness startup; the MCP server starts when Claude does. Order matters — restart Harness first.

## Use it

Ask Claude:

> Use `dsh_task` to check how much free space is on the C: drive and list the five largest directories.

To keep a conversation going, pass the returned `session_id` back on the next call. The tool description already tells Claude to write self-contained instructions and to reuse the session id, but say so explicitly if it forgets.

## Verify

```bash
# Plugin mounted? The file appears when a Harness window with the plugin starts.
cat ~/.dsh/mcp/endpoint.json

# MCP server alive? It logs every call here.
tail -f ~/.dsh/mcp/server.log
```

In Claude Desktop, ask *"list your tools"* — `dsh_task` should be there. Every result ends with either `ran in the open Harness window` (visible) or `ran headless (not shown in the Harness GUI)`.

## How it works

- The plugin binds an HTTP listener on `127.0.0.1:0` (OS-assigned port) and writes `{port, token, pid}` to `<DSH_HOME>/mcp/endpoint.json` with `0600` permissions.
- A token of 24 random bytes is minted per boot, so a fixed port is unnecessary and a hostile local process cannot guess the credential.
- `POST /task` with `x-dsh-bridge-token` calls `sessionController.create({ workspaceId })` then `sessionController.prompt(...)`, and polls the derived message list until the last message is an assistant message and the log has been quiet for 1.5 s.
- `GET /health` and `GET /debug?session=<id>` exist for diagnostics.

## Security

The plugin grants an MCP client the same power the Harness agent already has in that window: your shell, your files, your credentials. Treat it accordingly.

- Listener binds loopback only; no LAN exposure.
- Every request must present the per-boot token from `endpoint.json`.
- The endpoint file is written `0600` on POSIX; on Windows it inherits your user profile's ACLs.
- Any process running as **you** can read that file and therefore drive the bridge. If that is unacceptable, do not install this.

## Known limitations

- **Approval prompts block.** If a task triggers a tool the Harness policy gates (for example writing outside the session workspace), the approval card appears in the GUI and the HTTP call waits for a human. Open that session to approve; otherwise the turn times out.
- **One Harness window per `DSH_HOME`.** Each window overwrites `endpoint.json`, so the MCP server talks to the most recently started one.
- **Separate session, not your current chat.** Delegated turns never inherit the conversation you are having in the GUI; put context in `task`.
- **Host route needs the plugin mounted.** Without it the headless fallback runs, which is functional but invisible — and headless must be able to authenticate (see below).
- **Headless fallback needs credentials.** The shipped headless profile routes to `deepseek-official`, which requires `DEEPSEEK_API_KEY`. The account login used by the desktop app is not available to a plain CLI process.

## Compatibility

Built against DeepSeek Harness `0.2.1-rc.2`. Uses only public services (`sessionController`, `workspaceRegistry`, `sessions`) and Node built-ins — no runtime dependencies, no build step.

## Uninstall

```bash
dsh plugin --profile desktop remove claude-to-dsh
```

Then delete the `mcpServers.dsh` entry from your Claude Desktop config.

## License

MIT