persistq 1.0.0 → 1.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.1.0] - 2025-12-06
9
+
10
+ ### Added
11
+ - **GitHub Copilot CLI Support**: Full compatibility with GitHub Copilot CLI MCP integration
12
+ - Global error handlers for uncaught exceptions and unhandled promise rejections
13
+ - Enhanced logging with configuration details on startup
14
+ - Protocol version logging (supports 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07)
15
+ - Detailed error logging for tool and resource operations
16
+ - Enhanced error messages with HTTP status codes and response data
17
+ - Comprehensive README documentation for GitHub Copilot CLI setup
18
+
19
+ ### Changed
20
+ - Updated `@modelcontextprotocol/sdk` from `^1.20.2` to `^1.24.0`
21
+ - Improved error handling with structured stderr logging
22
+ - Enhanced startup logging with environment variable status
23
+ - Updated README with Copilot CLI configuration instructions
24
+ - Added MCP Inspector testing instructions
25
+
26
+ ### Fixed
27
+ - All logging now correctly outputs to stderr (stdout reserved for MCP protocol only)
28
+ - Enhanced error context for better debugging
29
+
30
+ ### Notes
31
+ - **Breaking Changes**: None - fully backward compatible with Claude Code
32
+ - **GitHub Copilot CLI**: Only supports MCP tools (not resources) - this is a Copilot limitation, not a package limitation
33
+ - **Claude Code**: Continues to support both tools and resources
34
+
35
+ ## [1.0.0] - 2024-11-29
36
+
37
+ ### Added
38
+ - Initial release
39
+ - MCP Server implementation for PersistQ
40
+ - 4 MCP tools: `add_memory`, `search_memory`, `list_memories`, `get_memory_stats`
41
+ - 2 MCP resources: `persistq://memories/all`, `persistq://stats`
42
+ - Claude Code integration support
43
+ - Stdio transport for MCP communication
44
+ - Environment variable configuration (PERSISTQ_URL, PERSISTQ_API_KEY, PERSISTQ_TOPIC)
45
+ - Comprehensive README with setup instructions
46
+
47
+ [1.1.0]: https://github.com/yourusername/persistq/compare/v1.0.0...v1.1.0
48
+ [1.0.0]: https://github.com/yourusername/persistq/releases/tag/v1.0.0
package/README.md CHANGED
@@ -1,117 +1,155 @@
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
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
+ ## Integration with GitHub Copilot CLI
97
+
98
+ Add to your Copilot CLI MCP configuration (`~/.copilot/mcp-config.json` or `~/.config/mcp-config.json`):
99
+
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "persistq": {
104
+ "command": "node",
105
+ "args": ["D:/Projects/MemoryHub-Monorepo/mcp-server/pq-mcp-server.js"],
106
+ "env": {
107
+ "PERSISTQ_URL": "http://localhost:3000",
108
+ "PERSISTQ_API_KEY": "${PERSISTQ_API_KEY}"
109
+ }
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ **Important Notes:**
116
+ - GitHub Copilot CLI currently **only supports MCP tools** (not resources or prompts)
117
+ - The following 4 tools are available in Copilot CLI:
118
+ - `add_memory` - Store new memories
119
+ - `search_memory` - Search stored memories
120
+ - `get_memory_stats` - Get memory statistics
121
+ - `list_memories` - List memories with filtering
122
+ - Resources (`persistq://memories/all`, `persistq://stats`) are only available in Claude Code
123
+
124
+ ### Testing with MCP Inspector
125
+
126
+ Validate the server using the official MCP Inspector:
127
+
128
+ ```bash
129
+ npx @modelcontextprotocol/inspector node D:/Projects/MemoryHub-Monorepo/mcp-server/pq-mcp-server.js
130
+ ```
131
+
132
+ This will open a web interface to test protocol compliance, tool execution, and error handling.
133
+
134
+ ## Architecture
135
+
136
+ ```
137
+ Claude Code / AI Tool
138
+
139
+ MCP Protocol (stdio)
140
+
141
+ pq-mcp-server.js
142
+
143
+ HTTP → PersistQ Server (port 3000)
144
+
145
+ Database Storage
146
+ ```
147
+
148
+ ## Requirements
149
+
150
+ - Node.js 16+
151
+ - PersistQ HTTP server running on port 3000
152
+
153
+ ## License
154
+
155
+ MIT
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "persistq",
3
- "version": "1.0.0",
4
- "description": "MCP Server for PersistQ - Persistent Memory Management for Claude Code and AI assistants",
3
+ "version": "1.1.0",
4
+ "description": "MCP Server for PersistQ - Persistent Memory Management for Claude Code, GitHub Copilot CLI, and AI assistants",
5
5
  "main": "pq-mcp-server.js",
6
6
  "type": "commonjs",
7
7
  "bin": {
@@ -12,7 +12,7 @@
12
12
  "dev": "node pq-mcp-server.js"
13
13
  },
14
14
  "dependencies": {
15
- "@modelcontextprotocol/sdk": "^1.20.2",
15
+ "@modelcontextprotocol/sdk": "^1.24.0",
16
16
  "axios": "^1.6.0"
17
17
  },
18
18
  "keywords": [
@@ -21,10 +21,14 @@
21
21
  "mcp-server",
22
22
  "memory",
23
23
  "claude",
24
+ "copilot",
25
+ "github-copilot",
24
26
  "ai",
25
27
  "model-context-protocol",
26
28
  "claude-code",
27
- "memory-management"
29
+ "memory-management",
30
+ "persistent-memory",
31
+ "ai-memory"
28
32
  ],
29
33
  "author": "",
30
34
  "license": "MIT",
@@ -33,6 +37,7 @@
33
37
  },
34
38
  "files": [
35
39
  "pq-mcp-server.js",
36
- "README.md"
40
+ "README.md",
41
+ "CHANGELOG.md"
37
42
  ]
38
43
  }
package/pq-mcp-server.js CHANGED
@@ -1,244 +1,278 @@
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
- });
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
+ // Global error handlers - all logs MUST go to stderr to keep stdout clean
23
+ process.on('uncaughtException', (error) => {
24
+ console.error('[PersistQ MCP Server] FATAL: Uncaught exception:', error.message);
25
+ console.error(error.stack);
26
+ process.exit(1);
27
+ });
28
+
29
+ process.on('unhandledRejection', (reason, promise) => {
30
+ console.error('[PersistQ MCP Server] FATAL: Unhandled promise rejection:', reason);
31
+ process.exit(1);
32
+ });
33
+
34
+ // Log configuration on startup (stderr only)
35
+ console.error('[PersistQ MCP Server] Configuration:');
36
+ console.error(` PERSISTQ_URL: ${PERSISTQ_URL}`);
37
+ console.error(` PERSISTQ_TOPIC: ${TOPIC}`);
38
+ console.error(` API Key: ${API_KEY ? '[SET]' : '[NOT SET]'}`);
39
+
40
+ // Create MCP server
41
+ const server = new Server(
42
+ {
43
+ name: 'persistq-mcp-server',
44
+ version: '1.0.0'
45
+ },
46
+ {
47
+ capabilities: {
48
+ tools: {},
49
+ resources: {}
50
+ // Note: GitHub Copilot CLI only supports tools (not resources)
51
+ // Resources remain available for Claude Code compatibility
52
+ }
53
+ }
54
+ );
55
+
56
+ // Tool: list_tools
57
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
58
+ tools: [
59
+ {
60
+ name: 'add_memory',
61
+ description: 'Add a new memory to PersistQ',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ text: { type: 'string', description: 'The memory content to store' },
66
+ topic: { type: 'string', description: 'Category or topic for this memory', default: TOPIC },
67
+ metadata: { type: 'object', description: 'Additional metadata' }
68
+ },
69
+ required: ['text']
70
+ }
71
+ },
72
+ {
73
+ name: 'search_memory',
74
+ description: 'Search memories by keyword or topic',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {
78
+ query: { type: 'string', description: 'Search query or keyword' },
79
+ topic: { type: 'string', description: 'Filter by topic' },
80
+ limit: { type: 'number', description: 'Maximum results', default: 10 }
81
+ },
82
+ required: ['query']
83
+ }
84
+ },
85
+ {
86
+ name: 'get_memory_stats',
87
+ description: 'Get statistics about stored memories',
88
+ inputSchema: {
89
+ type: 'object',
90
+ properties: {}
91
+ }
92
+ },
93
+ {
94
+ name: 'list_memories',
95
+ description: 'List memories with optional filtering by project/tag',
96
+ inputSchema: {
97
+ type: 'object',
98
+ properties: {
99
+ project: { type: 'string', description: 'Filter by project/tag name' },
100
+ limit: { type: 'number', description: 'Maximum results', default: 10 },
101
+ offset: { type: 'number', description: 'Offset for pagination', default: 0 }
102
+ }
103
+ }
104
+ }
105
+ ]
106
+ }));
107
+
108
+ // Tool: call_tool
109
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
110
+ const { name, arguments: args } = request.params;
111
+
112
+ try {
113
+ switch (name) {
114
+ case 'add_memory': {
115
+ const response = await axios.post(`${PERSISTQ_URL}/api/memory`, {
116
+ content: args.text,
117
+ project: args.topic || TOPIC,
118
+ metadata: args.metadata || {}
119
+ }, {
120
+ headers: {
121
+ 'Authorization': `Bearer ${API_KEY}`,
122
+ 'Content-Type': 'application/json'
123
+ }
124
+ });
125
+ const memoryId = response.data.data?.memoryId || response.data.memory?.id || response.data.id || 'OK';
126
+ return {
127
+ content: [{ type: 'text', text: `Memory added successfully: ${memoryId}` }]
128
+ };
129
+ }
130
+
131
+ case 'search_memory': {
132
+ const response = await axios.post(`${PERSISTQ_URL}/api/memory/search`, {
133
+ query: args.query,
134
+ limit: args.limit || 10,
135
+ threshold: 0.7
136
+ }, {
137
+ headers: {
138
+ 'Authorization': `Bearer ${API_KEY}`,
139
+ 'Content-Type': 'application/json'
140
+ }
141
+ });
142
+ const results = response.data.data || response.data.memories || [];
143
+ const text = results.length > 0
144
+ ? results.map(r => `[${r.id}] ${(r.text || r.content || '').substring(0, 200)}... (Score: ${r.score?.toFixed(2) || 'N/A'})`).join('\n\n')
145
+ : 'No memories found';
146
+ return {
147
+ content: [{ type: 'text', text }]
148
+ };
149
+ }
150
+
151
+ case 'get_memory_stats': {
152
+ const response = await axios.get(`${PERSISTQ_URL}/api/memory/stats`, {
153
+ headers: {
154
+ 'Authorization': `Bearer ${API_KEY}`
155
+ }
156
+ });
157
+ const stats = response.data.data || response.data.stats || response.data || {};
158
+ return {
159
+ content: [{ type: 'text', text: JSON.stringify(stats, null, 2) }]
160
+ };
161
+ }
162
+
163
+ case 'list_memories': {
164
+ const params = new URLSearchParams();
165
+ if (args.project) params.append('project', args.project);
166
+ if (args.limit) params.append('limit', String(args.limit));
167
+ if (args.offset) params.append('offset', String(args.offset));
168
+
169
+ const response = await axios.get(`${PERSISTQ_URL}/api/memory/list?${params.toString()}`, {
170
+ headers: {
171
+ 'Authorization': `Bearer ${API_KEY}`
172
+ }
173
+ });
174
+ const memories = response.data.data?.memories || response.data.memories || [];
175
+ const text = memories.length > 0
176
+ ? memories.map(m => `[${m.id}]\nProject: ${m.project}\nContent: ${m.content}\nCreated: ${m.createdAt}\n`).join('\n')
177
+ : 'No memories found';
178
+ return {
179
+ content: [{ type: 'text', text }]
180
+ };
181
+ }
182
+
183
+ default:
184
+ throw new Error(`Unknown tool: ${name}`);
185
+ }
186
+ } catch (error) {
187
+ // Log error to stderr for debugging (never stdout)
188
+ console.error(`[PersistQ MCP Server] Tool '${name}' error:`, error.message);
189
+ if (error.response) {
190
+ console.error(` HTTP Status: ${error.response.status}`);
191
+ console.error(` Response Data:`, JSON.stringify(error.response.data));
192
+ }
193
+
194
+ return {
195
+ content: [{
196
+ type: 'text',
197
+ text: `Error executing ${name}: ${error.message}`
198
+ }],
199
+ isError: true
200
+ };
201
+ }
202
+ });
203
+
204
+ // Resources: list_resources
205
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
206
+ resources: [
207
+ {
208
+ uri: 'persistq://memories/all',
209
+ name: 'All Memories',
210
+ description: 'List of all stored memories',
211
+ mimeType: 'application/json'
212
+ },
213
+ {
214
+ uri: 'persistq://stats',
215
+ name: 'Memory Statistics',
216
+ description: 'Overview of memory storage',
217
+ mimeType: 'application/json'
218
+ }
219
+ ]
220
+ }));
221
+
222
+ // Resources: read_resource
223
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
224
+ const { uri } = request.params;
225
+
226
+ try {
227
+ if (uri === 'persistq://memories/all') {
228
+ const response = await axios.get(`${PERSISTQ_URL}/api/memory/list?limit=100&offset=0`, {
229
+ headers: {
230
+ 'Authorization': `Bearer ${API_KEY}`
231
+ }
232
+ });
233
+ return {
234
+ contents: [{
235
+ uri,
236
+ mimeType: 'application/json',
237
+ text: JSON.stringify(response.data.memories || [], null, 2)
238
+ }]
239
+ };
240
+ }
241
+
242
+ if (uri === 'persistq://stats') {
243
+ const response = await axios.get(`${PERSISTQ_URL}/api/memory/stats`, {
244
+ headers: {
245
+ 'Authorization': `Bearer ${API_KEY}`
246
+ }
247
+ });
248
+ const stats = response.data.stats || response.data || {};
249
+ return {
250
+ contents: [{
251
+ uri,
252
+ mimeType: 'application/json',
253
+ text: JSON.stringify(stats, null, 2)
254
+ }]
255
+ };
256
+ }
257
+
258
+ throw new Error(`Unknown resource: ${uri}`);
259
+ } catch (error) {
260
+ console.error(`[PersistQ MCP Server] Resource read error for '${uri}':`, error.message);
261
+ throw new Error(`Failed to read resource '${uri}': ${error.message}`);
262
+ }
263
+ });
264
+
265
+ // Start server with stdio transport
266
+ async function main() {
267
+ const transport = new StdioServerTransport();
268
+ await server.connect(transport);
269
+ console.error('[PersistQ MCP Server] Started on stdio');
270
+ console.error('[PersistQ MCP Server] Protocol versions supported: 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07');
271
+ console.error('[PersistQ MCP Server] Ready to receive requests');
272
+ }
273
+
274
+ main().catch((error) => {
275
+ console.error('[PersistQ MCP Server] Fatal startup error:', error.message);
276
+ console.error('Stack trace:', error.stack);
277
+ process.exit(1);
278
+ });