Preface¶
The DSH plugin mechanism allows capabilities to be separated out and maintained independently. If MCP servers are configured at the profile or global level, multiple projects will share the same set of tools; tools from project A may also appear in project B’s sessions. If you want a project’s MCP to only take effect within that project’s sessions, you need to load it per workspace.
Below we introduce Momojie-S/dsh-workspace-mcp: it reads .dsh/mcp.servers.yml from the project root, automatically loads/unloads MCP servers based on the session’s current directory, and registers tools into the agent scope.
What This Is¶
Momojie-S/dsh-workspace-mcp is a DSH plugin licensed under MIT.
The core problem it solves: MCP servers from different projects don’t interfere with each other. Each project’s own .dsh/mcp.servers.yml only takes effect within its own sessions; directories without this file load no MCP at all. MCP tools are registered into the agent scope and are automatically reclaimed when the agent is created or destroyed.
Core Features¶
- Agent creation triggers immediate connection and registration (
agent/created), so the first model request already includes these tools. - Automatic reconnection on disconnect: both startup failures and mid-session disconnections trigger exponential backoff reconnection with tool re-registration. During reconnection, old tools remain registered but calls will fail; once the server recovers, they are automatically replaced with new ones.
- Session invalidation self-healing: after a streamable-http server restart or session eviction, errors like
Session not foundare treated as disconnections and trigger a new generation with reconnection. - stdio environment inheritance: child processes receive a scrubbed parent environment with the yml’s
envapplied as overrides on top. The parent environment has names matchingKEY|PASSWORD|SECRET|TOKENand names prefixed withDSH_*stripped;envis an overlay layer, not a complete environment. - When a server declares a supported
outputSchema, it enters thestructuredContentoutput contract. - Invalid tool lists or registration name conflicts cause the entire generation to be rejected/rolled back.
- Automatic tool list resynchronization when the server sends a
toolListChangednotification. - Configuration file changes are watched by chokidar, and saving triggers a reload.
- On name conflicts with global MCPs, the agent scope shadows the global scope by tool name.
- This plugin is a bundle package (
dsh.bundle); after installing it into a profile viadsh plugin, the configuration layer is automatically appended without needing to manually write patches. - Items with the same name in
.dsh/mcp.servers.ymlcan override plugin-level configuration.
Installation and Activation¶
Environment requirements: DSH >= 0.1.0-rc.6, verified up to 0.1.1-rc.2.
The GitHub installation command is as follows. Installing from a private repository requires git credentials; with pnpm >=10, the first add may prompt for build authorization — follow the prompt to write the package key into allowBuilds in ~/.dsh/profiles/web/pnpm-workspace.yaml, then run add again. Tarball installation has no authorization requirement.
dsh plugin --profile web add github:Momojie-S/dsh-workspace-mcp
After the installation steps above, verify that the plugin layer is in place. Use --dump-config here to check the workspace-mcp related layer:
dsh web --dump-config | Select-String workspace-mcp
The headless or tui profiles do not have workspace-mcp mounted by default; when needed, you can use --patch to temporarily mount a file:/// line pointing to its lib/index.js. Pure host-side plugins can be temporarily mounted this way, but plugins with a browser-side component cannot. The specific paths in the examples below are from reference material and may not be universally applicable — they need to be replaced with actual paths on your machine:
dsh --profile headless --patch <(echo "- insert:
- id: workspace-mcp
name: file:///D:/code/workspace/deepseek-harness-101/plugins/dsh-workspace-mcp/lib/index.js") "task…"
In development mode with direct source connection, the approach is to build first, then manually add lines to the profile’s cordis.patch.yml:
npm install && npm run build
When manually adding lines, name points to your local lib/index.js, and you can configure configFile and verbose. The paths in the example below are also from reference material and may not be universally applicable:
- insert:
- id: workspace-mcp
name: file:///D:/code/workspace/deepseek-harness-101/plugins/dsh-workspace-mcp/lib/index.js
config:
configFile: '.dsh/mcp.servers.yml'
verbose: true
Typical Usage¶
First, create .dsh/mcp.servers.yml in the project root. stdio servers can be configured with transport: stdio, command, args, env; remote servers can be configured with transport: streamable-http, url, headers. Example:
servers:
my-server:
transport: stdio
command: npx
args: ["-y", "some-mcp-server@latest"]
env: {}
remote:
transport: streamable-http
url: https://example.com/mcp
headers:
Authorization: "Bearer <token>"
After saving the configuration, ask the agent to list tools in the session. Tool names follow this format:
mcp__<serverName>__<toolName>
When tools appear, it means the workspace loaded successfully; switching to a directory without .dsh/mcp.servers.yml causes these tools to disappear, confirming that isolation is working.
Same-Name Shadowing¶
Tool names on both sides follow mcp__<serverName>__<tool>. When project-level MCP conflicts with global MCP, the agent scope shadows the global scope by tool name.
- Different
serverName: both tool sets coexist and both are visible to the model. - Same
serverName(tool name collision): project-level takes priority; the model sees and calls the project version. The global version remains unaffected for other workspaces that don’t have this server configured. - Two entries with the same
serverNamein the global patch: the latter generation’s entire registration is rolled back, with analready registeredlog entry, and that server has no tools at all. - Duplicate server keys in the same yml: YAML’s later key overrides the earlier one.
If the two tool lists are not completely identical, only the overlapping names are shadowed; the remaining tools remain visible on both sides. Pointing a global server to a local dev instance is a common use case for same-name shadowing; if the name collision is unintentional, change the serverName.
Configuration¶
Plugin-level configuration goes in the patch’s config field. Configuration items confirmed in verified usage include configFile and verbose. Per-server overrides can configure reconnect.enabled, reconnect.initialDelayMs, reconnect.maxAttempts. Same-name items take priority over plugin-level settings:
servers:
flaky:
transport: stdio
command: npx
args: ["-y", "some-mcp"]
reconnect:
enabled: true
initialDelayMs: 1000
maxAttempts: 5
Verification and Troubleshooting¶
To verify isolation, first place a test server in the project root, then ask the agent to list tools in the session; switch to a directory without configuration and confirm these tools disappear.
For troubleshooting, enable verbose logging in the mount configuration:
verbose: true
In stdio scenarios, connection/registration logs appear on stderr, with common lines including:
[ws-mcp] server "xxx": registered N tools
Disconnection/reconnection logic can be verified with the test script:
npm test
The tests cover disconnection/reconnection, startup failure backoff, Session not found generation replacement with reconnection, and five official alignment tests.
Applicable Scenarios and Notes¶
Suitable for environments with multiple projects coexisting where each project has different MCP servers; also suitable for temporarily pointing a global server to a local dev instance for debugging.
Usage notes:
- The plugin runs with the current DSH process permissions; review the source code, dependencies, and license before installation.
- The
envfor stdio servers is only an overlay layer; when parent environment variables are needed, the parent environment is first scrubbed — names matchingKEY|PASSWORD|SECRET|TOKENand names prefixed withDSH_*are stripped. - During disconnection/reconnection, old tools remain visible but calls will fail.
- The
headless/tuiprofiles do not have this plugin mounted by default; temporary mountfile:///paths need to point to actual local directories. - Installing from a private GitHub repository requires git credentials; with pnpm
>=10, the firstaddmay require build authorization. - This plugin comes from a community GitHub repository; the community directory listing is not an official DeepSeek / High-Flyer app store.
Repository address:
https://github.com/Momojie-S/dsh-workspace-mcp