@prmichaelsen/remember-mcp 2.2.1 → 2.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/AGENT.md +4 -4
- package/CHANGELOG.md +45 -0
- package/README.md +43 -3
- package/agent/commands/acp.init.md +376 -0
- package/agent/commands/acp.proceed.md +311 -0
- package/agent/commands/acp.status.md +280 -0
- package/agent/commands/acp.version-check-for-updates.md +275 -0
- package/agent/commands/acp.version-check.md +190 -0
- package/agent/commands/acp.version-update.md +288 -0
- package/agent/commands/command.template.md +273 -0
- package/agent/design/core-memory-user-profile.md +1253 -0
- package/agent/design/ghost-profiles-pseudonymous-identity.md +194 -0
- package/agent/design/publish-tools-confirmation-flow.md +922 -0
- package/agent/milestones/milestone-10-shared-spaces.md +169 -0
- package/agent/progress.yaml +90 -4
- package/agent/scripts/install.sh +118 -0
- package/agent/scripts/update.sh +22 -10
- package/agent/scripts/version.sh +35 -0
- package/agent/tasks/task-27-implement-llm-provider-interface.md +51 -0
- package/agent/tasks/task-28-implement-llm-provider-factory.md +64 -0
- package/agent/tasks/task-29-update-config-for-llm.md +71 -0
- package/agent/tasks/task-30-implement-bedrock-provider.md +147 -0
- package/agent/tasks/task-31-implement-background-job-service.md +120 -0
- package/agent/tasks/task-32-test-llm-provider-integration.md +152 -0
- package/agent/tasks/task-34-create-confirmation-token-service.md +191 -0
- package/agent/tasks/task-35-create-space-memory-types-schema.md +183 -0
- package/agent/tasks/task-36-implement-remember-publish.md +227 -0
- package/agent/tasks/task-37-implement-remember-confirm.md +225 -0
- package/agent/tasks/task-38-implement-remember-deny.md +161 -0
- package/agent/tasks/task-39-implement-remember-search-space.md +188 -0
- package/agent/tasks/task-40-implement-remember-query-space.md +193 -0
- package/agent/tasks/task-41-configure-firestore-ttl.md +188 -0
- package/agent/tasks/task-42-create-tests-shared-spaces.md +216 -0
- package/agent/tasks/task-43-update-documentation.md +255 -0
- package/dist/llm/types.d.ts +1 -0
- package/dist/server-factory.js +914 -1
- package/dist/server.js +916 -3
- package/dist/services/confirmation-token.service.d.ts +99 -0
- package/dist/services/confirmation-token.service.spec.d.ts +5 -0
- package/dist/tools/confirm.d.ts +20 -0
- package/dist/tools/deny.d.ts +19 -0
- package/dist/tools/publish.d.ts +22 -0
- package/dist/tools/query-space.d.ts +28 -0
- package/dist/tools/search-space.d.ts +29 -0
- package/dist/types/space-memory.d.ts +80 -0
- package/dist/weaviate/space-schema.d.ts +59 -0
- package/dist/weaviate/space-schema.spec.d.ts +5 -0
- package/package.json +1 -1
- package/src/llm/types.ts +0 -0
- package/src/server-factory.ts +33 -0
- package/src/server.ts +33 -0
- package/src/services/confirmation-token.service.spec.ts +254 -0
- package/src/services/confirmation-token.service.ts +232 -0
- package/src/tools/confirm.ts +176 -0
- package/src/tools/deny.ts +70 -0
- package/src/tools/publish.ts +167 -0
- package/src/tools/query-space.ts +197 -0
- package/src/tools/search-space.ts +189 -0
- package/src/types/space-memory.ts +94 -0
- package/src/weaviate/space-schema.spec.ts +131 -0
- package/src/weaviate/space-schema.ts +275 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_deny tool
|
|
3
|
+
*
|
|
4
|
+
* Generic denial tool for any pending action.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
+
import { confirmationTokenService } from '../services/confirmation-token.service.js';
|
|
9
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tool definition for remember_deny
|
|
13
|
+
*/
|
|
14
|
+
export const denyTool: Tool = {
|
|
15
|
+
name: 'remember_deny',
|
|
16
|
+
description: 'Deny a pending action. The request will be marked as denied and the token invalidated. Works for any action that requires confirmation.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
token: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'The confirmation token from the action tool',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['token'],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
interface DenyArgs {
|
|
30
|
+
token: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Handle remember_deny tool execution
|
|
35
|
+
*/
|
|
36
|
+
export async function handleDeny(
|
|
37
|
+
args: DenyArgs,
|
|
38
|
+
userId: string
|
|
39
|
+
): Promise<string> {
|
|
40
|
+
try {
|
|
41
|
+
const success = await confirmationTokenService.denyRequest(userId, args.token);
|
|
42
|
+
|
|
43
|
+
if (!success) {
|
|
44
|
+
return JSON.stringify(
|
|
45
|
+
{
|
|
46
|
+
success: false,
|
|
47
|
+
error: 'Invalid token',
|
|
48
|
+
message: 'Token not found or already used',
|
|
49
|
+
},
|
|
50
|
+
null,
|
|
51
|
+
2
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return JSON.stringify(
|
|
56
|
+
{
|
|
57
|
+
success: true,
|
|
58
|
+
},
|
|
59
|
+
null,
|
|
60
|
+
2
|
|
61
|
+
);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
handleToolError(error, {
|
|
64
|
+
toolName: 'remember_deny',
|
|
65
|
+
userId,
|
|
66
|
+
operation: 'deny action',
|
|
67
|
+
token: args.token,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_publish tool
|
|
3
|
+
*
|
|
4
|
+
* Generates a confirmation token for publishing a memory to a shared space.
|
|
5
|
+
* This is the first phase of the two-phase publish workflow.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { confirmationTokenService } from '../services/confirmation-token.service.js';
|
|
10
|
+
import { getWeaviateClient, getMemoryCollectionName } from '../weaviate/client.js';
|
|
11
|
+
import { isValidSpaceId } from '../weaviate/space-schema.js';
|
|
12
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
13
|
+
import { SUPPORTED_SPACES } from '../types/space-memory.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Tool definition for remember_publish
|
|
17
|
+
*/
|
|
18
|
+
export const publishTool: Tool = {
|
|
19
|
+
name: 'remember_publish',
|
|
20
|
+
description: 'Publish a memory to a shared space (like "The Void"). The memory will be COPIED (not moved) from your personal collection. Generates a confirmation token. Use remember_confirm to execute.',
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
memory_id: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'ID of the memory from your personal collection to publish',
|
|
27
|
+
},
|
|
28
|
+
target: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Target space to publish to (snake_case ID)',
|
|
31
|
+
enum: SUPPORTED_SPACES,
|
|
32
|
+
default: 'the_void',
|
|
33
|
+
},
|
|
34
|
+
additional_tags: {
|
|
35
|
+
type: 'array',
|
|
36
|
+
items: { type: 'string' },
|
|
37
|
+
description: 'Additional tags for discovery (merged with original tags)',
|
|
38
|
+
default: [],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ['memory_id', 'target'],
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
interface PublishArgs {
|
|
46
|
+
memory_id: string;
|
|
47
|
+
target: string;
|
|
48
|
+
additional_tags?: string[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Handle remember_publish tool execution
|
|
53
|
+
*/
|
|
54
|
+
export async function handlePublish(
|
|
55
|
+
args: PublishArgs,
|
|
56
|
+
userId: string
|
|
57
|
+
): Promise<string> {
|
|
58
|
+
try {
|
|
59
|
+
// Validate space ID
|
|
60
|
+
if (!isValidSpaceId(args.target)) {
|
|
61
|
+
return JSON.stringify(
|
|
62
|
+
{
|
|
63
|
+
success: false,
|
|
64
|
+
error: 'Invalid space ID',
|
|
65
|
+
message: `Space "${args.target}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(', ')}`,
|
|
66
|
+
context: {
|
|
67
|
+
provided_space: args.target,
|
|
68
|
+
supported_spaces: SUPPORTED_SPACES,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
null,
|
|
72
|
+
2
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Verify memory exists and user owns it
|
|
77
|
+
const weaviateClient = getWeaviateClient();
|
|
78
|
+
const userCollection = weaviateClient.collections.get(
|
|
79
|
+
getMemoryCollectionName(userId)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const memory = await userCollection.query.fetchObjectById(args.memory_id);
|
|
83
|
+
|
|
84
|
+
if (!memory) {
|
|
85
|
+
return JSON.stringify(
|
|
86
|
+
{
|
|
87
|
+
success: false,
|
|
88
|
+
error: 'Memory not found',
|
|
89
|
+
message: `No memory found with ID: ${args.memory_id}`,
|
|
90
|
+
context: {
|
|
91
|
+
collection_name: getMemoryCollectionName(userId),
|
|
92
|
+
memory_id: args.memory_id,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
null,
|
|
96
|
+
2
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Verify ownership
|
|
101
|
+
if (memory.properties.user_id !== userId) {
|
|
102
|
+
return JSON.stringify(
|
|
103
|
+
{
|
|
104
|
+
success: false,
|
|
105
|
+
error: 'Permission denied',
|
|
106
|
+
message: 'You can only publish your own memories',
|
|
107
|
+
context: {
|
|
108
|
+
memory_id: args.memory_id,
|
|
109
|
+
memory_owner: memory.properties.user_id,
|
|
110
|
+
requesting_user: userId,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
null,
|
|
114
|
+
2
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Verify it's a memory (not a relationship)
|
|
119
|
+
if (memory.properties.doc_type !== 'memory') {
|
|
120
|
+
return JSON.stringify(
|
|
121
|
+
{
|
|
122
|
+
success: false,
|
|
123
|
+
error: 'Invalid document type',
|
|
124
|
+
message: 'Only memories can be published (not relationships)',
|
|
125
|
+
context: {
|
|
126
|
+
memory_id: args.memory_id,
|
|
127
|
+
doc_type: memory.properties.doc_type,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
null,
|
|
131
|
+
2
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Create payload with only memory_id (content fetched during confirmation)
|
|
136
|
+
const payload = {
|
|
137
|
+
memory_id: args.memory_id,
|
|
138
|
+
additional_tags: args.additional_tags || [],
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Generate confirmation token
|
|
142
|
+
const { requestId, token } = await confirmationTokenService.createRequest(
|
|
143
|
+
userId,
|
|
144
|
+
'publish_memory',
|
|
145
|
+
payload,
|
|
146
|
+
args.target
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Return minimal response - agent already knows memory details
|
|
150
|
+
return JSON.stringify(
|
|
151
|
+
{
|
|
152
|
+
success: true,
|
|
153
|
+
token,
|
|
154
|
+
},
|
|
155
|
+
null,
|
|
156
|
+
2
|
|
157
|
+
);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
handleToolError(error, {
|
|
160
|
+
toolName: 'remember_publish',
|
|
161
|
+
userId,
|
|
162
|
+
operation: 'publish memory',
|
|
163
|
+
memory_id: args.memory_id,
|
|
164
|
+
target: args.target,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_query_space tool
|
|
3
|
+
*
|
|
4
|
+
* RAG-optimized natural language queries for shared spaces.
|
|
5
|
+
* Similar to remember_query_memory but queries space collections.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { Filters } from 'weaviate-client';
|
|
10
|
+
import { getWeaviateClient } from '../weaviate/client.js';
|
|
11
|
+
import { ensureSpaceCollection, isValidSpaceId } from '../weaviate/space-schema.js';
|
|
12
|
+
import { SUPPORTED_SPACES } from '../types/space-memory.js';
|
|
13
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Tool definition for remember_query_space
|
|
17
|
+
*/
|
|
18
|
+
export const querySpaceTool: Tool = {
|
|
19
|
+
name: 'remember_query_space',
|
|
20
|
+
description: 'Ask natural language questions about memories in shared spaces. Works like remember_query_memory but queries shared spaces.',
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
question: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Natural language question',
|
|
27
|
+
},
|
|
28
|
+
space: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Which space to query',
|
|
31
|
+
enum: SUPPORTED_SPACES,
|
|
32
|
+
default: 'the_void',
|
|
33
|
+
},
|
|
34
|
+
content_type: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'Filter by content type',
|
|
37
|
+
},
|
|
38
|
+
tags: {
|
|
39
|
+
type: 'array',
|
|
40
|
+
items: { type: 'string' },
|
|
41
|
+
description: 'Filter by tags',
|
|
42
|
+
},
|
|
43
|
+
min_weight: {
|
|
44
|
+
type: 'number',
|
|
45
|
+
minimum: 0,
|
|
46
|
+
maximum: 1,
|
|
47
|
+
description: 'Minimum weight/significance (0-1)',
|
|
48
|
+
},
|
|
49
|
+
date_from: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Filter memories created after this date (ISO 8601)',
|
|
52
|
+
},
|
|
53
|
+
date_to: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: 'Filter memories created before this date (ISO 8601)',
|
|
56
|
+
},
|
|
57
|
+
limit: {
|
|
58
|
+
type: 'number',
|
|
59
|
+
default: 10,
|
|
60
|
+
description: 'Maximum number of results',
|
|
61
|
+
},
|
|
62
|
+
format: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
enum: ['detailed', 'compact'],
|
|
65
|
+
default: 'detailed',
|
|
66
|
+
description: 'Output format: detailed (full objects) or compact (text summary)',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: ['question', 'space'],
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
interface QuerySpaceArgs {
|
|
74
|
+
question: string;
|
|
75
|
+
space: string;
|
|
76
|
+
content_type?: string;
|
|
77
|
+
tags?: string[];
|
|
78
|
+
min_weight?: number;
|
|
79
|
+
date_from?: string;
|
|
80
|
+
date_to?: string;
|
|
81
|
+
limit?: number;
|
|
82
|
+
format?: 'detailed' | 'compact';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Handle remember_query_space tool execution
|
|
87
|
+
*/
|
|
88
|
+
export async function handleQuerySpace(
|
|
89
|
+
args: QuerySpaceArgs,
|
|
90
|
+
userId: string // May be used for private spaces in future
|
|
91
|
+
): Promise<string> {
|
|
92
|
+
try {
|
|
93
|
+
// Validate space ID
|
|
94
|
+
if (!isValidSpaceId(args.space)) {
|
|
95
|
+
return JSON.stringify(
|
|
96
|
+
{
|
|
97
|
+
success: false,
|
|
98
|
+
error: 'Invalid space ID',
|
|
99
|
+
message: `Space "${args.space}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(', ')}`,
|
|
100
|
+
},
|
|
101
|
+
null,
|
|
102
|
+
2
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const weaviateClient = getWeaviateClient();
|
|
107
|
+
const spaceCollection = await ensureSpaceCollection(weaviateClient, args.space);
|
|
108
|
+
|
|
109
|
+
// Build filters
|
|
110
|
+
const filterList: any[] = [];
|
|
111
|
+
|
|
112
|
+
// Filter by space_id
|
|
113
|
+
filterList.push(spaceCollection.filter.byProperty('space_id').equal(args.space));
|
|
114
|
+
|
|
115
|
+
// Filter by doc_type (space_memory)
|
|
116
|
+
filterList.push(spaceCollection.filter.byProperty('doc_type').equal('space_memory'));
|
|
117
|
+
|
|
118
|
+
// Apply content type filter
|
|
119
|
+
if (args.content_type) {
|
|
120
|
+
filterList.push(spaceCollection.filter.byProperty('type').equal(args.content_type));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Apply tags filter
|
|
124
|
+
if (args.tags && args.tags.length > 0) {
|
|
125
|
+
args.tags.forEach(tag => {
|
|
126
|
+
filterList.push(spaceCollection.filter.byProperty('tags').containsAny([tag]));
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Apply weight filter
|
|
131
|
+
if (args.min_weight !== undefined) {
|
|
132
|
+
filterList.push(spaceCollection.filter.byProperty('weight').greaterOrEqual(args.min_weight));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Apply date filters
|
|
136
|
+
if (args.date_from) {
|
|
137
|
+
filterList.push(spaceCollection.filter.byProperty('created_at').greaterOrEqual(new Date(args.date_from)));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (args.date_to) {
|
|
141
|
+
filterList.push(spaceCollection.filter.byProperty('created_at').lessOrEqual(new Date(args.date_to)));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const whereFilter = filterList.length > 0 ? Filters.and(...filterList) : undefined;
|
|
145
|
+
|
|
146
|
+
// Execute semantic search using nearText
|
|
147
|
+
const searchResults = await spaceCollection.query.nearText(args.question, {
|
|
148
|
+
limit: args.limit || 10,
|
|
149
|
+
...(whereFilter && { where: whereFilter }),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Format results based on requested format
|
|
153
|
+
const format = args.format || 'detailed';
|
|
154
|
+
|
|
155
|
+
if (format === 'compact') {
|
|
156
|
+
// Compact format: text summary for LLM context
|
|
157
|
+
const summaries = searchResults.objects.map((obj, idx) => {
|
|
158
|
+
const props = obj.properties;
|
|
159
|
+
return `${idx + 1}. ${props.title || props.content?.substring(0, 100) || 'Untitled'}`;
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const result = {
|
|
163
|
+
question: args.question,
|
|
164
|
+
space: args.space,
|
|
165
|
+
format: 'compact',
|
|
166
|
+
summary: summaries.join('\n'),
|
|
167
|
+
count: searchResults.objects.length,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return JSON.stringify(result, null, 2);
|
|
171
|
+
} else {
|
|
172
|
+
// Detailed format: full objects
|
|
173
|
+
const memories = searchResults.objects.map((obj) => ({
|
|
174
|
+
id: obj.uuid,
|
|
175
|
+
...obj.properties,
|
|
176
|
+
_distance: obj.metadata?.distance,
|
|
177
|
+
}));
|
|
178
|
+
|
|
179
|
+
const result = {
|
|
180
|
+
question: args.question,
|
|
181
|
+
space: args.space,
|
|
182
|
+
format: 'detailed',
|
|
183
|
+
memories,
|
|
184
|
+
total: memories.length,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return JSON.stringify(result, null, 2);
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
handleToolError(error, {
|
|
191
|
+
toolName: 'remember_query_space',
|
|
192
|
+
operation: 'query space',
|
|
193
|
+
space: args.space,
|
|
194
|
+
question: args.question,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_search_space tool
|
|
3
|
+
*
|
|
4
|
+
* Search shared spaces to discover memories from other users.
|
|
5
|
+
* Similar to remember_search_memory but searches space collections.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import { Filters } from 'weaviate-client';
|
|
10
|
+
import { getWeaviateClient } from '../weaviate/client.js';
|
|
11
|
+
import { ensureSpaceCollection, isValidSpaceId } from '../weaviate/space-schema.js';
|
|
12
|
+
import { SUPPORTED_SPACES } from '../types/space-memory.js';
|
|
13
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
14
|
+
import type { SearchFilters } from '../types/memory.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Tool definition for remember_search_space
|
|
18
|
+
*/
|
|
19
|
+
export const searchSpaceTool: Tool = {
|
|
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.',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
query: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Search query (semantic + keyword hybrid)',
|
|
28
|
+
},
|
|
29
|
+
space: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'Which space to search',
|
|
32
|
+
enum: SUPPORTED_SPACES,
|
|
33
|
+
default: 'the_void',
|
|
34
|
+
},
|
|
35
|
+
content_type: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'Filter by content type',
|
|
38
|
+
},
|
|
39
|
+
tags: {
|
|
40
|
+
type: 'array',
|
|
41
|
+
items: { type: 'string' },
|
|
42
|
+
description: 'Filter by tags (must have all specified tags)',
|
|
43
|
+
},
|
|
44
|
+
min_weight: {
|
|
45
|
+
type: 'number',
|
|
46
|
+
minimum: 0,
|
|
47
|
+
maximum: 1,
|
|
48
|
+
description: 'Minimum weight/significance (0-1)',
|
|
49
|
+
},
|
|
50
|
+
max_weight: {
|
|
51
|
+
type: 'number',
|
|
52
|
+
minimum: 0,
|
|
53
|
+
maximum: 1,
|
|
54
|
+
description: 'Maximum weight/significance (0-1)',
|
|
55
|
+
},
|
|
56
|
+
date_from: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
description: 'Filter memories created after this date (ISO 8601)',
|
|
59
|
+
},
|
|
60
|
+
date_to: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: 'Filter memories created before this date (ISO 8601)',
|
|
63
|
+
},
|
|
64
|
+
limit: {
|
|
65
|
+
type: 'number',
|
|
66
|
+
default: 10,
|
|
67
|
+
description: 'Maximum number of results',
|
|
68
|
+
},
|
|
69
|
+
offset: {
|
|
70
|
+
type: 'number',
|
|
71
|
+
default: 0,
|
|
72
|
+
description: 'Offset for pagination',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ['query', 'space'],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
interface SearchSpaceArgs {
|
|
80
|
+
query: string;
|
|
81
|
+
space: string;
|
|
82
|
+
content_type?: string;
|
|
83
|
+
tags?: string[];
|
|
84
|
+
min_weight?: number;
|
|
85
|
+
max_weight?: number;
|
|
86
|
+
date_from?: string;
|
|
87
|
+
date_to?: string;
|
|
88
|
+
limit?: number;
|
|
89
|
+
offset?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Handle remember_search_space tool execution
|
|
94
|
+
*/
|
|
95
|
+
export async function handleSearchSpace(
|
|
96
|
+
args: SearchSpaceArgs,
|
|
97
|
+
userId: string // May be used for private spaces in future
|
|
98
|
+
): Promise<string> {
|
|
99
|
+
try {
|
|
100
|
+
// Validate space ID
|
|
101
|
+
if (!isValidSpaceId(args.space)) {
|
|
102
|
+
return JSON.stringify(
|
|
103
|
+
{
|
|
104
|
+
success: false,
|
|
105
|
+
error: 'Invalid space ID',
|
|
106
|
+
message: `Space "${args.space}" is not supported. Supported spaces: ${SUPPORTED_SPACES.join(', ')}`,
|
|
107
|
+
},
|
|
108
|
+
null,
|
|
109
|
+
2
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const weaviateClient = getWeaviateClient();
|
|
114
|
+
const spaceCollection = await ensureSpaceCollection(weaviateClient, args.space);
|
|
115
|
+
|
|
116
|
+
// Build filters for space search
|
|
117
|
+
const filterList: any[] = [];
|
|
118
|
+
|
|
119
|
+
// Filter by space_id
|
|
120
|
+
filterList.push(spaceCollection.filter.byProperty('space_id').equal(args.space));
|
|
121
|
+
|
|
122
|
+
// Filter by doc_type (space_memory)
|
|
123
|
+
filterList.push(spaceCollection.filter.byProperty('doc_type').equal('space_memory'));
|
|
124
|
+
|
|
125
|
+
// Apply content type filter
|
|
126
|
+
if (args.content_type) {
|
|
127
|
+
filterList.push(spaceCollection.filter.byProperty('type').equal(args.content_type));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Apply tags filter
|
|
131
|
+
if (args.tags && args.tags.length > 0) {
|
|
132
|
+
args.tags.forEach(tag => {
|
|
133
|
+
filterList.push(spaceCollection.filter.byProperty('tags').containsAny([tag]));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Apply weight filters
|
|
138
|
+
if (args.min_weight !== undefined) {
|
|
139
|
+
filterList.push(spaceCollection.filter.byProperty('weight').greaterOrEqual(args.min_weight));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (args.max_weight !== undefined) {
|
|
143
|
+
filterList.push(spaceCollection.filter.byProperty('weight').lessOrEqual(args.max_weight));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Apply date filters (convert ISO strings to Date objects)
|
|
147
|
+
if (args.date_from) {
|
|
148
|
+
filterList.push(spaceCollection.filter.byProperty('created_at').greaterOrEqual(new Date(args.date_from)));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (args.date_to) {
|
|
152
|
+
filterList.push(spaceCollection.filter.byProperty('created_at').lessOrEqual(new Date(args.date_to)));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const whereFilter = filterList.length > 0 ? Filters.and(...filterList) : undefined;
|
|
156
|
+
|
|
157
|
+
// Execute hybrid search
|
|
158
|
+
const searchResults = await spaceCollection.query.hybrid(args.query, {
|
|
159
|
+
limit: args.limit || 10,
|
|
160
|
+
offset: args.offset || 0,
|
|
161
|
+
...(whereFilter && { where: whereFilter }),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Format results
|
|
165
|
+
const memories = searchResults.objects.map((obj) => ({
|
|
166
|
+
id: obj.uuid,
|
|
167
|
+
...obj.properties,
|
|
168
|
+
_score: obj.metadata?.score,
|
|
169
|
+
}));
|
|
170
|
+
|
|
171
|
+
const result = {
|
|
172
|
+
space: args.space,
|
|
173
|
+
query: args.query,
|
|
174
|
+
memories,
|
|
175
|
+
total: memories.length,
|
|
176
|
+
offset: args.offset || 0,
|
|
177
|
+
limit: args.limit || 10,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return JSON.stringify(result, null, 2);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
handleToolError(error, {
|
|
183
|
+
toolName: 'remember_search_space',
|
|
184
|
+
operation: 'search space',
|
|
185
|
+
space: args.space,
|
|
186
|
+
query: args.query,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Space Memory type definitions for remember-mcp
|
|
3
|
+
*
|
|
4
|
+
* Space memories are memories published to shared collections
|
|
5
|
+
* where they can be discovered by other users.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Memory, SearchOptions, SearchResult } from './memory.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Space memory - a memory published to a shared space
|
|
12
|
+
*
|
|
13
|
+
* Extends Memory with additional fields for attribution and discovery
|
|
14
|
+
*/
|
|
15
|
+
export interface SpaceMemory extends Omit<Memory, 'user_id' | 'doc_type'> {
|
|
16
|
+
/**
|
|
17
|
+
* Space identifier (snake_case)
|
|
18
|
+
* Examples: 'the_void', 'public_space'
|
|
19
|
+
*/
|
|
20
|
+
space_id: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Original author's user_id (for permissions)
|
|
24
|
+
* This is private and not shown publicly
|
|
25
|
+
*/
|
|
26
|
+
author_id: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Optional ghost profile ID for pseudonymous publishing
|
|
30
|
+
* If present, memory is attributed to ghost instead of user
|
|
31
|
+
*/
|
|
32
|
+
ghost_id?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* When the memory was published to the space
|
|
36
|
+
*/
|
|
37
|
+
published_at: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* How many times this memory has been discovered/viewed
|
|
41
|
+
*/
|
|
42
|
+
discovery_count: number;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Attribution type
|
|
46
|
+
* - 'user': Published as the user (shows author_id)
|
|
47
|
+
* - 'ghost': Published as a ghost (shows ghost_id)
|
|
48
|
+
*/
|
|
49
|
+
attribution: 'user' | 'ghost';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Document type discriminator
|
|
53
|
+
* Always 'space_memory' for space memories
|
|
54
|
+
*/
|
|
55
|
+
doc_type: 'space_memory';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Search options for space memories
|
|
60
|
+
* Same as SearchOptions but for space collections
|
|
61
|
+
*/
|
|
62
|
+
export interface SpaceSearchOptions extends Omit<SearchOptions, 'include_relationships'> {
|
|
63
|
+
/**
|
|
64
|
+
* Space to search
|
|
65
|
+
*/
|
|
66
|
+
space: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Search result for space memories
|
|
71
|
+
*/
|
|
72
|
+
export interface SpaceSearchResult extends Omit<SearchResult, 'memories' | 'relationships'> {
|
|
73
|
+
/**
|
|
74
|
+
* Found space memories
|
|
75
|
+
*/
|
|
76
|
+
space_memories: SpaceMemory[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Supported space IDs
|
|
81
|
+
*/
|
|
82
|
+
export type SpaceId = 'the_void';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Space display names mapped to IDs
|
|
86
|
+
*/
|
|
87
|
+
export const SPACE_DISPLAY_NAMES: Record<SpaceId, string> = {
|
|
88
|
+
the_void: 'The Void',
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Supported spaces constant
|
|
93
|
+
*/
|
|
94
|
+
export const SUPPORTED_SPACES: SpaceId[] = ['the_void'];
|