@datafrog-io/n2n-nexus 0.2.0 → 0.3.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/README.md +59 -81
- package/build/config.js +176 -4
- package/build/index.js +133 -16
- package/build/resources/index.js +21 -29
- 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 +38 -27
- package/build/tools/index.js +6 -9
- package/build/tools/schemas.js +18 -15
- package/build/utils/auth.js +5 -5
- package/docs/ARCHITECTURE.md +63 -0
- package/docs/ARCHITECTURE_zh.md +43 -0
- package/docs/ASSISTANT_GUIDE.md +23 -17
- package/docs/CHANGELOG_zh.md +94 -166
- package/{README_zh.md → docs/README_zh.md} +60 -57
- package/docs/TODO_zh.md +71 -0
- package/package.json +2 -2
- package/docs/CHANGELOG.md +0 -170
package/build/resources/index.js
CHANGED
|
@@ -22,8 +22,8 @@ export async function getResourceContent(uri, currentProject) {
|
|
|
22
22
|
if (uri === "mcp://nexus/session") {
|
|
23
23
|
const info = {
|
|
24
24
|
yourId: CONFIG.instanceId,
|
|
25
|
-
role: CONFIG.
|
|
26
|
-
|
|
25
|
+
role: CONFIG.isHost ? "Host" : "Regular",
|
|
26
|
+
isHost: CONFIG.isHost,
|
|
27
27
|
activeProject: currentProject || "None"
|
|
28
28
|
};
|
|
29
29
|
return { mimeType: "application/json", text: JSON.stringify(info, null, 2) };
|
|
@@ -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: "[Host] 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: "[Host] 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
|
+
// --- Host (Host Only) ---
|
|
262
|
+
{
|
|
263
|
+
name: "host_maintenance",
|
|
264
|
+
description: "[Host] 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: "host_delete_project",
|
|
276
|
+
description: "[ASYNC][Host] 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;
|