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.
Files changed (3) hide show
  1. package/README.md +117 -0
  2. package/package.json +38 -0
  3. 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
+ }
@@ -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
+ });