@pragmatic-growth/memory-mcp 3.4.0 → 3.5.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 +92 -18
- package/dist/index.js +295 -204
- package/package.json +10 -9
- package/dist/CLAUDE.md +0 -11
- package/dist/index.d.ts +0 -19
package/README.md
CHANGED
|
@@ -37,16 +37,68 @@ For clients that only support stdio (Raycast, local tools):
|
|
|
37
37
|
}
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
## Operating
|
|
40
|
+
## Operating Modes
|
|
41
41
|
|
|
42
|
-
PG-Memory
|
|
43
|
-
- **Core Knowledge**: search, get content, list content, list categories, health check, smart gap logging
|
|
44
|
-
- **AI-Powered**: Automatic question quality evaluation, deduplication, and priority assignment
|
|
42
|
+
PG-Memory supports two operating modes:
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
### Read-Only Mode (default, 6 tools)
|
|
45
|
+
Semantic search, content retrieval, and smart gap logging. All tools are available by default.
|
|
46
|
+
|
|
47
|
+
### Admin Mode (14 tools)
|
|
48
|
+
Full CRUD operations on articles and Q&A records. Enable with:
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"env": {
|
|
52
|
+
"CLERK_API_KEY": "your-api-key",
|
|
53
|
+
"PG_MEMORY_ADMIN": "true"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
47
57
|
|
|
48
58
|
## Installation
|
|
49
59
|
|
|
60
|
+
### For Claude Code (`.mcp.json`)
|
|
61
|
+
|
|
62
|
+
Add to your project's `.mcp.json` or `~/.claude/.mcp.json`:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"pg-memory": {
|
|
68
|
+
"type": "stdio",
|
|
69
|
+
"command": "npx",
|
|
70
|
+
"args": ["-y", "@pragmatic-growth/memory-mcp"],
|
|
71
|
+
"env": {
|
|
72
|
+
"CLERK_API_KEY": "your-api-key-here",
|
|
73
|
+
"PG_MEMORY_ADMIN": "true"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Remove `PG_MEMORY_ADMIN` for read-only mode (6 tools).
|
|
81
|
+
|
|
82
|
+
### For Claude Desktop (`claude_desktop_config.json`)
|
|
83
|
+
|
|
84
|
+
macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
85
|
+
Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"mcpServers": {
|
|
90
|
+
"pg-memory": {
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["-y", "@pragmatic-growth/memory-mcp"],
|
|
93
|
+
"env": {
|
|
94
|
+
"CLERK_API_KEY": "your-api-key-here",
|
|
95
|
+
"PG_MEMORY_ADMIN": "true"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
50
102
|
### For Raycast
|
|
51
103
|
|
|
52
104
|
```json
|
|
@@ -79,28 +131,41 @@ CLERK_API_KEY=your-key memory-mcp
|
|
|
79
131
|
|----------|----------|-------------|
|
|
80
132
|
| `CLERK_API_KEY` | Yes | Clerk API key for authentication |
|
|
81
133
|
| `MCP_SERVER_URL` | No | Custom server URL (default: production) |
|
|
134
|
+
| `PG_MEMORY_ADMIN` | No | Set to `true` to enable admin tools (default: read-only) |
|
|
82
135
|
|
|
83
|
-
## Available Tools
|
|
136
|
+
## Available Tools
|
|
84
137
|
|
|
85
|
-
|
|
138
|
+
### Read Tools (6, always available)
|
|
86
139
|
|
|
87
140
|
| Tool | Description |
|
|
88
141
|
|------|-------------|
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
|
|
142
|
+
| `search` | Unified semantic search across articles + approved Q&A. Returns lightweight results (ID, type, title/question, similarity, date). |
|
|
143
|
+
| `get_article` | Retrieve full article content by ID |
|
|
144
|
+
| `get_qa` | Retrieve a Q&A record by ID (question, answer, status, metadata) |
|
|
145
|
+
| `log_gap` | Smart gap logging with deduplication (>85%), answer detection (>70%), AI quality filtering, and auto-priority |
|
|
146
|
+
| `list_categories` | List all knowledge base categories with content counts |
|
|
147
|
+
| `health_check` | System health status including article counts, Q&A counts, and connection pool stats |
|
|
148
|
+
|
|
149
|
+
### Admin Tools (8, requires `PG_MEMORY_ADMIN=true`)
|
|
150
|
+
|
|
151
|
+
| Tool | Description |
|
|
152
|
+
|------|-------------|
|
|
153
|
+
| `list_gaps` | List unanswered knowledge gaps (Q&A with status='gap') |
|
|
154
|
+
| `create_article` | Create a new article with auto-generated embedding |
|
|
155
|
+
| `update_article` | Update an existing article with optional re-embedding |
|
|
156
|
+
| `create_qa` | Create a Q&A record with full control over status and fields |
|
|
157
|
+
| `draft_qa_answer` | Draft an answer for a Q&A record (sets status to 'draft') |
|
|
158
|
+
| `approve_qa` | Approve a Q&A record and generate search embedding |
|
|
159
|
+
| `delete_article` | Permanently delete an article |
|
|
160
|
+
| `delete_qa` | Permanently delete a Q&A record |
|
|
96
161
|
|
|
97
162
|
## Architecture Philosophy
|
|
98
163
|
|
|
99
|
-
**
|
|
164
|
+
**Unified Search**: `search` queries both articles and approved Q&A in parallel with graceful degradation. Returns metadata only for token efficiency — use `get_article` or `get_qa` for full content.
|
|
100
165
|
|
|
101
|
-
**
|
|
166
|
+
**Q&A Lifecycle**: Questions flow through `gap` → `draft` → `approved` status. Only approved Q&A appears in search results.
|
|
102
167
|
|
|
103
|
-
**
|
|
168
|
+
**AI-Powered Quality**: `log_gap` uses LLM evaluation to filter spam, detect duplicates (>85% similarity), suggest existing answers (>70% match), and auto-assign priority.
|
|
104
169
|
|
|
105
170
|
## How It Works
|
|
106
171
|
|
|
@@ -116,7 +181,16 @@ This package runs locally as a stdio MCP server and proxies requests to the remo
|
|
|
116
181
|
|
|
117
182
|
## Changelog
|
|
118
183
|
|
|
119
|
-
### v3.
|
|
184
|
+
### v3.5.0 (Latest)
|
|
185
|
+
- **Two operating modes**: Read-only (6 tools) and Admin (14 tools) via `PG_MEMORY_ADMIN=true`
|
|
186
|
+
- **Unified search**: `search` replaces `search_knowledge`, queries articles + approved Q&A in parallel
|
|
187
|
+
- **Q&A lifecycle**: Full CRUD with `create_qa`, `draft_qa_answer`, `approve_qa`, `delete_qa`
|
|
188
|
+
- **Article management**: `create_article`, `update_article`, `delete_article` admin tools
|
|
189
|
+
- **Smart log_gap**: Re-added deduplication, answer detection, and AI quality filtering
|
|
190
|
+
- **Centralized schemas**: Shared Zod schemas between HTTP and stdio servers
|
|
191
|
+
- **Version alignment**: HTTP server and stdio proxy both report v3.5.0
|
|
192
|
+
|
|
193
|
+
### v3.4.0
|
|
120
194
|
- **Server URL Update**: Changed default server to `mem-mcp.pragmaticgrowth.com` (API subdomain)
|
|
121
195
|
- **Validation Documentation**: Added constraints to tool descriptions (enforced server-side):
|
|
122
196
|
- `search_knowledge.query`: Minimum 3 characters
|
package/dist/index.js
CHANGED
|
@@ -1,232 +1,323 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// ../mcp/src/schemas.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
var searchSchema = z.object({
|
|
10
|
+
query: z.string().min(3, "Query must be at least 3 characters").describe("Search query"),
|
|
11
|
+
threshold: z.number().min(0).max(1).optional().default(0.6).describe("Similarity threshold (0-1)"),
|
|
12
|
+
limit: z.number().min(1).max(50).optional().default(5).describe("Max results")
|
|
13
|
+
});
|
|
14
|
+
var getArticleSchema = z.object({
|
|
15
|
+
id: z.string().describe("Article ID (e.g., art_...)")
|
|
16
|
+
});
|
|
17
|
+
var getQASchema = z.object({
|
|
18
|
+
id: z.string().describe("Q&A ID (e.g., qa_...)")
|
|
19
|
+
});
|
|
20
|
+
var logGapSchema = z.object({
|
|
21
|
+
question: z.string().min(10, "Question must be at least 10 characters").max(2e3).describe("The unanswered question"),
|
|
22
|
+
context: z.string().max(1e3).optional().describe("Additional context (email subject, sender, etc)"),
|
|
23
|
+
source: z.enum(["mcp", "email", "chat"]).optional().default("mcp").describe("Question source")
|
|
24
|
+
});
|
|
25
|
+
var listCategoriesSchema = z.object({});
|
|
26
|
+
var healthCheckSchema = z.object({});
|
|
27
|
+
var listGapsSchema = z.object({
|
|
28
|
+
limit: z.number().min(1).max(100).optional().default(20).describe("Max results"),
|
|
29
|
+
offset: z.number().min(0).optional().default(0).describe("Pagination offset")
|
|
30
|
+
});
|
|
31
|
+
var createArticleSchema = z.object({
|
|
32
|
+
title: z.string().min(1).describe("Article title"),
|
|
33
|
+
content: z.string().min(1).describe("Full article content (markdown)"),
|
|
34
|
+
summary: z.string().optional().describe("Article summary"),
|
|
35
|
+
category_id: z.string().optional().describe("Category UUID"),
|
|
36
|
+
metadata: z.record(z.string(), z.unknown()).optional().describe("Additional metadata")
|
|
37
|
+
});
|
|
38
|
+
var updateArticleSchema = z.object({
|
|
39
|
+
id: z.string().describe("Article ID"),
|
|
40
|
+
title: z.string().optional().describe("New title"),
|
|
41
|
+
content: z.string().optional().describe("New content"),
|
|
42
|
+
summary: z.string().optional().describe("New summary"),
|
|
43
|
+
category_id: z.string().nullable().optional().describe("New category UUID (null to remove)"),
|
|
44
|
+
metadata: z.record(z.string(), z.unknown()).optional().describe("New metadata")
|
|
45
|
+
});
|
|
46
|
+
var createQASchema = z.object({
|
|
47
|
+
question: z.string().min(1).describe("The question"),
|
|
48
|
+
answer: z.string().optional().describe("The answer (optional for gaps)"),
|
|
49
|
+
status: z.enum(["gap", "draft", "approved"]).optional().default("gap").describe("Q&A status"),
|
|
50
|
+
source: z.enum(["email", "chat", "mcp", "manual"]).optional().default("manual").describe("Question source"),
|
|
51
|
+
related_article_id: z.string().optional().describe("Related article ID"),
|
|
52
|
+
category_id: z.string().optional().describe("Category UUID"),
|
|
53
|
+
metadata: z.record(z.string(), z.unknown()).optional().describe("Additional metadata")
|
|
54
|
+
});
|
|
55
|
+
var draftQAAnswerSchema = z.object({
|
|
56
|
+
id: z.string().describe("Q&A ID"),
|
|
57
|
+
answer: z.string().min(1).describe("The drafted answer")
|
|
58
|
+
});
|
|
59
|
+
var approveQASchema = z.object({
|
|
60
|
+
id: z.string().describe("Q&A ID to approve")
|
|
61
|
+
});
|
|
62
|
+
var deleteArticleSchema = z.object({
|
|
63
|
+
id: z.string().describe("Article ID to delete")
|
|
64
|
+
});
|
|
65
|
+
var deleteQASchema = z.object({
|
|
66
|
+
id: z.string().describe("Q&A ID to delete")
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// src/index.ts
|
|
70
|
+
var SERVER_URL = process.env.MCP_SERVER_URL || "https://mem-mcp.pragmaticgrowth.com/mcp";
|
|
71
|
+
var API_KEY = process.env.CLERK_API_KEY;
|
|
25
72
|
if (!API_KEY) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
73
|
+
console.error("Error: CLERK_API_KEY environment variable is required");
|
|
74
|
+
console.error("Get your Clerk API key from the Clerk Dashboard or app API Docs page");
|
|
75
|
+
console.error('Set it in your MCP client configuration under "env"');
|
|
76
|
+
process.exit(1);
|
|
30
77
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Make an HTTP request to the remote MCP server.
|
|
36
|
-
* Uses X-API-Key header for Clerk API key authentication.
|
|
37
|
-
*/
|
|
78
|
+
var IS_ADMIN = process.env.PG_MEMORY_ADMIN === "true";
|
|
79
|
+
var sessionId = null;
|
|
80
|
+
var negotiatedProtocolVersion = null;
|
|
38
81
|
async function callRemoteServer(method, params) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
const headers = {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
"X-API-Key": API_KEY
|
|
85
|
+
// Safe: already validated above
|
|
86
|
+
};
|
|
87
|
+
if (sessionId) {
|
|
88
|
+
headers["Mcp-Session-Id"] = sessionId;
|
|
89
|
+
}
|
|
90
|
+
if (negotiatedProtocolVersion && method !== "initialize") {
|
|
91
|
+
headers["Mcp-Protocol-Version"] = negotiatedProtocolVersion;
|
|
92
|
+
}
|
|
93
|
+
const body = {
|
|
94
|
+
jsonrpc: "2.0",
|
|
95
|
+
id: Date.now(),
|
|
96
|
+
method,
|
|
97
|
+
params
|
|
98
|
+
};
|
|
99
|
+
const response = await fetch(SERVER_URL, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers,
|
|
102
|
+
body: JSON.stringify(body)
|
|
103
|
+
});
|
|
104
|
+
const newSessionId = response.headers.get("mcp-session-id");
|
|
105
|
+
if (newSessionId) {
|
|
106
|
+
sessionId = newSessionId;
|
|
107
|
+
}
|
|
108
|
+
const result = await response.json();
|
|
109
|
+
if (result.error) {
|
|
110
|
+
throw new Error(result.error.message);
|
|
111
|
+
}
|
|
112
|
+
return result.result;
|
|
70
113
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Initialize connection to remote server.
|
|
73
|
-
*/
|
|
74
114
|
async function initializeRemoteSession() {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
if (typeof result === 'object' && result && 'protocolVersion' in result) {
|
|
84
|
-
const protocolVersion = result.protocolVersion;
|
|
85
|
-
if (protocolVersion) {
|
|
86
|
-
negotiatedProtocolVersion = protocolVersion;
|
|
87
|
-
}
|
|
115
|
+
const result = await callRemoteServer("initialize", {
|
|
116
|
+
protocolVersion: "2025-11-25",
|
|
117
|
+
capabilities: {},
|
|
118
|
+
clientInfo: {
|
|
119
|
+
name: "pg-memory-stdio",
|
|
120
|
+
version: "3.5.0"
|
|
88
121
|
}
|
|
122
|
+
});
|
|
123
|
+
if (typeof result === "object" && result && "protocolVersion" in result) {
|
|
124
|
+
const protocolVersion = result.protocolVersion;
|
|
125
|
+
if (protocolVersion) {
|
|
126
|
+
negotiatedProtocolVersion = protocolVersion;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
89
129
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Create and start the stdio MCP server.
|
|
92
|
-
*/
|
|
93
130
|
async function main() {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
131
|
+
try {
|
|
132
|
+
await initializeRemoteSession();
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error("Failed to connect to remote server:", error);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
const server = new McpServer({
|
|
138
|
+
name: "pg-memory",
|
|
139
|
+
version: "3.5.0",
|
|
140
|
+
description: `PG-Memory knowledge base with ${IS_ADMIN ? "14" : "6"} tools (${IS_ADMIN ? "admin mode" : "read-only mode"})`
|
|
141
|
+
});
|
|
142
|
+
server.tool(
|
|
143
|
+
"search",
|
|
144
|
+
`Search knowledge base across articles and approved Q&A by semantic similarity.
|
|
145
|
+
Returns lightweight results (ID, type, title/question, similarity, date).
|
|
146
|
+
Use get_article or get_qa to fetch full content.
|
|
147
|
+
ALWAYS call this before answering domain questions.`,
|
|
148
|
+
searchSchema.shape,
|
|
149
|
+
async (args) => {
|
|
150
|
+
const result = await callRemoteServer("tools/call", {
|
|
151
|
+
name: "search",
|
|
152
|
+
arguments: args
|
|
153
|
+
});
|
|
154
|
+
return result;
|
|
97
155
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
156
|
+
);
|
|
157
|
+
server.tool(
|
|
158
|
+
"get_article",
|
|
159
|
+
`Retrieve full content of a knowledge base article by ID.
|
|
160
|
+
Call this after finding relevant items via search.`,
|
|
161
|
+
getArticleSchema.shape,
|
|
162
|
+
async (args) => {
|
|
163
|
+
const result = await callRemoteServer("tools/call", {
|
|
164
|
+
name: "get_article",
|
|
165
|
+
arguments: args
|
|
166
|
+
});
|
|
167
|
+
return result;
|
|
101
168
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
169
|
+
);
|
|
170
|
+
server.tool(
|
|
171
|
+
"get_qa",
|
|
172
|
+
`Retrieve a Q&A record by ID. Returns question, answer, status, and metadata.`,
|
|
173
|
+
getQASchema.shape,
|
|
174
|
+
async (args) => {
|
|
175
|
+
const result = await callRemoteServer("tools/call", {
|
|
176
|
+
name: "get_qa",
|
|
177
|
+
arguments: args
|
|
178
|
+
});
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
server.tool(
|
|
183
|
+
"log_gap",
|
|
184
|
+
`Log an unanswered question as a knowledge gap.
|
|
185
|
+
Creates a Q&A record with status='gap'. Search first before logging.`,
|
|
186
|
+
logGapSchema.shape,
|
|
187
|
+
async (args) => {
|
|
188
|
+
const result = await callRemoteServer("tools/call", {
|
|
189
|
+
name: "log_gap",
|
|
190
|
+
arguments: args
|
|
191
|
+
});
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
server.tool(
|
|
196
|
+
"list_categories",
|
|
197
|
+
`List all categories in the knowledge base with content counts.`,
|
|
198
|
+
listCategoriesSchema.shape,
|
|
199
|
+
async () => {
|
|
200
|
+
const result = await callRemoteServer("tools/call", {
|
|
201
|
+
name: "list_categories",
|
|
202
|
+
arguments: {}
|
|
203
|
+
});
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
server.tool(
|
|
208
|
+
"health_check",
|
|
209
|
+
"Check the health status of the knowledge base system including article counts, Q&A counts, and connection pool stats.",
|
|
210
|
+
healthCheckSchema.shape,
|
|
211
|
+
async () => {
|
|
212
|
+
const result = await callRemoteServer("tools/call", {
|
|
213
|
+
name: "health_check",
|
|
214
|
+
arguments: {}
|
|
215
|
+
});
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
if (IS_ADMIN) {
|
|
220
|
+
server.tool(
|
|
221
|
+
"list_gaps",
|
|
222
|
+
`[ADMIN] List unanswered knowledge gaps (Q&A with status='gap').`,
|
|
223
|
+
listGapsSchema.shape,
|
|
224
|
+
async (args) => {
|
|
225
|
+
const result = await callRemoteServer("tools/call", {
|
|
226
|
+
name: "list_gaps",
|
|
227
|
+
arguments: args
|
|
135
228
|
});
|
|
136
229
|
return result;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
limit: z.number().optional().describe('Maximum results to return (default: 5)'),
|
|
148
|
-
threshold: z.number().optional().describe('Minimum similarity 0-1 (default: 0.55)'),
|
|
149
|
-
}, async (args) => {
|
|
150
|
-
const result = await callRemoteServer('tools/call', {
|
|
151
|
-
name: 'search_knowledge',
|
|
152
|
-
arguments: args,
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
server.tool(
|
|
233
|
+
"create_article",
|
|
234
|
+
`[ADMIN] Create a new knowledge base article. Auto-generates embedding from title + summary.`,
|
|
235
|
+
createArticleSchema.shape,
|
|
236
|
+
async (args) => {
|
|
237
|
+
const result = await callRemoteServer("tools/call", {
|
|
238
|
+
name: "create_article",
|
|
239
|
+
arguments: args
|
|
153
240
|
});
|
|
154
241
|
return result;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
server.tool(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const result = await callRemoteServer(
|
|
163
|
-
|
|
164
|
-
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
server.tool(
|
|
245
|
+
"update_article",
|
|
246
|
+
`[ADMIN] Update an existing article. Re-generates embedding if title or content changes.`,
|
|
247
|
+
updateArticleSchema.shape,
|
|
248
|
+
async (args) => {
|
|
249
|
+
const result = await callRemoteServer("tools/call", {
|
|
250
|
+
name: "update_article",
|
|
251
|
+
arguments: args
|
|
165
252
|
});
|
|
166
253
|
return result;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
ALWAYS search first before calling this. Do not log without searching.
|
|
179
|
-
|
|
180
|
-
Returns one of:
|
|
181
|
-
- {action: 'logged', gapId, priority, quality} - Question logged
|
|
182
|
-
- {action: 'duplicate', existingGapId, similarity} - Duplicate detected
|
|
183
|
-
- {action: 'answered', articleId, title, similarity} - Already answered
|
|
184
|
-
- {action: 'rejected', reason, quality} - Spam/garbage filtered`, {
|
|
185
|
-
question: z.string().describe('The unanswered question'),
|
|
186
|
-
context: z.string().optional().describe('Additional context (email subject, sender, etc)'),
|
|
187
|
-
source: z.enum(['mcp', 'email', 'chat']).optional().describe('Question source (default: mcp)'),
|
|
188
|
-
}, async (args) => {
|
|
189
|
-
const result = await callRemoteServer('tools/call', {
|
|
190
|
-
name: 'smart_log_gap',
|
|
191
|
-
arguments: args,
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
server.tool(
|
|
257
|
+
"create_qa",
|
|
258
|
+
`[ADMIN] Create a new Q&A record with full control over status and fields.`,
|
|
259
|
+
createQASchema.shape,
|
|
260
|
+
async (args) => {
|
|
261
|
+
const result = await callRemoteServer("tools/call", {
|
|
262
|
+
name: "create_qa",
|
|
263
|
+
arguments: args
|
|
192
264
|
});
|
|
193
265
|
return result;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
server.tool(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
arguments: args,
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
server.tool(
|
|
269
|
+
"draft_qa_answer",
|
|
270
|
+
`[ADMIN] Draft an answer for a Q&A record. Sets status to 'draft' for review.`,
|
|
271
|
+
draftQAAnswerSchema.shape,
|
|
272
|
+
async (args) => {
|
|
273
|
+
const result = await callRemoteServer("tools/call", {
|
|
274
|
+
name: "draft_qa_answer",
|
|
275
|
+
arguments: args
|
|
205
276
|
});
|
|
206
277
|
return result;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
server.tool(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
server.tool(
|
|
281
|
+
"approve_qa",
|
|
282
|
+
`[ADMIN] Approve a Q&A record. Generates embedding from question+answer for search indexing.`,
|
|
283
|
+
approveQASchema.shape,
|
|
284
|
+
async (args) => {
|
|
285
|
+
const result = await callRemoteServer("tools/call", {
|
|
286
|
+
name: "approve_qa",
|
|
287
|
+
arguments: args
|
|
213
288
|
});
|
|
214
289
|
return result;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
server.tool(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
290
|
+
}
|
|
291
|
+
);
|
|
292
|
+
server.tool(
|
|
293
|
+
"delete_article",
|
|
294
|
+
`[ADMIN] Delete an article from the knowledge base. This is permanent.`,
|
|
295
|
+
deleteArticleSchema.shape,
|
|
296
|
+
async (args) => {
|
|
297
|
+
const result = await callRemoteServer("tools/call", {
|
|
298
|
+
name: "delete_article",
|
|
299
|
+
arguments: args
|
|
222
300
|
});
|
|
223
301
|
return result;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
server.tool(
|
|
305
|
+
"delete_qa",
|
|
306
|
+
`[ADMIN] Delete a Q&A record. This is permanent.`,
|
|
307
|
+
deleteQASchema.shape,
|
|
308
|
+
async (args) => {
|
|
309
|
+
const result = await callRemoteServer("tools/call", {
|
|
310
|
+
name: "delete_qa",
|
|
311
|
+
arguments: args
|
|
312
|
+
});
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
const transport = new StdioServerTransport();
|
|
318
|
+
await server.connect(transport);
|
|
228
319
|
}
|
|
229
320
|
main().catch((error) => {
|
|
230
|
-
|
|
231
|
-
|
|
321
|
+
console.error("Fatal error:", error);
|
|
322
|
+
process.exit(1);
|
|
232
323
|
});
|
package/package.json
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pragmatic-growth/memory-mcp",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Stdio proxy for PG-Memory - connects stdio-based MCP clients (Claude Desktop, Raycast) to your PG-Memory HTTP server using Clerk API key authentication. Personal knowledge base with
|
|
3
|
+
"version": "3.5.0",
|
|
4
|
+
"description": "Stdio proxy for PG-Memory - connects stdio-based MCP clients (Claude Desktop, Raycast) to your PG-Memory HTTP server using Clerk API key authentication. Personal knowledge base with 14 tools (6 read + 8 admin) including articles, Q&A, semantic search, and admin-gated knowledge management.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"memory-mcp": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"start": "node dist/index.js",
|
|
13
|
-
"dev": "tsx src/index.ts"
|
|
14
|
-
},
|
|
15
10
|
"keywords": [
|
|
16
11
|
"mcp",
|
|
17
12
|
"model-context-protocol",
|
|
@@ -31,10 +26,11 @@
|
|
|
31
26
|
"license": "MIT",
|
|
32
27
|
"dependencies": {
|
|
33
28
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
34
|
-
"zod": "^
|
|
29
|
+
"zod": "^4.2.1"
|
|
35
30
|
},
|
|
36
31
|
"devDependencies": {
|
|
37
32
|
"@types/node": "^20",
|
|
33
|
+
"tsup": "^8.5.1",
|
|
38
34
|
"tsx": "^4.19.0",
|
|
39
35
|
"typescript": "^5"
|
|
40
36
|
},
|
|
@@ -54,5 +50,10 @@
|
|
|
54
50
|
],
|
|
55
51
|
"publishConfig": {
|
|
56
52
|
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"start": "node dist/index.js",
|
|
57
|
+
"dev": "tsx src/index.ts"
|
|
57
58
|
}
|
|
58
|
-
}
|
|
59
|
+
}
|
package/dist/CLAUDE.md
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<claude-mem-context>
|
|
2
|
-
# Recent Activity
|
|
3
|
-
|
|
4
|
-
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
|
|
5
|
-
|
|
6
|
-
### Jan 16, 2026
|
|
7
|
-
|
|
8
|
-
| ID | Time | T | Title | Read |
|
|
9
|
-
|----|------|---|-------|------|
|
|
10
|
-
| #26372 | 2:48 AM | ✅ | Committed v3.4.0 release to main branch | ~238 |
|
|
11
|
-
</claude-mem-context>
|
package/dist/index.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* PG-Memory MCP Stdio Server
|
|
4
|
-
*
|
|
5
|
-
* This is a local stdio MCP server that proxies requests to the
|
|
6
|
-
* remote PG-Memory HTTP server on Railway.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* npx @pragmatic-growth/memory-mcp # Read-only mode (default)
|
|
10
|
-
*
|
|
11
|
-
* Environment variables:
|
|
12
|
-
* CLERK_API_KEY - Clerk API key for authentication (required)
|
|
13
|
-
* MCP_SERVER_URL - Server URL (default: https://mem-mcp.pragmaticgrowth.com/mcp)
|
|
14
|
-
*
|
|
15
|
-
* Authentication:
|
|
16
|
-
* Uses Clerk API key authentication via X-API-Key header.
|
|
17
|
-
* Get your API key from the Clerk Dashboard or the app's API Docs page.
|
|
18
|
-
*/
|
|
19
|
-
export {};
|