@pragmatic-growth/memory-mcp 1.0.0 → 2.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.
Files changed (3) hide show
  1. package/dist/index.js +114 -32
  2. package/package.json +1 -1
  3. package/src/index.ts +119 -37
package/dist/index.js CHANGED
@@ -71,11 +71,11 @@ async function callRemoteServer(method, params) {
71
71
  */
72
72
  async function initializeRemoteSession() {
73
73
  await callRemoteServer('initialize', {
74
- protocolVersion: '2024-11-05',
74
+ protocolVersion: '2025-11-25',
75
75
  capabilities: {},
76
76
  clientInfo: {
77
77
  name: 'pg-memory-stdio',
78
- version: '1.0.0',
78
+ version: '2.1.0',
79
79
  },
80
80
  });
81
81
  }
@@ -95,81 +95,163 @@ async function main() {
95
95
  // Create local MCP server
96
96
  const server = new McpServer({
97
97
  name: 'pg-memory',
98
- version: '1.0.0',
98
+ version: '2.1.0',
99
99
  description: `PG-Memory knowledge base (${modeLabel} mode)`,
100
100
  });
101
- // Register tools that proxy to remote server
102
- // These match the tools registered on the remote server
103
- server.tool('search_tax_knowledge', 'Search the US non-resident tax compliance knowledge base using semantic similarity', {
104
- query: z.string().describe('The tax question or topic to search for'),
101
+ // ============================================================
102
+ // READ-ONLY TOOLS (available in all modes)
103
+ // ============================================================
104
+ // Tool 1: Search Knowledge (matches HTTP server's search_knowledge)
105
+ server.tool('search_knowledge', `Search the nonresident.tax knowledge base using semantic similarity.
106
+
107
+ This knowledge base contains comprehensive information about:
108
+ - Services: LLC formation, tax filing, registered agent, accounting
109
+ - Pricing: Package costs, annual fees, consultation fees
110
+ - Tax compliance: Form 5472, Form 1120, ECI rules, penalties, deadlines
111
+ - Company formation: Wyoming LLC, Delaware LLC, single-member LLC
112
+ - Banking: Mercury, Wise, US bank account requirements
113
+ - Process: Onboarding steps, document requirements, timelines
114
+ - Dissolution: LLC closure, final filings, IRS compliance
115
+
116
+ Returns relevant articles with relevance scores. Use get_article to retrieve full content.`, {
117
+ query: z.string().describe('The question or topic to search for'),
105
118
  limit: z.number().optional().describe('Maximum number of results to return (default: 5)'),
106
119
  threshold: z.number().optional().describe('Minimum similarity threshold 0-1 (default: 0.55)'),
107
120
  }, async (args) => {
108
121
  const result = await callRemoteServer('tools/call', {
109
- name: 'search_tax_knowledge',
122
+ name: 'search_knowledge',
110
123
  arguments: args,
111
124
  });
112
125
  return result;
113
126
  });
114
- server.tool('answer_question', 'Answer a tax question using the knowledge base with RAG', {
115
- question: z.string().describe('The tax question to answer'),
116
- context: z.string().optional().describe('Additional context'),
127
+ // Tool 2: Get Article
128
+ server.tool('get_article', `Retrieve the complete content of a knowledge base article by ID.
129
+
130
+ Use this tool when:
131
+ - You have an article ID from search_knowledge or list_articles
132
+ - You need the full article content (not just summary)
133
+ - You want to verify specific details from search results
134
+
135
+ Returns: Full article with title, content, summary, category, tags, version, and last update date.`, {
136
+ article_id: z.string().describe('The article ID (e.g., art_abc123)'),
117
137
  }, async (args) => {
118
138
  const result = await callRemoteServer('tools/call', {
119
- name: 'answer_question',
139
+ name: 'get_article',
120
140
  arguments: args,
121
141
  });
122
142
  return result;
123
143
  });
124
- server.tool('get_article', 'Retrieve the complete content of a knowledge base article by its ID', {
125
- article_id: z.string().describe('The article ID (e.g., art_abc123)'),
144
+ // Tool 3: Answer Question (Full RAG)
145
+ server.tool('answer_question', `Answer a customer question using the knowledge base with full RAG.
146
+
147
+ This tool:
148
+ 1. Searches for relevant articles in the knowledge base
149
+ 2. If found: Generates an answer synthesizing the retrieved content
150
+ 3. If not found: Optionally generates a new article to fill the gap
151
+
152
+ Use for questions about:
153
+ - Services and pricing
154
+ - Tax filing requirements and deadlines
155
+ - LLC formation process
156
+ - Bank account options
157
+ - Document requirements
158
+ - How onboarding works
159
+ - Any nonresident.tax topic
160
+
161
+ The answer includes source references for transparency.`, {
162
+ question: z.string().describe('The customer question to answer'),
163
+ context: z.string().optional().describe('Additional context about the question'),
164
+ generate_if_not_found: z.boolean().optional().describe('Generate new article if no relevant content found (default: true)'),
126
165
  }, async (args) => {
127
166
  const result = await callRemoteServer('tools/call', {
128
- name: 'get_article',
167
+ name: 'answer_question',
129
168
  arguments: args,
130
169
  });
131
170
  return result;
132
171
  });
133
- server.tool('list_articles', 'List recent articles from the knowledge base', {
134
- limit: z.number().optional().describe('Maximum articles (default: 10)'),
135
- category: z.string().optional().describe('Filter by category'),
172
+ // Tool 4: Log Unanswered
173
+ server.tool('log_unanswered', `Flag a customer question as unanswered for knowledge gap analysis.
174
+
175
+ WHEN TO USE:
176
+ - After search_knowledge returns no relevant results
177
+ - When existing articles don't fully answer the question
178
+ - When a customer asks something completely new
179
+ - To track recurring questions that need dedicated articles
180
+
181
+ WHY IT MATTERS:
182
+ - Flagged questions appear in the admin "Knowledge Gaps" section
183
+ - Helps prioritize which new articles to create
184
+ - Builds a list of topics customers actually need
185
+ - Improves the knowledge base over time`, {
186
+ query: z.string().describe('The question that could not be answered'),
187
+ reason: z.string().optional().describe('Reason why the question could not be answered'),
136
188
  }, async (args) => {
137
189
  const result = await callRemoteServer('tools/call', {
138
- name: 'list_articles',
190
+ name: 'log_unanswered',
139
191
  arguments: args,
140
192
  });
141
193
  return result;
142
194
  });
143
- server.tool('log_unanswered', 'Explicitly flag a question as unanswered for gap analysis', {
144
- query: z.string().describe('The question that could not be answered'),
145
- reason: z.string().optional().describe('Reason why the question could not be answered'),
195
+ // Tool 5: List Articles
196
+ server.tool('list_articles', `Browse articles in the knowledge base with optional filtering.
197
+
198
+ Parameters:
199
+ - limit: Number of articles (default 10, max 50)
200
+ - category: Filter by category slug (use list_categories first)
201
+
202
+ Returns article ID, title, and summary for each match.
203
+ Use get_article with the ID to retrieve full content.
204
+
205
+ WORKFLOW:
206
+ 1. Call list_categories to discover available categories
207
+ 2. Call list_articles with specific category for focused results
208
+ 3. Call get_article for articles that look relevant`, {
209
+ limit: z.number().optional().describe('Number of articles to return (max 50)'),
210
+ category: z.string().optional().describe('Filter by category slug'),
146
211
  }, async (args) => {
147
212
  const result = await callRemoteServer('tools/call', {
148
- name: 'log_unanswered',
213
+ name: 'list_articles',
149
214
  arguments: args,
150
215
  });
151
216
  return result;
152
217
  });
153
- server.tool('health_check', 'Check the health status of the knowledge base system', {}, async () => {
218
+ // Tool 6: Health Check
219
+ server.tool('health_check', 'Check the health status of the memory system.', {}, async () => {
154
220
  const result = await callRemoteServer('tools/call', {
155
221
  name: 'health_check',
156
222
  arguments: {},
157
223
  });
158
224
  return result;
159
225
  });
160
- // Tool 7: List Categories (available in all modes)
161
- server.tool('list_categories', 'List all categories in the knowledge base with article counts', {}, async () => {
226
+ // Tool 7: List Categories
227
+ server.tool('list_categories', `List all categories in the knowledge base with article counts.
228
+
229
+ CRITICAL FOR EFFICIENT SEARCH:
230
+ Categories help you quickly narrow down to relevant content.
231
+
232
+ Returns for each category:
233
+ - slug: Category identifier for filtering
234
+ - name: Human-readable name
235
+ - count: Number of articles
236
+ - description: What the category covers
237
+
238
+ RECOMMENDED WORKFLOW:
239
+ 1. Call list_categories to see what's available
240
+ 2. Call list_articles with category slug
241
+ 3. Call get_article for full content
242
+ 4. Or use search_knowledge for semantic search across all`, {}, async () => {
162
243
  const result = await callRemoteServer('tools/call', {
163
244
  name: 'list_categories',
164
245
  arguments: {},
165
246
  });
166
247
  return result;
167
248
  });
168
- // === FULL MODE TOOLS ===
169
- // These tools are only available when --full flag is passed or MCP_MODE=full
249
+ // ============================================================
250
+ // FULL MODE TOOLS (only available with --full flag or MCP_MODE=full)
251
+ // ============================================================
170
252
  if (isFullMode) {
171
253
  // Tool 8: Add Article
172
- server.tool('add_article', 'Create a new article in the knowledge base (full mode only)', {
254
+ server.tool('add_article', 'Create a new article in the knowledge base. Automatically generates embedding for semantic search.', {
173
255
  title: z.string().describe('Article title'),
174
256
  content: z.string().describe('Article content in markdown format'),
175
257
  summary: z.string().optional().describe('Brief summary (auto-generated if not provided)'),
@@ -184,7 +266,7 @@ async function main() {
184
266
  return result;
185
267
  });
186
268
  // Tool 9: Edit Article
187
- server.tool('edit_article', 'Update an existing article in the knowledge base (full mode only)', {
269
+ server.tool('edit_article', 'Update an existing article. Re-generates embedding if content changes.', {
188
270
  article_id: z.string().describe('Article ID to update'),
189
271
  title: z.string().optional().describe('New title'),
190
272
  content: z.string().optional().describe('New content'),
@@ -199,7 +281,7 @@ async function main() {
199
281
  return result;
200
282
  });
201
283
  // Tool 10: Remove Article
202
- server.tool('remove_article', 'Soft-delete an article from the knowledge base (full mode only)', {
284
+ server.tool('remove_article', 'Soft-delete an article from the knowledge base. Article is marked as deleted but not permanently removed.', {
203
285
  article_id: z.string().describe('Article ID to remove'),
204
286
  reason: z.string().optional().describe('Reason for deletion (for audit log)'),
205
287
  }, async (args) => {
@@ -210,7 +292,7 @@ async function main() {
210
292
  return result;
211
293
  });
212
294
  // Tool 11: Rate Answer
213
- server.tool('rate_answer', 'Rate a previous answer as helpful, unhelpful, or neutral (full mode only)', {
295
+ server.tool('rate_answer', 'Rate a previous answer as helpful, unhelpful, or neutral. Helps improve the knowledge base.', {
214
296
  query_id: z.string().describe('Query log ID from a previous answer_question response'),
215
297
  rating: z.number().describe('Rating: -1 (unhelpful), 0 (neutral), 1 (helpful)'),
216
298
  feedback: z.string().optional().describe('Optional text feedback'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pragmatic-growth/memory-mcp",
3
- "version": "1.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "MCP stdio client for PG-Memory knowledge base - connect AI agents to your PostgreSQL knowledge base via Model Context Protocol",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -93,11 +93,11 @@ async function callRemoteServer(method: string, params?: Record<string, unknown>
93
93
  */
94
94
  async function initializeRemoteSession(): Promise<void> {
95
95
  await callRemoteServer('initialize', {
96
- protocolVersion: '2024-11-05',
96
+ protocolVersion: '2025-11-25',
97
97
  capabilities: {},
98
98
  clientInfo: {
99
99
  name: 'pg-memory-stdio',
100
- version: '1.0.0',
100
+ version: '2.1.0',
101
101
  },
102
102
  });
103
103
  }
@@ -119,96 +119,162 @@ async function main(): Promise<void> {
119
119
  // Create local MCP server
120
120
  const server = new McpServer({
121
121
  name: 'pg-memory',
122
- version: '1.0.0',
122
+ version: '2.1.0',
123
123
  description: `PG-Memory knowledge base (${modeLabel} mode)`,
124
124
  });
125
125
 
126
- // Register tools that proxy to remote server
127
- // These match the tools registered on the remote server
126
+ // ============================================================
127
+ // READ-ONLY TOOLS (available in all modes)
128
+ // ============================================================
128
129
 
130
+ // Tool 1: Search Knowledge (matches HTTP server's search_knowledge)
129
131
  server.tool(
130
- 'search_tax_knowledge',
131
- 'Search the US non-resident tax compliance knowledge base using semantic similarity',
132
+ 'search_knowledge',
133
+ `Search the nonresident.tax knowledge base using semantic similarity.
134
+
135
+ This knowledge base contains comprehensive information about:
136
+ - Services: LLC formation, tax filing, registered agent, accounting
137
+ - Pricing: Package costs, annual fees, consultation fees
138
+ - Tax compliance: Form 5472, Form 1120, ECI rules, penalties, deadlines
139
+ - Company formation: Wyoming LLC, Delaware LLC, single-member LLC
140
+ - Banking: Mercury, Wise, US bank account requirements
141
+ - Process: Onboarding steps, document requirements, timelines
142
+ - Dissolution: LLC closure, final filings, IRS compliance
143
+
144
+ Returns relevant articles with relevance scores. Use get_article to retrieve full content.`,
132
145
  {
133
- query: z.string().describe('The tax question or topic to search for'),
146
+ query: z.string().describe('The question or topic to search for'),
134
147
  limit: z.number().optional().describe('Maximum number of results to return (default: 5)'),
135
148
  threshold: z.number().optional().describe('Minimum similarity threshold 0-1 (default: 0.55)'),
136
149
  },
137
150
  async (args) => {
138
151
  const result = await callRemoteServer('tools/call', {
139
- name: 'search_tax_knowledge',
152
+ name: 'search_knowledge',
140
153
  arguments: args,
141
154
  }) as { content: Array<{ type: 'text'; text: string }> };
142
155
  return result;
143
156
  }
144
157
  );
145
158
 
159
+ // Tool 2: Get Article
146
160
  server.tool(
147
- 'answer_question',
148
- 'Answer a tax question using the knowledge base with RAG',
161
+ 'get_article',
162
+ `Retrieve the complete content of a knowledge base article by ID.
163
+
164
+ Use this tool when:
165
+ - You have an article ID from search_knowledge or list_articles
166
+ - You need the full article content (not just summary)
167
+ - You want to verify specific details from search results
168
+
169
+ Returns: Full article with title, content, summary, category, tags, version, and last update date.`,
149
170
  {
150
- question: z.string().describe('The tax question to answer'),
151
- context: z.string().optional().describe('Additional context'),
171
+ article_id: z.string().describe('The article ID (e.g., art_abc123)'),
152
172
  },
153
173
  async (args) => {
154
174
  const result = await callRemoteServer('tools/call', {
155
- name: 'answer_question',
175
+ name: 'get_article',
156
176
  arguments: args,
157
177
  }) as { content: Array<{ type: 'text'; text: string }> };
158
178
  return result;
159
179
  }
160
180
  );
161
181
 
182
+ // Tool 3: Answer Question (Full RAG)
162
183
  server.tool(
163
- 'get_article',
164
- 'Retrieve the complete content of a knowledge base article by its ID',
184
+ 'answer_question',
185
+ `Answer a customer question using the knowledge base with full RAG.
186
+
187
+ This tool:
188
+ 1. Searches for relevant articles in the knowledge base
189
+ 2. If found: Generates an answer synthesizing the retrieved content
190
+ 3. If not found: Optionally generates a new article to fill the gap
191
+
192
+ Use for questions about:
193
+ - Services and pricing
194
+ - Tax filing requirements and deadlines
195
+ - LLC formation process
196
+ - Bank account options
197
+ - Document requirements
198
+ - How onboarding works
199
+ - Any nonresident.tax topic
200
+
201
+ The answer includes source references for transparency.`,
165
202
  {
166
- article_id: z.string().describe('The article ID (e.g., art_abc123)'),
203
+ question: z.string().describe('The customer question to answer'),
204
+ context: z.string().optional().describe('Additional context about the question'),
205
+ generate_if_not_found: z.boolean().optional().describe('Generate new article if no relevant content found (default: true)'),
167
206
  },
168
207
  async (args) => {
169
208
  const result = await callRemoteServer('tools/call', {
170
- name: 'get_article',
209
+ name: 'answer_question',
171
210
  arguments: args,
172
211
  }) as { content: Array<{ type: 'text'; text: string }> };
173
212
  return result;
174
213
  }
175
214
  );
176
215
 
216
+ // Tool 4: Log Unanswered
177
217
  server.tool(
178
- 'list_articles',
179
- 'List recent articles from the knowledge base',
218
+ 'log_unanswered',
219
+ `Flag a customer question as unanswered for knowledge gap analysis.
220
+
221
+ WHEN TO USE:
222
+ - After search_knowledge returns no relevant results
223
+ - When existing articles don't fully answer the question
224
+ - When a customer asks something completely new
225
+ - To track recurring questions that need dedicated articles
226
+
227
+ WHY IT MATTERS:
228
+ - Flagged questions appear in the admin "Knowledge Gaps" section
229
+ - Helps prioritize which new articles to create
230
+ - Builds a list of topics customers actually need
231
+ - Improves the knowledge base over time`,
180
232
  {
181
- limit: z.number().optional().describe('Maximum articles (default: 10)'),
182
- category: z.string().optional().describe('Filter by category'),
233
+ query: z.string().describe('The question that could not be answered'),
234
+ reason: z.string().optional().describe('Reason why the question could not be answered'),
183
235
  },
184
236
  async (args) => {
185
237
  const result = await callRemoteServer('tools/call', {
186
- name: 'list_articles',
238
+ name: 'log_unanswered',
187
239
  arguments: args,
188
240
  }) as { content: Array<{ type: 'text'; text: string }> };
189
241
  return result;
190
242
  }
191
243
  );
192
244
 
245
+ // Tool 5: List Articles
193
246
  server.tool(
194
- 'log_unanswered',
195
- 'Explicitly flag a question as unanswered for gap analysis',
247
+ 'list_articles',
248
+ `Browse articles in the knowledge base with optional filtering.
249
+
250
+ Parameters:
251
+ - limit: Number of articles (default 10, max 50)
252
+ - category: Filter by category slug (use list_categories first)
253
+
254
+ Returns article ID, title, and summary for each match.
255
+ Use get_article with the ID to retrieve full content.
256
+
257
+ WORKFLOW:
258
+ 1. Call list_categories to discover available categories
259
+ 2. Call list_articles with specific category for focused results
260
+ 3. Call get_article for articles that look relevant`,
196
261
  {
197
- query: z.string().describe('The question that could not be answered'),
198
- reason: z.string().optional().describe('Reason why the question could not be answered'),
262
+ limit: z.number().optional().describe('Number of articles to return (max 50)'),
263
+ category: z.string().optional().describe('Filter by category slug'),
199
264
  },
200
265
  async (args) => {
201
266
  const result = await callRemoteServer('tools/call', {
202
- name: 'log_unanswered',
267
+ name: 'list_articles',
203
268
  arguments: args,
204
269
  }) as { content: Array<{ type: 'text'; text: string }> };
205
270
  return result;
206
271
  }
207
272
  );
208
273
 
274
+ // Tool 6: Health Check
209
275
  server.tool(
210
276
  'health_check',
211
- 'Check the health status of the knowledge base system',
277
+ 'Check the health status of the memory system.',
212
278
  {},
213
279
  async () => {
214
280
  const result = await callRemoteServer('tools/call', {
@@ -219,10 +285,25 @@ async function main(): Promise<void> {
219
285
  }
220
286
  );
221
287
 
222
- // Tool 7: List Categories (available in all modes)
288
+ // Tool 7: List Categories
223
289
  server.tool(
224
290
  'list_categories',
225
- 'List all categories in the knowledge base with article counts',
291
+ `List all categories in the knowledge base with article counts.
292
+
293
+ CRITICAL FOR EFFICIENT SEARCH:
294
+ Categories help you quickly narrow down to relevant content.
295
+
296
+ Returns for each category:
297
+ - slug: Category identifier for filtering
298
+ - name: Human-readable name
299
+ - count: Number of articles
300
+ - description: What the category covers
301
+
302
+ RECOMMENDED WORKFLOW:
303
+ 1. Call list_categories to see what's available
304
+ 2. Call list_articles with category slug
305
+ 3. Call get_article for full content
306
+ 4. Or use search_knowledge for semantic search across all`,
226
307
  {},
227
308
  async () => {
228
309
  const result = await callRemoteServer('tools/call', {
@@ -233,14 +314,15 @@ async function main(): Promise<void> {
233
314
  }
234
315
  );
235
316
 
236
- // === FULL MODE TOOLS ===
237
- // These tools are only available when --full flag is passed or MCP_MODE=full
317
+ // ============================================================
318
+ // FULL MODE TOOLS (only available with --full flag or MCP_MODE=full)
319
+ // ============================================================
238
320
 
239
321
  if (isFullMode) {
240
322
  // Tool 8: Add Article
241
323
  server.tool(
242
324
  'add_article',
243
- 'Create a new article in the knowledge base (full mode only)',
325
+ 'Create a new article in the knowledge base. Automatically generates embedding for semantic search.',
244
326
  {
245
327
  title: z.string().describe('Article title'),
246
328
  content: z.string().describe('Article content in markdown format'),
@@ -261,7 +343,7 @@ async function main(): Promise<void> {
261
343
  // Tool 9: Edit Article
262
344
  server.tool(
263
345
  'edit_article',
264
- 'Update an existing article in the knowledge base (full mode only)',
346
+ 'Update an existing article. Re-generates embedding if content changes.',
265
347
  {
266
348
  article_id: z.string().describe('Article ID to update'),
267
349
  title: z.string().optional().describe('New title'),
@@ -282,7 +364,7 @@ async function main(): Promise<void> {
282
364
  // Tool 10: Remove Article
283
365
  server.tool(
284
366
  'remove_article',
285
- 'Soft-delete an article from the knowledge base (full mode only)',
367
+ 'Soft-delete an article from the knowledge base. Article is marked as deleted but not permanently removed.',
286
368
  {
287
369
  article_id: z.string().describe('Article ID to remove'),
288
370
  reason: z.string().optional().describe('Reason for deletion (for audit log)'),
@@ -299,7 +381,7 @@ async function main(): Promise<void> {
299
381
  // Tool 11: Rate Answer
300
382
  server.tool(
301
383
  'rate_answer',
302
- 'Rate a previous answer as helpful, unhelpful, or neutral (full mode only)',
384
+ 'Rate a previous answer as helpful, unhelpful, or neutral. Helps improve the knowledge base.',
303
385
  {
304
386
  query_id: z.string().describe('Query log ID from a previous answer_question response'),
305
387
  rating: z.number().describe('Rating: -1 (unhelpful), 0 (neutral), 1 (helpful)'),