dsh-project-mcp
Per-project MCP servers for DeepSeek Harness: mounts the MCP servers a project declares into the sessions working in it 适合需要在不同项目中自动加载和使用特定 MCP 工具服务器的开发者。
同名パッケージの別リポジトリ
インストール
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:borgez/dsh-project-mcpドキュメント
README 全文を読む ↗Configuration
Plugin config is the cordis.patch.yml row (defaults shown):
- id: project-mcp
config:
enabled: true # master switch
localFiles: ['.dsh/mcp.json'] # project documents, lowest priority first
globalFiles: [] # documents read before a project's own; [] reads none
envFiles: true # read
/.env and
/.dsh/.env for ${...}
inputs: {} # explicit ${input:NAME} values, e.g. COVERAGE_MCP_ALLOWED_ROOT: /repo
toolCallTimeoutMs: 60000
failOnStartupError: false # true = a dead server fails its mount instead of reconnecting
connectTimeoutMs: 60000 # a mount with no tool after this window becomes an error row; 0 disables
lazy: true # mount a session's project on its first turn, not at session creation
idleTimeoutMs: 300000 # release a session's mounts after this much inactivity; 0 disables
activationWaitMs: 2000 # how long the first step of a turn may wait for a pending mount; 0 never waits
profileWins: true # a profile-level serverName wins; the project row becomes `conflict`
watch: true # watch project documents for edits
debounceMs: 300
rescanIntervalMs: 10000
credentialsFile: '' # default: $DSH_HOME/.credentials.yaml
allowGlobalWrite: false # let the editor write the documents of globalFiles
activationEnabled: true # offer MCP tools through `mcp_search_tools` + activation
activationSeeded: 8 # counter-seeded tools that are always offered
activationMinCalls: 5 # calls one tool needs before it is seeded
toolIdleMs: 1800000 # drop a session-activated tool after this long without a call; 0 disables
guidanceEnabled: true # one short project-MCP guidance section in the session system prompt
activationAutoLimit: 12 # names the task-context tier may offer at once; 0 disables that tier
activationAutoStickySteps: 2 # extra user messages a context offer stays offered
activationToolBudgetChars: 40000 # serialized MCP surface above which tools are deferred; 0 defers always
projectMarkers: ['.git', '.dsh', '.kimi-code', 'package.json']
fileMarkers: ['.sln', '.slnx', '.csproj']
Live editing
On DSH ≥ 0.1.7 the keys below are volatile: the host serves them as a
config form, an edit commits into the running plugin without a restart, and the
runtime picks the new value up on the loader's loader/volatile-update event.
Every other key — enabled, localFiles, globalFiles, projectMarkers,
fileMarkers, inputs, credentialsFile, watch, rescanIntervalMs,
failOnStartupError — is structural and still
needs a remount (restart or config reload) to take effect. An older host just
hands over plain values, and nothing about the boot path changes.
Volatile keys: activationEnabled, activationSeeded, activationMinCalls,
toolIdleMs, guidanceEnabled, activationAutoLimit,
activationAutoStickySteps, activationToolBudgetChars, allowGlobalWrite,
envFiles, lazy, localPrefix, profileWins, connectTimeoutMs,
toolCallTimeoutMs, idleTimeoutMs, debounceMs, activationWaitMs.
profileWins
true(default) — a project entry whoseserverNameis already live in the profile is not mounted; its row readsconflictwith the hint to remove the profile entry. Safe: no duplicate processes, no shadowing.false— the project instance is mounted too. Nearer scopes shadow the profile's tools for that session, so the project's own configuration (for example a project-specificIJ_MCP_SERVER_PROJECT_PATH) wins. Costs one server process/connection per server for as long as a session of that project holds it.
Usage counters
The host half counts every tool call whose server this plugin mounted, keyed
by project root and serverName, so a project's numbers outlive the sessions
that produced them:
ctx.get('projectMcp').snapshot().projects[0].usage
// { alpha: { calls: 12, errors: 1, tools: { search: 9, fetch: 3 },
// lastUsedAt: '2024-05-06T07:08:09.000Z' } }
callscounts every dispatched call,errorsthose whose result carried an error, andlastUsedAt(ISO) moves on success only.toolskeys the tool name as the server declares it, without themcp____prefix.- Attribution never parses the prefix blindly: only tool names belonging to a
server this plugin mounted for the calling agent's project are counted, so a
profile-level
mcp__*server never lands in a project's counters, and a registry name truncated to the function-name budget still resolves to its own server. - The counters live in
$DSH_HOME/dsh-project-mcp-usage.json, a versioned document ({ version: 1, projects: … }) written atomically (temp file + rename) on a debounce — never on the tool-call path. A missing, unparsable or older-version document starts clean, and a failed write is logged and dropped: counting is never a reason for a tool call or the plugin to fail. usageis absent for a project with no counted calls yet.