# Driving a real browser profile from DeepSeek's dsh over MCP

> Source: <https://dev.to/nullprintai/driving-a-real-browser-profile-from-deepseeks-dsh-over-mcp-3e84>
> Published: 2026-10-22 00:26:22+00:00

DeepSeek has an official agent harness called **dsh** (npm: `@deepseek-ai/dsh`, source at `deepseek-ai/deepseek-harness`). It speaks MCP, which means you can hand it tools. We wired it to Nullprint, a multi-profile browser, so a plain-language request turns into: create a browser profile, launch it, open a page, read it, close it.

This is the short version of what it took. Tested with dsh `0.2.1-rc.2` and nullprint-mcp `0.1.3`; dsh is still a pre-release, so check its own docs if anything below has moved.

The `dsh` package on **PyPI is not DeepSeek's**. Install from npm.

You need Node.js, a DeepSeek API key, and Nullprint running and signed in on the same machine.

```
npm install -g @deepseek-ai/dsh
export DEEPSEEK_API_KEY=sk-your-key
pipx install nullprint-mcp
```

dsh has two modes: `web` (a chat page in your browser) and `headless` (one task from the command line, then exit). Each is a separate profile with its own config, and the config folder only appears after the profile has run once, so start the one you want (`dsh web`, or any small headless task) before editing anything.

Add Nullprint to `~/.dsh/profiles/<web or headless>/cordis.patch.yml`:

```
- insert:
    - id: mcp-nullprint
      name: "@deepseek-ai/dsh-mcp-client"
      config:
        serverName: nullprint
        transport: stdio
        command: nullprint-mcp
        args: []
```

It has to be wrapped in `- insert:`. A bare `- id: mcp-nullprint` only modifies an entry that already exists, so a new entry written that way is ignored, and dsh starts normally without the tools. Check with:

```
dsh --profile headless --dump-config
```

If `mcp-nullprint` is in the output, it loaded. The tools show up as `mcp__nullprint__<tool>`, e.g. `mcp__nullprint__profile_start` and `mcp__nullprint__session_goto`.

No port or key is needed on the same machine: nullprint-mcp reads Nullprint's local connection file by itself.

```
dsh --profile headless "Use Nullprint to create a new profile, open example.com, tell me the page title, then close the profile."
```

dsh creates the profile, starts it, navigates, reads the title through `eval`, and closes it. The new profile appears in the Nullprint app afterwards. After that, requests like "list my profiles and which are running" or "open shop-01 and go to example.com" work the same way. Profile names are case-sensitive.

Set `NULLPRINT_DAEMON_URL` and `NULLPRINT_API_KEY`. One gotcha: when dsh launches an MCP server it **drops inherited environment variables whose names contain KEY, TOKEN, SECRET or PASSWORD**. Exporting `NULLPRINT_API_KEY` in your shell isn't enough; pass it explicitly in the config:

```
        env:
          NULLPRINT_DAEMON_URL: !!js process.env.NULLPRINT_DAEMON_URL
          NULLPRINT_API_KEY: !!js process.env.NULLPRINT_API_KEY
```

Don't paste the key itself into the file.

Full guide: [https://nullprint.ai/docs/mcp/dsh/](https://nullprint.ai/docs/mcp/dsh/)

*This post was drafted with AI assistance and reviewed by the Nullprint team.*
