cross-agent-teams-mcp 0.2.0
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/LICENSE +21 -0
- package/README.md +296 -0
- package/README.zh-CN.md +306 -0
- package/dist/channel-cli.d.ts +18 -0
- package/dist/channel-cli.js +358 -0
- package/dist/channel-cli.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +4585 -0
- package/dist/cli.js.map +1 -0
- package/package.json +62 -0
- package/src/channel/auto-daemon.ts +130 -0
- package/src/channel/daemon-client.ts +155 -0
- package/src/channel/proxy.ts +28 -0
- package/src/channel-cli.ts +122 -0
- package/src/cli.ts +136 -0
- package/src/daemon/auth.ts +17 -0
- package/src/daemon/channel-wake-fanout.ts +39 -0
- package/src/daemon/channel-wake-send.ts +38 -0
- package/src/daemon/cleanup.ts +38 -0
- package/src/daemon/errors.ts +18 -0
- package/src/daemon/pid.ts +33 -0
- package/src/daemon/port.ts +16 -0
- package/src/daemon/runtime-identity.ts +238 -0
- package/src/daemon/server.ts +64 -0
- package/src/daemon/shutdown.ts +12 -0
- package/src/daemon/sse-fanout.ts +96 -0
- package/src/daemon/tmux-cli.ts +61 -0
- package/src/daemon/tmux-pane-detect.ts +276 -0
- package/src/lib/client-kind.ts +1 -0
- package/src/lib/default-team.ts +18 -0
- package/src/lib/delivery-spec.ts +172 -0
- package/src/lib/schema-diff.ts +79 -0
- package/src/mcp/agent-public-row.ts +52 -0
- package/src/mcp/auto-bind-channel.ts +106 -0
- package/src/mcp/auto-bind-codex-pane.ts +170 -0
- package/src/mcp/auto-poke-fanout.ts +129 -0
- package/src/mcp/bind-channel.ts +39 -0
- package/src/mcp/bind-runtime-identity.ts +43 -0
- package/src/mcp/broadcast-to-role.ts +127 -0
- package/src/mcp/broadcast.ts +115 -0
- package/src/mcp/codex-appserver-dispatch.ts +169 -0
- package/src/mcp/codex-appserver-rpc.ts +227 -0
- package/src/mcp/codex-pane-pre-register-repo.ts +57 -0
- package/src/mcp/delivery-status.ts +114 -0
- package/src/mcp/diff-contracts.ts +25 -0
- package/src/mcp/echo.ts +8 -0
- package/src/mcp/fanout-with-retry.ts +56 -0
- package/src/mcp/get-contract.ts +24 -0
- package/src/mcp/get-inbox.ts +57 -0
- package/src/mcp/identity.ts +8 -0
- package/src/mcp/pending-contract-events.ts +36 -0
- package/src/mcp/poke-guard.ts +32 -0
- package/src/mcp/poke-retry.ts +159 -0
- package/src/mcp/poke.ts +190 -0
- package/src/mcp/pre-register-codex-pane.ts +65 -0
- package/src/mcp/register-agent.ts +84 -0
- package/src/mcp/register-codex-self.ts +276 -0
- package/src/mcp/register-contract.ts +60 -0
- package/src/mcp/send-message.ts +159 -0
- package/src/mcp/subscribe-channel-wake.ts +31 -0
- package/src/mcp/subscribe-contract.ts +24 -0
- package/src/mcp/task-add.ts +37 -0
- package/src/mcp/task-claim.ts +54 -0
- package/src/mcp/task-complete.ts +36 -0
- package/src/mcp/task-list.ts +33 -0
- package/src/mcp/tools.ts +1240 -0
- package/src/mcp/transport-dispatch.ts +171 -0
- package/src/mcp/transport.ts +204 -0
- package/src/mcp/unregister-self.ts +46 -0
- package/src/storage/agents-repo.ts +328 -0
- package/src/storage/db.ts +13 -0
- package/src/storage/events-outbox.ts +44 -0
- package/src/storage/schema.ts +180 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jtianling
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# cross-agent-teams-mcp
|
|
2
|
+
|
|
3
|
+
[中文说明](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
An MCP daemon for cross-agent collaboration, with local delivery transports for tmux, Codex app-server, and Claude channel wake integration.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
The package ships two bins:
|
|
10
|
+
|
|
11
|
+
- `cross-agent-teams-mcp` — the daemon (HTTP MCP server on `127.0.0.1:9100`).
|
|
12
|
+
- `cross-agent-teams-channel` — a stdio MCP proxy that Claude Code talks to over stdio. On startup it auto-bootstraps the daemon if no healthy daemon is reachable on the configured loopback URL.
|
|
13
|
+
|
|
14
|
+
The recommended Claude Code MCP config collapses to a single stdio entry — no manual daemon startup needed:
|
|
15
|
+
|
|
16
|
+
```jsonc
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"cross-agent-teams": {
|
|
20
|
+
"command": "npx",
|
|
21
|
+
"args": ["-y", "-p", "cross-agent-teams-mcp", "cross-agent-teams-channel"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
When the channel CLI starts and its configured daemon URL points at `127.0.0.1` / `localhost`, it probes `<daemon-url>/health`; if the daemon is not running, it spawns a detached daemon child (the same `cross-agent-teams-mcp` bin), waits up to 5s for `/health` to return 200, and only then opens its MCP client connection. The auto-spawned daemon's stdout/stderr append to `~/.cross-agent-teams-mcp/daemon.log`.
|
|
28
|
+
|
|
29
|
+
If the URL is non-loopback (e.g. `http://10.0.0.5:9100/mcp`), auto-spawn is disabled — the channel probes once and exits with a clear error if the daemon is unreachable.
|
|
30
|
+
|
|
31
|
+
If you prefer explicit lifecycle control or are developing locally, you can still build and start the daemon manually:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm build
|
|
35
|
+
node dist/cli.js daemon --port 9100
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The one-command local setup remains:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
./start-server.sh
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The script runs `pnpm build` first, then starts the background services. To stop them:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
./stop-server.sh
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Logs and pid files are written into `logs/`.
|
|
51
|
+
|
|
52
|
+
## Distribution
|
|
53
|
+
|
|
54
|
+
This package publishes both `cross-agent-teams-mcp` (daemon) and `cross-agent-teams-channel` (Claude Code stdio proxy) bins from a single tarball. The recommended configuration is the single-entry stdio form above; the auto-daemon bootstrap means most users never need to start the daemon manually.
|
|
55
|
+
|
|
56
|
+
Developers who want explicit lifecycle control still have `start-server.sh` / `stop-server.sh` (and the manual `node dist/cli.js daemon --port 9100`). Auto-daemon bootstrap is purely additive and does not change the daemon's HTTP API, MCP tool surface, or sqlite schema.
|
|
57
|
+
|
|
58
|
+
### Breaking change: bin and package rename
|
|
59
|
+
|
|
60
|
+
The previous workspace plugin `cross-agent-teams-channel` (which exposed a bin named `cross-agent-teams-proxy`) is removed. Its code now lives inside `cross-agent-teams-mcp` as the `cross-agent-teams-channel` bin. Any external MCP config that references the legacy `cross-agent-teams-proxy` bin or a hard-coded path under `plugins/cross-agent-teams-channel/dist/` must update to the unified single-bin form above.
|
|
61
|
+
|
|
62
|
+
The HTTP `cross-agent-teams-mcp` MCP entry continues to work for users who prefer the explicit two-entry form.
|
|
63
|
+
|
|
64
|
+
To run directly from source:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx tsx src/cli.ts daemon --port 9100
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
By default the daemon listens on `127.0.0.1:9100`. The MCP endpoint is `http://127.0.0.1:9100/mcp`, and the health check endpoint is `http://127.0.0.1:9100/health`.
|
|
71
|
+
|
|
72
|
+
You can verify the service with:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
curl http://127.0.0.1:9100/health
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Common Flags
|
|
79
|
+
|
|
80
|
+
- `--port <port>`: listening port, default `9100`
|
|
81
|
+
- `--token <token>`: enable Bearer token authentication
|
|
82
|
+
- `--db <path>`: SQLite database path
|
|
83
|
+
- `--pid-file <path>`: pid file path
|
|
84
|
+
|
|
85
|
+
The default data directory is `~/.cross-agent-teams-mcp/`. The default database file is `~/.cross-agent-teams-mcp/data.db`, and the default pid file is `~/.cross-agent-teams-mcp/daemon.pid`.
|
|
86
|
+
|
|
87
|
+
If another instance is already running, startup returns `daemon already running pid=...`.
|
|
88
|
+
|
|
89
|
+
## Delivery Transports
|
|
90
|
+
|
|
91
|
+
The daemon currently supports these wake-up paths:
|
|
92
|
+
|
|
93
|
+
- `tmux_pane_id`: inject text directly into a target tmux pane
|
|
94
|
+
- `delivery.kind='codex-appserver'`: resume a Codex thread over websocket and start a turn
|
|
95
|
+
- `delivery.kind='claude-channel'`: bind a Claude channel session and deliver channel wake notifications
|
|
96
|
+
|
|
97
|
+
`register_agent(...)` now requires an explicit `client`. Use one of `codex`, `claude-code`, or `opencode` for first-class runtimes. For other agent harnesses, pass `client: "custom"` and optionally `client_name` for observability.
|
|
98
|
+
|
|
99
|
+
When you do not explicitly choose a `team`, pass `project_dir` as the caller's current working directory. The daemon derives the default team from that directory's basename, and still falls back to `"default"` when both fields are omitted.
|
|
100
|
+
|
|
101
|
+
## Codex App-Server Delivery
|
|
102
|
+
|
|
103
|
+
For daily Codex usage, the recommended entry point is `register_agent({ client: "codex", ... })`. It registers a caller-supplied `thread_id` as a `codex-appserver` delivery target through the unified registration API. It does not auto-bind a tmux pane. If you want tmux fallback delivery, call `bind_runtime_identity(...)` after registration.
|
|
104
|
+
|
|
105
|
+
`register_agent({ client: "codex", ... })` no longer guesses the caller's current thread from `thread/loaded/list`. The daemon cannot safely infer "which loaded thread is mine" from the MCP session alone. If `thread_id` is omitted, the tool returns `thread_id_required` with resumable thread ids for debugging instead of registering the wrong thread.
|
|
106
|
+
|
|
107
|
+
Minimal example:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
register_agent({
|
|
111
|
+
client: "codex",
|
|
112
|
+
model: "gpt-5",
|
|
113
|
+
name: "lead",
|
|
114
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
115
|
+
role: "worker",
|
|
116
|
+
thread_id: "11111111-1111-4111-8111-111111111111"
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
If you also want tmux fallback routing, bind runtime identity explicitly after registration:
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
bind_runtime_identity({
|
|
124
|
+
agent: "codex",
|
|
125
|
+
ui_pid: 81979
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
If you do not have the UI pid, you can fall back to `ui_tty + tmux_pane_id`:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
bind_runtime_identity({
|
|
133
|
+
agent: "codex",
|
|
134
|
+
ui_tty: "/dev/ttys026",
|
|
135
|
+
tmux_pane_id: "%1902"
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Override the websocket URL when needed:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
register_agent({
|
|
143
|
+
client: "codex",
|
|
144
|
+
model: "gpt-5",
|
|
145
|
+
name: "lead",
|
|
146
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
147
|
+
role: "worker",
|
|
148
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
149
|
+
ws_url: "ws://127.0.0.1:8799"
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
If the app-server requires a Bearer token, pass `auth_token_ref` as an environment variable name visible to the daemon:
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
register_agent({
|
|
157
|
+
client: "codex",
|
|
158
|
+
model: "gpt-5",
|
|
159
|
+
name: "lead",
|
|
160
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
161
|
+
role: "worker",
|
|
162
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
163
|
+
auth_token_ref: "CODEX_REMOTE_TOKEN"
|
|
164
|
+
})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Behavior notes:
|
|
168
|
+
|
|
169
|
+
- `register_agent({ client: "codex", ... })` is the recommended entry point
|
|
170
|
+
- Default `ws_url` is `ws://127.0.0.1:8799`
|
|
171
|
+
- `thread_id` is required for successful registration
|
|
172
|
+
- tmux pane binding is handled separately by `bind_runtime_identity`
|
|
173
|
+
- `detect_tmux_pane(...)` remains available for debugging, but does not write registry state
|
|
174
|
+
- No loaded thread returns `no_loaded_threads`
|
|
175
|
+
- Omitted `thread_id` returns `thread_id_required` with resumable thread ids for debugging
|
|
176
|
+
- Success returns `{ agent_id, team, thread_id, ws_url }`
|
|
177
|
+
|
|
178
|
+
Minimal Codex app-server startup:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
codex app-server --listen ws://127.0.0.1:8799
|
|
182
|
+
codex --remote ws://127.0.0.1:8799
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
You can also register a target manually with `register_agent`:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
register_agent({
|
|
189
|
+
model: "...",
|
|
190
|
+
name: "...",
|
|
191
|
+
role: "...",
|
|
192
|
+
team: "...",
|
|
193
|
+
delivery: {
|
|
194
|
+
kind: "codex-appserver",
|
|
195
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
196
|
+
ws_url: "ws://127.0.0.1:8799"
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
If the app-server requires a Bearer token:
|
|
202
|
+
|
|
203
|
+
```text
|
|
204
|
+
register_agent({
|
|
205
|
+
model: "...",
|
|
206
|
+
name: "...",
|
|
207
|
+
role: "...",
|
|
208
|
+
team: "...",
|
|
209
|
+
delivery: {
|
|
210
|
+
kind: "codex-appserver",
|
|
211
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
212
|
+
ws_url: "ws://127.0.0.1:8799",
|
|
213
|
+
auth_token_ref: "CODEX_REMOTE_TOKEN"
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Behavior notes:
|
|
219
|
+
|
|
220
|
+
- `thread_id` must be a UUID
|
|
221
|
+
- `ws_url` must use `ws://` or `wss://`
|
|
222
|
+
- `auth_token_ref` is interpreted only as an environment variable name
|
|
223
|
+
- On success, `poke()` returns `{ ok: true, transport_used: 'codex-appserver', thread_id }`
|
|
224
|
+
- On failure, `poke()` returns machine-readable errors such as `codex_connect_failed`, `codex_initialize_failed`, `codex_resume_failed`, `codex_turn_start_failed`, or `missing_auth_token`
|
|
225
|
+
- When a target is explicitly registered as `codex-appserver`, the daemon does not fall back to tmux
|
|
226
|
+
|
|
227
|
+
For a more complete Codex CLI setup example, see [docs/configs/codex-cli.md](docs/configs/codex-cli.md).
|
|
228
|
+
|
|
229
|
+
## Claude Code Channel Delivery
|
|
230
|
+
|
|
231
|
+
For Claude Code sessions, prefer registering from the active MCP session with `register_claude_self(...)`. If you do not explicitly choose a `team`, pass `project_dir` as the current working directory so the daemon derives the project team from its basename.
|
|
232
|
+
|
|
233
|
+
```text
|
|
234
|
+
register_claude_self({
|
|
235
|
+
name: "lead",
|
|
236
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
237
|
+
role: "worker",
|
|
238
|
+
channel_session_id: "csid-abc"
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
You can also use the unified entry point:
|
|
243
|
+
|
|
244
|
+
```text
|
|
245
|
+
register_agent({
|
|
246
|
+
client: "claude-code",
|
|
247
|
+
model: "opus-4-7",
|
|
248
|
+
name: "lead",
|
|
249
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
250
|
+
role: "worker",
|
|
251
|
+
channel_session_id: "csid-abc"
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
For a more complete Claude Code setup example, see [docs/configs/claude-code.md](docs/configs/claude-code.md).
|
|
256
|
+
|
|
257
|
+
## Using opencode with xats (tmux)
|
|
258
|
+
|
|
259
|
+
opencode integrates with xats as a plain tmux-hosted TUI. There is no dedicated launcher and no HTTP transport — pokes are delivered by pasting into the opencode pane via tmux, exactly the same path used for `client: "custom"`.
|
|
260
|
+
|
|
261
|
+
Start opencode inside a tmux window, then register from within opencode's MCP session:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
tmux new-window opencode
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
register_agent({
|
|
269
|
+
client: "opencode",
|
|
270
|
+
model: "opencode-default",
|
|
271
|
+
name: "my-opencode-agent",
|
|
272
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
273
|
+
role: "worker",
|
|
274
|
+
ui_pid: <opencode pid>
|
|
275
|
+
})
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Pass the opencode process pid as `ui_pid`. The daemon resolves `pid → tty → tmux pane` and populates `tmux_pane_id` in the same registration call. After registration, pokes from other agents route to the opencode pane as `transport_used: "tmux-poke"`.
|
|
279
|
+
|
|
280
|
+
### Transport selection
|
|
281
|
+
|
|
282
|
+
Transport selection is client-aware:
|
|
283
|
+
|
|
284
|
+
- `client="claude-code"`: `claude-channel` first, then `tmux-poke`
|
|
285
|
+
- `client="opencode"`: `tmux-poke`
|
|
286
|
+
- `client="codex"`: `codex-appserver` first, then `tmux-poke`
|
|
287
|
+
|
|
288
|
+
### Operator cutover
|
|
289
|
+
|
|
290
|
+
If you are upgrading from a version that shipped the `opencode-server` transport:
|
|
291
|
+
|
|
292
|
+
1. Stop the daemon with `./stop-server.sh` (this wipes `data.db` on purpose — the dropped `opencode_base_url` / `opencode_session_id` columns and the `opencode_pane_pre_registrations` table are not migrated).
|
|
293
|
+
2. Rebuild: `pnpm build`.
|
|
294
|
+
3. Restart with `./start-server.sh`.
|
|
295
|
+
4. Remove any shell alias that pointed at `launch-opencode.sh` (the script no longer exists).
|
|
296
|
+
5. Re-register opencode agents using the `register_agent({ client: "opencode", ui_pid, ... })` flow shown above.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# cross-agent-teams-mcp
|
|
2
|
+
|
|
3
|
+
[English README](./README.md)
|
|
4
|
+
|
|
5
|
+
用于跨 agent 协作的 MCP daemon, 支持 tmux, Codex app-server 和 Claude channel wake 等本地投递方式.
|
|
6
|
+
|
|
7
|
+
## 快速开始
|
|
8
|
+
|
|
9
|
+
本包提供两个 bin:
|
|
10
|
+
|
|
11
|
+
- `cross-agent-teams-mcp` — daemon (HTTP MCP server, 监听 `127.0.0.1:9100`).
|
|
12
|
+
- `cross-agent-teams-channel` — Claude Code 用的 stdio MCP proxy. 启动时会自动 bootstrap daemon: 如果配置的 loopback URL 上没有健康的 daemon, 它会 spawn 一个 detached daemon 子进程, 等到 `/health` 返回 200 后再打开 MCP client。
|
|
13
|
+
|
|
14
|
+
推荐的 Claude Code MCP config 折叠成单一 stdio entry, 不再需要手动起 daemon:
|
|
15
|
+
|
|
16
|
+
```jsonc
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"cross-agent-teams": {
|
|
20
|
+
"command": "npx",
|
|
21
|
+
"args": ["-y", "-p", "cross-agent-teams-mcp", "cross-agent-teams-channel"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
channel CLI 启动时, 如果配置的 daemon URL 指向 `127.0.0.1` / `localhost`, 它会先去探测 `<daemon-url>/health`; 如果 daemon 没起来, 就 spawn 一个 detached daemon child (同一个 `cross-agent-teams-mcp` bin), 最多等 5 秒等 `/health` 返回 200, 之后再打开 MCP client. 自动起来的 daemon 的 stdout/stderr 会 append 到 `~/.cross-agent-teams-mcp/daemon.log`.
|
|
28
|
+
|
|
29
|
+
如果 URL 不是 loopback (比如 `http://10.0.0.5:9100/mcp`), 自动 spawn 会被禁用 — channel 只探测一次, daemon 不可达就报错退出。
|
|
30
|
+
|
|
31
|
+
如果你想显式控制 daemon 生命周期 (例如本地开发), 仍可以手动 build + 启动 daemon:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm build
|
|
35
|
+
node dist/cli.js daemon --port 9100
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
一键脚本仍然可用:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
./start-server.sh
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
这个脚本会先执行 `pnpm build`, 然后再启动后台服务. 停止服务可以用:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
./stop-server.sh
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
日志和 pid 文件会写到 `logs/` 目录.
|
|
51
|
+
|
|
52
|
+
## 分发说明
|
|
53
|
+
|
|
54
|
+
本包同时发布 `cross-agent-teams-mcp` (daemon) 和 `cross-agent-teams-channel` (Claude Code stdio proxy) 两个 bin, 来自同一个 tarball. 推荐使用上面的单 entry stdio 配置; 由于有 auto-daemon bootstrap, 大多数使用者不需要手动起 daemon。
|
|
55
|
+
|
|
56
|
+
希望显式控制生命周期的开发者仍可以用 `start-server.sh` / `stop-server.sh` (或 `node dist/cli.js daemon --port 9100`). Auto-daemon bootstrap 是纯增量行为, 不改变 daemon 的 HTTP API, MCP tool 表面或 sqlite schema。
|
|
57
|
+
|
|
58
|
+
### Breaking change: bin 与 package 改名
|
|
59
|
+
|
|
60
|
+
之前的 workspace plugin `cross-agent-teams-channel` (它暴露的 bin 名为 `cross-agent-teams-proxy`) 已删除. 代码合并进了 `cross-agent-teams-mcp`, 作为新的 `cross-agent-teams-channel` bin. 所有外部 MCP config 中引用旧 bin 名 `cross-agent-teams-proxy`, 或硬编码 `plugins/cross-agent-teams-channel/dist/` 路径的位置, 都需要改成上面的统一单 bin 形式。
|
|
61
|
+
|
|
62
|
+
直接 HTTP 连接 `cross-agent-teams-mcp` 的 MCP entry 仍然可用, 适合喜欢显式两 entry 的用户。
|
|
63
|
+
|
|
64
|
+
如果你想直接跑源码, 可以用:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx tsx src/cli.ts daemon --port 9100
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
服务默认监听 `127.0.0.1:9100`. MCP endpoint 是 `http://127.0.0.1:9100/mcp`, 健康检查地址是 `http://127.0.0.1:9100/health`.
|
|
71
|
+
|
|
72
|
+
启动后可以用下面的命令确认服务正常:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
curl http://127.0.0.1:9100/health
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 常用参数
|
|
79
|
+
|
|
80
|
+
- `--port <port>`: 指定监听端口, 默认 `9100`
|
|
81
|
+
- `--token <token>`: 开启 Bearer token 鉴权
|
|
82
|
+
- `--db <path>`: 指定 SQLite 数据库路径
|
|
83
|
+
- `--pid-file <path>`: 指定 pid 文件路径
|
|
84
|
+
|
|
85
|
+
默认数据目录是 `~/.cross-agent-teams-mcp/`. 默认数据库文件是 `~/.cross-agent-teams-mcp/data.db`, 默认 pid 文件是 `~/.cross-agent-teams-mcp/daemon.pid`.
|
|
86
|
+
|
|
87
|
+
如果已有实例在运行, 启动时会返回 `daemon already running pid=...`.
|
|
88
|
+
|
|
89
|
+
## 投递方式
|
|
90
|
+
|
|
91
|
+
当前 daemon 支持这些唤醒路径:
|
|
92
|
+
|
|
93
|
+
- `tmux_pane_id`: 直接把文本注入目标 tmux pane
|
|
94
|
+
- `delivery.kind='codex-appserver'`: 通过 websocket 恢复 Codex thread 并启动一轮 turn
|
|
95
|
+
- `delivery.kind='claude-channel'`: 绑定 Claude channel session 并发送 channel wake 通知
|
|
96
|
+
|
|
97
|
+
`register_agent(...)` 现在要求显式传 `client`. 一等运行时使用 `codex` / `claude-code` / `opencode`. 其它 agent harness 请传 `client: "custom"`, 并且可以选填 `client_name` 方便排查。
|
|
98
|
+
|
|
99
|
+
如果同时传了 `ui_pid`, `client` 必须描述这个 `ui_pid` 背后的真实 runtime, 不是当前发起 MCP 调用的宿主。 例如, 在 Claude Code 里替 opencode pane 做注册时, 也要传 `client: "opencode"`。
|
|
100
|
+
|
|
101
|
+
当用户没有显式指定 `team` 时, 调用方推荐传 `project_dir` 为当前工作目录. daemon 会用该目录 basename 派生默认 team, 两者都不传时仍回落到 `"default"`.
|
|
102
|
+
|
|
103
|
+
## Codex App-Server Delivery
|
|
104
|
+
|
|
105
|
+
如果你平时主要在 Codex 里使用, 更推荐直接调用 `register_agent({ client: "codex", ... })`. 它会用调用者显式提供的 `thread_id` 把当前会话注册成 `codex-appserver` delivery, 同时保持统一入口. 它不会自动绑定 tmux pane. 如果你还需要 tmux 作为兜底唤醒路径, 请在注册成功后单独调用 `bind_runtime_identity(...)`.
|
|
106
|
+
|
|
107
|
+
`register_agent({ client: "codex", ... })` 不再根据 `thread/loaded/list` 去猜“当前调用者自己的 thread”. daemon 仅凭 MCP session 无法安全判断 loaded threads 里哪一个属于当前调用者. 如果省略 `thread_id`, 工具会返回 `thread_id_required`, 并附带可恢复的 thread id 列表供排查, 但不会继续注册.
|
|
108
|
+
|
|
109
|
+
最简用法:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
register_agent({
|
|
113
|
+
client: "codex",
|
|
114
|
+
model: "gpt-5",
|
|
115
|
+
name: "lead",
|
|
116
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
117
|
+
role: "worker",
|
|
118
|
+
thread_id: "11111111-1111-4111-8111-111111111111"
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
如果你还需要 tmux fallback delivery, 在注册后显式绑定 runtime identity:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
bind_runtime_identity({
|
|
126
|
+
agent: "codex",
|
|
127
|
+
ui_pid: 81979
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
如果拿不到 UI pid, 可以退化到 `ui_tty + tmux_pane_id`:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
bind_runtime_identity({
|
|
135
|
+
agent: "codex",
|
|
136
|
+
ui_tty: "/dev/ttys026",
|
|
137
|
+
tmux_pane_id: "%1902"
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
如果本地不是默认地址, 可以显式覆盖 `ws_url`:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
register_agent({
|
|
145
|
+
client: "codex",
|
|
146
|
+
model: "gpt-5",
|
|
147
|
+
name: "lead",
|
|
148
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
149
|
+
role: "worker",
|
|
150
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
151
|
+
ws_url: "ws://127.0.0.1:8799"
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
如果 app-server 开启了 Bearer token, 可以传 `auth_token_ref`, 它的值是 daemon 进程可见的环境变量名:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
register_agent({
|
|
159
|
+
client: "codex",
|
|
160
|
+
model: "gpt-5",
|
|
161
|
+
name: "lead",
|
|
162
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
163
|
+
role: "worker",
|
|
164
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
165
|
+
auth_token_ref: "CODEX_REMOTE_TOKEN"
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
行为说明:
|
|
170
|
+
|
|
171
|
+
- `register_agent({ client: "codex", ... })` 是新的推荐入口
|
|
172
|
+
- 默认 `ws_url` 是 `ws://127.0.0.1:8799`
|
|
173
|
+
- 成功注册必须显式提供 `thread_id`
|
|
174
|
+
- tmux pane 绑定需要单独调用 `bind_runtime_identity(...)`
|
|
175
|
+
- `bind_runtime_identity(...)` 的 `agent` 参数是必填, 用于选择内置进程匹配器
|
|
176
|
+
- 优先使用 `ui_pid`, 也支持 `ui_tty + tmux_pane_id` 的降级校验路径
|
|
177
|
+
- 没有 loaded thread 时返回 `no_loaded_threads`
|
|
178
|
+
- 省略 `thread_id` 时返回 `thread_id_required`, 并附带可恢复 thread id 列表供排查
|
|
179
|
+
- 成功时返回 `{ agent_id, team, thread_id, ws_url }`
|
|
180
|
+
|
|
181
|
+
Codex app-server 的最小启动方式:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
codex app-server --listen ws://127.0.0.1:8799
|
|
185
|
+
codex --remote ws://127.0.0.1:8799
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
你也可以手动通过 `register_agent` 注册目标:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
register_agent({
|
|
192
|
+
model: "...",
|
|
193
|
+
name: "...",
|
|
194
|
+
role: "...",
|
|
195
|
+
team: "...",
|
|
196
|
+
delivery: {
|
|
197
|
+
kind: "codex-appserver",
|
|
198
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
199
|
+
ws_url: "ws://127.0.0.1:8799"
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
如果 app-server 开启了 Bearer token:
|
|
205
|
+
|
|
206
|
+
```text
|
|
207
|
+
register_agent({
|
|
208
|
+
model: "...",
|
|
209
|
+
name: "...",
|
|
210
|
+
role: "...",
|
|
211
|
+
team: "...",
|
|
212
|
+
delivery: {
|
|
213
|
+
kind: "codex-appserver",
|
|
214
|
+
thread_id: "11111111-1111-4111-8111-111111111111",
|
|
215
|
+
ws_url: "ws://127.0.0.1:8799",
|
|
216
|
+
auth_token_ref: "CODEX_REMOTE_TOKEN"
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
行为说明:
|
|
222
|
+
|
|
223
|
+
- `thread_id` 必须是 UUID
|
|
224
|
+
- `ws_url` 只能使用 `ws://` 或 `wss://`
|
|
225
|
+
- `auth_token_ref` 只会被解释为环境变量名
|
|
226
|
+
- 成功时, `poke()` 返回 `{ ok: true, transport_used: 'codex-appserver', thread_id }`
|
|
227
|
+
- 失败时, `poke()` 返回 machine-readable 错误, 例如 `codex_connect_failed`, `codex_initialize_failed`, `codex_resume_failed`, `codex_turn_start_failed`, `missing_auth_token`
|
|
228
|
+
- 当目标显式注册为 `codex-appserver` 时, daemon 不会自动 fallback 到 tmux
|
|
229
|
+
|
|
230
|
+
更完整的 Codex CLI 配置和启动示例见 [docs/configs/codex-cli.md](docs/configs/codex-cli.md).
|
|
231
|
+
|
|
232
|
+
## Claude Code Channel Delivery
|
|
233
|
+
|
|
234
|
+
如果你平时主要在 Claude Code 里使用, 更推荐直接在当前 Claude 会话里调用 `register_claude_self(...)`. 这条 helper 会把注册写到当前 host session 上, 可以直接避免外部 `curl` 注册带来的 session 错位。 如果当前 host 已经知道 channel proxy 宣告的 `channel_session_id`, 可以这样完成自注册和 channel 绑定:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
register_claude_self({
|
|
238
|
+
name: "lead",
|
|
239
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
240
|
+
role: "worker",
|
|
241
|
+
channel_session_id: "csid-abc"
|
|
242
|
+
})
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
如果你更想走统一入口, 也可以在当前 Claude 会话里直接调用 `register_agent({ client: "claude-code", ... })`:
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
register_agent({
|
|
249
|
+
client: "claude-code",
|
|
250
|
+
model: "opus-4-7",
|
|
251
|
+
name: "lead",
|
|
252
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
253
|
+
role: "worker",
|
|
254
|
+
channel_session_id: "csid-abc"
|
|
255
|
+
})
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
行为说明:
|
|
259
|
+
|
|
260
|
+
- Claude Code 的 proxy session 不是 owner Claude session。 不要用外部 `curl` 代替当前 Claude 会话做注册, 否则后续工具调用仍然可能看到 `unknown_agent`
|
|
261
|
+
- `register_claude_self(...)` 是 Claude Code 的首选路径, 因为它天然运行在当前 session 上
|
|
262
|
+
- `client="claude-code"` 时, `poke` 会优先走 `claude-channel`, 失败后再回退到 `tmux`
|
|
263
|
+
- 如果注册响应里仍然带 `hint`, 说明 tmux fallback 还没有完成绑定, 这时调用 `bind_runtime_identity(...)`
|
|
264
|
+
- `bind_channel(...)` 仍然保留, 但它只是低层重绑工具, 适合已注册 row 在 proxy 切换到新 `channel_session_id` 后补绑
|
|
265
|
+
|
|
266
|
+
更完整的 Claude Code 配置见 [docs/configs/claude-code.md](docs/configs/claude-code.md).
|
|
267
|
+
|
|
268
|
+
## 在 tmux 里使用 opencode
|
|
269
|
+
|
|
270
|
+
opencode 以普通 tmux TUI 的形式接入 xats, 不再提供专用 launcher, 也不再走 HTTP 专用 transport. 投递方式和 `client: "custom"` 完全一致: 由 daemon 将文本粘贴到 opencode 所在 tmux pane.
|
|
271
|
+
|
|
272
|
+
在 tmux 窗口里启动 opencode, 然后在 opencode 自己的 MCP session 里注册:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
tmux new-window opencode
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
```text
|
|
279
|
+
register_agent({
|
|
280
|
+
client: "opencode",
|
|
281
|
+
model: "opencode-default",
|
|
282
|
+
name: "my-opencode-agent",
|
|
283
|
+
project_dir: "/Users/me/workspace/cross-agent-teams-mcp",
|
|
284
|
+
role: "worker",
|
|
285
|
+
ui_pid: <opencode 进程 pid>
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
把 opencode 进程 pid 作为 `ui_pid` 传入, daemon 会在这次注册里走 `pid → tty → tmux pane` 的链路完成 `tmux_pane_id` 绑定. 其它 agent 对此 agent 的 `poke` 统一走 `transport_used: "tmux-poke"`.
|
|
290
|
+
|
|
291
|
+
行为说明:
|
|
292
|
+
|
|
293
|
+
- `client="opencode"` 时, `poke` 走 `tmux-poke`
|
|
294
|
+
- 如果注册响应里仍然带 `hint`, 说明 tmux fallback 还没有完成绑定, 这时调用 `bind_runtime_identity(...)`
|
|
295
|
+
|
|
296
|
+
### 运维 cutover
|
|
297
|
+
|
|
298
|
+
如果你从带 `opencode-server` 专用 transport 的旧版本升级:
|
|
299
|
+
|
|
300
|
+
1. 用 `./stop-server.sh` 停掉 daemon (会顺便清空 `data.db`; 被删除的 `opencode_base_url` / `opencode_session_id` 列和 `opencode_pane_pre_registrations` 表并不做迁移)
|
|
301
|
+
2. `pnpm build` 重新构建
|
|
302
|
+
3. `./start-server.sh` 启动新版 daemon
|
|
303
|
+
4. 删掉任何指向 `launch-opencode.sh` 的 shell alias (脚本已经不存在)
|
|
304
|
+
5. 按上面的 `register_agent({ client: "opencode", ui_pid, ... })` 重新注册 opencode agent
|
|
305
|
+
|
|
306
|
+
更完整的 opencode 配置见 [docs/configs/opencode.md](docs/configs/opencode.md).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface CliArgs {
|
|
3
|
+
daemonUrl: string;
|
|
4
|
+
}
|
|
5
|
+
declare class CliArgError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
declare function buildStartupHint(csid: string): {
|
|
9
|
+
content: string;
|
|
10
|
+
meta: {
|
|
11
|
+
source: string;
|
|
12
|
+
kind: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
declare function parseCliArgs(argv: readonly string[], env?: NodeJS.ProcessEnv): CliArgs;
|
|
16
|
+
declare function main(argv?: readonly string[], env?: NodeJS.ProcessEnv): Promise<void>;
|
|
17
|
+
|
|
18
|
+
export { CliArgError, buildStartupHint, main, parseCliArgs };
|