persistq 1.0.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 +117 -0
- package/package.json +38 -0
- package/pq-mcp-server.js +244 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# PersistQ MCP Server
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for PersistQ - enabling persistent memory management for Claude Code and other AI tools.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
PersistQ MCP Server provides a bridge between AI assistants (like Claude Code) and the PersistQ memory management system. It allows AI tools to:
|
|
8
|
+
|
|
9
|
+
- Store and retrieve memories
|
|
10
|
+
- Search across stored content
|
|
11
|
+
- Organize memories by topics/projects
|
|
12
|
+
- Access persistent memory statistics
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd mcp-server
|
|
18
|
+
npm install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
The server can be configured using environment variables:
|
|
24
|
+
|
|
25
|
+
- `PERSISTQ_URL`: URL of the PersistQ HTTP server (default: `http://localhost:3000`)
|
|
26
|
+
- `PERSISTQ_API_KEY`: API key for authentication (default: auto-generated)
|
|
27
|
+
- `PERSISTQ_TOPIC`: Default topic for memories (default: `ClaudeConversations`)
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Starting the Server
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm start
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Available Tools
|
|
38
|
+
|
|
39
|
+
The MCP server exposes the following tools:
|
|
40
|
+
|
|
41
|
+
#### 1. add_memory
|
|
42
|
+
Add a new memory to PersistQ.
|
|
43
|
+
|
|
44
|
+
**Parameters:**
|
|
45
|
+
- `text` (required): The memory content to store
|
|
46
|
+
- `topic` (optional): Category or topic for this memory
|
|
47
|
+
- `metadata` (optional): Additional metadata object
|
|
48
|
+
|
|
49
|
+
#### 2. search_memory
|
|
50
|
+
Search memories by keyword or topic.
|
|
51
|
+
|
|
52
|
+
**Parameters:**
|
|
53
|
+
- `query` (required): Search query or keyword
|
|
54
|
+
- `topic` (optional): Filter by topic
|
|
55
|
+
- `limit` (optional): Maximum number of results (default: 10)
|
|
56
|
+
|
|
57
|
+
#### 3. get_memory_stats
|
|
58
|
+
Get statistics about stored memories.
|
|
59
|
+
|
|
60
|
+
**Parameters:** None
|
|
61
|
+
|
|
62
|
+
#### 4. list_memories
|
|
63
|
+
List memories with optional filtering.
|
|
64
|
+
|
|
65
|
+
**Parameters:**
|
|
66
|
+
- `project` (optional): Filter by project/tag name
|
|
67
|
+
- `limit` (optional): Maximum results (default: 10)
|
|
68
|
+
- `offset` (optional): Offset for pagination (default: 0)
|
|
69
|
+
|
|
70
|
+
### Available Resources
|
|
71
|
+
|
|
72
|
+
The server provides the following resources:
|
|
73
|
+
|
|
74
|
+
- `persistq://memories/all`: List of all stored memories
|
|
75
|
+
- `persistq://stats`: Overview of memory storage statistics
|
|
76
|
+
|
|
77
|
+
## Integration with Claude Code
|
|
78
|
+
|
|
79
|
+
Add to your Claude Code MCP configuration (`~/.claude/mcp.json`):
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"persistq": {
|
|
85
|
+
"command": "node",
|
|
86
|
+
"args": ["D:/Projects/MemoryHub-Monorepo/mcp-server/pq-mcp-server.js"],
|
|
87
|
+
"env": {
|
|
88
|
+
"PERSISTQ_URL": "http://localhost:3000",
|
|
89
|
+
"PERSISTQ_API_KEY": "your_api_key_here"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Architecture
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Claude Code / AI Tool
|
|
100
|
+
↓
|
|
101
|
+
MCP Protocol (stdio)
|
|
102
|
+
↓
|
|
103
|
+
pq-mcp-server.js
|
|
104
|
+
↓
|
|
105
|
+
HTTP → PersistQ Server (port 3000)
|
|
106
|
+
↓
|
|
107
|
+
Database Storage
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Requirements
|
|
111
|
+
|
|
112
|
+
- Node.js 16+
|
|
113
|
+
- PersistQ HTTP server running on port 3000
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "persistq",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP Server for PersistQ - Persistent Memory Management for Claude Code and AI assistants",
|
|
5
|
+
"main": "pq-mcp-server.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"persistq": "./pq-mcp-server.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node pq-mcp-server.js",
|
|
12
|
+
"dev": "node pq-mcp-server.js"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
16
|
+
"axios": "^1.6.0"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"persistq",
|
|
20
|
+
"mcp",
|
|
21
|
+
"mcp-server",
|
|
22
|
+
"memory",
|
|
23
|
+
"claude",
|
|
24
|
+
"ai",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"claude-code",
|
|
27
|
+
"memory-management"
|
|
28
|
+
],
|
|
29
|
+
"author": "",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=16.0.0"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"pq-mcp-server.js",
|
|
36
|
+
"README.md"
|
|
37
|
+
]
|
|
38
|
+
}
|
package/pq-mcp-server.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PersistQ MCP Server (Stdio Transport)
|
|
4
|
+
* Uses official @modelcontextprotocol/sdk for stdio communication with Claude Code
|
|
5
|
+
* Connects to PersistQ HTTP server on port 3000
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
9
|
+
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
10
|
+
const {
|
|
11
|
+
CallToolRequestSchema,
|
|
12
|
+
ListToolsRequestSchema,
|
|
13
|
+
ListResourcesRequestSchema,
|
|
14
|
+
ReadResourceRequestSchema
|
|
15
|
+
} = require('@modelcontextprotocol/sdk/types.js');
|
|
16
|
+
const axios = require('axios');
|
|
17
|
+
|
|
18
|
+
const PERSISTQ_URL = process.env.PERSISTQ_URL || 'http://localhost:3000';
|
|
19
|
+
const API_KEY = process.env.PERSISTQ_API_KEY || 'pq_9df422f134ef9ec93e8337dd5bde0540dfd3d0de714e6e00379f3f7f174cfdff';
|
|
20
|
+
const TOPIC = process.env.PERSISTQ_TOPIC || 'ClaudeConversations';
|
|
21
|
+
|
|
22
|
+
// Create MCP server
|
|
23
|
+
const server = new Server(
|
|
24
|
+
{
|
|
25
|
+
name: 'persistq-mcp-server',
|
|
26
|
+
version: '1.0.0'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
capabilities: {
|
|
30
|
+
tools: {},
|
|
31
|
+
resources: {}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Tool: list_tools
|
|
37
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
38
|
+
tools: [
|
|
39
|
+
{
|
|
40
|
+
name: 'add_memory',
|
|
41
|
+
description: 'Add a new memory to PersistQ',
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
text: { type: 'string', description: 'The memory content to store' },
|
|
46
|
+
topic: { type: 'string', description: 'Category or topic for this memory', default: TOPIC },
|
|
47
|
+
metadata: { type: 'object', description: 'Additional metadata' }
|
|
48
|
+
},
|
|
49
|
+
required: ['text']
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'search_memory',
|
|
54
|
+
description: 'Search memories by keyword or topic',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
query: { type: 'string', description: 'Search query or keyword' },
|
|
59
|
+
topic: { type: 'string', description: 'Filter by topic' },
|
|
60
|
+
limit: { type: 'number', description: 'Maximum results', default: 10 }
|
|
61
|
+
},
|
|
62
|
+
required: ['query']
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'get_memory_stats',
|
|
67
|
+
description: 'Get statistics about stored memories',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'list_memories',
|
|
75
|
+
description: 'List memories with optional filtering by project/tag',
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {
|
|
79
|
+
project: { type: 'string', description: 'Filter by project/tag name' },
|
|
80
|
+
limit: { type: 'number', description: 'Maximum results', default: 10 },
|
|
81
|
+
offset: { type: 'number', description: 'Offset for pagination', default: 0 }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
// Tool: call_tool
|
|
89
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
90
|
+
const { name, arguments: args } = request.params;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
switch (name) {
|
|
94
|
+
case 'add_memory': {
|
|
95
|
+
const response = await axios.post(`${PERSISTQ_URL}/api/memory`, {
|
|
96
|
+
content: args.text,
|
|
97
|
+
project: args.topic || TOPIC,
|
|
98
|
+
metadata: args.metadata || {}
|
|
99
|
+
}, {
|
|
100
|
+
headers: {
|
|
101
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
102
|
+
'Content-Type': 'application/json'
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const memoryId = response.data.data?.memoryId || response.data.memory?.id || response.data.id || 'OK';
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: 'text', text: `Memory added successfully: ${memoryId}` }]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
case 'search_memory': {
|
|
112
|
+
const response = await axios.post(`${PERSISTQ_URL}/api/memory/search`, {
|
|
113
|
+
query: args.query,
|
|
114
|
+
limit: args.limit || 10,
|
|
115
|
+
threshold: 0.7
|
|
116
|
+
}, {
|
|
117
|
+
headers: {
|
|
118
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
119
|
+
'Content-Type': 'application/json'
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
const results = response.data.data || response.data.memories || [];
|
|
123
|
+
const text = results.length > 0
|
|
124
|
+
? results.map(r => `[${r.id}] ${(r.text || r.content || '').substring(0, 200)}... (Score: ${r.score?.toFixed(2) || 'N/A'})`).join('\n\n')
|
|
125
|
+
: 'No memories found';
|
|
126
|
+
return {
|
|
127
|
+
content: [{ type: 'text', text }]
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
case 'get_memory_stats': {
|
|
132
|
+
const response = await axios.get(`${PERSISTQ_URL}/api/memory/stats`, {
|
|
133
|
+
headers: {
|
|
134
|
+
'Authorization': `Bearer ${API_KEY}`
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
const stats = response.data.data || response.data.stats || response.data || {};
|
|
138
|
+
return {
|
|
139
|
+
content: [{ type: 'text', text: JSON.stringify(stats, null, 2) }]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
case 'list_memories': {
|
|
144
|
+
const params = new URLSearchParams();
|
|
145
|
+
if (args.project) params.append('project', args.project);
|
|
146
|
+
if (args.limit) params.append('limit', String(args.limit));
|
|
147
|
+
if (args.offset) params.append('offset', String(args.offset));
|
|
148
|
+
|
|
149
|
+
const response = await axios.get(`${PERSISTQ_URL}/api/memory/list?${params.toString()}`, {
|
|
150
|
+
headers: {
|
|
151
|
+
'Authorization': `Bearer ${API_KEY}`
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const memories = response.data.data?.memories || response.data.memories || [];
|
|
155
|
+
const text = memories.length > 0
|
|
156
|
+
? memories.map(m => `[${m.id}]\nProject: ${m.project}\nContent: ${m.content}\nCreated: ${m.createdAt}\n`).join('\n')
|
|
157
|
+
: 'No memories found';
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: 'text', text }]
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
default:
|
|
164
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
169
|
+
isError: true
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Resources: list_resources
|
|
175
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
176
|
+
resources: [
|
|
177
|
+
{
|
|
178
|
+
uri: 'persistq://memories/all',
|
|
179
|
+
name: 'All Memories',
|
|
180
|
+
description: 'List of all stored memories',
|
|
181
|
+
mimeType: 'application/json'
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
uri: 'persistq://stats',
|
|
185
|
+
name: 'Memory Statistics',
|
|
186
|
+
description: 'Overview of memory storage',
|
|
187
|
+
mimeType: 'application/json'
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}));
|
|
191
|
+
|
|
192
|
+
// Resources: read_resource
|
|
193
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
194
|
+
const { uri } = request.params;
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
if (uri === 'persistq://memories/all') {
|
|
198
|
+
const response = await axios.get(`${PERSISTQ_URL}/api/memory/list?limit=100&offset=0`, {
|
|
199
|
+
headers: {
|
|
200
|
+
'Authorization': `Bearer ${API_KEY}`
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
contents: [{
|
|
205
|
+
uri,
|
|
206
|
+
mimeType: 'application/json',
|
|
207
|
+
text: JSON.stringify(response.data.memories || [], null, 2)
|
|
208
|
+
}]
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (uri === 'persistq://stats') {
|
|
213
|
+
const response = await axios.get(`${PERSISTQ_URL}/api/memory/stats`, {
|
|
214
|
+
headers: {
|
|
215
|
+
'Authorization': `Bearer ${API_KEY}`
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
const stats = response.data.stats || response.data || {};
|
|
219
|
+
return {
|
|
220
|
+
contents: [{
|
|
221
|
+
uri,
|
|
222
|
+
mimeType: 'application/json',
|
|
223
|
+
text: JSON.stringify(stats, null, 2)
|
|
224
|
+
}]
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
throw new Error(`Failed to read resource: ${error.message}`);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Start server with stdio transport
|
|
235
|
+
async function main() {
|
|
236
|
+
const transport = new StdioServerTransport();
|
|
237
|
+
await server.connect(transport);
|
|
238
|
+
console.error('[PersistQ MCP Server] Started on stdio');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
main().catch((error) => {
|
|
242
|
+
console.error('[PersistQ MCP Server] Fatal error:', error);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
});
|