n2n-nexus 0.4.2

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.md ADDED
@@ -0,0 +1,286 @@
1
+ # n2n-nexus
2
+
3
+ Local-first MCP coordination hub from N2NS Lab for multi-AI assistant collaboration across IDEs, machines, and projects.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/n2n-nexus)](https://www.npmjs.com/package/n2n-nexus)
6
+ [![npm total downloads](https://img.shields.io/npm/dt/n2n-nexus)](https://www.npmjs.com/package/n2n-nexus)
7
+ [![license](https://img.shields.io/github/license/n2ns/n2n-nexus)](https://github.com/n2ns/n2n-nexus/blob/main/LICENSE)
8
+ [![MCP Protocol](https://img.shields.io/badge/MCP-Protocol-blue)](https://modelcontextprotocol.io)
9
+ [![node version](https://img.shields.io/node/v/n2n-nexus)](https://nodejs.org)
10
+ [![DataFrog.io](https://datafrog.io/badges/datafrog.svg)](https://datafrog.io)
11
+
12
+ [中文版](./docs/README_zh.md)
13
+
14
+ ---
15
+
16
+ > **One daemon. Many assistants. Shared project state.**
17
+
18
+ n2n-nexus is an open-source Model Context Protocol (MCP) coordination server for teams and developers who use multiple AI coding assistants. A long-running local daemon owns project state, meetings, messages, tasks, shared docs, and project assets. Lightweight MCP adapters connect each IDE or assistant to the same daemon.
19
+
20
+ Use it when Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, JetBrains, or other MCP-enabled clients need a shared local coordination layer instead of isolated per-chat context.
21
+
22
+ ## Search-friendly positioning
23
+
24
+ If you are searching for:
25
+
26
+ - multi-agent MCP coordination
27
+ - multi-AI assistant collaboration
28
+ - local MCP coordination hub
29
+ - shared project context for AI coding agents
30
+ - cross-IDE MCP collaboration server
31
+ - local-first project registry for AI assistants
32
+ - meeting and task coordination for coding agents
33
+
34
+ n2n-nexus is designed for these goals: shared daemon state, stateless MCP adapters, project-aware collaboration, local storage, and cross-environment IDE workflows.
35
+
36
+ ## What Is n2n-nexus?
37
+
38
+ n2n-nexus gives AI assistants a shared coordination workspace. Instead of each assistant keeping its own isolated conversation state, all connected MCP clients can read and write to the same local daemon.
39
+
40
+ **TL;DR**
41
+
42
+ - **Install**: `npx n2n-nexus daemon --port 5688`
43
+ - **Protocol**: Model Context Protocol (MCP), adapter-to-daemon HTTP bridge
44
+ - **Storage**: local filesystem plus SQLite under `~/.n2n-nexus` by default
45
+ - **Best for**: multi-assistant coding sessions, project handoffs, shared decisions, meeting notes, async tasks, project manifests
46
+ - **Not for**: cloud team chat, public project management SaaS, source code indexing, vector search, or remote database hosting
47
+
48
+ ## Why Use It?
49
+
50
+ - Coordinate multiple AI assistants working on the same project.
51
+ - Share project manifests, internal notes, assets, and topology across IDEs.
52
+ - Keep decisions, proposals, and updates in a local meeting/message log.
53
+ - Run the daemon once and connect adapters from Windows, WSL, SSH hosts, VMs, or multiple editors.
54
+ - Avoid giving each assistant broad filesystem or backend access just to exchange context.
55
+
56
+ ## Architecture
57
+
58
+ ```text
59
+ ┌──────────────────────────────────────┐
60
+ │ n2n-nexus daemon │
61
+ │ Standalone HTTP server · always on │
62
+ │ Owns data, tools, tasks, messages │
63
+ └──────────────┬───────────────────────┘
64
+ │ HTTP (NEXUS_ENDPOINT)
65
+ ┌───────┼───────┐
66
+ ▼ ▼ ▼
67
+ MCP-A MCP-B MCP-C
68
+ (Win) (WSL) (SSH/VM)
69
+ Stateless adapter per IDE
70
+ ```
71
+
72
+ - **Daemon is the source of truth**: start it once and keep it running.
73
+ - **MCP adapters are stateless**: each IDE starts an adapter through `npx`, then forwards tool calls to the daemon.
74
+ - **Cross-environment by design**: point `NEXUS_ENDPOINT` at the same daemon from different machines or shells. The daemon binds to localhost by default; use `--host 0.0.0.0` only on a trusted network.
75
+
76
+ ## Quick Start
77
+
78
+ ### 1. Start the daemon
79
+
80
+ ```bash
81
+ npx n2n-nexus daemon --port 5688
82
+ ```
83
+
84
+ ### 2. Configure an MCP client
85
+
86
+ ```json
87
+ {
88
+ "mcpServers": {
89
+ "n2n-nexus": {
90
+ "command": "npx",
91
+ "args": ["-y", "n2n-nexus", "mcp"],
92
+ "env": {
93
+ "NEXUS_ENDPOINT": "http://127.0.0.1:5688"
94
+ }
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ The adapter can start before the daemon. It loads the daemon tool list once the daemon becomes reachable.
101
+
102
+ ### Cross-environment endpoint examples
103
+
104
+ | Scenario | `NEXUS_ENDPOINT` |
105
+ | --- | --- |
106
+ | Same machine | `http://127.0.0.1:5688` |
107
+ | WSL IDE to Windows daemon | `http://host.docker.internal:5688` |
108
+ | Windows IDE to WSL daemon | `http://<WSL-IP>:5688` |
109
+ | Remote machine | `http://<Server-IP>:5688` |
110
+
111
+ ## Toolset
112
+
113
+ ### Session and context
114
+
115
+ - `register_session_context`: declare the active project ID.
116
+
117
+ ### Project asset management
118
+
119
+ - `sync_project_assets`: submit a project manifest and internal docs.
120
+ - `update_project`: patch a project manifest.
121
+ - `rename_project`: rename a project ID and update relations.
122
+ - `upload_project_asset`: upload binary or text assets.
123
+ - `search_projects`: search the project registry.
124
+ - `get_global_topology`: inspect project topology and dependencies.
125
+
126
+ ### Messaging and collaboration
127
+
128
+ - `send_message`: post meeting or global messages.
129
+ - `read_messages`: read unread messages incrementally.
130
+ - `update_global_strategy`: update the master strategy document.
131
+ - `sync_global_doc`: create or update shared docs.
132
+
133
+ ### Meeting management
134
+
135
+ - `start_meeting`: open a meeting session.
136
+ - `end_meeting`: close and lock a meeting.
137
+ - `archive_meeting`: move a closed meeting to archive.
138
+ - `reopen_meeting`: reactivate a closed or archived meeting.
139
+
140
+ ### Async task management
141
+
142
+ - `create_task`: create a background task.
143
+ - `get_task`: poll task status and result.
144
+ - `list_tasks`: list tasks by status.
145
+ - `cancel_task`: cancel a pending or running task.
146
+
147
+ ### Maintenance
148
+
149
+ - `host_maintenance`: prune or clear system logs.
150
+ - `host_delete_project`: delete a project and assets.
151
+
152
+ ## Data Storage
153
+
154
+ Default storage root:
155
+
156
+ | Platform | Path |
157
+ | --- | --- |
158
+ | Linux / WSL | `~/.n2n-nexus` |
159
+ | Windows | `%USERPROFILE%\.n2n-nexus` |
160
+ | macOS | `~/.n2n-nexus` |
161
+
162
+ Override with `--root <path>` or `NEXUS_ROOT`.
163
+
164
+ ```text
165
+ ~/.n2n-nexus/
166
+ ├── global/
167
+ │ ├── blueprint.md
168
+ │ ├── docs_index.json
169
+ │ └── docs/
170
+ ├── projects/
171
+ │ └── {project-id}/
172
+ │ ├── manifest.json
173
+ │ ├── internal_blueprint.md
174
+ │ └── assets/
175
+ ├── registry.json
176
+ └── nexus.db
177
+ ```
178
+
179
+ ## Project ID Conventions
180
+
181
+ Project IDs follow `[prefix]_[name]`.
182
+
183
+ | Prefix | Category | Example |
184
+ | --- | --- | --- |
185
+ | `web_` | Websites | `web_datafrog.io` |
186
+ | `api_` | Backend services | `api_user-auth` |
187
+ | `mcp_` | MCP servers | `mcp_nexus` |
188
+ | `lib_` | Libraries / SDKs | `lib_crypto-core` |
189
+ | `chrome_` | Chrome extensions | `chrome_evisa-helper` |
190
+ | `vscode_` | VS Code extensions | `vscode_super-theme` |
191
+ | `desktop_` | Desktop apps | `desktop_main-hub` |
192
+ | `infra_` | Infrastructure / DevOps | `infra_k8s-config` |
193
+ | `doc_` | Documentation | `doc_coding-guide` |
194
+
195
+ ## CLI Reference
196
+
197
+ ```bash
198
+ # Start daemon
199
+ n2n-nexus daemon [--port 5688] [--root ~/.n2n-nexus] [--host 127.0.0.1]
200
+
201
+ # Start MCP adapter
202
+ NEXUS_ENDPOINT=http://127.0.0.1:5688 n2n-nexus mcp
203
+ ```
204
+
205
+ ### Environment variables
206
+
207
+ | Variable | Description | Default |
208
+ | --- | --- | --- |
209
+ | `NEXUS_ENDPOINT` | Daemon URL for MCP adapter | `http://127.0.0.1:5688` |
210
+ | `NEXUS_ROOT` | Storage root for daemon | `~/.n2n-nexus` |
211
+ | `NEXUS_HOST` | Daemon bind host | `127.0.0.1` |
212
+ | `NEXUS_INSTANCE_ID` | Override MCP instance ID | auto-generated |
213
+
214
+ ## Security and governance notes
215
+
216
+ - n2n-nexus stores coordination data locally by default.
217
+ - Do not put secrets, credentials, customer data, or private tokens in meeting messages or project assets unless your local policy allows it.
218
+ - Destructive tools such as project deletion should be used through explicit review workflows.
219
+ - Exposing the daemon beyond localhost is an operational choice; restrict network access when using remote endpoints.
220
+ - Treat project manifests and internal docs as potentially sensitive implementation data.
221
+
222
+ ## Real-world example
223
+
224
+ The docs include a sample multi-agent session where several AI assistants collaborated on architecture and protocol decisions:
225
+
226
+ | File | Description |
227
+ | --- | --- |
228
+ | [Meeting Minutes](docs/MEETING_MINUTES_2025-12-29.md) | Structured decisions and test notes |
229
+ | [Discussion Log](docs/discussion_2025-12-29_en.md) | Human-readable discussion transcript |
230
+
231
+ ## Local Development
232
+
233
+ ```bash
234
+ git clone https://github.com/n2ns/n2n-nexus.git
235
+ cd n2n-nexus
236
+ npm install
237
+ npm run build
238
+
239
+ # Run daemon
240
+ node build/index.js daemon --root /tmp/nexus-test --port 5688
241
+
242
+ # Run MCP adapter
243
+ NEXUS_ENDPOINT=http://127.0.0.1:5688 node build/index.js mcp
244
+ ```
245
+
246
+ ## FAQ
247
+
248
+ ### Is n2n-nexus a project management SaaS?
249
+
250
+ No. It is a local MCP coordination server for AI assistants. It stores state locally and exposes MCP tools to connected clients.
251
+
252
+ ### Does it replace n2n-memory?
253
+
254
+ No. `n2n-memory` is repository-local memory for one project. `n2n-nexus` is a shared coordination hub for multiple assistants, project manifests, messages, meetings, and tasks.
255
+
256
+ ### Does it work with Claude Desktop, Cursor, VS Code, and other IDEs?
257
+
258
+ Yes, when the client supports local MCP command servers. The adapter is started by the IDE and connects to the local daemon through `NEXUS_ENDPOINT`.
259
+
260
+ ### Can it coordinate assistants across Windows and WSL?
261
+
262
+ Yes. Run the daemon in one environment and point each adapter at it using `NEXUS_ENDPOINT`.
263
+
264
+ ### Does it send data to the cloud?
265
+
266
+ No cloud service is required. Data is stored under the configured local root. If you expose the daemon to a remote machine, that is your own network configuration.
267
+
268
+ ### Is this a vector database or code indexer?
269
+
270
+ No. n2n-nexus coordinates project metadata, messages, meetings, docs, tasks, and assets. It is not a semantic code search engine.
271
+
272
+ ## Related docs
273
+
274
+ - [Architecture](./docs/ARCHITECTURE.md)
275
+ - [AI Assistant Guide](./docs/ASSISTANT_GUIDE.md)
276
+ - [Chinese README](./docs/README_zh.md)
277
+ - [Changelog](./CHANGELOG.md)
278
+ - [llms.txt](./llms.txt)
279
+
280
+ ## License
281
+
282
+ This project is licensed under the [Apache-2.0 License](./LICENSE).
283
+
284
+ ---
285
+
286
+ Built by N2NS Lab, short for Next-to-Native Systems Lab, Datafrog's open-source lab for practical AI developer tools.
@@ -0,0 +1,71 @@
1
+ import http from "node:http";
2
+ import https from "node:https";
3
+ import { URL } from "node:url";
4
+ function normalizeEndpoint(endpoint) {
5
+ const raw = endpoint || process.env.NEXUS_ENDPOINT || "http://127.0.0.1:5688";
6
+ if (/^https?:\/\//i.test(raw))
7
+ return raw;
8
+ return `http://${raw}`;
9
+ }
10
+ export class NexusClient {
11
+ endpoint;
12
+ timeoutMs;
13
+ constructor(options = {}) {
14
+ this.endpoint = normalizeEndpoint(options.endpoint);
15
+ this.timeoutMs = options.timeoutMs ?? 5000;
16
+ }
17
+ request(method, path, body) {
18
+ const url = new URL(path, this.endpoint);
19
+ const payload = body !== undefined ? JSON.stringify(body) : undefined;
20
+ const transport = url.protocol === "https:" ? https : http;
21
+ return new Promise((resolve, reject) => {
22
+ const req = transport.request({
23
+ method,
24
+ protocol: url.protocol,
25
+ hostname: url.hostname,
26
+ port: url.port,
27
+ path: `${url.pathname}${url.search}`,
28
+ headers: {
29
+ "Content-Type": "application/json",
30
+ ...(payload ? { "Content-Length": Buffer.byteLength(payload) } : {})
31
+ },
32
+ timeout: this.timeoutMs
33
+ }, (res) => {
34
+ let data = "";
35
+ res.on("data", chunk => { data += chunk; });
36
+ res.on("end", () => {
37
+ try {
38
+ const parsed = data ? JSON.parse(data) : {};
39
+ if (res.statusCode && res.statusCode >= 400) {
40
+ reject(new Error(parsed.error || `HTTP ${res.statusCode}`));
41
+ }
42
+ else {
43
+ resolve(parsed);
44
+ }
45
+ }
46
+ catch {
47
+ reject(new Error(`Invalid JSON response: ${data}`));
48
+ }
49
+ });
50
+ });
51
+ req.on("error", reject);
52
+ req.on("timeout", () => req.destroy(new Error("Request timeout")));
53
+ if (payload)
54
+ req.write(payload);
55
+ req.end();
56
+ });
57
+ }
58
+ async health() {
59
+ return this.request("GET", "/health");
60
+ }
61
+ async fetchTools() {
62
+ const res = await this.request("GET", "/api/tools");
63
+ return res.tools;
64
+ }
65
+ async callTool(name, args, instanceId) {
66
+ const res = await this.request("POST", "/api/tools/call", { tool: name, args: args || {}, instanceId: instanceId || "unknown" });
67
+ if (!res.ok)
68
+ throw new Error(res.error || "Tool call failed");
69
+ return res.result;
70
+ }
71
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * CLI Argument Parsing
3
+ */
4
+ const args = process.argv.slice(2);
5
+ export function getArg(k) {
6
+ const i = args.indexOf(k);
7
+ return i !== -1 && args[i + 1] ? args[i + 1] : "";
8
+ }
9
+ export function hasFlag(k) {
10
+ return args.includes(k) || args.includes(k.charAt(1) === "-" ? k : k.substring(0, 2));
11
+ }
@@ -0,0 +1,16 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const pkgPath = path.resolve(__dirname, "../../package.json");
6
+ export const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
7
+ export function getRootPath() {
8
+ // Priority: --root CLI arg → NEXUS_ROOT env → ~/.n2n-nexus
9
+ const argIndex = process.argv.indexOf("--root");
10
+ if (argIndex !== -1 && process.argv[argIndex + 1]) {
11
+ return process.argv[argIndex + 1];
12
+ }
13
+ if (process.env.NEXUS_ROOT)
14
+ return process.env.NEXUS_ROOT;
15
+ return path.join(process.env.HOME || process.env.USERPROFILE || ".", ".n2n-nexus");
16
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Path Resolution Logic
3
+ */
4
+ import path from "path";
5
+ import os from "os";
6
+ import { SERVICE_NAME } from "../constants.js";
7
+ import { getArg } from "./cli.js";
8
+ /**
9
+ * Normalize and resolve the root storage path
10
+ */
11
+ export function normalizeRootPath(inputPath) {
12
+ // Priority: CLI --root > ENV NEXUS_ROOT > System Default
13
+ let root = inputPath || process.env.NEXUS_ROOT || getDefaultDataDir();
14
+ // Resolve ~ to home directory
15
+ if (root.startsWith("~")) {
16
+ root = path.join(os.homedir(), root.slice(1));
17
+ }
18
+ // Cross-platform adaptation (WSL <-> Windows)
19
+ if (process.platform === "linux" && /^[a-zA-Z]:[/\\]/.test(root)) {
20
+ const drive = root[0].toLowerCase();
21
+ root = `/mnt/${drive}${root.slice(2).replace(/\\/g, "/")}`;
22
+ }
23
+ return path.resolve(root);
24
+ }
25
+ /**
26
+ * Get the default data directory
27
+ */
28
+ export function getDefaultDataDir() {
29
+ const home = os.homedir();
30
+ // Use ~/.n2n-nexus for all platforms (developer-friendly convention)
31
+ return path.join(home, `.${SERVICE_NAME}`);
32
+ }
33
+ /**
34
+ * Get the root storage path from CLI or environment
35
+ */
36
+ export function getRootPath() {
37
+ return normalizeRootPath(getArg("--root"));
38
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Nexus Network Constants
3
+ *
4
+ * Centralized configuration for network-related settings.
5
+ */
6
+ // Service identification
7
+ export const SERVICE_NAME = "n2n-nexus";
8
+ // Host address for binding and connecting.
9
+ // Default to localhost because the daemon exposes unauthenticated local admin APIs.
10
+ // Use "0.0.0.0" only when you intentionally expose it to another trusted environment.
11
+ export const NEXUS_HOST = "127.0.0.1";
12
+ // Port range for auto-election
13
+ export const PORT_RANGE_START = 5688;
14
+ export const PORT_RANGE_END = 5800;
15
+ // Timeouts (milliseconds)
16
+ export const HANDSHAKE_TIMEOUT = 200;
17
+ export const HEARTBEAT_INTERVAL = 30000;
18
+ // Task cleanup
19
+ export const TASK_CLEANUP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
20
+ // File I/O
21
+ export const FILE_ENCODING = "utf-8";
22
+ export const PACKAGE_JSON = "package.json";
@@ -0,0 +1,41 @@
1
+ import { createDaemonServer } from "./server.js";
2
+ import { pkg, getRootPath } from "../config/index.js";
3
+ import { NEXUS_HOST } from "../constants.js";
4
+ function parsePort() {
5
+ const argIndex = process.argv.indexOf("--port");
6
+ if (argIndex !== -1) {
7
+ const parsed = parseInt(process.argv[argIndex + 1] || "", 10);
8
+ if (!isNaN(parsed) && parsed > 0)
9
+ return parsed;
10
+ }
11
+ const envPort = parseInt(process.env.NEXUS_DAEMON_PORT || process.env.NEXUS_PORT || "", 10);
12
+ if (!isNaN(envPort) && envPort > 0)
13
+ return envPort;
14
+ return 5688;
15
+ }
16
+ function parseHost() {
17
+ const argIndex = process.argv.indexOf("--host");
18
+ if (argIndex !== -1 && process.argv[argIndex + 1]) {
19
+ return process.argv[argIndex + 1];
20
+ }
21
+ return process.env.NEXUS_HOST || NEXUS_HOST;
22
+ }
23
+ export async function runDaemon() {
24
+ const port = parsePort();
25
+ const host = parseHost();
26
+ const root = getRootPath();
27
+ // Inject root path into CONFIG via env so StorageManager picks it up
28
+ process.env.NEXUS_ROOT = root;
29
+ const { server, storageInfo } = await createDaemonServer({ port, host, version: pkg.version });
30
+ await new Promise((resolve, reject) => {
31
+ server.once("error", reject);
32
+ server.listen(port, host, () => {
33
+ console.error(`[n2n-nexus] Daemon v${pkg.version} listening on http://${host}:${port}`);
34
+ console.error(`[n2n-nexus] Storage: ${root} (${storageInfo.storageMode}${storageInfo.isDegraded ? ", degraded" : ""})`);
35
+ resolve();
36
+ });
37
+ });
38
+ process.once("SIGINT", () => { console.error("[n2n-nexus] Shutting down."); server.close(() => process.exit(0)); });
39
+ process.once("SIGTERM", () => { server.close(() => process.exit(0)); });
40
+ console.error("[n2n-nexus] Ready. Press Ctrl+C to stop.");
41
+ }