@syndicai/stack-mcp 0.2.1 → 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 +10 -5
- package/ingest-source.js +4 -1
- package/memory-client.js +9 -0
- package/package.json +1 -1
- package/tools.js +84 -5
package/README.md
CHANGED
|
@@ -39,11 +39,16 @@ Monorepo / unpublished:
|
|
|
39
39
|
|
|
40
40
|
## Tools
|
|
41
41
|
|
|
42
|
-
| Tool
|
|
43
|
-
|
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
42
|
+
| Tool | Purpose |
|
|
43
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
44
|
+
| `memory_list_collections` | List collections (name, type, visibility, stats, soft-TTL hints) |
|
|
45
|
+
| `memory_search` | Hybrid retrieve (optional `collectionId`; omit = all) |
|
|
46
|
+
| `memory_ingest` | Start ingest async; **requires `collectionId`**; returns `jobId` (text/markdown/code only) |
|
|
47
|
+
| `memory_ingest_status` | Poll job until `completed` / `failed` |
|
|
48
|
+
| `memory_delete` | Delete documents by `sourceId` within a collection |
|
|
49
|
+
| `memory_delete_collection` | Delete an entire collection (not the default General) |
|
|
50
|
+
|
|
51
|
+
Soft TTL on collections is hint-only — tools never auto-delete. When ingesting, call `memory_list_collections` and ask the user which collection to use if ambiguous.
|
|
47
52
|
|
|
48
53
|
## Dev
|
|
49
54
|
|
package/ingest-source.js
CHANGED
|
@@ -24,11 +24,14 @@ export async function buildIngestBody(args) {
|
|
|
24
24
|
if (!args.sourceKey?.trim() || !args.contentType?.trim()) {
|
|
25
25
|
throw new Error('sourceKey and contentType are required');
|
|
26
26
|
}
|
|
27
|
+
if (!args.collectionId?.trim()) {
|
|
28
|
+
throw new Error('collectionId is required. Call memory_list_collections and ask the user which Memory collection to use.');
|
|
29
|
+
}
|
|
27
30
|
const base = {
|
|
28
31
|
sourceKey: args.sourceKey.trim(),
|
|
29
32
|
contentType: args.contentType.trim(),
|
|
30
33
|
uri: args.uri,
|
|
31
|
-
collectionId: args.collectionId,
|
|
34
|
+
collectionId: args.collectionId.trim(),
|
|
32
35
|
path: args.path,
|
|
33
36
|
};
|
|
34
37
|
if (args.path?.trim()) {
|
package/memory-client.js
CHANGED
|
@@ -50,6 +50,15 @@ export function createMemoryClient(config) {
|
|
|
50
50
|
getIngest(jobId) {
|
|
51
51
|
return request('GET', `/memory/ingest/${encodeURIComponent(jobId)}`);
|
|
52
52
|
},
|
|
53
|
+
listCollections() {
|
|
54
|
+
return request('GET', '/memory/collections');
|
|
55
|
+
},
|
|
56
|
+
deleteBySource(params) {
|
|
57
|
+
return request('POST', '/memory/delete', params);
|
|
58
|
+
},
|
|
59
|
+
deleteCollection(collectionId) {
|
|
60
|
+
return request('DELETE', `/memory/collections/${encodeURIComponent(collectionId)}`);
|
|
61
|
+
},
|
|
53
62
|
};
|
|
54
63
|
}
|
|
55
64
|
function extractErrorMessage(body) {
|
package/package.json
CHANGED
package/tools.js
CHANGED
|
@@ -5,6 +5,9 @@ export const MEMORY_MCP_TOOL_NAMES = [
|
|
|
5
5
|
'memory_search',
|
|
6
6
|
'memory_ingest',
|
|
7
7
|
'memory_ingest_status',
|
|
8
|
+
'memory_list_collections',
|
|
9
|
+
'memory_delete',
|
|
10
|
+
'memory_delete_collection',
|
|
8
11
|
];
|
|
9
12
|
function textResult(data) {
|
|
10
13
|
return {
|
|
@@ -42,8 +45,17 @@ export async function runMemorySearch(client, args) {
|
|
|
42
45
|
}
|
|
43
46
|
export async function runMemoryIngest(client, args) {
|
|
44
47
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
if (!args.collectionId?.trim()) {
|
|
49
|
+
return toolErrorResult(new Error('collectionId is required. Call memory_list_collections and ask the user which Memory collection to use.'));
|
|
50
|
+
}
|
|
51
|
+
const body = await buildIngestBody({
|
|
52
|
+
...args,
|
|
53
|
+
collectionId: args.collectionId.trim(),
|
|
54
|
+
});
|
|
55
|
+
const data = await client.ingest({
|
|
56
|
+
...body,
|
|
57
|
+
collectionId: args.collectionId.trim(),
|
|
58
|
+
});
|
|
47
59
|
return textResult(data);
|
|
48
60
|
}
|
|
49
61
|
catch (err) {
|
|
@@ -62,12 +74,48 @@ export async function runMemoryIngestStatus(client, args) {
|
|
|
62
74
|
return toolErrorResult(err);
|
|
63
75
|
}
|
|
64
76
|
}
|
|
77
|
+
export async function runMemoryListCollections(client) {
|
|
78
|
+
try {
|
|
79
|
+
const data = await client.listCollections();
|
|
80
|
+
return textResult(data);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
return toolErrorResult(err);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function runMemoryDelete(client, args) {
|
|
87
|
+
try {
|
|
88
|
+
if (!args.collectionId?.trim() || !args.sourceId?.trim()) {
|
|
89
|
+
return toolErrorResult(new Error('collectionId and sourceId are required'));
|
|
90
|
+
}
|
|
91
|
+
const data = await client.deleteBySource({
|
|
92
|
+
collectionId: args.collectionId.trim(),
|
|
93
|
+
sourceId: args.sourceId.trim(),
|
|
94
|
+
});
|
|
95
|
+
return textResult(data);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return toolErrorResult(err);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export async function runMemoryDeleteCollection(client, args) {
|
|
102
|
+
try {
|
|
103
|
+
if (!args.collectionId?.trim()) {
|
|
104
|
+
return toolErrorResult(new Error('collectionId is required'));
|
|
105
|
+
}
|
|
106
|
+
const data = await client.deleteCollection(args.collectionId.trim());
|
|
107
|
+
return textResult(data);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return toolErrorResult(err);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
65
113
|
/** Register Memory tools. Future Stack tools can be added here. */
|
|
66
114
|
export function registerStackMcpTools(server, client) {
|
|
67
115
|
server.registerTool('memory_search', {
|
|
68
116
|
title: 'Search Memory',
|
|
69
117
|
description: 'Hybrid search over the squad Memory corpus (vector + lexical). Returns ranked chunk hits with content and scores. ' +
|
|
70
|
-
'Query embeddings run on the stack-datalayer (CPU Granite).',
|
|
118
|
+
'Query embeddings run on the stack-datalayer (CPU Granite). Optional collectionId filters to one collection; omit to search all.',
|
|
71
119
|
inputSchema: {
|
|
72
120
|
query: z.string().describe('Natural-language search query'),
|
|
73
121
|
limit: z
|
|
@@ -82,10 +130,20 @@ export function registerStackMcpTools(server, client) {
|
|
|
82
130
|
.describe('Optional collection filter; omit to search all'),
|
|
83
131
|
},
|
|
84
132
|
}, async (args) => runMemorySearch(client, args));
|
|
133
|
+
server.registerTool('memory_list_collections', {
|
|
134
|
+
title: 'List Memory collections',
|
|
135
|
+
description: 'List Memory collections with id, name, type (library|scratch), visibility (shared|personal), ' +
|
|
136
|
+
'document/chunk stats, and soft-TTL hint fields (ttlExpiresAt, ttlExpired, cleanupHint). ' +
|
|
137
|
+
'Soft TTL never auto-deletes — use memory_delete / memory_delete_collection for cleanup. ' +
|
|
138
|
+
'Call this before memory_ingest and ask the user which collection to use when ambiguous. ' +
|
|
139
|
+
'Personal visibility is cooperative segregation only (not a security boundary).',
|
|
140
|
+
inputSchema: {},
|
|
141
|
+
}, async () => runMemoryListCollections(client));
|
|
85
142
|
server.registerTool('memory_ingest', {
|
|
86
143
|
title: 'Ingest into Memory',
|
|
87
144
|
description: 'Start a Memory ingest job asynchronously (chunk → embed → store). Returns immediately with jobId and status ' +
|
|
88
|
-
'(usually "running"; validation failures may be "cancelled").
|
|
145
|
+
'(usually "running"; validation failures may be "cancelled"). **Requires collectionId** — call memory_list_collections ' +
|
|
146
|
+
'first and ask the user which collection to use when ambiguous. Accepts text/markdown/code only — convert PDFs/images ' +
|
|
89
147
|
'to markdown on the client first. Prefer local `path` so file bytes never enter the model context; or pass inline `text`. ' +
|
|
90
148
|
'Embeddings run on the stack-datalayer (CPU Granite). Poll memory_ingest_status with the jobId until completed/failed. ' +
|
|
91
149
|
'Do not open a second ingest for the same sourceKey while a non-terminal job exists.',
|
|
@@ -105,7 +163,9 @@ export function registerStackMcpTools(server, client) {
|
|
|
105
163
|
.optional()
|
|
106
164
|
.describe('Plain text / markdown / source content'),
|
|
107
165
|
uri: z.string().optional().describe('Optional source URI metadata'),
|
|
108
|
-
collectionId: z
|
|
166
|
+
collectionId: z
|
|
167
|
+
.string()
|
|
168
|
+
.describe('Required Memory collection id. List with memory_list_collections and ask the user which to use.'),
|
|
109
169
|
},
|
|
110
170
|
}, async (args) => runMemoryIngest(client, args));
|
|
111
171
|
server.registerTool('memory_ingest_status', {
|
|
@@ -117,4 +177,23 @@ export function registerStackMcpTools(server, client) {
|
|
|
117
177
|
jobId: z.string().describe('Ingest job id from memory_ingest'),
|
|
118
178
|
},
|
|
119
179
|
}, async (args) => runMemoryIngestStatus(client, args));
|
|
180
|
+
server.registerTool('memory_delete', {
|
|
181
|
+
title: 'Delete Memory by source',
|
|
182
|
+
description: 'Delete documents (and their chunks) for a sourceId within a Memory collection. ' +
|
|
183
|
+
'Explicit cleanup only — soft TTL does not auto-delete.',
|
|
184
|
+
inputSchema: {
|
|
185
|
+
collectionId: z.string().describe('Memory collection id'),
|
|
186
|
+
sourceId: z
|
|
187
|
+
.string()
|
|
188
|
+
.describe('Source id / sourceKey previously used for ingest'),
|
|
189
|
+
},
|
|
190
|
+
}, async (args) => runMemoryDelete(client, args));
|
|
191
|
+
server.registerTool('memory_delete_collection', {
|
|
192
|
+
title: 'Delete Memory collection',
|
|
193
|
+
description: 'Delete an entire Memory collection and all of its documents/chunks. ' +
|
|
194
|
+
'Cannot delete the default General collection. Explicit cleanup only — soft TTL does not auto-delete.',
|
|
195
|
+
inputSchema: {
|
|
196
|
+
collectionId: z.string().describe('Memory collection id to delete'),
|
|
197
|
+
},
|
|
198
|
+
}, async (args) => runMemoryDeleteCollection(client, args));
|
|
120
199
|
}
|