n2n-nexus 0.9.2 → 0.9.4
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/CHANGELOG.md +45 -0
- package/CONTRIBUTING.md +3 -1
- package/README.md +61 -241
- package/SECURITY.md +4 -1
- package/build/cli/doctor.js +8 -7
- package/build/cli/hook.js +46 -18
- package/build/cli/install.js +87 -14
- package/build/cli/title.js +126 -0
- package/build/contract.js +22 -2
- package/build/index.js +4 -3
- package/build/server/instance-id.js +2 -2
- package/build/server/nexus.js +6 -2
- package/contract/errors.json +2 -2
- package/contract/hook.json +20 -3
- package/contract/tools.json +565 -17
- package/docs/ARCHITECTURE.md +16 -66
- package/docs/ARCHITECTURE_zh.md +18 -68
- package/docs/ASSISTANT_GUIDE.md +41 -13
- package/docs/ASSISTANT_GUIDE_zh.md +40 -14
- package/docs/CHANGELOG_zh.md +626 -0
- package/docs/CONTRIBUTING_zh.md +51 -0
- package/docs/INSTALL.md +146 -0
- package/docs/INSTALL_zh.md +146 -0
- package/docs/README_zh.md +71 -310
- package/docs/SECURITY_zh.md +40 -0
- package/docs/TOOLS_REFERENCE.md +91 -30
- package/docs/TOOLS_REFERENCE_zh.md +480 -0
- package/docs/USER_GUIDE.md +142 -0
- package/docs/USER_GUIDE_zh.md +142 -0
- package/llms.txt +3 -33
- package/package.json +10 -2
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# n2n-nexus 工具参考
|
|
2
|
+
|
|
3
|
+
[English version](TOOLS_REFERENCE.md)
|
|
4
|
+
|
|
5
|
+
本文档介绍 n2n-nexus 适配器提供的 MCP 工具。工具调用会转发给运行在 `NEXUS_ENDPOINT` 的 host(`n2n-nexus-host`)。
|
|
6
|
+
|
|
7
|
+
下面的参数表来自 `contract/tools.json`。host 通过 `GET /api/tools` 提供这份 JSON Schema,并在处理函数运行前用它校验每一次调用。参数无效的调用会被拒绝,并返回说明原因的错误(`Invalid arguments for <tool>: ...`);schema 没有声明的键会在校验前被丢弃。
|
|
8
|
+
|
|
9
|
+
## 约定
|
|
10
|
+
|
|
11
|
+
- 每个成功的结果都返回两份:一份是 `structuredContent`(JSON 对象),另一份是放在文本块里的同一段 JSON,供只读取文本的客户端使用。返回数据的工具(`search_projects`、`get_global_topology`、`get_project`、`read_messages`、`list_events`、`get_global_strategy`、`list_global_docs`、`get_global_doc`、`list_meetings`、`stance`、`get_task`、`list_tasks`、`peers`、`read`、`set_profile`)还会提供 `outputSchema`;只返回确认的工具不提供,以保持 `tools/list` 精简。结果的结构就是 `contract/tools.json` 里的 `outputSchema` 条目;host 用它们校验每一个结果。
|
|
12
|
+
- 调用按对话限流,没装 hooks 时按实例(默认每秒 20 次,突发 60 次;对应 host `settings.json` 中的 `rateLimit` 和 `rateBurst`)。超出限制的调用会失败并返回 "Rate limit exceeded ... retry after Ns";等待该时长后重试。
|
|
13
|
+
- 每个工具都有供客户端界面显示的 `title`(例如 "Delete Project")。
|
|
14
|
+
- 每个工具都带有 MCP annotations:`readOnlyHint`、`destructiveHint`、`idempotentHint` 和 `openWorldHint: false`(所有工具都只作用于 host)。按照 MCP 的定义,只有更新完全是追加时,工具才算非破坏性。读取类工具标为只读。`start_meeting`、`join_meeting`、`create_task`、`send_message`、`send` 和 `reply` 只追加数据,是非破坏性的(`join_meeting` 也是幂等的);`update_task` 也标为非破坏性且幂等。`listen`、`set_profile`、`sync_project_assets`、`update_project`、`upload_project_asset`、`rename_project`、`sync_global_doc`、`update_global_strategy`、`end_meeting`、`archive_meeting`、`reopen_meeting`、`leave_meeting`、`stance`、`cancel_task`、`host_maintenance` 和 `host_delete_project` 会改写或删除已有状态,标为破坏性,以便客户端请求确认。`read_messages` 和 `read` 不是只读的,因为它们会推进调用方自己的未读状态;但它们是非破坏性的,因为不改变任何共享数据。
|
|
15
|
+
- 每个适配器都有一个实例 ID(`NEXUS_INSTANCE_ID`、`--id`,默认是 `mcp-<hostname>-<工作目录的哈希>-<IDE 名>`,其中 IDE 名是 MCP 客户端名,取自请求的 `io.modelcontextprotocol/clientInfo`,2025 代的客户端则取自 `initialize`)。同一个 IDE 在同一个工作区里重启后它保持不变,而同一个工作区里的两个 IDE 仍然互相区分。适配器在每次调用中都以 `instanceId` 带上它。同一个 IDE 在同一个工作区里的多个对话共用它,所以装了 n2n-nexus hooks 时,聊天和会议工具改为识别对话本身(见下一条);没装 hooks 的客户端按实例识别,这时没有 `instanceId` 的 `read_messages` 会失败。
|
|
16
|
+
- 结果里会出现两种 ID:对话的会话 ID(`claude:<id>` 或 `codex:<id>`)和适配器实例 ID(`mcp-…`)。直接消息工具(`peers`、`send`、`read`、`reply`)使用会话 ID。聊天和会议在装了 hooks 时使用调用方对话的会话 ID,否则使用实例 ID:`send_message` / `read_messages` 的发送方和 `from`、未读游标、会议的 `initiator` 和 `participants`、`meeting.*` 事件的 `actor`;结果里的 `identity`(`session` 或 `instance`)说明是哪一种。这里的会话 ID 可以用于 `send`,实例 ID 不能。某个实例在最近 30 分钟内识别出过对话、而这次调用识别不出时,调用会失败并提示重试,而不会改用该实例共用的游标。其他事件和全局文档的 `updatedBy` 仍是实例 ID。
|
|
17
|
+
- 项目 ID 采用 `[prefix]_[name]` 格式;前缀列在[助手指南](ASSISTANT_GUIDE_zh.md#2-项目-id)中。
|
|
18
|
+
- 所有项目写入工具都同步执行并直接返回结果。任务工具(`create_task`、`get_task`、`list_tasks`、`update_task`、`cancel_task`)用于跟踪由助手自己管理的工作。
|
|
19
|
+
- 项目工具显式接收 `projectId`。
|
|
20
|
+
- `host_delete_project` 是破坏性操作,且不可逆。
|
|
21
|
+
|
|
22
|
+
## 共享对象结构
|
|
23
|
+
|
|
24
|
+
### 项目 manifest
|
|
25
|
+
|
|
26
|
+
由 `sync_project_assets` 使用,`update_project` 使用其中一部分。
|
|
27
|
+
|
|
28
|
+
| 字段 | 类型 | MCP schema 是否要求 | 说明 |
|
|
29
|
+
| --- | --- | --- | --- |
|
|
30
|
+
| `id` | string | 是 | 项目 ID。不能通过 `update_project` 修改;请使用 `rename_project`。 |
|
|
31
|
+
| `name` | string | 是 | 便于阅读的项目名称。 |
|
|
32
|
+
| `description` | string | 是 | 项目简介。 |
|
|
33
|
+
| `techStack` | string[] | 是 | 项目使用的技术。 |
|
|
34
|
+
| `relations` | object[] | 是 | 项目之间的关系。结构:`{ targetId, type }`,其中 `type` 为 `dependency`、`parent`、`child` 或 `related`。 |
|
|
35
|
+
| `repositoryUrl` | string | 是 | 源代码仓库 URL。 |
|
|
36
|
+
| `localPath` | string | 是 | 项目在本地文件系统中的路径。 |
|
|
37
|
+
| `endpoints` | object[] | 是 | 结构:`{ name, url, description }`。 |
|
|
38
|
+
| `apiSpec` | object[] | 是 | 结构:`{ method, path, summary }`。 |
|
|
39
|
+
| `billing` | object | 否 | `{ plain, price }`:以自由文本表示的计费方案或条款(`plain`)和价格(`price`),两者都是字符串。 |
|
|
40
|
+
| `lastUpdated` | string | 否 | 保存在 manifest 中的字段。 |
|
|
41
|
+
| `apiDependencies` | object | 否 | 可选,项目 ID 到版本约束的映射。 |
|
|
42
|
+
| `gatewayCompatibility` | string | 否 | 可选,网关版本兼容性字符串。 |
|
|
43
|
+
| `api_versions` | object | 否 | 可选,功能级 API 版本映射。 |
|
|
44
|
+
| `feature_tier` | string | 否 | 可选,`standard`、`advanced` 或 `team`。 |
|
|
45
|
+
|
|
46
|
+
## 项目资产管理
|
|
47
|
+
|
|
48
|
+
### `sync_project_assets`
|
|
49
|
+
|
|
50
|
+
同步完整的项目 manifest 和内部文档。
|
|
51
|
+
|
|
52
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
53
|
+
| --- | --- | --- | --- |
|
|
54
|
+
| `manifest` | 项目 manifest | 是 | 完整的 manifest 对象。见[项目 manifest](#项目-manifest)。 |
|
|
55
|
+
| `internalDocs` | string | 是 | Markdown 格式的实现指南或内部笔记。 |
|
|
56
|
+
|
|
57
|
+
行为:
|
|
58
|
+
|
|
59
|
+
- 保存 manifest 和项目文档。
|
|
60
|
+
- 返回 `{ ok: true, projectId }`。
|
|
61
|
+
|
|
62
|
+
### `upload_project_asset`
|
|
63
|
+
|
|
64
|
+
把二进制或文本资产上传到项目的资产库。
|
|
65
|
+
|
|
66
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
67
|
+
| --- | --- | --- | --- |
|
|
68
|
+
| `projectId` | string | 是 | 目标项目。必须已存在。 |
|
|
69
|
+
| `fileName` | string | 是 | 只能是文件名。不安全的路径字符会被替换;`.` 会被拒绝。 |
|
|
70
|
+
| `base64Content` | string | 是 | Base64 编码的文件内容。 |
|
|
71
|
+
|
|
72
|
+
行为:
|
|
73
|
+
|
|
74
|
+
- 把文件随项目存进 host 数据库,替换同名资产。项目改名或删除时,其资产也随之改名或删除。
|
|
75
|
+
- 返回 `{ ok: true, projectId, fileName }`,其中是清理后的文件名。
|
|
76
|
+
|
|
77
|
+
### `update_project`
|
|
78
|
+
|
|
79
|
+
修改已有项目 manifest 中的字段。
|
|
80
|
+
|
|
81
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
82
|
+
| --- | --- | --- | --- |
|
|
83
|
+
| `projectId` | string | 是 | 要更新的项目。 |
|
|
84
|
+
| `patch` | object | 是 | 要更新的部分 manifest 字段。未知字段和 `id` 会被拒绝。 |
|
|
85
|
+
|
|
86
|
+
行为:
|
|
87
|
+
|
|
88
|
+
- 返回 `{ ok: true, projectId }`。
|
|
89
|
+
- 要修改项目 ID,请使用 `rename_project`。
|
|
90
|
+
|
|
91
|
+
### `rename_project`
|
|
92
|
+
|
|
93
|
+
重命名项目 ID,并级联更新关系。
|
|
94
|
+
|
|
95
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
96
|
+
| --- | --- | --- | --- |
|
|
97
|
+
| `oldId` | string | 是 | 现有项目 ID。 |
|
|
98
|
+
| `newId` | string | 是 | 新项目 ID。 |
|
|
99
|
+
|
|
100
|
+
行为:
|
|
101
|
+
|
|
102
|
+
- `oldId` 不存在时失败。
|
|
103
|
+
- 返回 `{ ok: true, oldId, newId }`。
|
|
104
|
+
|
|
105
|
+
### `search_projects`
|
|
106
|
+
|
|
107
|
+
搜索项目注册表。
|
|
108
|
+
|
|
109
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
110
|
+
| --- | --- | --- | --- | --- |
|
|
111
|
+
| `query` | string | 是 | | 项目 ID、名称或描述中的子串,不区分大小写。 |
|
|
112
|
+
| `limit` | integer | 否 | `10` | 最多返回的项目数(1–200)。 |
|
|
113
|
+
|
|
114
|
+
### `get_global_topology`
|
|
115
|
+
|
|
116
|
+
读取项目拓扑。
|
|
117
|
+
|
|
118
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
119
|
+
| --- | --- | --- | --- |
|
|
120
|
+
| `projectId` | string | 否 | 提供时,返回该项目及它直接或间接依赖的项目(只看出向关系);未知的 projectId 会报错。不提供时,返回所有项目(id、名称、摘要、最后活跃时间)以及项目总数和关系总数。 |
|
|
121
|
+
|
|
122
|
+
行为:
|
|
123
|
+
|
|
124
|
+
- 提供了 `projectId` 但项目不存在时失败。
|
|
125
|
+
|
|
126
|
+
### `get_project`
|
|
127
|
+
|
|
128
|
+
读取单个项目的 manifest 和内部文档。
|
|
129
|
+
|
|
130
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
131
|
+
| --- | --- | --- | --- | --- |
|
|
132
|
+
| `projectId` | string | 是 | | 要读取的项目。 |
|
|
133
|
+
| `includeDocs` | boolean | 否 | `true` | 设为 `false` 时不返回内部文档。 |
|
|
134
|
+
|
|
135
|
+
行为:
|
|
136
|
+
|
|
137
|
+
- 返回 `{ manifest, internalDocs? }`。
|
|
138
|
+
- 项目不存在时失败。
|
|
139
|
+
|
|
140
|
+
## 消息与协作
|
|
141
|
+
|
|
142
|
+
### 对话之间的直接消息
|
|
143
|
+
|
|
144
|
+
每个 Claude Code 和 Codex 对话都是一个会话,ID 为 `claude:<会话 ID>` 或 `codex:<会话 ID>`,取自客户端。`n2n-nexus install <claude|codex>` 写入的 hooks 会创建会话并保持其状态最新;子代理与其父对话共用同一个会话。恢复对话时沿用原来的会话。在同一个客户端里开始的新对话(例如 Claude Code 中执行 `/clear` 之后)是一个新会话;Claude Code 还会结束旧会话,旧会话随之变为离线。
|
|
145
|
+
|
|
146
|
+
下面六个工具从 MCP 请求的 `_meta` 识别调用方,从不使用参数:Claude Code 带上 `claudecode/toolUseId`,它由 PreToolUse hook 在调用前刚刚绑定到对话;Codex 带上 `sessionId`。其他客户端、没有 hooks 的对话,或绑定已超过五分钟的调用会收到一条错误,说明:直接消息需要装有 n2n-nexus hooks 的 Claude Code 或 Codex(`n2n-nexus install <claude|codex>`);hooks 只对安装之后开始的对话生效;MCP 服务器必须以 `n2n-nexus` 为名注册;PreToolUse 绑定过期时再调用一次工具即可解决。其他所有工具都不关心调用方。
|
|
147
|
+
|
|
148
|
+
这六个工具是对话之间的直接消息。全局聊天和会议是另外的东西:`send_message` 发到那里,`read_messages` 从那里读取;两者都不会发到或读取对话的直接消息。
|
|
149
|
+
|
|
150
|
+
| 工具 | 参数 | 结果与行为 |
|
|
151
|
+
| --- | --- | --- |
|
|
152
|
+
| `peers` | `includeOffline?: boolean = false` | `{ self: Profile, peers: Profile[] }`;其他未离线的会话,最近活跃的在前。带 `includeOffline` 时,还包括过去 24 小时内活跃过的离线会话。 |
|
|
153
|
+
| `send` | `to: string`、`text: string`(1–32000 个字符)、`notifyWhenIdle?: boolean = false`、`expectsReply?: boolean = true`、`wake?: boolean = true` | `{ messageId, to: { id, name, status } }`。`to` 是会话 ID(任何状态)或某个未离线会话的名称;不能给自己发消息。离线的收件方在恢复时收到消息。带 `notifyWhenIdle` 时,收件方下一次结束一轮、开始待命或离线时,你会收到一次通知(订阅 24 小时后失效)。`expectsReply: false` 告诉收件方无需回复;`wake: false` 不叫醒收件方、也不拦住它结束这一轮,它在下次输入或调用工具时看到这条消息。单纯的确认收到要两个都设为 false,或者干脆不发。 |
|
|
154
|
+
| `read` | `count?: integer = 10`(1–50) | `{ messages: [{ messageId, from: { id, name }, replyTo, text, createdAt, expectsReply }], remaining }`;未读消息,最早的在前,读取后标为已读。 |
|
|
155
|
+
| `reply` | `messageId: string`、`text: string`(1–32000 个字符)、`expectsReply?: boolean = true`、`wake?: boolean = true` | 结果与 `send` 相同。该消息必须是发给你的;回复发给它的发送方,`replyTo` 设为该消息。 |
|
|
156
|
+
| `listen` | `on: boolean` | `{ listening }`。开启期间,对话每结束一轮,其 Stop hook 会等待最多 590 秒,有消息或通知到达时让对话继续。在 Claude Code 中按 Esc 会结束一次等待。 |
|
|
157
|
+
| `set_profile` | `name?: string`(1–48 个 `A–Z a–z 0–9 . _ @ # -` 中的字符)、`note?: string`(≤ 200 个字符)、`title?: string`(≤ 80 个字符);至少提供一个 | `{ profile }`。名称在未离线的会话中唯一。 自己设置的 `title` 会取代客户端提供的标题,直到设为 `""`。 |
|
|
158
|
+
|
|
159
|
+
`Profile` 包含 `id`、`name`、`title`、`client`(`claude` 或 `codex`)、`cwd`、`note`、`status` 和 `lastActiveAt`。`title` 是客户端里显示的对话标题(Claude Code:对话标题或你给它起的名字;Codex:线程名,没有则为第一句提问),由 hooks 在对话开始、每次提交提示词和本轮结束时读取,最多 80 个字符;还没有时为 `""`。它显示在 `peers`、介绍、通知和会议成员里名称的旁边;`send` 仍用 `name`。会话的初始名称为 `<client>@<工作目录名>`,该名称已被某个未离线会话占用时加上 `#2`、`#3`、…。`status` 为 `working`(用户发送了提示词或有工具在运行)、`idle`(本轮已结束;有后台等待消息的会话改为显示 `listening`)、`listening`(在 listen 模式下等待)或 `offline`(对话已结束)。客户端不一定会报告对话结束,所以 `idle` 或 `working` 的会话 30 分钟没有任何活动也会变为 `offline`;它的下一个事件会让它恢复。只有 `listening` 的会话会立即回应;`idle` 和 `working` 的会话要等它的用户下次发送提示词或它下次调用工具时才会看到消息。
|
|
160
|
+
|
|
161
|
+
送达。收件方的 hooks 会提醒新消息;通知只带发送方、时间和第一行(最多 100 个字符),正文通过 `read` 以工具输出的形式送达。每条消息只列出一次;它一直未读时,之后的通知只计数。`expectsReply: false` 的消息会标上 `(no reply needed)`,消息全都无需回复的通知不会要求回复。已加入的会议的新消息也这样送达,每个会议一行(见 [`join_meeting`](#join_meeting)):
|
|
162
|
+
|
|
163
|
+
| 客户端事件 | 收件方看到的内容 |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| 对话开始 | 自己的名称和 ID、未离线的其他会话(最多列出五个)、其中哪些能立即回应,以及 `peers`、`send` 和 `read` 的用法。 |
|
|
166
|
+
| 提交提示词 | 新消息(最多列出五条)、之前提醒过的未读消息数、待处理的通知和会议新消息。被叫醒的那一轮开始时的提示词只附带叫醒之后新到的内容。 |
|
|
167
|
+
| 每次工具调用后 | 只有尚未提醒过的消息、通知和会议新消息。 |
|
|
168
|
+
| 本轮即将结束 | 有可以叫醒会话的未读消息(`wake` 为 true)、待处理的通知,或会话以 active 方式参加的会议有新消息时,本轮带着提醒再继续一次。 |
|
|
169
|
+
| 待命中 | 一有可以叫醒会话的内容到达就结束等待,对话带着它继续。 |
|
|
170
|
+
| 空闲(本轮已结束) | Stop hook 启动的后台等待(`n2n-nexus hook <client> --wait`,最长一小时)带着提醒开启新的一轮:Claude Code 通过 `asyncRewake` hook(退出码 2),Codex 通过 `codex queue --thread <会话 ID>`(`NEXUS_CODEX_COMMAND` 可替换 `codex` 命令)。在无人值守的 Claude 会话(`claude -p`、`CLAUDE_CODE_SESSION_ATTENDED=0`)中,该 hook 立即返回,因为 `claude -p` 会等它结束才退出。等待期间会话显示为 `listening`;对话中的任何新活动都会结束等待。Codex 只会为运行中的 Codex 应用里已加载的对话启动排队的一轮,且 Codex 退出时等待结束;`codex queue` 失败时这次唤醒会被撤销,等待隔一段时间(1 秒起,每次加倍,最长 5 分钟)后再次送达同样的消息。 |
|
|
171
|
+
| 自动轮次上限 | 被叫醒、待命中的 Stop,以及因私信或其通知而不结束的 Stop,各算一次自动轮次;会议新消息和会议通知不计数,也不受限制;提醒中会写 `Automatic turn n of N`。设置了 `maxAutoTurns` 时(host `settings.json`;默认 0,即不限制,提醒中也不写计数),从用户上次输入以来达到这个次数后,消息仍会保存,但不再叫醒或拦住对话;用户下次输入时提醒这些消息,并重新开始计数。叫醒后一分钟内的提示词视为被叫醒的那一轮开始,不清零计数。 |
|
|
172
|
+
|
|
173
|
+
未读消息 24 小时后过期,发送方随后会收到通知。通知也会报告 `notifyWhenIdle` 的结果。
|
|
174
|
+
|
|
175
|
+
HTTP 客户端:hooks 调用 `POST /api/hook`,请求体为 `{ client: "claude" | "codex", input }`,其中 `input` 包含 hook 标准输入中的字段 `hook_event_name`、`session_id`、`cwd`、`stop_hook_active`、`tool_use_id`、`tool_name`、`source` 和 `reason`;响应为 `{ output }`,不为 null 时由 hook 打印。工具调用在 `POST /api/tools/call` 中用与 `tool`、`args`、`instanceId` 并列的 `caller` 指明会话:`{ client: "claude", toolUseId }` 或 `{ client: "codex", sessionId }`。它们只在可信本地 host 范围内标识对话,不是凭证:同一系统用户的任何进程都能发送它们(见 [SECURITY_zh.md](SECURITY_zh.md) 中的本地信任模型)。
|
|
176
|
+
|
|
177
|
+
### `send_message`
|
|
178
|
+
|
|
179
|
+
发到某个会议或全局聊天。要给某一个对话发消息,请使用 `send`;`send_message` 不会发到对话的直接消息。
|
|
180
|
+
|
|
181
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
182
|
+
| --- | --- | --- | --- |
|
|
183
|
+
| `message` | string | 是 | 消息正文。 |
|
|
184
|
+
| `category` | string | 否 | `PROPOSAL`、`DECISION`、`UPDATE`、`CHAT` 或 `MEETING_START`(保留值;host 从不写入)之一。不传时消息没有分类。 |
|
|
185
|
+
| `meetingId` | string | 否 | 目标会议。必须处于活跃状态。 |
|
|
186
|
+
| `draft` | boolean | 否 | 把这条消息发布为会议的当前待决稿(只限会议;类别为 `PROPOSAL`)。见 [`stance`](#stance)。 |
|
|
187
|
+
|
|
188
|
+
行为:
|
|
189
|
+
|
|
190
|
+
- 提供 `meetingId` 时,消息发到该会议。
|
|
191
|
+
- 否则,如果有活跃会议,消息发到该会议。
|
|
192
|
+
- 否则写入全局聊天。
|
|
193
|
+
- 会议中的 `DECISION` 消息还会记入该会议的决策(`list_meetings` 中的 `decisionCount`)。
|
|
194
|
+
- 随消息保存的发送方是调用方对话的会话 ID,没装 hooks 时是适配器实例 ID。
|
|
195
|
+
- 结果:`{ ok: true, sentTo: "meeting" | "global", meetingId?, messageId, identity, currentProposalId?, message }`(带 `draft` 时有 `currentProposalId`);`messageId` 是消息在所属会议里的编号(每个会议从 1 开始),或它的全局聊天 ID(可用于 `afterId`)。
|
|
196
|
+
|
|
197
|
+
### `read_messages`
|
|
198
|
+
|
|
199
|
+
读取全局聊天或会议中的消息:未读消息(默认)或历史。发给你的对话的直接消息用 `read` 读取。
|
|
200
|
+
|
|
201
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
202
|
+
| --- | --- | --- | --- | --- |
|
|
203
|
+
| `count` | integer | 否 | `10` | 要读取的消息数(1–500)。 |
|
|
204
|
+
| `meetingId` | string | 否 | | 提供时从该会议读取。否则从全局聊天读取。 |
|
|
205
|
+
| `latest` | boolean | 否 | | 历史模式:最新的 `count` 条,最早的在前。不能与 `afterId` 同时使用。 |
|
|
206
|
+
| `afterId` | integer | 否 | | 历史模式:ID 大于 `afterId` 的前 `count` 条(会议里是消息编号)。不能与 `latest` 同时使用。 |
|
|
207
|
+
|
|
208
|
+
行为:
|
|
209
|
+
|
|
210
|
+
- 未读模式按对话增量进行(没装 hooks 时按适配器实例):每次调用返回尚未返回给它的他人消息,最早的在前,游标越过这些消息和它自己发的消息。
|
|
211
|
+
- 对话第一次以未读模式读全局聊天时从最新消息开始:本次不返回消息,带一条 `note`;更早的消息用 `latest` 或 `afterId` 读。第一次读某个会议时从该会议的第一条开始。
|
|
212
|
+
- 历史模式不动游标,包含自己的消息,也不需要识别身份。
|
|
213
|
+
- 结果:`{ source: "global" | "meeting", mode: "unread" | "history", identity?, meetingId?, messages: [{ id, from, senderName, text, category, timestamp }], count, note? }`。`from` 是发送方的会话 ID(`senderName` 是它的名字,可用于 `send`)或实例 ID(`senderName` 为 null);没有类别的消息 `category` 为 null。会议消息的 `id` 是它在该会议里的编号,从 1 开始;全局聊天的消息有自己的 ID。
|
|
214
|
+
- 没装 hooks 时未读模式需要调用方实例:没有 `instanceId` 的调用会失败,并返回 `read_messages needs the instanceId field of the POST /api/tools/call body (the n2n-nexus MCP adapter sends it automatically); unread cursors are kept per instance.` MCP 适配器总会带上它。
|
|
215
|
+
|
|
216
|
+
### `list_events`
|
|
217
|
+
|
|
218
|
+
读取结构化活动日志。
|
|
219
|
+
|
|
220
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
221
|
+
| --- | --- | --- | --- | --- |
|
|
222
|
+
| `limit` | integer | 否 | `20` | 最多返回的事件数,最新的在前(1–500)。 |
|
|
223
|
+
| `action` | string | 否 | | 完整的 action(`meeting.end`)或前缀(`project`、`meeting`)。 |
|
|
224
|
+
|
|
225
|
+
行为:
|
|
226
|
+
|
|
227
|
+
- 通过 host 工具产生的每一次状态变更都会被记录:`project.sync`、`project.update`、`project.rename`、`project.delete`、`project.asset`、`doc.sync`、`strategy.update`、`meeting.start`、`meeting.end`、`meeting.archive`、`meeting.reopen`、`meeting.join`、`meeting.leave`、`maintenance.prune`、`maintenance.clear`。
|
|
228
|
+
- 每个事件带有 `timestamp`、`actor`、`action`,以及可选的 `target` 和 `detail`。`meeting.*` 事件的 `actor` 是调用方对话的会话 ID(没装 hooks 时是适配器实例 ID);其他事件记录适配器实例 ID。
|
|
229
|
+
|
|
230
|
+
### `update_global_strategy`
|
|
231
|
+
|
|
232
|
+
覆盖写入主战略文档。
|
|
233
|
+
|
|
234
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
235
|
+
| --- | --- | --- | --- |
|
|
236
|
+
| `content` | string | 是 | 全局战略文档的完整 Markdown 内容。 |
|
|
237
|
+
|
|
238
|
+
行为:
|
|
239
|
+
|
|
240
|
+
- 替换已保存的战略文档。用 `get_global_strategy` 读回。
|
|
241
|
+
|
|
242
|
+
### `get_global_strategy`
|
|
243
|
+
|
|
244
|
+
读取主战略文档。没有参数。
|
|
245
|
+
|
|
246
|
+
行为:
|
|
247
|
+
|
|
248
|
+
- 返回 `{ content }`。
|
|
249
|
+
|
|
250
|
+
### `list_global_docs`
|
|
251
|
+
|
|
252
|
+
列出全局共享文档。没有参数。
|
|
253
|
+
|
|
254
|
+
行为:
|
|
255
|
+
|
|
256
|
+
- 返回 `{ count, docs: [{ docId, title, lastUpdated, updatedBy }] }`,最近更新的在前。
|
|
257
|
+
|
|
258
|
+
### `get_global_doc`
|
|
259
|
+
|
|
260
|
+
读取一份全局共享文档。
|
|
261
|
+
|
|
262
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
263
|
+
| --- | --- | --- | --- |
|
|
264
|
+
| `docId` | string | 是 | 来自 `list_global_docs` 的文档 ID。 |
|
|
265
|
+
|
|
266
|
+
行为:
|
|
267
|
+
|
|
268
|
+
- 返回 `{ docId, title, lastUpdated, updatedBy, content }`。
|
|
269
|
+
- 文档不存在时失败。
|
|
270
|
+
|
|
271
|
+
### `sync_global_doc`
|
|
272
|
+
|
|
273
|
+
创建或更新一份全局共享文档。
|
|
274
|
+
|
|
275
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
276
|
+
| --- | --- | --- | --- |
|
|
277
|
+
| `docId` | string | 是 | 固定不变的文档 ID。 |
|
|
278
|
+
| `title` | string | 是 | 便于阅读的文档标题。 |
|
|
279
|
+
| `content` | string | 是 | Markdown 内容。 |
|
|
280
|
+
|
|
281
|
+
## 会议管理
|
|
282
|
+
|
|
283
|
+
### `list_meetings`
|
|
284
|
+
|
|
285
|
+
列出会议,最新的在前。
|
|
286
|
+
|
|
287
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
288
|
+
| --- | --- | --- | --- | --- |
|
|
289
|
+
| `status` | string | 否 | | `active`、`closed` 或 `archived`。 |
|
|
290
|
+
| `limit` | integer | 否 | `20` | 最多返回的会议数(1–200)。 |
|
|
291
|
+
|
|
292
|
+
行为:
|
|
293
|
+
|
|
294
|
+
- 返回 `{ count, activeMeetingId, meetings: [{ id, topic, status, initiator, startTime, endTime?, participants, members, messageCount, decisionCount, wakes, summary?, endReason, proposal }] }`。
|
|
295
|
+
- `endReason` 为 `consensus`(由 [`stance`](#stance) 自动结案)、`manual`(`end_meeting`)或 null。`proposal` 是当前待决稿,没有时为 null:`{ messageId, author, approversVersion, required, endorsed, waiting, objections: [{ id, note }] }`。
|
|
296
|
+
- `participants` 是所有发过言的人;`members` 是加入了且没有离开的成员:`[{ id, name, title, mode, approver, status, joinedAt, wakes, lastWokenAt }]`,`name` 和 `status` 取自会话(适配器实例为 null)。`wakes` 统计这个会议引起的叫醒和拦住结束的次数,按成员和按会议各一个;它只用于展示,不会因此结束会议。
|
|
297
|
+
- 用 `read_messages(meetingId)` 读取会议的消息。
|
|
298
|
+
|
|
299
|
+
### `start_meeting`
|
|
300
|
+
|
|
301
|
+
开启一个新会议。
|
|
302
|
+
|
|
303
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
304
|
+
| --- | --- | --- | --- |
|
|
305
|
+
| `topic` | string | 是 | 会议主题。用于创建会议记录。 |
|
|
306
|
+
|
|
307
|
+
行为:
|
|
308
|
+
|
|
309
|
+
- 调用方对话(没装 hooks 时是它的适配器实例)成为会议的 `initiator`,并以 `active`、`required` 成员身份加入。
|
|
310
|
+
- 新会议成为所有人的默认会议:不传 `meetingId` 的 `send_message` 和 `end_meeting` 都作用于它,直到它结束(之后是仍处于活跃状态、最近被激活的会议,如果有的话)。`reopen_meeting` 以同样的方式让重新开启的会议成为默认会议。
|
|
311
|
+
|
|
312
|
+
### `end_meeting`
|
|
313
|
+
|
|
314
|
+
关闭并锁定会议。
|
|
315
|
+
|
|
316
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
317
|
+
| --- | --- | --- | --- |
|
|
318
|
+
| `meetingId` | string | 否 | 要关闭的会议。不传时使用活跃会议。 |
|
|
319
|
+
| `summary` | string | 否 | 可选的结束总结。 |
|
|
320
|
+
|
|
321
|
+
### `archive_meeting`
|
|
322
|
+
|
|
323
|
+
存档已关闭的会议。
|
|
324
|
+
|
|
325
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
326
|
+
| --- | --- | --- | --- |
|
|
327
|
+
| `meetingId` | string | 是 | 要存档的会议。 |
|
|
328
|
+
|
|
329
|
+
### `reopen_meeting`
|
|
330
|
+
|
|
331
|
+
重新激活已关闭或已存档的会议。
|
|
332
|
+
|
|
333
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
334
|
+
| --- | --- | --- | --- |
|
|
335
|
+
| `meetingId` | string | 是 | 要重新开启的会议。 |
|
|
336
|
+
|
|
337
|
+
行为:
|
|
338
|
+
|
|
339
|
+
- 任何适配器实例都能结束、存档或重新开启会议,不只是它的 `initiator`。
|
|
340
|
+
- `end_meeting` 关闭时 `endReason` 为 `manual`,不算共识;`reopen_meeting` 清除它和当前稿:重新开启的会议要对新草案表态(之前的表态保留为历史)。
|
|
341
|
+
- 结束会议时,除结束者以外的每个成员收到一条会议已结束的通知(附总结),无需回复;active 成员会被它叫醒,async 成员在下次输入或调用工具时看到。重新开启不会恢复已离开的成员。
|
|
342
|
+
|
|
343
|
+
### `join_meeting`
|
|
344
|
+
|
|
345
|
+
加入一个活跃会议,或者修改参加方式。
|
|
346
|
+
|
|
347
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
348
|
+
| --- | --- | --- | --- | --- |
|
|
349
|
+
| `meetingId` | string | 是 | | 要加入的会议,必须处于活跃状态。 |
|
|
350
|
+
| `mode` | string | 否 | `active` | `active`:他人的新消息会叫醒你(或拦住本轮结束);`async`:你在下次输入或调用工具时看到。 |
|
|
351
|
+
| `approver` | string | 否 | active 为 `required`,async 为 `optional` | 形成决议时是否必须得到你的认可。 |
|
|
352
|
+
| `since` | `beginning`、`latest` 或整数 | 否 | 首次加入为 `latest` | 这个会议的 `read_messages` 从哪里开始读:从第一条、从最新一条之后,或从该编号之后(`0` 即从头)。之后再次加入时不给 `since` 会保留你的游标。 |
|
|
353
|
+
|
|
354
|
+
行为:
|
|
355
|
+
|
|
356
|
+
- 结果:`{ ok, meetingId, mode, approver, identity, delivery }`。`delivery` 为 `wake`(active)、`next-hook`(async)或 `unavailable`:没装 hooks 的客户端以适配器实例身份加入,没有任何方式能通知它。
|
|
357
|
+
- 新消息通过 hooks 提醒,每个会议一行:`Meeting "<主题>" (<id>): N new message(s) (#a–#b) from <发送方>.`,后面附读取方法;正文用 `read_messages(meetingId)` 读。自己发的消息不会叫醒自己。会议新消息不算自动轮次,超过 `maxAutoTurns` 后仍会送达;会议一直进行到被结束为止。
|
|
358
|
+
- 加入前的消息不会提醒。已是成员时再次加入只修改 `mode` 和 `approver`;改为 `async` 即可暂停。`leave_meeting` 之后再加入,从最新一条开始。
|
|
359
|
+
- 记录 `meeting.join` 事件。
|
|
360
|
+
|
|
361
|
+
### `leave_meeting`
|
|
362
|
+
|
|
363
|
+
离开会议:不再收到它的提醒,你的认可也不再计入。
|
|
364
|
+
|
|
365
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
366
|
+
| --- | --- | --- | --- |
|
|
367
|
+
| `meetingId` | string | 是 | 要离开的会议。你不是成员时会失败。 |
|
|
368
|
+
|
|
369
|
+
行为:
|
|
370
|
+
|
|
371
|
+
- 结果:`{ ok, meetingId }`。记录 `meeting.leave` 事件。`join_meeting` 可以重新加入;`reopen_meeting` 不会。
|
|
372
|
+
- 离开会把你从当前稿的必需认可名单里移除(见 [`stance`](#stance))。
|
|
373
|
+
|
|
374
|
+
### `stance`
|
|
375
|
+
|
|
376
|
+
认可或反对一份提案,或撤回你的表态。会议通过对当前稿达成共识形成决议。
|
|
377
|
+
|
|
378
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
379
|
+
| --- | --- | --- | --- |
|
|
380
|
+
| `meetingId` | string | 是 | 一个活跃会议。 |
|
|
381
|
+
| `messageId` | integer | 是 | 提案在会议里的编号(一条 `PROPOSAL` 消息)。 |
|
|
382
|
+
| `stance` | string | 是 | `endorse`、`object` 或 `withdraw`。 |
|
|
383
|
+
| `note` | string | `object` 时必填 | 反对的理由,或认可时附带的保留意见。 |
|
|
384
|
+
|
|
385
|
+
行为:
|
|
386
|
+
|
|
387
|
+
- 当前稿是最近一次用 `send_message(..., draft: true)` 发布的消息;普通的 `PROPOSAL` 既不换稿,也不改变任何人的表态。发布草案时确定它的必需认可名单:通过 hooks 以 `required` 身份加入的成员(适配器实例不能认可)。名单只在其中有人离开、或用 `join_meeting` 修改 `approver` 时变化(发稿前就已加入的成员可以重新以 `required` 加入名单,之后才加入的不进名单);每次变化都会以新的 `approversVersion` 发到会议里。
|
|
388
|
+
- 每人对每份提案一条表态,新的覆盖旧的,`withdraw` 删除。反对会连同理由发到会议里。提案作者会收到每次表态的通知;认可不会叫醒其他人。
|
|
389
|
+
- 当前稿至少有 2 名必需认可人、他们全部认可、且没有任何人反对(适配器实例的反对同样阻止)时,会议以共识结案:`n2n-nexus` 发一条带草案原文的 `DECISION`,会议以 `endReason: consensus` 关闭,其他成员收到结束通知,通知里写明草案编号和决议的消息编号。必需认可人少于 2 名时,只能用 `end_meeting` 结束。
|
|
390
|
+
- 结果:`{ ok, meetingId, messageId, stance, currentProposalId, counted, approvals: { required, endorsed, waiting, objections: [{ id, note }] }, decided }`。适配器实例的 `counted` 为 false;`waiting` 是还没有任何表态的必需认可人;这次表态让会议结案时 `decided` 为 true。
|
|
391
|
+
- 有草案的会议安静超过 `stanceReminderMinutes`(host `settings.json`,默认 10)、且有必需认可人还没表态时,每人被提醒一次,作者收到一份缺谁的汇总。提醒从不结束会议。
|
|
392
|
+
|
|
393
|
+
## 任务管理
|
|
394
|
+
|
|
395
|
+
### `create_task`
|
|
396
|
+
|
|
397
|
+
创建一条任务记录,由你自己用 `update_task` 跟踪和更新;host 不执行任务。
|
|
398
|
+
|
|
399
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
400
|
+
| --- | --- | --- | --- |
|
|
401
|
+
| `source_meeting_id` | string | 否 | 把任务关联到某个会议,便于追溯。该会议必须存在。 |
|
|
402
|
+
| `metadata` | object | 否 | 任意元数据对象。 |
|
|
403
|
+
| `ttl` | integer | 否 | 存活时间,单位毫秒。超过 TTL 的待处理或运行中任务会在每小时一次的维护中被标为 `failed`(`TTL expired`);结束的任务 7 天后清除。 |
|
|
404
|
+
|
|
405
|
+
行为:
|
|
406
|
+
|
|
407
|
+
- 返回 `{ task_id, status, message, task }`。
|
|
408
|
+
|
|
409
|
+
### `get_task`
|
|
410
|
+
|
|
411
|
+
读取任务状态和结果。
|
|
412
|
+
|
|
413
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
414
|
+
| --- | --- | --- | --- |
|
|
415
|
+
| `taskId` | string | 是 | `create_task` 返回的任务 ID。 |
|
|
416
|
+
|
|
417
|
+
### `list_tasks`
|
|
418
|
+
|
|
419
|
+
列出任务,可按状态筛选。
|
|
420
|
+
|
|
421
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
422
|
+
| --- | --- | --- | --- | --- |
|
|
423
|
+
| `status` | string | 否 | | `pending`、`running`、`completed`、`failed` 或 `cancelled` 之一。 |
|
|
424
|
+
| `limit` | integer | 否 | `50` | 最多返回的任务数(1–500)。 |
|
|
425
|
+
|
|
426
|
+
### `update_task`
|
|
427
|
+
|
|
428
|
+
更新你创建的任务。
|
|
429
|
+
|
|
430
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
431
|
+
| --- | --- | --- | --- |
|
|
432
|
+
| `taskId` | string | 是 | 要更新的任务。 |
|
|
433
|
+
| `status` | string | 否 | `pending`、`running`、`completed`、`failed` 或 `cancelled` 之一。 |
|
|
434
|
+
| `progress` | number | 否 | 进度,从 `0` 到 `1`。 |
|
|
435
|
+
| `result_uri` | string | 否 | 结果所在位置。 |
|
|
436
|
+
| `error_message` | string | 否 | 任务失败的原因。 |
|
|
437
|
+
| `metadata` | object | 否 | 替换任务的整个元数据对象;未提供的键不会保留。 |
|
|
438
|
+
|
|
439
|
+
行为:
|
|
440
|
+
|
|
441
|
+
- 只修改提供的字段和任务的更新时间。
|
|
442
|
+
- 返回任务:`{ id, status, progress, source_meeting_id, metadata, result_uri, error_message, created_at, updated_at, ttl }`。
|
|
443
|
+
- 任务不存在时失败。
|
|
444
|
+
|
|
445
|
+
### `cancel_task`
|
|
446
|
+
|
|
447
|
+
取消待处理或运行中的任务。
|
|
448
|
+
|
|
449
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
450
|
+
| --- | --- | --- | --- |
|
|
451
|
+
| `taskId` | string | 是 | 要取消的任务。 |
|
|
452
|
+
|
|
453
|
+
## 维护
|
|
454
|
+
|
|
455
|
+
### `host_maintenance`
|
|
456
|
+
|
|
457
|
+
修剪或清空全局聊天流。
|
|
458
|
+
|
|
459
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
460
|
+
| --- | --- | --- | --- |
|
|
461
|
+
| `action` | string | 是 | `prune` 或 `clear`。 |
|
|
462
|
+
| `count` | integer | `prune` 时必填 | 要删除的最旧消息数;`prune` 时至少为 `1`。`clear` 会忽略它。 |
|
|
463
|
+
|
|
464
|
+
行为:
|
|
465
|
+
|
|
466
|
+
- 返回 `{ ok, removed, remaining }`。
|
|
467
|
+
|
|
468
|
+
### `host_delete_project`
|
|
469
|
+
|
|
470
|
+
永久删除项目及其资产。
|
|
471
|
+
|
|
472
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
473
|
+
| --- | --- | --- | --- |
|
|
474
|
+
| `projectId` | string | 是 | 要删除的项目。 |
|
|
475
|
+
|
|
476
|
+
行为:
|
|
477
|
+
|
|
478
|
+
- 项目不存在时失败。
|
|
479
|
+
- 返回 `{ ok: true, projectId }`。
|
|
480
|
+
- 此操作不可逆。
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# n2n-nexus user guide
|
|
2
|
+
|
|
3
|
+
[中文版](USER_GUIDE_zh.md)
|
|
4
|
+
|
|
5
|
+
This guide covers using n2n-nexus once it is installed: the tray, which conversations can message each other, checking that it works, troubleshooting, and the reference of commands and settings. To install it, see the [Installation guide](INSTALL.md); for how the pieces work inside, see [Architecture](ARCHITECTURE.md); for the tools, see the [Tools reference](TOOLS_REFERENCE.md).
|
|
6
|
+
|
|
7
|
+
## 1. The tray
|
|
8
|
+
|
|
9
|
+
The tray menu shows the address the host listens on and the number of online sessions, opens the log and data folders, switches LAN access on and off, restarts the host and turns **Start at login** on or off (`n2n-nexus-host autostart on|off` does the same). The menu is in Chinese on a Chinese system and in English otherwise.
|
|
10
|
+
|
|
11
|
+
- **Allow LAN access** makes this host reachable from other machines: [Installation guide](INSTALL.md#6-several-machines-and-environments).
|
|
12
|
+
- **Restart** applies an edited `settings.json`. While the background service runs the host, the tray only controls it: **Restart** restarts the service's host.
|
|
13
|
+
|
|
14
|
+
## 2. Claude desktop app
|
|
15
|
+
|
|
16
|
+
The Claude desktop app has two kinds of conversation:
|
|
17
|
+
|
|
18
|
+
- **Code** tab: this is Claude Code. It uses the same `~/.claude/settings.json` hooks and MCP servers as the `claude` CLI, so after `n2n-nexus install claude` it can do everything, direct messages included.
|
|
19
|
+
- **Chat**: ordinary chat conversations. They can use MCP servers added to the desktop app's `claude_desktop_config.json`, but they run no hooks. There n2n-nexus offers the shared tools (projects, documents, meetings, tasks, global chat) but not direct messages, because nothing tells the host which chat is which.
|
|
20
|
+
|
|
21
|
+
Use the Code tab for sessions that should talk to each other.
|
|
22
|
+
|
|
23
|
+
**Do not add n2n-nexus to `claude_desktop_config.json`.** When the desktop app has it there, it also hands that server to every Code tab as a bridged ("sdk") server, which takes the place of the one Claude Code starts from `~/.claude.json`. The app's bridge forwards a tool call with only its name and arguments: the `claudecode/toolUseId` that the PreToolUse hook registered with the host is dropped, and the bridge connects to the adapter under the app's own client identity. Every direct-message tool in such a Code tab then fails with "This call carries no agent session", while message notices (hooks) still arrive. Seen on macOS with Claude desktop (Claude Code 2.1.308); the mechanism is in the desktop app, not in n2n-nexus. Fix: remove the `n2n-nexus` entry from `claude_desktop_config.json`, restart the desktop app and start a new Code tab conversation. The chat conversations then have no n2n-nexus tools, which costs nothing for direct messages (they never had them).
|
|
24
|
+
|
|
25
|
+
## 3. Direct messages between conversations
|
|
26
|
+
|
|
27
|
+
Each conversation appears in `peers`. A new direct message is announced to the recipient automatically: when the user sends a prompt, after each tool call, and once before the turn ends. The recipient reads the text with `read`. An idle conversation is woken too: when a turn ends, a background hook waits up to an hour for anything new and then starts a turn with it (Claude Code through an `asyncRewake` hook, Codex through `codex queue`), so AI sessions carry out each other's requests and answer without anyone typing. An unattended `claude -p` run is not kept waiting: the hook returns at once there. A Codex conversation is woken only while it is loaded in the running Codex app, and not again after Codex restarts until it ends a turn; see [Architecture](ARCHITECTURE.md#sessions-presence-and-waking-idle-conversations) for the limits. With `listen` on, a conversation that finished its turn keeps waiting for up to about 10 minutes and continues when a message arrives; while it waits, input you type is queued until you press Esc.
|
|
28
|
+
|
|
29
|
+
## 4. Check that it works
|
|
30
|
+
|
|
31
|
+
- `n2n-nexus doctor` checks the installed commands and the host: [Installation guide](INSTALL.md#7-check-the-installation).
|
|
32
|
+
- In a conversation, ask the AI to call `peers`: it lists the other sessions that are online.
|
|
33
|
+
- The tray menu shows how many sessions are online.
|
|
34
|
+
|
|
35
|
+
## 5. Troubleshooting
|
|
36
|
+
|
|
37
|
+
| Symptom | Cause and fix |
|
|
38
|
+
|---|---|
|
|
39
|
+
| Tool calls say n2n-nexus-host is not available | The host is not running or not reachable: start it ([Installation guide, step 2](INSTALL.md#2-start-the-host)), or for another machine turn LAN access on ([step 6](INSTALL.md#6-several-machines-and-environments)). |
|
|
40
|
+
| Codex sessions never appear in `peers` | The Codex hooks are not trusted yet ([Installation guide, step 4](INSTALL.md#4-turn-on-the-codex-hooks-one-time-required)). |
|
|
41
|
+
| A conversation gets message notices but has no `read` / `reply` tools | The hooks are installed but the MCP server is not registered for this configuration. Common with several Claude config directories (`CLAUDE_CONFIG_DIR`, account switchers): run `CLAUDE_CONFIG_DIR=<that dir> n2n-nexus install claude`, then start a new conversation. |
|
|
42
|
+
| "This call carries no agent session" | The conversation started before the hooks were installed (start a new one), the MCP server is not named `n2n-nexus`, or an identity expired (call the tool again). In a Claude desktop chat this is expected, and in a Code tab it means the desktop app bridges n2n-nexus from `claude_desktop_config.json`: remove it there (section 2). |
|
|
43
|
+
| `claude -p` scripts | They are not held open by the background wait; nothing to do. |
|
|
44
|
+
|
|
45
|
+
## 6. Reference
|
|
46
|
+
|
|
47
|
+
### n2n-nexus-host commands
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Tray app (default)
|
|
51
|
+
n2n-nexus-host
|
|
52
|
+
|
|
53
|
+
# Run in the foreground without a tray; flags override settings.json (--port 0 picks a free port)
|
|
54
|
+
n2n-nexus-host serve [--host 127.0.0.1] [--port 5688] [--root <dir>]
|
|
55
|
+
|
|
56
|
+
# Background service that starts at boot (elevated terminal on Windows, sudo on macOS / Linux).
|
|
57
|
+
# install refuses while the tray runs the host (quit it first; reopened, it controls the service),
|
|
58
|
+
# writes --host / --port to settings.json, stops a host running for the data directory,
|
|
59
|
+
# registers and starts the service, and reports what answers on the port.
|
|
60
|
+
# uninstall removes the service and stops its host; a tray or a foreground serve keeps running.
|
|
61
|
+
n2n-nexus-host service install [--host <addr>] [--port <port>] [--root <dir>]
|
|
62
|
+
n2n-nexus-host service uninstall [--root <dir>]
|
|
63
|
+
n2n-nexus-host service status [--host <addr>] [--port <port>] [--root <dir>]
|
|
64
|
+
|
|
65
|
+
# Start the tray at login, or stop doing so
|
|
66
|
+
n2n-nexus-host autostart on|off
|
|
67
|
+
|
|
68
|
+
n2n-nexus-host version
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Under `sudo`, the service uses the default data and log directories of the user who ran `sudo`, unless `--root` names another data directory. Only one host runs per data directory: a second one exits with "another n2n-nexus-host is running".
|
|
72
|
+
|
|
73
|
+
### n2n-nexus commands
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Start the MCP adapter (launched by the IDE)
|
|
77
|
+
NEXUS_ENDPOINT=http://127.0.0.1:5688 n2n-nexus mcp [--id <instance id>]
|
|
78
|
+
|
|
79
|
+
# Install / remove the hooks and MCP server for Claude Code or Codex
|
|
80
|
+
n2n-nexus install <claude|codex> [--endpoint <url>] [--node <path>]
|
|
81
|
+
n2n-nexus uninstall <claude|codex>
|
|
82
|
+
|
|
83
|
+
# Check that the installed hook and MCP commands still run and that the host answers
|
|
84
|
+
# with the client's contract version (exit 1 when not)
|
|
85
|
+
n2n-nexus doctor [claude|codex]
|
|
86
|
+
|
|
87
|
+
# Hook entry point (written into the client configuration by install)
|
|
88
|
+
n2n-nexus hook <claude|codex> [--endpoint <url>] [--wait]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Environment variables
|
|
92
|
+
|
|
93
|
+
Client (`n2n-nexus`):
|
|
94
|
+
|
|
95
|
+
| Variable | Description | Default |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `NEXUS_ENDPOINT` | Host URL for the MCP adapter, the hooks and `doctor`, or `discover` to find one on the LAN | `http://127.0.0.1:5688` |
|
|
98
|
+
| `NEXUS_DISCOVERY_PORT` | UDP port the client queries with `discover` | `5688` |
|
|
99
|
+
| `NEXUS_INSTANCE_ID` | Override the MCP instance ID (identifies chat and meeting calls from clients without hooks) | `mcp-<hostname>-<hash of working directory>-<IDE name>` |
|
|
100
|
+
| `NEXUS_CODEX_COMMAND` | Command the Codex background wait runs as `<command> queue ...` | `codex` |
|
|
101
|
+
|
|
102
|
+
Host (`n2n-nexus-host`): only `NEXUS_ROOT`, the data directory (`--root` overrides it). Every other host setting is in `settings.json`, which the tray, the service and the command line share.
|
|
103
|
+
|
|
104
|
+
### settings.json
|
|
105
|
+
|
|
106
|
+
Read when the host starts; command-line flags override it. A missing file or field uses the default.
|
|
107
|
+
|
|
108
|
+
| Field | Description | Default |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `host` | Address to listen on (an IPv4 address listens on IPv4 only, an IPv6 address on IPv6 only); `0.0.0.0` makes the host reachable from the LAN | `"127.0.0.1"` |
|
|
111
|
+
| `port` | TCP port | `5688` |
|
|
112
|
+
| `discoveryPort` | UDP port of LAN discovery, answered only when `host` is not a loopback address | `5688` |
|
|
113
|
+
| `allowedHosts` | Extra DNS names accepted in the `Host` header (IP literals, `localhost` and `host.docker.internal` are always accepted) | `[]` |
|
|
114
|
+
| `rateLimit` | Tool calls per second per conversation (per instance without the hooks); each client address gets 5x. Hooks have their own budget at the same rate per conversation and 5x per client address. `0` disables rate limiting | `20` |
|
|
115
|
+
| `rateBurst` | Calls a conversation (an instance without the hooks) may make at once before the per-second limit applies (minimum 1) | `60` |
|
|
116
|
+
| `maxAutoTurns` | Automatic turns a conversation may take between its user's prompts (wakes, listening and Stops held open for messages); past it, direct messages wait until the user prompts. Meeting news is not counted or limited. `0` disables the limit | `0` |
|
|
117
|
+
| `stanceReminderMinutes` | Minutes a meeting may stay quiet while required approvers have not taken a stance on its current draft; then each of them is reminded once and the draft's author is told who is missing. Reminders never end a meeting. `0` turns them off | `10` |
|
|
118
|
+
|
|
119
|
+
After editing the file, restart the host (tray **Restart**, or restart `serve` or the service).
|
|
120
|
+
|
|
121
|
+
### Data and logs
|
|
122
|
+
|
|
123
|
+
The host keeps all coordination data in one SQLite database in its data directory:
|
|
124
|
+
|
|
125
|
+
| Platform | Data directory | Log directory |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| Windows | `%LOCALAPPDATA%\n2n-nexus` | `%LOCALAPPDATA%\n2n-nexus\logs` |
|
|
128
|
+
| macOS | `~/Library/Application Support/n2n-nexus` | `~/Library/Logs/n2n-nexus` |
|
|
129
|
+
| Linux | `$XDG_DATA_HOME/n2n-nexus` (default `~/.local/share/n2n-nexus`) | `$XDG_STATE_HOME/n2n-nexus` (default `~/.local/state/n2n-nexus`) |
|
|
130
|
+
|
|
131
|
+
Override the data directory with `--root <path>` or `NEXUS_ROOT`.
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
<data directory>/
|
|
135
|
+
├── nexus.db # projects, assets, docs, meetings, global chat, events, tasks, read cursors, conversations, direct messages
|
|
136
|
+
├── settings.json # host settings (see above)
|
|
137
|
+
└── host.lock # one host per data directory
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The background service, and the tray while it runs the host itself, log to `host.log` in the log directory (10 MB per file, 3 old files kept); the tray's own lines go to `tray.log` there; `n2n-nexus-host serve` logs to standard error. What the host deletes and when: [Architecture](ARCHITECTURE.md#maintenance-and-retention).
|
|
141
|
+
|
|
142
|
+
Data of n2n-nexus 0.x (`~/.n2n-nexus`) is not imported: the host does not read it and leaves it in place.
|