dsh-baton-mcp 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.zh-CN.md CHANGED
@@ -10,6 +10,10 @@
10
10
  macOS · Windows(<a href="WINDOWS.md">说明</a>)· 不用装 Node.js · 零依赖 · MIT
11
11
  </p>
12
12
 
13
+ Claude Code 和 Codex 各派一个任务给 DeepSeek Harness,它的子代理用 1 小时 41 分搭出了这条雨夜街道(40 秒,建议开声音):
14
+
15
+ https://github.com/user-attachments/assets/ed931637-acb3-4c16-8c52-72298ea90485
16
+
13
17
  <p align="center">
14
18
  <img src="./assets/readme/zh/features.svg" width="100%" alt="新建工作区和对话、续接指定对话、界面实时可见并标明发送方、完成即唤醒、不用另配 key、macOS 和 Windows 零依赖">
15
19
  </p>
package/host/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-baton-host",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "DeepSeek Harness plugin for dsh-baton: adding it is the whole install. Switched on, it registers the dsh-baton MCP server for Claude Code and Codex, then takes the baton over a local, owner-only control socket, runs it in a specific session through the host's own session controller, and hands the result back when the turn is done.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -20,7 +20,7 @@
20
20
  "LICENSE"
21
21
  ],
22
22
  "dependencies": {
23
- "dsh-baton-mcp": "0.5.4"
23
+ "dsh-baton-mcp": "0.5.6"
24
24
  },
25
25
  "license": "MIT",
26
26
  "dsh": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-baton-mcp",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "mcpName": "io.github.JialaoLiu/dsh-baton",
5
5
  "description": "Pass the baton from Claude Code, Codex or any MCP client to a local DeepSeek Harness: an MCP server that hands off tasks, continues a specific session, shows the run live in Harness and hands the result back when it is done.",
6
6
  "type": "module",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/JialaoLiu/dsh-baton",
8
8
  "source": "github"
9
9
  },
10
- "version": "0.5.4",
10
+ "version": "0.5.6",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "dsh-baton-mcp",
15
- "version": "0.5.4",
15
+ "version": "0.5.6",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }
package/src/server.mjs CHANGED
@@ -73,31 +73,31 @@ async function waitInChunks({ run_id }, progress) {
73
73
  const TOOLS = [
74
74
  {
75
75
  name: 'dsh_list_workspaces',
76
- description: 'List the workspaces (project folders) shown in the DeepSeek Harness sidebar. Needs the dsh-baton-host plugin.',
76
+ description: 'List the workspaces (project folders) in the DeepSeek Harness sidebar; dsh_list_sessions lists the sessions inside them and dsh_create_workspace adds one. Returns id, path, title and session_count for each; the id is the workspace_id for dsh_send and dsh_rename_workspace. Read-only. Needs the dsh-baton-host plugin.',
77
77
  inputSchema: { type: 'object', properties: {} },
78
78
  run: () => core.api.workspaces(),
79
79
  },
80
80
  {
81
81
  name: 'dsh_create_workspace',
82
- description: 'Register a folder as a workspace in the DeepSeek Harness sidebar (creates the folder unless create_dir is false). Idempotent: an already registered folder returns its existing workspace. Needs the dsh-baton-host plugin.',
82
+ description: 'Add a folder to the DeepSeek Harness sidebar as a workspace without starting a session; dsh_list_workspaces only lists existing ones, and dsh_send with a cwd registers the folder by itself. Creates the folder unless create_dir is false. Idempotent: an already registered folder returns its existing workspace. Returns id, path, title and created (false when it already existed). Needs the dsh-baton-host plugin.',
83
83
  inputSchema: { type: 'object', required: ['path'], properties: { path: str('Absolute folder path (~ allowed)'), title: str('Sidebar title; defaults to the folder name'), create_dir: { type: 'boolean', description: 'Create the folder if missing', default: true } } },
84
84
  run: (a) => core.api.create_workspace(a),
85
85
  },
86
86
  {
87
87
  name: 'dsh_list_sessions',
88
- description: `List saved DeepSeek Harness sessions, newest first, with title, workspace folder (cwd) and whether a run is active. Use it to find the session_id to continue. ${ARCHIVED}`,
89
- inputSchema: { type: 'object', properties: { cwd: str('Only sessions of this workspace folder'), query: str('Filter by text in title or folder'), limit: int('Max sessions', 20), include_subagents: { type: 'boolean', description: 'Also list sub-agent sessions', default: false } } },
88
+ description: `List saved DeepSeek Harness sessions (conversations), newest first, to find a session_id; dsh_list_runs lists only the messages sent through dsh_send. Returns session_id, title, cwd, updated_at, running and bridge_runs (active dsh-baton run ids) for each. There is no paging: the list stops at limit, so to see older sessions raise limit or narrow it with cwd or query. Read-only. ${ARCHIVED}`,
89
+ inputSchema: { type: 'object', properties: { cwd: str('Only sessions of this workspace folder'), query: str('Case-insensitive text to match in title, folder or session id'), limit: int('Max sessions returned', 20), include_subagents: { type: 'boolean', description: 'Also list sub-agent sessions', default: false } } },
90
90
  run: (a) => core.api.list_sessions(a),
91
91
  },
92
92
  {
93
93
  name: 'dsh_read_session',
94
- description: 'Read the recent visible conversation (user prompts and assistant replies, with tool names) of one session.',
95
- inputSchema: { type: 'object', required: ['session_id'], properties: { session_id: str('Session id, e.g. session-…'), last: int('How many recent messages', 10) } },
94
+ description: 'Read what was said in one session (its past messages), to catch up before continuing it; dsh_session_activity shows what it is doing now and dsh_status follows one run. Returns title, cwd, total_messages and the last messages, each with role (user or assistant), time, text and the names of tools the assistant called (tool output is not included). Read-only.',
95
+ inputSchema: { type: 'object', required: ['session_id'], properties: { session_id: str('Session id from dsh_list_sessions, e.g. session-…'), last: int('How many of the most recent messages to return; total_messages tells how many there are', 10) } },
96
96
  run: (a) => core.api.read_session(a),
97
97
  },
98
98
  {
99
99
  name: 'dsh_send',
100
- description: 'Send a message to DeepSeek Harness. With session_id it continues that existing session (same context, no new session); with cwd or workspace_id it starts a new session there (a cwd that is not a workspace yet is registered in the sidebar first). Returns immediately with a run_id; the turn runs in the background. If the session is already working, mode followup (default) queues the message after the current turn and steer adds it to the running turn; each message gets its own run_id. The message is delivered verbatim as a user message, with no prefix or label added: write it in the language the user works in, self-contained (Harness does not see this conversation), and say who is asking only if it matters. Then wait for the reply with dsh_wait (or the wait_command it returns, where offered).',
100
+ description: 'Send a message to a DeepSeek Harness session and return at once with a run_id, without waiting for the reply; dsh_wait then waits for it. With session_id it continues that existing session (same context, no new session); with cwd or workspace_id it starts a new session there (a cwd that is not a workspace yet is registered in the sidebar first). The turn runs in the background. If the session is already working, mode followup (default) queues the message after the current turn and steer adds it to the running turn; each message gets its own run_id. The message is delivered verbatim as a user message, with no prefix or label added: write it in the language the user works in, self-contained (Harness does not see this conversation), and say who is asking only if it matters. Then wait for the reply with dsh_wait (or the wait_command it returns, where offered).',
101
101
  inputSchema: { type: 'object', required: ['message'], properties: { message: str('The task or reply to send'), session_id: str('Existing session to continue'), cwd: str('Workspace folder for a new session'), workspace_id: str('Harness workspace for a new session (from dsh_list_workspaces)'), mode: { type: 'string', enum: ['followup', 'steer'], description: 'followup queues after the current turn (default); steer injects into a running turn', default: 'followup' } } },
102
102
  run: async (a) => {
103
103
  const st = await core.api.send({ ...a, sender: core.senderOf(core.setting('SENDER') || clientName) })
@@ -113,44 +113,44 @@ const TOOLS = [
113
113
  },
114
114
  {
115
115
  name: 'dsh_status',
116
- description: 'Progress of a run: state (queued/running/waiting/completed/failed/cancelled, or interrupted when a Harness restart cut it short), session_id, recent events (text, tool calls), final reply, waiting_on when the turn is over but sub-agents or background jobs are still working, and human_interjections when someone typed in the Harness UI during the run (its result then reflects that input too). A one-off look, never a way to wait: to wait for the reply use dsh_wait, and if dsh_wait timed out, call dsh_wait again.',
116
+ description: 'Look once at one run sent with dsh_send, without waiting; dsh_wait waits for it to end and dsh_session_activity covers the whole session. Returns state (queued/running/waiting/completed/failed/cancelled, or interrupted when a Harness restart cut it short), session_id, recent events (text, tool calls), final reply, waiting_on when the turn is over but sub-agents or background jobs are still working, and human_interjections when someone typed in the Harness UI during the run (its result then reflects that input too). A one-off look, never a way to wait: to wait for the reply use dsh_wait, and if dsh_wait timed out, call dsh_wait again.',
117
117
  inputSchema: { type: 'object', required: ['run_id'], properties: { run_id: str('Run id from dsh_send'), tail: int('How many recent events', 20) } },
118
118
  run: (a) => core.api.status(a),
119
119
  },
120
120
  {
121
121
  name: 'dsh_wait',
122
- description: () => `Wait for a run to finish and return its final status and reply.${isCodex() ? '' : ' Prefer the background wait_command from dsh_send when your client can run background shell commands and wake you when they exit: it costs no turns while waiting.'} Each call waits up to ${waitSeconds()} s (set by the server to stay under this client's tool timeout); if the run is still going it returns timed_out: true with just the state: then call dsh_wait again with the same run_id, without summarising progress.${isCodex() ? ` ${NO_POLL} ${codeModeYield()}` : ''} Sends progress notifications while waiting when the client asks for them.`,
122
+ description: () => `Wait for a run sent with dsh_send to end and return its final status and reply; dsh_status only looks once.${isCodex() ? '' : ' Prefer the background wait_command from dsh_send when your client can run background shell commands and wake you when they exit: it costs no turns while waiting.'} Each call waits up to ${waitSeconds()} s (set by the server to stay under this client's tool timeout); if the run is still going it returns timed_out: true with just the state: then call dsh_wait again with the same run_id, without summarising progress.${isCodex() ? ` ${NO_POLL} ${codeModeYield()}` : ''} Sends progress notifications while waiting when the client asks for them.`,
123
123
  inputSchema: { type: 'object', required: ['run_id'], properties: { run_id: str('Run id from dsh_send') } },
124
124
  run: (a, { progress }) => waitInChunks(a, progress),
125
125
  },
126
126
  {
127
127
  name: 'dsh_rename_session',
128
- description: 'Rename a DeepSeek Harness session (shown in the sidebar).',
129
- inputSchema: { type: 'object', required: ['session_id', 'title'], properties: { session_id: str('Session id'), title: str('New title') } },
128
+ description: 'Set the title of one DeepSeek Harness session (a conversation); dsh_rename_workspace renames a workspace (project folder) instead. Only the title changes; the conversation and any running turn are untouched. Can be called again at any time to change it back or to something else. Returns session_id and the saved title.',
129
+ inputSchema: { type: 'object', required: ['session_id', 'title'], properties: { session_id: str('Session id from dsh_list_sessions'), title: str('New title; surrounding spaces are trimmed and it must not be empty') } },
130
130
  run: (a) => core.api.rename_session(a),
131
131
  },
132
132
  {
133
133
  name: 'dsh_rename_workspace',
134
- description: 'Rename a DeepSeek Harness workspace (sidebar title only; the folder is not moved). Identify it by workspace_id or path.',
135
- inputSchema: { type: 'object', required: ['title'], properties: { workspace_id: str('Workspace id'), path: str('Workspace folder'), title: str('New title') } },
134
+ description: 'Set the sidebar title of a DeepSeek Harness workspace (project folder); dsh_rename_session renames a single session instead. The folder on disk is not moved or renamed. Identify it by workspace_id or path. Can be called again to change it back. Returns id, path and the saved title.',
135
+ inputSchema: { type: 'object', required: ['title'], properties: { workspace_id: str('Workspace id from dsh_list_workspaces'), path: str('Workspace folder, used when workspace_id is not given'), title: str('New title; surrounding spaces are trimmed and it must not be empty') } },
136
136
  run: (a) => core.api.rename_workspace(a),
137
137
  },
138
138
  {
139
139
  name: 'dsh_session_activity',
140
- description: 'What a session is doing right now: its running turn, sub-agents (any depth), background jobs and reminders, plus runs handed over through dsh-baton. Check this before sending to a session that might be busy.',
141
- inputSchema: { type: 'object', required: ['session_id'], properties: { session_id: str('Session id') } },
140
+ description: 'Show what one session is doing right now, across all its work; dsh_status follows a single run and dsh_read_session shows past messages. Returns busy, activity (its running turn, sub-agents at any depth, background jobs and reminders) and bridge_runs (active dsh-baton runs). Read-only. Check it before dsh_send to a session that might be busy, or before dsh_cancel.',
141
+ inputSchema: { type: 'object', required: ['session_id'], properties: { session_id: str('Session id from dsh_list_sessions') } },
142
142
  run: (a) => core.api.activity(a),
143
143
  },
144
144
  {
145
145
  name: 'dsh_cancel',
146
- description: 'Stop everything a session is doing (running turn, sub-agents, background jobs), by run_id or session_id. Messages still waiting in the queue are not removed.',
147
- inputSchema: { type: 'object', properties: { run_id: str('Run id'), session_id: str('Session id (to stop work dsh-baton did not start)') } },
146
+ description: 'Stop everything a session is doing (running turn, sub-agents, background jobs, reminders), by run_id or session_id; the session itself is kept, and this is the only tool that stops work. It retries a few times until the session is quiet, then returns session_id, run (when run_id was given), attempts, still_busy, activity (whatever is still going) and queued_runs. Messages still waiting in the queue are not removed. Safe to repeat: on an idle session it does nothing and returns still_busy: false. Use dsh_session_activity first to see what would be stopped.',
147
+ inputSchema: { type: 'object', properties: { run_id: str('Run id from dsh_send; stops the session that run belongs to'), session_id: str('Session id, to stop work dsh-baton did not start') } },
148
148
  run: (a) => core.api.cancel(a),
149
149
  },
150
150
  {
151
151
  name: 'dsh_list_runs',
152
- description: 'List recent runs handed over through dsh-baton, newest first.',
153
- inputSchema: { type: 'object', properties: { limit: int('Max runs', 10) } },
152
+ description: 'List the runs sent through dsh_send (the latest 100 are kept), newest first, to recover a lost run_id or review past hand-overs; dsh_list_sessions lists Harness sessions instead. Returns run_id, session_id, state, started_at, ended_at, message_preview, final_text and error for each; dsh_status shows one run in detail. Read-only.',
153
+ inputSchema: { type: 'object', properties: { limit: int('Max runs returned', 10) } },
154
154
  run: (a) => core.api.list_runs(a),
155
155
  },
156
156
  ]
@@ -171,7 +171,7 @@ async function handle(req) {
171
171
  protocolVersion: PROTOCOLS.includes(asked) ? asked : PROTOCOLS[0],
172
172
  capabilities: { tools: {} },
173
173
  serverInfo: SERVER,
174
- instructions: `Hands tasks to a local DeepSeek Harness and passes the results back. Find a session with dsh_list_sessions, continue it with dsh_send {session_id, message}, then wait for the reply: ${isCodex() ? 'call dsh_wait with the run_id until it returns the final state (not dsh_status)' : 'run the wait_command it returns as a background shell command if your client wakes you when one exits, otherwise call dsh_wait'}. A run in state "waiting" needs a person to answer in the Harness UI. ${ARCHIVED}`,
174
+ instructions: `Hands tasks to a local DeepSeek Harness and passes the results back. Typical flow: dsh_list_workspaces (or dsh_create_workspace) to pick a folder, dsh_list_sessions to find a session, dsh_send {session_id, message} to continue it (or {cwd, message} to start one), then wait for the reply: ${isCodex() ? 'call dsh_wait with the run_id until it returns the final state (not dsh_status)' : 'run the wait_command it returns as a background shell command if your client wakes you when one exits, otherwise call dsh_wait'}. Then dsh_read_session to read the conversation, dsh_session_activity to see what a session is doing, and dsh_cancel to stop it; dsh_list_runs recovers a lost run_id. A run in state "waiting" needs a person to answer in the Harness UI. ${ARCHIVED}`,
175
175
  }
176
176
  }
177
177
  case 'ping':