@prmichaelsen/remember-mcp 2.3.4 → 2.5.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/AGENT.md +2 -2
- package/CHANGELOG.md +63 -0
- package/README.md +50 -5
- package/agent/commands/git.commit.md +511 -0
- package/agent/commands/git.init.md +513 -0
- package/agent/milestones/milestone-11-unified-public-collection.md +205 -0
- package/agent/scripts/install.sh +31 -16
- package/agent/scripts/update.sh +32 -17
- package/agent/tasks/task-45-fix-publish-false-success-bug.md +114 -181
- package/agent/tasks/task-46-update-spacememory-types.md +94 -0
- package/agent/tasks/task-47-update-space-schema.md +102 -0
- package/agent/tasks/task-48-create-memory-public-collection.md +96 -0
- package/agent/tasks/task-49-update-remember-publish.md +153 -0
- package/agent/tasks/task-50-update-remember-confirm.md +111 -0
- package/agent/tasks/task-51-update-remember-search-space.md +154 -0
- package/agent/tasks/task-52-update-remember-query-space.md +142 -0
- package/agent/tasks/task-53-add-multispace-tests.md +193 -0
- package/agent/tasks/task-54-update-documentation-multispace.md +191 -0
- package/dist/server-factory.js +203 -107
- package/dist/server.js +203 -107
- package/dist/tools/publish.d.ts +1 -1
- package/dist/tools/query-space.d.ts +1 -1
- package/dist/tools/search-space.d.ts +1 -1
- package/dist/types/space-memory.d.ts +5 -3
- package/dist/weaviate/space-schema.d.ts +16 -0
- package/package.json +1 -1
- package/src/services/confirmation-token.service.ts +74 -30
- package/src/tools/confirm.ts +15 -16
- package/src/tools/publish.ts +42 -20
- package/src/tools/query-space.ts +38 -24
- package/src/tools/search-space.ts +51 -28
- package/src/types/space-memory.ts +5 -3
- package/src/weaviate/space-schema.spec.ts +55 -0
- package/src/weaviate/space-schema.ts +45 -6
|
@@ -50,40 +50,84 @@ export class ConfirmationTokenService {
|
|
|
50
50
|
payload: any,
|
|
51
51
|
targetCollection?: string
|
|
52
52
|
): Promise<{ requestId: string; token: string }> {
|
|
53
|
-
|
|
53
|
+
try {
|
|
54
|
+
const token = randomUUID();
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const now = new Date();
|
|
57
|
+
const expiresAt = new Date(now.getTime() + this.EXPIRY_MINUTES * 60 * 1000);
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
const request: ConfirmationRequest = {
|
|
60
|
+
user_id: userId,
|
|
61
|
+
token,
|
|
62
|
+
action,
|
|
63
|
+
target_collection: targetCollection,
|
|
64
|
+
payload,
|
|
65
|
+
created_at: now.toISOString(),
|
|
66
|
+
expires_at: expiresAt.toISOString(),
|
|
67
|
+
status: 'pending',
|
|
68
|
+
};
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
// Add document to Firestore (auto-generates ID)
|
|
71
|
+
const collectionPath = `users/${userId}/requests`;
|
|
72
|
+
console.log('[ConfirmationTokenService] Creating request:', {
|
|
73
|
+
userId,
|
|
74
|
+
action,
|
|
75
|
+
targetCollection,
|
|
76
|
+
collectionPath,
|
|
77
|
+
requestData: {
|
|
78
|
+
token,
|
|
79
|
+
action,
|
|
80
|
+
payloadKeys: Object.keys(payload),
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log('[ConfirmationTokenService] Calling addDocument...');
|
|
85
|
+
const docRef = await addDocument(collectionPath, request);
|
|
86
|
+
console.log('[ConfirmationTokenService] addDocument returned:', {
|
|
87
|
+
hasDocRef: !!docRef,
|
|
88
|
+
hasId: !!docRef?.id,
|
|
89
|
+
docRefId: docRef?.id,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Validate docRef
|
|
93
|
+
if (!docRef) {
|
|
94
|
+
const error = new Error('Firestore addDocument returned null/undefined');
|
|
95
|
+
console.error('[ConfirmationTokenService] CRITICAL: addDocument returned null', {
|
|
96
|
+
userId,
|
|
97
|
+
collectionPath,
|
|
98
|
+
});
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!docRef.id) {
|
|
103
|
+
const error = new Error('Firestore addDocument returned docRef without ID');
|
|
104
|
+
console.error('[ConfirmationTokenService] CRITICAL: docRef has no ID', {
|
|
105
|
+
userId,
|
|
106
|
+
collectionPath,
|
|
107
|
+
docRef,
|
|
108
|
+
});
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('[ConfirmationTokenService] Request created successfully:', {
|
|
113
|
+
requestId: docRef.id,
|
|
114
|
+
token,
|
|
115
|
+
expiresAt: request.expires_at,
|
|
116
|
+
});
|
|
85
117
|
|
|
86
|
-
|
|
118
|
+
return { requestId: docRef.id, token };
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('[ConfirmationTokenService] FAILED to create request:', {
|
|
121
|
+
error: error instanceof Error ? error.message : String(error),
|
|
122
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
123
|
+
userId,
|
|
124
|
+
action,
|
|
125
|
+
collectionPath: `users/${userId}/requests`,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Re-throw so caller (tool handler) can catch and return error response
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
87
131
|
}
|
|
88
132
|
|
|
89
133
|
/**
|
package/src/tools/confirm.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import { confirmationTokenService, type ConfirmationRequest } from '../services/confirmation-token.service.js';
|
|
10
10
|
import { getWeaviateClient, getMemoryCollectionName } from '../weaviate/client.js';
|
|
11
|
-
import {
|
|
11
|
+
import { ensurePublicCollection } from '../weaviate/space-schema.js';
|
|
12
12
|
import { handleToolError } from '../utils/error-handler.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -102,7 +102,8 @@ async function executePublishMemory(
|
|
|
102
102
|
console.log('[executePublishMemory] Starting execution:', {
|
|
103
103
|
userId,
|
|
104
104
|
memoryId: request.payload.memory_id,
|
|
105
|
-
|
|
105
|
+
spaces: request.payload.spaces,
|
|
106
|
+
spaceCount: request.payload.spaces?.length || 0,
|
|
106
107
|
});
|
|
107
108
|
|
|
108
109
|
// Fetch the memory NOW (during confirmation, not from stored payload)
|
|
@@ -149,15 +150,12 @@ async function executePublishMemory(
|
|
|
149
150
|
);
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
console.log('[executePublishMemory] Ensuring
|
|
153
|
+
console.log('[executePublishMemory] Ensuring public collection');
|
|
153
154
|
|
|
154
|
-
// Get
|
|
155
|
-
const
|
|
156
|
-
weaviateClient,
|
|
157
|
-
request.target_collection || 'the_void'
|
|
158
|
-
);
|
|
155
|
+
// Get unified public collection
|
|
156
|
+
const publicCollection = await ensurePublicCollection(weaviateClient);
|
|
159
157
|
|
|
160
|
-
console.log('[executePublishMemory]
|
|
158
|
+
console.log('[executePublishMemory] Public collection ready');
|
|
161
159
|
|
|
162
160
|
// Create published memory (copy with modifications)
|
|
163
161
|
const originalTags = Array.isArray(originalMemory.properties.tags)
|
|
@@ -171,7 +169,7 @@ async function executePublishMemory(
|
|
|
171
169
|
const publishedMemory = {
|
|
172
170
|
...originalMemory.properties,
|
|
173
171
|
// Add space-specific fields
|
|
174
|
-
|
|
172
|
+
spaces: request.payload.spaces || ['the_void'], // ✅ Array of spaces!
|
|
175
173
|
author_id: userId, // Track original author
|
|
176
174
|
published_at: new Date().toISOString(),
|
|
177
175
|
discovery_count: 0,
|
|
@@ -185,27 +183,28 @@ async function executePublishMemory(
|
|
|
185
183
|
version: 1,
|
|
186
184
|
};
|
|
187
185
|
|
|
188
|
-
console.log('[executePublishMemory] Inserting into
|
|
189
|
-
|
|
186
|
+
console.log('[executePublishMemory] Inserting into Memory_public:', {
|
|
187
|
+
spaces: request.payload.spaces,
|
|
188
|
+
spaceCount: request.payload.spaces?.length || 0,
|
|
190
189
|
memoryId: request.payload.memory_id,
|
|
191
190
|
hasUserId: !!(publishedMemory as any).user_id,
|
|
192
191
|
hasAuthorId: !!publishedMemory.author_id,
|
|
193
|
-
hasSpaceId: !!publishedMemory.space_id,
|
|
194
192
|
});
|
|
195
193
|
|
|
196
|
-
// Insert directly
|
|
197
|
-
const result = await
|
|
194
|
+
// Insert directly into unified public collection
|
|
195
|
+
const result = await publicCollection.data.insert(publishedMemory as any);
|
|
198
196
|
|
|
199
197
|
console.log('[executePublishMemory] Insert result:', {
|
|
200
198
|
success: !!result,
|
|
201
199
|
spaceMemoryId: result,
|
|
202
200
|
});
|
|
203
201
|
|
|
204
|
-
// Return minimal response
|
|
202
|
+
// Return minimal response with spaces array
|
|
205
203
|
return JSON.stringify(
|
|
206
204
|
{
|
|
207
205
|
success: true,
|
|
208
206
|
space_memory_id: result,
|
|
207
|
+
spaces: request.payload.spaces || ['the_void'],
|
|
209
208
|
},
|
|
210
209
|
null,
|
|
211
210
|
2
|
package/src/tools/publish.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { SUPPORTED_SPACES } from '../types/space-memory.js';
|
|
|
17
17
|
*/
|
|
18
18
|
export const publishTool: Tool = {
|
|
19
19
|
name: 'remember_publish',
|
|
20
|
-
description: 'Publish a memory to
|
|
20
|
+
description: 'Publish a memory to one or more shared spaces (like "The Void"). The memory will be COPIED (not moved) from your personal collection. Generates a confirmation token. Use remember_confirm to execute.',
|
|
21
21
|
inputSchema: {
|
|
22
22
|
type: 'object',
|
|
23
23
|
properties: {
|
|
@@ -25,11 +25,15 @@ export const publishTool: Tool = {
|
|
|
25
25
|
type: 'string',
|
|
26
26
|
description: 'ID of the memory from your personal collection to publish',
|
|
27
27
|
},
|
|
28
|
-
|
|
29
|
-
type: '
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
spaces: {
|
|
29
|
+
type: 'array',
|
|
30
|
+
items: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
enum: SUPPORTED_SPACES,
|
|
33
|
+
},
|
|
34
|
+
description: 'Spaces to publish to (e.g., ["the_void", "dogs"]). Can publish to multiple spaces at once.',
|
|
35
|
+
minItems: 1,
|
|
36
|
+
default: ['the_void'],
|
|
33
37
|
},
|
|
34
38
|
additional_tags: {
|
|
35
39
|
type: 'array',
|
|
@@ -38,13 +42,13 @@ export const publishTool: Tool = {
|
|
|
38
42
|
default: [],
|
|
39
43
|
},
|
|
40
44
|
},
|
|
41
|
-
required: ['memory_id', '
|
|
45
|
+
required: ['memory_id', 'spaces'],
|
|
42
46
|
},
|
|
43
47
|
};
|
|
44
48
|
|
|
45
49
|
interface PublishArgs {
|
|
46
50
|
memory_id: string;
|
|
47
|
-
|
|
51
|
+
spaces: string[];
|
|
48
52
|
additional_tags?: string[];
|
|
49
53
|
}
|
|
50
54
|
|
|
@@ -59,20 +63,23 @@ export async function handlePublish(
|
|
|
59
63
|
console.log('[remember_publish] Starting publish request:', {
|
|
60
64
|
userId,
|
|
61
65
|
memoryId: args.memory_id,
|
|
62
|
-
|
|
66
|
+
spaces: args.spaces,
|
|
67
|
+
spaceCount: args.spaces.length,
|
|
63
68
|
additionalTags: args.additional_tags?.length || 0,
|
|
64
69
|
});
|
|
65
70
|
|
|
66
|
-
// Validate space
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
// Validate all space IDs
|
|
72
|
+
const invalidSpaces = args.spaces.filter(s => !isValidSpaceId(s));
|
|
73
|
+
if (invalidSpaces.length > 0) {
|
|
74
|
+
console.log('[remember_publish] Invalid space IDs:', invalidSpaces);
|
|
69
75
|
return JSON.stringify(
|
|
70
76
|
{
|
|
71
77
|
success: false,
|
|
72
|
-
error: 'Invalid space
|
|
73
|
-
message: `
|
|
78
|
+
error: 'Invalid space IDs',
|
|
79
|
+
message: `Invalid spaces: ${invalidSpaces.join(', ')}. Supported spaces: ${SUPPORTED_SPACES.join(', ')}`,
|
|
74
80
|
context: {
|
|
75
|
-
|
|
81
|
+
invalid_spaces: invalidSpaces,
|
|
82
|
+
provided_spaces: args.spaces,
|
|
76
83
|
supported_spaces: SUPPORTED_SPACES,
|
|
77
84
|
},
|
|
78
85
|
},
|
|
@@ -80,6 +87,20 @@ export async function handlePublish(
|
|
|
80
87
|
2
|
|
81
88
|
);
|
|
82
89
|
}
|
|
90
|
+
|
|
91
|
+
// Validate not empty
|
|
92
|
+
if (args.spaces.length === 0) {
|
|
93
|
+
console.log('[remember_publish] Empty spaces array');
|
|
94
|
+
return JSON.stringify(
|
|
95
|
+
{
|
|
96
|
+
success: false,
|
|
97
|
+
error: 'Empty spaces array',
|
|
98
|
+
message: 'Must specify at least one space to publish to',
|
|
99
|
+
},
|
|
100
|
+
null,
|
|
101
|
+
2
|
|
102
|
+
);
|
|
103
|
+
}
|
|
83
104
|
|
|
84
105
|
// Verify memory exists and user owns it
|
|
85
106
|
const weaviateClient = getWeaviateClient();
|
|
@@ -147,20 +168,21 @@ export async function handlePublish(
|
|
|
147
168
|
);
|
|
148
169
|
}
|
|
149
170
|
|
|
150
|
-
// Create payload with
|
|
171
|
+
// Create payload with memory_id and spaces array
|
|
151
172
|
const payload = {
|
|
152
173
|
memory_id: args.memory_id,
|
|
174
|
+
spaces: args.spaces,
|
|
153
175
|
additional_tags: args.additional_tags || [],
|
|
154
176
|
};
|
|
155
177
|
|
|
156
178
|
console.log('[remember_publish] Generating confirmation token');
|
|
157
179
|
|
|
158
180
|
// Generate confirmation token
|
|
159
|
-
const { requestId, token
|
|
181
|
+
const { requestId, token} = await confirmationTokenService.createRequest(
|
|
160
182
|
userId,
|
|
161
183
|
'publish_memory',
|
|
162
184
|
payload,
|
|
163
|
-
|
|
185
|
+
undefined // No single target_collection anymore
|
|
164
186
|
);
|
|
165
187
|
|
|
166
188
|
console.log('[remember_publish] Token generated:', {
|
|
@@ -179,12 +201,12 @@ export async function handlePublish(
|
|
|
179
201
|
2
|
|
180
202
|
);
|
|
181
203
|
} catch (error) {
|
|
182
|
-
handleToolError(error, {
|
|
204
|
+
return handleToolError(error, {
|
|
183
205
|
toolName: 'remember_publish',
|
|
184
206
|
userId,
|
|
185
207
|
operation: 'publish memory',
|
|
186
208
|
memory_id: args.memory_id,
|
|
187
|
-
|
|
209
|
+
spaces: args.spaces,
|
|
188
210
|
});
|
|
189
211
|
}
|
|
190
212
|
}
|
package/src/tools/query-space.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import { Filters } from 'weaviate-client';
|
|
10
10
|
import { getWeaviateClient } from '../weaviate/client.js';
|
|
11
|
-
import {
|
|
11
|
+
import { ensurePublicCollection, isValidSpaceId } from '../weaviate/space-schema.js';
|
|
12
12
|
import { SUPPORTED_SPACES } from '../types/space-memory.js';
|
|
13
13
|
import { handleToolError } from '../utils/error-handler.js';
|
|
14
14
|
|
|
@@ -66,13 +66,13 @@ export const querySpaceTool: Tool = {
|
|
|
66
66
|
description: 'Output format: detailed (full objects) or compact (text summary)',
|
|
67
67
|
},
|
|
68
68
|
},
|
|
69
|
-
required: ['question', '
|
|
69
|
+
required: ['question', 'spaces'],
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
interface QuerySpaceArgs {
|
|
74
74
|
question: string;
|
|
75
|
-
|
|
75
|
+
spaces: string[];
|
|
76
76
|
content_type?: string;
|
|
77
77
|
tags?: string[];
|
|
78
78
|
min_weight?: number;
|
|
@@ -90,13 +90,27 @@ export async function handleQuerySpace(
|
|
|
90
90
|
userId: string // May be used for private spaces in future
|
|
91
91
|
): Promise<string> {
|
|
92
92
|
try {
|
|
93
|
-
// Validate space
|
|
94
|
-
|
|
93
|
+
// Validate all space IDs
|
|
94
|
+
const invalidSpaces = args.spaces.filter(s => !isValidSpaceId(s));
|
|
95
|
+
if (invalidSpaces.length > 0) {
|
|
95
96
|
return JSON.stringify(
|
|
96
97
|
{
|
|
97
98
|
success: false,
|
|
98
|
-
error: 'Invalid space
|
|
99
|
-
message: `
|
|
99
|
+
error: 'Invalid space IDs',
|
|
100
|
+
message: `Invalid spaces: ${invalidSpaces.join(', ')}. Supported spaces: ${SUPPORTED_SPACES.join(', ')}`,
|
|
101
|
+
},
|
|
102
|
+
null,
|
|
103
|
+
2
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Validate not empty
|
|
108
|
+
if (args.spaces.length === 0) {
|
|
109
|
+
return JSON.stringify(
|
|
110
|
+
{
|
|
111
|
+
success: false,
|
|
112
|
+
error: 'Empty spaces array',
|
|
113
|
+
message: 'Must specify at least one space to query',
|
|
100
114
|
},
|
|
101
115
|
null,
|
|
102
116
|
2
|
|
@@ -104,47 +118,47 @@ export async function handleQuerySpace(
|
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
const weaviateClient = getWeaviateClient();
|
|
107
|
-
const
|
|
121
|
+
const publicCollection = await ensurePublicCollection(weaviateClient);
|
|
108
122
|
|
|
109
123
|
// Build filters
|
|
110
124
|
const filterList: any[] = [];
|
|
111
125
|
|
|
112
|
-
// Filter by
|
|
113
|
-
filterList.push(
|
|
126
|
+
// Filter by spaces array (memory must be in at least one requested space)
|
|
127
|
+
filterList.push(publicCollection.filter.byProperty('spaces').containsAny(args.spaces));
|
|
114
128
|
|
|
115
129
|
// Filter by doc_type (space_memory)
|
|
116
|
-
filterList.push(
|
|
130
|
+
filterList.push(publicCollection.filter.byProperty('doc_type').equal('space_memory'));
|
|
117
131
|
|
|
118
132
|
// Apply content type filter
|
|
119
133
|
if (args.content_type) {
|
|
120
|
-
filterList.push(
|
|
134
|
+
filterList.push(publicCollection.filter.byProperty('type').equal(args.content_type));
|
|
121
135
|
}
|
|
122
136
|
|
|
123
137
|
// Apply tags filter
|
|
124
138
|
if (args.tags && args.tags.length > 0) {
|
|
125
139
|
args.tags.forEach(tag => {
|
|
126
|
-
filterList.push(
|
|
140
|
+
filterList.push(publicCollection.filter.byProperty('tags').containsAny([tag]));
|
|
127
141
|
});
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
// Apply weight filter
|
|
131
145
|
if (args.min_weight !== undefined) {
|
|
132
|
-
filterList.push(
|
|
146
|
+
filterList.push(publicCollection.filter.byProperty('weight').greaterOrEqual(args.min_weight));
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
// Apply date filters
|
|
136
150
|
if (args.date_from) {
|
|
137
|
-
filterList.push(
|
|
151
|
+
filterList.push(publicCollection.filter.byProperty('created_at').greaterOrEqual(new Date(args.date_from)));
|
|
138
152
|
}
|
|
139
153
|
|
|
140
154
|
if (args.date_to) {
|
|
141
|
-
filterList.push(
|
|
155
|
+
filterList.push(publicCollection.filter.byProperty('created_at').lessOrEqual(new Date(args.date_to)));
|
|
142
156
|
}
|
|
143
157
|
|
|
144
158
|
const whereFilter = filterList.length > 0 ? Filters.and(...filterList) : undefined;
|
|
145
159
|
|
|
146
160
|
// Execute semantic search using nearText
|
|
147
|
-
const searchResults = await
|
|
161
|
+
const searchResults = await publicCollection.query.nearText(args.question, {
|
|
148
162
|
limit: args.limit || 10,
|
|
149
163
|
...(whereFilter && { where: whereFilter }),
|
|
150
164
|
});
|
|
@@ -154,14 +168,14 @@ export async function handleQuerySpace(
|
|
|
154
168
|
|
|
155
169
|
if (format === 'compact') {
|
|
156
170
|
// Compact format: text summary for LLM context
|
|
157
|
-
const summaries = searchResults.objects.map((obj, idx) => {
|
|
171
|
+
const summaries = searchResults.objects.map((obj: any, idx: number) => {
|
|
158
172
|
const props = obj.properties;
|
|
159
173
|
return `${idx + 1}. ${props.title || props.content?.substring(0, 100) || 'Untitled'}`;
|
|
160
174
|
});
|
|
161
175
|
|
|
162
176
|
const result = {
|
|
163
177
|
question: args.question,
|
|
164
|
-
|
|
178
|
+
spaces_queried: args.spaces,
|
|
165
179
|
format: 'compact',
|
|
166
180
|
summary: summaries.join('\n'),
|
|
167
181
|
count: searchResults.objects.length,
|
|
@@ -170,7 +184,7 @@ export async function handleQuerySpace(
|
|
|
170
184
|
return JSON.stringify(result, null, 2);
|
|
171
185
|
} else {
|
|
172
186
|
// Detailed format: full objects
|
|
173
|
-
const memories = searchResults.objects.map((obj) => ({
|
|
187
|
+
const memories = searchResults.objects.map((obj: any) => ({
|
|
174
188
|
id: obj.uuid,
|
|
175
189
|
...obj.properties,
|
|
176
190
|
_distance: obj.metadata?.distance,
|
|
@@ -178,7 +192,7 @@ export async function handleQuerySpace(
|
|
|
178
192
|
|
|
179
193
|
const result = {
|
|
180
194
|
question: args.question,
|
|
181
|
-
|
|
195
|
+
spaces_queried: args.spaces,
|
|
182
196
|
format: 'detailed',
|
|
183
197
|
memories,
|
|
184
198
|
total: memories.length,
|
|
@@ -187,10 +201,10 @@ export async function handleQuerySpace(
|
|
|
187
201
|
return JSON.stringify(result, null, 2);
|
|
188
202
|
}
|
|
189
203
|
} catch (error) {
|
|
190
|
-
handleToolError(error, {
|
|
204
|
+
return handleToolError(error, {
|
|
191
205
|
toolName: 'remember_query_space',
|
|
192
|
-
operation: 'query
|
|
193
|
-
|
|
206
|
+
operation: 'query spaces',
|
|
207
|
+
spaces: args.spaces,
|
|
194
208
|
question: args.question,
|
|
195
209
|
});
|
|
196
210
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import { Filters } from 'weaviate-client';
|
|
10
10
|
import { getWeaviateClient } from '../weaviate/client.js';
|
|
11
|
-
import {
|
|
11
|
+
import { ensurePublicCollection, isValidSpaceId } from '../weaviate/space-schema.js';
|
|
12
12
|
import { SUPPORTED_SPACES } from '../types/space-memory.js';
|
|
13
13
|
import { handleToolError } from '../utils/error-handler.js';
|
|
14
14
|
import type { SearchFilters } from '../types/memory.js';
|
|
@@ -18,7 +18,7 @@ import type { SearchFilters } from '../types/memory.js';
|
|
|
18
18
|
*/
|
|
19
19
|
export const searchSpaceTool: Tool = {
|
|
20
20
|
name: 'remember_search_space',
|
|
21
|
-
description: 'Search shared spaces to discover thoughts, ideas, and memories. Works like remember_search_memory but searches shared spaces instead of personal memories.',
|
|
21
|
+
description: 'Search one or more shared spaces to discover thoughts, ideas, and memories. Works like remember_search_memory but searches shared spaces instead of personal memories. Can search multiple spaces in a single query.',
|
|
22
22
|
inputSchema: {
|
|
23
23
|
type: 'object',
|
|
24
24
|
properties: {
|
|
@@ -26,11 +26,15 @@ export const searchSpaceTool: Tool = {
|
|
|
26
26
|
type: 'string',
|
|
27
27
|
description: 'Search query (semantic + keyword hybrid)',
|
|
28
28
|
},
|
|
29
|
-
|
|
30
|
-
type: '
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
spaces: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
enum: SUPPORTED_SPACES,
|
|
34
|
+
},
|
|
35
|
+
description: 'Spaces to search (e.g., ["the_void", "dogs"]). Can search multiple spaces at once.',
|
|
36
|
+
minItems: 1,
|
|
37
|
+
default: ['the_void'],
|
|
34
38
|
},
|
|
35
39
|
content_type: {
|
|
36
40
|
type: 'string',
|
|
@@ -72,13 +76,13 @@ export const searchSpaceTool: Tool = {
|
|
|
72
76
|
description: 'Offset for pagination',
|
|
73
77
|
},
|
|
74
78
|
},
|
|
75
|
-
required: ['query', '
|
|
79
|
+
required: ['query', 'spaces'],
|
|
76
80
|
},
|
|
77
81
|
};
|
|
78
82
|
|
|
79
83
|
interface SearchSpaceArgs {
|
|
80
84
|
query: string;
|
|
81
|
-
|
|
85
|
+
spaces: string[];
|
|
82
86
|
content_type?: string;
|
|
83
87
|
tags?: string[];
|
|
84
88
|
min_weight?: number;
|
|
@@ -97,13 +101,32 @@ export async function handleSearchSpace(
|
|
|
97
101
|
userId: string // May be used for private spaces in future
|
|
98
102
|
): Promise<string> {
|
|
99
103
|
try {
|
|
100
|
-
// Validate space
|
|
101
|
-
|
|
104
|
+
// Validate all space IDs
|
|
105
|
+
const invalidSpaces = args.spaces.filter(s => !isValidSpaceId(s));
|
|
106
|
+
if (invalidSpaces.length > 0) {
|
|
107
|
+
return JSON.stringify(
|
|
108
|
+
{
|
|
109
|
+
success: false,
|
|
110
|
+
error: 'Invalid space IDs',
|
|
111
|
+
message: `Invalid spaces: ${invalidSpaces.join(', ')}. Supported spaces: ${SUPPORTED_SPACES.join(', ')}`,
|
|
112
|
+
context: {
|
|
113
|
+
invalid_spaces: invalidSpaces,
|
|
114
|
+
provided_spaces: args.spaces,
|
|
115
|
+
supported_spaces: SUPPORTED_SPACES,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
null,
|
|
119
|
+
2
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Validate not empty
|
|
124
|
+
if (args.spaces.length === 0) {
|
|
102
125
|
return JSON.stringify(
|
|
103
126
|
{
|
|
104
127
|
success: false,
|
|
105
|
-
error: '
|
|
106
|
-
message:
|
|
128
|
+
error: 'Empty spaces array',
|
|
129
|
+
message: 'Must specify at least one space to search',
|
|
107
130
|
},
|
|
108
131
|
null,
|
|
109
132
|
2
|
|
@@ -111,51 +134,51 @@ export async function handleSearchSpace(
|
|
|
111
134
|
}
|
|
112
135
|
|
|
113
136
|
const weaviateClient = getWeaviateClient();
|
|
114
|
-
const
|
|
137
|
+
const publicCollection = await ensurePublicCollection(weaviateClient);
|
|
115
138
|
|
|
116
139
|
// Build filters for space search
|
|
117
140
|
const filterList: any[] = [];
|
|
118
141
|
|
|
119
|
-
// Filter by
|
|
120
|
-
filterList.push(
|
|
142
|
+
// Filter by spaces array (memory must be in at least one requested space)
|
|
143
|
+
filterList.push(publicCollection.filter.byProperty('spaces').containsAny(args.spaces));
|
|
121
144
|
|
|
122
145
|
// Filter by doc_type (space_memory)
|
|
123
|
-
filterList.push(
|
|
146
|
+
filterList.push(publicCollection.filter.byProperty('doc_type').equal('space_memory'));
|
|
124
147
|
|
|
125
148
|
// Apply content type filter
|
|
126
149
|
if (args.content_type) {
|
|
127
|
-
filterList.push(
|
|
150
|
+
filterList.push(publicCollection.filter.byProperty('type').equal(args.content_type));
|
|
128
151
|
}
|
|
129
152
|
|
|
130
153
|
// Apply tags filter
|
|
131
154
|
if (args.tags && args.tags.length > 0) {
|
|
132
155
|
args.tags.forEach(tag => {
|
|
133
|
-
filterList.push(
|
|
156
|
+
filterList.push(publicCollection.filter.byProperty('tags').containsAny([tag]));
|
|
134
157
|
});
|
|
135
158
|
}
|
|
136
159
|
|
|
137
160
|
// Apply weight filters
|
|
138
161
|
if (args.min_weight !== undefined) {
|
|
139
|
-
filterList.push(
|
|
162
|
+
filterList.push(publicCollection.filter.byProperty('weight').greaterOrEqual(args.min_weight));
|
|
140
163
|
}
|
|
141
164
|
|
|
142
165
|
if (args.max_weight !== undefined) {
|
|
143
|
-
filterList.push(
|
|
166
|
+
filterList.push(publicCollection.filter.byProperty('weight').lessOrEqual(args.max_weight));
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
// Apply date filters (convert ISO strings to Date objects)
|
|
147
170
|
if (args.date_from) {
|
|
148
|
-
filterList.push(
|
|
171
|
+
filterList.push(publicCollection.filter.byProperty('created_at').greaterOrEqual(new Date(args.date_from)));
|
|
149
172
|
}
|
|
150
173
|
|
|
151
174
|
if (args.date_to) {
|
|
152
|
-
filterList.push(
|
|
175
|
+
filterList.push(publicCollection.filter.byProperty('created_at').lessOrEqual(new Date(args.date_to)));
|
|
153
176
|
}
|
|
154
177
|
|
|
155
178
|
const whereFilter = filterList.length > 0 ? Filters.and(...filterList) : undefined;
|
|
156
179
|
|
|
157
180
|
// Execute hybrid search
|
|
158
|
-
const searchResults = await
|
|
181
|
+
const searchResults = await publicCollection.query.hybrid(args.query, {
|
|
159
182
|
limit: args.limit || 10,
|
|
160
183
|
offset: args.offset || 0,
|
|
161
184
|
...(whereFilter && { where: whereFilter }),
|
|
@@ -169,7 +192,7 @@ export async function handleSearchSpace(
|
|
|
169
192
|
}));
|
|
170
193
|
|
|
171
194
|
const result = {
|
|
172
|
-
|
|
195
|
+
spaces_searched: args.spaces,
|
|
173
196
|
query: args.query,
|
|
174
197
|
memories,
|
|
175
198
|
total: memories.length,
|
|
@@ -179,10 +202,10 @@ export async function handleSearchSpace(
|
|
|
179
202
|
|
|
180
203
|
return JSON.stringify(result, null, 2);
|
|
181
204
|
} catch (error) {
|
|
182
|
-
handleToolError(error, {
|
|
205
|
+
return handleToolError(error, {
|
|
183
206
|
toolName: 'remember_search_space',
|
|
184
|
-
operation: 'search
|
|
185
|
-
|
|
207
|
+
operation: 'search spaces',
|
|
208
|
+
spaces: args.spaces,
|
|
186
209
|
query: args.query,
|
|
187
210
|
});
|
|
188
211
|
}
|