@datafrog-io/n2n-nexus 0.2.0 → 0.2.1
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 +24 -13
- package/build/resources/index.js +19 -27
- package/build/storage/index.js +64 -9
- package/build/storage/sqlite-meeting.js +38 -8
- package/build/storage/sqlite.js +10 -0
- package/build/storage/store.js +11 -4
- package/build/tools/definitions.js +295 -0
- package/build/tools/handlers.js +19 -8
- package/build/tools/index.js +6 -9
- package/build/tools/schemas.js +6 -3
- package/docs/ASSISTANT_GUIDE.md +23 -17
- package/docs/CHANGELOG_zh.md +50 -166
- package/{README_zh.md → docs/README_zh.md} +26 -15
- package/docs/TODO_zh.md +71 -0
- package/package.json +2 -2
- package/docs/CHANGELOG.md +0 -170
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
> **Works with:** Claude Code · Claude Desktop · VS Code · Cursor · Windsurf · Zed · JetBrains · Theia · Google Antigravity
|
|
12
12
|
|
|
13
|
+
📖 **Documentation:** [CHANGELOG](CHANGELOG.md) | [TODO](TODO.md) | [中文文档](docs/README_zh.md) | [AI Assistant Guide](docs/ASSISTANT_GUIDE.md)
|
|
14
|
+
|
|
13
15
|
## 🏛️ Architecture
|
|
14
16
|
|
|
15
17
|
1. **Nexus Room (Discussion)**: Unified public channel for all IDE assistants to coordinate across projects.
|
|
@@ -31,19 +33,21 @@ Nexus stores all data in the local file system (customizable path), ensuring com
|
|
|
31
33
|
Nexus_Storage/
|
|
32
34
|
├── global/
|
|
33
35
|
│ ├── blueprint.md # Master Strategy
|
|
34
|
-
│ ├── discussion.json # Chat History
|
|
35
|
-
│ ├── docs_index.json # Global Docs
|
|
36
|
+
│ ├── discussion.json # Global Chat History (fallback)
|
|
37
|
+
│ ├── docs_index.json # Global Docs Index
|
|
36
38
|
│ └── docs/ # Global Markdown Docs
|
|
37
39
|
│ ├── coding-standards.md
|
|
38
40
|
│ └── deployment-flow.md
|
|
39
41
|
├── projects/
|
|
40
|
-
│
|
|
41
|
-
│
|
|
42
|
-
│
|
|
43
|
-
│
|
|
44
|
-
|
|
42
|
+
│ └── {project-id}/
|
|
43
|
+
│ ├── manifest.json # Project Metadata
|
|
44
|
+
│ ├── internal_blueprint.md # Technical Implementation Docs
|
|
45
|
+
│ └── assets/ # Binary Assets (images, PDFs)
|
|
46
|
+
├── meetings/ # Meeting files (JSON fallback mode)
|
|
47
|
+
│ └── {meeting-id}.json
|
|
45
48
|
├── registry.json # Global Project Index
|
|
46
|
-
|
|
49
|
+
├── archives/ # Reserved for backups
|
|
50
|
+
└── nexus.db # SQLite Database (meetings, tasks, state)
|
|
47
51
|
```
|
|
48
52
|
|
|
49
53
|
**Self-healing**: Core data files (e.g., `registry.json`, `discussion.json`) include automatic detection and repair mechanisms. If files are corrupted or missing, the system automatically rebuilds the initial state to ensure uninterrupted service.
|
|
@@ -89,9 +93,9 @@ To ensure clarity and prevent collisions in the flat local namespace, all Projec
|
|
|
89
93
|
|
|
90
94
|
### C. Global Collaboration
|
|
91
95
|
- `send_message`: Post a message to the team (Auto-routes to active meeting).
|
|
92
|
-
- `read_messages`:
|
|
96
|
+
- `read_messages`: **[Incremental]** Returns only unread messages per IDE instance. Server tracks read cursor automatically.
|
|
93
97
|
- `update_global_strategy`: Update the core strategic blueprint (`# Master Plan`).
|
|
94
|
-
- `get_global_topology`:
|
|
98
|
+
- `get_global_topology`: **[Progressive]** Default: summary list. With `projectId`: detailed subgraph.
|
|
95
99
|
- `sync_global_doc`: Create or update a shared cross-project document.
|
|
96
100
|
|
|
97
101
|
### D. Meeting Management
|
|
@@ -113,14 +117,21 @@ To ensure clarity and prevent collisions in the flat local namespace, all Projec
|
|
|
113
117
|
|
|
114
118
|
## 📄 Resources (URI)
|
|
115
119
|
|
|
120
|
+
**Core Resources (Static):**
|
|
116
121
|
- `mcp://nexus/chat/global`: Real-time conversation history.
|
|
117
|
-
- `mcp://nexus/hub/registry`: Global project registry
|
|
122
|
+
- `mcp://nexus/hub/registry`: Global project registry - **read this first to discover project IDs**.
|
|
118
123
|
- `mcp://nexus/docs/global-strategy`: Strategic blueprint.
|
|
124
|
+
- `mcp://nexus/docs/list`: Index of shared documents.
|
|
125
|
+
- `mcp://nexus/meetings/list`: List of active and closed meetings.
|
|
119
126
|
- `mcp://nexus/session`: Current session status and identity.
|
|
120
127
|
- `mcp://nexus/status`: System operational status and storage mode.
|
|
121
128
|
- `mcp://nexus/active-meeting`: Real-time transcript of the current active meeting.
|
|
122
|
-
|
|
123
|
-
|
|
129
|
+
|
|
130
|
+
**Resource Templates (Use registry to discover IDs):**
|
|
131
|
+
- `mcp://nexus/projects/{projectId}/manifest`: Full metadata for a specific project.
|
|
132
|
+
- `mcp://nexus/projects/{projectId}/internal-docs`: Internal technical docs for a project.
|
|
133
|
+
- `mcp://nexus/docs/{docId}`: Read a specific shared document.
|
|
134
|
+
- `mcp://nexus/meetings/{meetingId}`: Full transcript for a specific meeting.
|
|
124
135
|
|
|
125
136
|
## 🚀 Quick Start
|
|
126
137
|
|
package/build/resources/index.js
CHANGED
|
@@ -33,7 +33,7 @@ export async function getResourceContent(uri, currentProject) {
|
|
|
33
33
|
const storageInfo = await UnifiedMeetingStore.getStorageInfo();
|
|
34
34
|
const status = {
|
|
35
35
|
status: "online",
|
|
36
|
-
version: "0.2.
|
|
36
|
+
version: "0.2.1",
|
|
37
37
|
...storageInfo,
|
|
38
38
|
active_meetings_count: state.activeMeetings.length,
|
|
39
39
|
default_meeting: state.defaultMeetingId
|
|
@@ -89,39 +89,31 @@ export async function getResourceContent(uri, currentProject) {
|
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
91
|
* Returns the list of available resources for MCP ListResourcesRequestSchema
|
|
92
|
+
*
|
|
93
|
+
* OPTIMIZATION: No longer lists individual projects dynamically.
|
|
94
|
+
* Uses resourceTemplates instead - AI should query registry first.
|
|
92
95
|
*/
|
|
93
96
|
export async function listResources() {
|
|
94
97
|
const registry = await StorageManager.listRegistry();
|
|
95
|
-
const
|
|
98
|
+
const projectCount = Object.keys(registry.projects).length;
|
|
96
99
|
return {
|
|
97
100
|
resources: [
|
|
98
|
-
|
|
99
|
-
{ uri: "mcp://nexus/
|
|
100
|
-
{ uri: "mcp://nexus/
|
|
101
|
-
{ uri: "mcp://nexus/docs/
|
|
102
|
-
{ uri: "mcp://nexus/
|
|
103
|
-
{ uri: "mcp://nexus/
|
|
104
|
-
{ uri: "mcp://nexus/
|
|
105
|
-
{ uri: "mcp://nexus/
|
|
106
|
-
|
|
107
|
-
const prefix = id.split("_")[0];
|
|
108
|
-
const typeLabel = {
|
|
109
|
-
web: "🌐 Website", api: "⚙️ API", chrome: "🧩 Chrome Ext",
|
|
110
|
-
vscode: "💻 VSCode Ext", mcp: "🔌 MCP Server", android: "📱 Android",
|
|
111
|
-
ios: "🍎 iOS", flutter: "📲 Flutter", desktop: "🖥️ Desktop",
|
|
112
|
-
lib: "📦 Library", bot: "🤖 Bot", infra: "☁️ Infra", doc: "📄 Docs"
|
|
113
|
-
}[prefix] || "📁 Project";
|
|
114
|
-
return {
|
|
115
|
-
uri: `mcp://nexus/projects/${id}/manifest`,
|
|
116
|
-
name: `${typeLabel}: ${id}`,
|
|
117
|
-
description: `Structured metadata (Tech stack, relations) for ${id}`
|
|
118
|
-
};
|
|
119
|
-
})
|
|
101
|
+
// Core resources (static, always available)
|
|
102
|
+
{ uri: "mcp://nexus/chat/global", name: "Global Chat", description: "Real-time discussion stream." },
|
|
103
|
+
{ uri: "mcp://nexus/hub/registry", name: "Project Registry", description: `Index of ${projectCount} registered projects. Read this first to discover project IDs.` },
|
|
104
|
+
{ uri: "mcp://nexus/docs/list", name: "Docs Index", description: "List of shared cross-project documents." },
|
|
105
|
+
{ uri: "mcp://nexus/docs/global-strategy", name: "Strategy Blueprint", description: "Top-level coordination document." },
|
|
106
|
+
{ uri: "mcp://nexus/meetings/list", name: "Meetings List", description: "Active and closed meetings." },
|
|
107
|
+
{ uri: "mcp://nexus/session", name: "Session Info", description: "Your identity and role." },
|
|
108
|
+
{ uri: "mcp://nexus/status", name: "System Status", description: "Storage mode and active meeting count." },
|
|
109
|
+
{ uri: "mcp://nexus/active-meeting", name: "Active Meeting", description: "Current default meeting transcript." }
|
|
120
110
|
],
|
|
121
111
|
resourceTemplates: [
|
|
122
|
-
|
|
123
|
-
{ uriTemplate: "mcp://nexus/
|
|
124
|
-
{ uriTemplate: "mcp://nexus/
|
|
112
|
+
// Project resources - use registry to discover IDs first
|
|
113
|
+
{ uriTemplate: "mcp://nexus/projects/{projectId}/manifest", name: "Project Manifest", description: "Metadata for a specific project. Get projectId from registry." },
|
|
114
|
+
{ uriTemplate: "mcp://nexus/projects/{projectId}/internal-docs", name: "Project Docs", description: "Internal implementation guide for a project." },
|
|
115
|
+
{ uriTemplate: "mcp://nexus/docs/{docId}", name: "Global Doc", description: "Read a specific shared document." },
|
|
116
|
+
{ uriTemplate: "mcp://nexus/meetings/{meetingId}", name: "Meeting Details", description: "Full transcript for a specific meeting." }
|
|
125
117
|
]
|
|
126
118
|
};
|
|
127
119
|
}
|
package/build/storage/index.js
CHANGED
|
@@ -104,21 +104,76 @@ export class StorageManager {
|
|
|
104
104
|
await fs.writeFile(path.join(assetDir, fileName), content);
|
|
105
105
|
return path.join("projects", id, "assets", fileName);
|
|
106
106
|
}
|
|
107
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Calculate project dependency topology.
|
|
109
|
+
*
|
|
110
|
+
* Context7-style progressive loading:
|
|
111
|
+
* - Default: Returns summary (project list + stats) - lightweight
|
|
112
|
+
* - With projectId: Returns detailed subgraph for that project
|
|
113
|
+
*/
|
|
114
|
+
static async calculateTopology(projectId) {
|
|
108
115
|
const registry = await this.listRegistry();
|
|
109
116
|
const projectIds = Object.keys(registry.projects);
|
|
110
|
-
|
|
111
|
-
const
|
|
117
|
+
// Collect manifests
|
|
118
|
+
const manifests = new Map();
|
|
119
|
+
let totalEdges = 0;
|
|
112
120
|
for (const id of projectIds) {
|
|
113
121
|
const manifest = await this.getProjectManifest(id);
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
if (manifest) {
|
|
123
|
+
manifests.set(id, manifest);
|
|
124
|
+
totalEdges += (manifest.relations || []).length;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// === FOCUSED MODE: Return detailed subgraph ===
|
|
128
|
+
if (projectId) {
|
|
129
|
+
const targetManifest = manifests.get(projectId);
|
|
130
|
+
if (!targetManifest) {
|
|
131
|
+
return { mode: "focused", projectId, error: "Project not found", nodes: [], edges: [] };
|
|
132
|
+
}
|
|
133
|
+
const nodes = [];
|
|
134
|
+
const edges = [];
|
|
135
|
+
// Add target node
|
|
136
|
+
nodes.push({ id: projectId, name: targetManifest.name });
|
|
137
|
+
// Outgoing relations
|
|
138
|
+
const connectedIds = new Set();
|
|
139
|
+
(targetManifest.relations || []).forEach(rel => {
|
|
140
|
+
edges.push({ from: projectId, to: rel.targetId, type: rel.type });
|
|
141
|
+
connectedIds.add(rel.targetId);
|
|
119
142
|
});
|
|
143
|
+
// Incoming relations
|
|
144
|
+
for (const [id, manifest] of manifests) {
|
|
145
|
+
if (id === projectId)
|
|
146
|
+
continue;
|
|
147
|
+
(manifest.relations || []).forEach(rel => {
|
|
148
|
+
if (rel.targetId === projectId) {
|
|
149
|
+
edges.push({ from: id, to: projectId, type: rel.type });
|
|
150
|
+
connectedIds.add(id);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// Add connected nodes
|
|
155
|
+
for (const id of connectedIds) {
|
|
156
|
+
const m = manifests.get(id);
|
|
157
|
+
if (m)
|
|
158
|
+
nodes.push({ id, name: m.name });
|
|
159
|
+
}
|
|
160
|
+
return { mode: "focused", projectId, nodes, edges };
|
|
120
161
|
}
|
|
121
|
-
|
|
162
|
+
// === LIST MODE: Return lightweight summary ===
|
|
163
|
+
const projects = Array.from(manifests.entries()).map(([id, m]) => ({
|
|
164
|
+
id,
|
|
165
|
+
name: m.name,
|
|
166
|
+
relationsCount: (m.relations || []).length
|
|
167
|
+
}));
|
|
168
|
+
return {
|
|
169
|
+
mode: "list",
|
|
170
|
+
summary: {
|
|
171
|
+
totalProjects: projects.length,
|
|
172
|
+
totalEdges,
|
|
173
|
+
hint: "Call with projectId to get detailed subgraph"
|
|
174
|
+
},
|
|
175
|
+
projects
|
|
176
|
+
};
|
|
122
177
|
}
|
|
123
178
|
// --- Discussion & Log Management ---
|
|
124
179
|
/**
|
|
@@ -233,21 +233,51 @@ export class SqliteMeetingStore {
|
|
|
233
233
|
return this.getMeeting(defaultId);
|
|
234
234
|
}
|
|
235
235
|
/**
|
|
236
|
-
* Get recent messages from a meeting
|
|
236
|
+
* Get recent messages from a meeting (incremental read based on instance cursor)
|
|
237
|
+
* Automatically returns only unread messages for the current instance.
|
|
237
238
|
*/
|
|
238
|
-
static getRecentMessages(count = 10, meetingId) {
|
|
239
|
+
static getRecentMessages(count = 10, meetingId, instanceId) {
|
|
239
240
|
const db = getDatabase();
|
|
240
241
|
const targetId = meetingId || this.getState("default_meeting");
|
|
241
242
|
if (!targetId)
|
|
242
243
|
return [];
|
|
244
|
+
// If no instanceId provided, fall back to returning all recent messages
|
|
245
|
+
if (!instanceId) {
|
|
246
|
+
const stmt = db.prepare(`
|
|
247
|
+
SELECT id, sender as "from", text, category, timestamp
|
|
248
|
+
FROM messages WHERE meeting_id = ?
|
|
249
|
+
ORDER BY id DESC LIMIT ?
|
|
250
|
+
`);
|
|
251
|
+
const messages = stmt.all(targetId, count);
|
|
252
|
+
return messages.reverse();
|
|
253
|
+
}
|
|
254
|
+
// Get cursor for this instance + meeting
|
|
255
|
+
const cursorStmt = db.prepare(`
|
|
256
|
+
SELECT last_read_id FROM read_cursors
|
|
257
|
+
WHERE instance_id = ? AND context_type = 'meeting' AND context_id = ?
|
|
258
|
+
`);
|
|
259
|
+
const cursorRow = cursorStmt.get(instanceId, targetId);
|
|
260
|
+
const lastReadId = cursorRow?.last_read_id || 0;
|
|
261
|
+
// Fetch unread messages (id > lastReadId)
|
|
243
262
|
const stmt = db.prepare(`
|
|
244
|
-
SELECT sender as "from", text, category, timestamp
|
|
245
|
-
FROM messages
|
|
246
|
-
|
|
263
|
+
SELECT id, sender as "from", text, category, timestamp
|
|
264
|
+
FROM messages
|
|
265
|
+
WHERE meeting_id = ? AND id > ?
|
|
266
|
+
ORDER BY id ASC
|
|
267
|
+
LIMIT ?
|
|
247
268
|
`);
|
|
248
|
-
const messages = stmt.all(targetId, count);
|
|
249
|
-
//
|
|
250
|
-
|
|
269
|
+
const messages = stmt.all(targetId, lastReadId, count);
|
|
270
|
+
// Update cursor if we got messages
|
|
271
|
+
if (messages.length > 0) {
|
|
272
|
+
const maxId = messages[messages.length - 1].id;
|
|
273
|
+
const now = new Date().toISOString();
|
|
274
|
+
const updateStmt = db.prepare(`
|
|
275
|
+
INSERT OR REPLACE INTO read_cursors (instance_id, context_type, context_id, last_read_id, updated_at)
|
|
276
|
+
VALUES (?, 'meeting', ?, ?, ?)
|
|
277
|
+
`);
|
|
278
|
+
updateStmt.run(instanceId, targetId, maxId, now);
|
|
279
|
+
}
|
|
280
|
+
return messages;
|
|
251
281
|
}
|
|
252
282
|
/**
|
|
253
283
|
* Get meeting state value
|
package/build/storage/sqlite.js
CHANGED
|
@@ -50,6 +50,16 @@ CREATE INDEX IF NOT EXISTS idx_messages_meeting ON messages(meeting_id);
|
|
|
50
50
|
CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON messages(timestamp);
|
|
51
51
|
CREATE INDEX IF NOT EXISTS idx_decisions_meeting ON decisions(meeting_id);
|
|
52
52
|
CREATE INDEX IF NOT EXISTS idx_meetings_status ON meetings(status);
|
|
53
|
+
|
|
54
|
+
-- Read cursors table (tracks each IDE's last read message per context)
|
|
55
|
+
CREATE TABLE IF NOT EXISTS read_cursors (
|
|
56
|
+
instance_id TEXT NOT NULL,
|
|
57
|
+
context_type TEXT NOT NULL CHECK(context_type IN ('meeting', 'global')),
|
|
58
|
+
context_id TEXT,
|
|
59
|
+
last_read_id INTEGER DEFAULT 0,
|
|
60
|
+
updated_at TEXT,
|
|
61
|
+
PRIMARY KEY (instance_id, context_type, context_id)
|
|
62
|
+
);
|
|
53
63
|
`;
|
|
54
64
|
/**
|
|
55
65
|
* Get the database file path
|
package/build/storage/store.js
CHANGED
|
@@ -101,11 +101,18 @@ export const UnifiedMeetingStore = {
|
|
|
101
101
|
return store.getActiveMeeting();
|
|
102
102
|
},
|
|
103
103
|
/**
|
|
104
|
-
* Get recent messages
|
|
104
|
+
* Get recent messages (incremental for SQLite with instanceId)
|
|
105
105
|
*/
|
|
106
|
-
async getRecentMessages(count, meetingId) {
|
|
107
|
-
const { store } = await getStore();
|
|
108
|
-
|
|
106
|
+
async getRecentMessages(count, meetingId, instanceId) {
|
|
107
|
+
const { type, store } = await getStore();
|
|
108
|
+
if (type === "sqlite") {
|
|
109
|
+
// SQLite supports incremental reads with cursor tracking
|
|
110
|
+
return store.getRecentMessages(count || 10, meetingId, instanceId);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// JSON fallback - no incremental support
|
|
114
|
+
return store.getRecentMessages(count || 10, meetingId);
|
|
115
|
+
}
|
|
109
116
|
},
|
|
110
117
|
/**
|
|
111
118
|
* Get the current backend type
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimized Tool Definitions for Token Economy
|
|
3
|
+
*
|
|
4
|
+
* This file provides minimal, human-readable tool definitions that are
|
|
5
|
+
* directly used by MCP ListTools. We avoid Zod's toJSONSchema() which
|
|
6
|
+
* generates verbose output with redundant $schema declarations.
|
|
7
|
+
*
|
|
8
|
+
* KEY OPTIMIZATIONS:
|
|
9
|
+
* 1. No $schema on every inputSchema (saves ~50 chars/tool)
|
|
10
|
+
* 2. Concise descriptions (half the length of originals)
|
|
11
|
+
* 3. Internal tools (update_task) hidden from public listing
|
|
12
|
+
* 4. No additionalProperties spam
|
|
13
|
+
*/
|
|
14
|
+
// Hidden tools that should not be exposed to clients
|
|
15
|
+
const HIDDEN_TOOLS = new Set(["update_task"]);
|
|
16
|
+
/**
|
|
17
|
+
* Minimal tool definitions optimized for token economy.
|
|
18
|
+
* Each tool has a concise description and a compact inputSchema.
|
|
19
|
+
*/
|
|
20
|
+
const ALL_TOOLS = [
|
|
21
|
+
// --- Session & Identity ---
|
|
22
|
+
{
|
|
23
|
+
name: "register_session_context",
|
|
24
|
+
description: "Declare active project. Format: [prefix]_[name] (e.g., 'web_example.com', 'mcp_nexus').",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
projectId: { type: "string", description: "Project ID with prefix (web_, api_, mcp_, etc.)" }
|
|
29
|
+
},
|
|
30
|
+
required: ["projectId"]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
// --- Project Assets ---
|
|
34
|
+
{
|
|
35
|
+
name: "sync_project_assets",
|
|
36
|
+
description: "[ASYNC] Sync full project manifest + internal docs. Returns taskId.",
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
manifest: {
|
|
41
|
+
type: "object",
|
|
42
|
+
description: "Project metadata (id, name, description, techStack, relations, endpoints, apiSpec)",
|
|
43
|
+
properties: {
|
|
44
|
+
id: { type: "string" },
|
|
45
|
+
name: { type: "string" },
|
|
46
|
+
description: { type: "string" },
|
|
47
|
+
techStack: { type: "array", items: { type: "string" } },
|
|
48
|
+
relations: { type: "array", items: { type: "object" } },
|
|
49
|
+
repositoryUrl: { type: "string" },
|
|
50
|
+
localPath: { type: "string" },
|
|
51
|
+
endpoints: { type: "array", items: { type: "object" } },
|
|
52
|
+
apiSpec: { type: "array", items: { type: "object" } }
|
|
53
|
+
},
|
|
54
|
+
required: ["id", "name", "description", "techStack", "relations", "repositoryUrl", "localPath", "endpoints", "apiSpec"]
|
|
55
|
+
},
|
|
56
|
+
internalDocs: { type: "string", description: "Markdown implementation guide" }
|
|
57
|
+
},
|
|
58
|
+
required: ["manifest", "internalDocs"]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "upload_project_asset",
|
|
63
|
+
description: "Upload binary file (base64) to active project's asset folder.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: "object",
|
|
66
|
+
properties: {
|
|
67
|
+
fileName: { type: "string", description: "Safe filename (no path traversal)" },
|
|
68
|
+
base64Content: { type: "string" }
|
|
69
|
+
},
|
|
70
|
+
required: ["fileName", "base64Content"]
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "update_project",
|
|
75
|
+
description: "Patch project manifest fields (partial update).",
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
projectId: { type: "string" },
|
|
80
|
+
patch: { type: "object", description: "Fields to update" }
|
|
81
|
+
},
|
|
82
|
+
required: ["projectId", "patch"]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "rename_project",
|
|
87
|
+
description: "[ASYNC] Rename project ID with cascading relation updates. Returns taskId.",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
oldId: { type: "string" },
|
|
92
|
+
newId: { type: "string" }
|
|
93
|
+
},
|
|
94
|
+
required: ["oldId", "newId"]
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
// --- Global Collaboration ---
|
|
98
|
+
{
|
|
99
|
+
name: "get_global_topology",
|
|
100
|
+
description: "Default: project list + stats. With projectId: detailed subgraph.",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
projectId: { type: "string", description: "Focus on specific project (optional)" }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "send_message",
|
|
110
|
+
description: "Post message to active meeting or global chat.",
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: "object",
|
|
113
|
+
properties: {
|
|
114
|
+
message: { type: "string" },
|
|
115
|
+
category: { type: "string", enum: ["MEETING_START", "PROPOSAL", "DECISION", "UPDATE", "CHAT"] }
|
|
116
|
+
},
|
|
117
|
+
required: ["message"]
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "read_messages",
|
|
122
|
+
description: "Read unread messages (auto-incremental per IDE instance).",
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: {
|
|
126
|
+
count: { type: "integer", default: 10 },
|
|
127
|
+
meetingId: { type: "string" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "update_global_strategy",
|
|
133
|
+
description: "Overwrite master strategy document.",
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: "object",
|
|
136
|
+
properties: {
|
|
137
|
+
content: { type: "string" }
|
|
138
|
+
},
|
|
139
|
+
required: ["content"]
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "sync_global_doc",
|
|
144
|
+
description: "Create/update a global shared document.",
|
|
145
|
+
inputSchema: {
|
|
146
|
+
type: "object",
|
|
147
|
+
properties: {
|
|
148
|
+
docId: { type: "string" },
|
|
149
|
+
title: { type: "string" },
|
|
150
|
+
content: { type: "string" }
|
|
151
|
+
},
|
|
152
|
+
required: ["docId", "title", "content"]
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
// --- Meeting Management ---
|
|
156
|
+
{
|
|
157
|
+
name: "start_meeting",
|
|
158
|
+
description: "Start new meeting session. Returns meeting ID.",
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {
|
|
162
|
+
topic: { type: "string" }
|
|
163
|
+
},
|
|
164
|
+
required: ["topic"]
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "end_meeting",
|
|
169
|
+
description: "[Moderator] End active meeting. Locks history.",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: "object",
|
|
172
|
+
properties: {
|
|
173
|
+
meetingId: { type: "string" },
|
|
174
|
+
summary: { type: "string" }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "archive_meeting",
|
|
180
|
+
description: "[Moderator] Archive closed meeting. Read-only after.",
|
|
181
|
+
inputSchema: {
|
|
182
|
+
type: "object",
|
|
183
|
+
properties: {
|
|
184
|
+
meetingId: { type: "string" }
|
|
185
|
+
},
|
|
186
|
+
required: ["meetingId"]
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "reopen_meeting",
|
|
191
|
+
description: "Reopen closed/archived meeting.",
|
|
192
|
+
inputSchema: {
|
|
193
|
+
type: "object",
|
|
194
|
+
properties: {
|
|
195
|
+
meetingId: { type: "string" }
|
|
196
|
+
},
|
|
197
|
+
required: ["meetingId"]
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
// --- Task Management (Phase 2) ---
|
|
201
|
+
{
|
|
202
|
+
name: "create_task",
|
|
203
|
+
description: "[ASYNC] Create background task. Returns taskId for polling.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
source_meeting_id: { type: "string", description: "Link to meeting for traceability" },
|
|
208
|
+
metadata: { type: "object" },
|
|
209
|
+
ttl: { type: "integer", description: "TTL in milliseconds" }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
name: "get_task",
|
|
215
|
+
description: "Get task status and progress by ID.",
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: "object",
|
|
218
|
+
properties: {
|
|
219
|
+
taskId: { type: "string" }
|
|
220
|
+
},
|
|
221
|
+
required: ["taskId"]
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "list_tasks",
|
|
226
|
+
description: "List tasks with optional status filter.",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
status: { type: "string", enum: ["pending", "running", "completed", "failed", "cancelled"] },
|
|
231
|
+
limit: { type: "integer", default: 50 }
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: "update_task",
|
|
237
|
+
description: "[INTERNAL] Update task state. For workers only.",
|
|
238
|
+
inputSchema: {
|
|
239
|
+
type: "object",
|
|
240
|
+
properties: {
|
|
241
|
+
taskId: { type: "string" },
|
|
242
|
+
status: { type: "string", enum: ["pending", "running", "completed", "failed", "cancelled"] },
|
|
243
|
+
progress: { type: "number", minimum: 0, maximum: 1 },
|
|
244
|
+
result_uri: { type: "string" },
|
|
245
|
+
error_message: { type: "string" }
|
|
246
|
+
},
|
|
247
|
+
required: ["taskId"]
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: "cancel_task",
|
|
252
|
+
description: "Cancel pending/running task.",
|
|
253
|
+
inputSchema: {
|
|
254
|
+
type: "object",
|
|
255
|
+
properties: {
|
|
256
|
+
taskId: { type: "string" }
|
|
257
|
+
},
|
|
258
|
+
required: ["taskId"]
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
// --- Admin (Moderator Only) ---
|
|
262
|
+
{
|
|
263
|
+
name: "moderator_maintenance",
|
|
264
|
+
description: "[Moderator] Manage logs: 'prune' oldest N or 'clear' all.",
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: "object",
|
|
267
|
+
properties: {
|
|
268
|
+
action: { type: "string", enum: ["prune", "clear"] },
|
|
269
|
+
count: { type: "integer", minimum: 0 }
|
|
270
|
+
},
|
|
271
|
+
required: ["action", "count"]
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "moderator_delete_project",
|
|
276
|
+
description: "[ASYNC][Moderator] Delete project. Irreversible. Returns taskId.",
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: "object",
|
|
279
|
+
properties: {
|
|
280
|
+
projectId: { type: "string" }
|
|
281
|
+
},
|
|
282
|
+
required: ["projectId"]
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
];
|
|
286
|
+
/**
|
|
287
|
+
* Public tool definitions (excludes internal tools).
|
|
288
|
+
* This is what gets returned to MCP ListTools.
|
|
289
|
+
*/
|
|
290
|
+
export const TOOL_DEFINITIONS = ALL_TOOLS.filter(tool => !HIDDEN_TOOLS.has(tool.name));
|
|
291
|
+
/**
|
|
292
|
+
* Full tool definitions (includes internal tools).
|
|
293
|
+
* Used internally for handler registration.
|
|
294
|
+
*/
|
|
295
|
+
export const ALL_TOOL_DEFINITIONS = ALL_TOOLS;
|