@pragmatic-growth/memory-mcp 2.1.0 → 2.2.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 +121 -34
- package/dist/index.js +198 -97
- package/package.json +8 -3
- package/src/index.ts +265 -101
- package/.npmrc.bak +0 -2
package/README.md
CHANGED
|
@@ -1,20 +1,51 @@
|
|
|
1
|
-
#
|
|
1
|
+
# PG-Memory MCP (stdio proxy)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Stdio proxy for PG-Memory - connects stdio-based MCP clients to your PG-Memory HTTP server.
|
|
4
|
+
|
|
5
|
+
## Two Transport Modes
|
|
6
|
+
|
|
7
|
+
PG-Memory supports **two MCP transport modes**:
|
|
8
|
+
|
|
9
|
+
### 1. HTTP Transport (Direct)
|
|
10
|
+
For clients that support HTTP/SSE transport (Claude Code, mcp-remote):
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"pg-memory": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "mcp-remote", "https://your-server.up.railway.app/api/mcp"],
|
|
17
|
+
"env": {
|
|
18
|
+
"API_KEY": "your-api-key"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Stdio Transport (This Package)
|
|
26
|
+
For clients that only support stdio (Raycast, local tools):
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"name": "pg-memory",
|
|
30
|
+
"type": "stdio",
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["-y", "@pragmatic-growth/memory-mcp"],
|
|
33
|
+
"env": {
|
|
34
|
+
"MCP_API_KEY": "your-api-key"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
4
38
|
|
|
5
39
|
## Two Operating Modes
|
|
6
40
|
|
|
7
|
-
|
|
41
|
+
Both transports support two operating modes:
|
|
8
42
|
|
|
9
43
|
### Read-Only Mode (Default)
|
|
10
|
-
Safe for general use -
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- `list_categories` - List categories with counts
|
|
16
|
-
- `log_unanswered` - Flag gaps
|
|
17
|
-
- `health_check` - System status
|
|
44
|
+
Safe for general use - search, read, and conversation tracking:
|
|
45
|
+
- **Core Knowledge**: search, get content, answer questions, list content
|
|
46
|
+
- **Knowledge Graph**: find related articles, get entities, search entities
|
|
47
|
+
- **Episodic Memory**: start/track conversations, get session context
|
|
48
|
+
- **Q&A**: add questions for later answering
|
|
18
49
|
|
|
19
50
|
### Full Mode (with `--full` flag)
|
|
20
51
|
Includes all read-only tools PLUS write operations:
|
|
@@ -29,10 +60,10 @@ Includes all read-only tools PLUS write operations:
|
|
|
29
60
|
|
|
30
61
|
```json
|
|
31
62
|
{
|
|
32
|
-
"name": "
|
|
63
|
+
"name": "pg-memory",
|
|
33
64
|
"type": "stdio",
|
|
34
65
|
"command": "npx",
|
|
35
|
-
"args": ["-y", "@
|
|
66
|
+
"args": ["-y", "@pragmatic-growth/memory-mcp"],
|
|
36
67
|
"env": {
|
|
37
68
|
"MCP_API_KEY": "your-api-key-here"
|
|
38
69
|
}
|
|
@@ -43,10 +74,10 @@ Includes all read-only tools PLUS write operations:
|
|
|
43
74
|
|
|
44
75
|
```json
|
|
45
76
|
{
|
|
46
|
-
"name": "
|
|
77
|
+
"name": "pg-memory-full",
|
|
47
78
|
"type": "stdio",
|
|
48
79
|
"command": "npx",
|
|
49
|
-
"args": ["-y", "@
|
|
80
|
+
"args": ["-y", "@pragmatic-growth/memory-mcp", "--full"],
|
|
50
81
|
"env": {
|
|
51
82
|
"MCP_API_KEY": "your-api-key-here"
|
|
52
83
|
}
|
|
@@ -56,17 +87,17 @@ Includes all read-only tools PLUS write operations:
|
|
|
56
87
|
### Global Installation
|
|
57
88
|
|
|
58
89
|
```bash
|
|
59
|
-
npm install -g @
|
|
90
|
+
npm install -g @pragmatic-growth/memory-mcp
|
|
60
91
|
```
|
|
61
92
|
|
|
62
93
|
Then run directly:
|
|
63
94
|
|
|
64
95
|
```bash
|
|
65
96
|
# Read-only mode (default)
|
|
66
|
-
MCP_API_KEY=your-key
|
|
97
|
+
MCP_API_KEY=your-key memory-mcp
|
|
67
98
|
|
|
68
99
|
# Full mode
|
|
69
|
-
MCP_API_KEY=your-key
|
|
100
|
+
MCP_API_KEY=your-key memory-mcp --full
|
|
70
101
|
```
|
|
71
102
|
|
|
72
103
|
## Environment Variables
|
|
@@ -79,41 +110,97 @@ MCP_API_KEY=your-key nt-memory-mcp --full
|
|
|
79
110
|
|
|
80
111
|
Note: The `--full` CLI flag takes precedence over `MCP_MODE` env var.
|
|
81
112
|
|
|
82
|
-
## Available Tools
|
|
113
|
+
## Available Tools (21 Total)
|
|
114
|
+
|
|
115
|
+
### Core Knowledge Tools (7)
|
|
83
116
|
|
|
84
|
-
|
|
117
|
+
| Tool | Description |
|
|
118
|
+
|------|-------------|
|
|
119
|
+
| `search_knowledge` | Semantic search across articles AND Q&A entries |
|
|
120
|
+
| `get_content` | Retrieve full content by ID (articles or Q&A) |
|
|
121
|
+
| `answer_question` | Full RAG pipeline with graph-augmented retrieval |
|
|
122
|
+
| `log_unanswered` | Flag questions for knowledge gap analysis |
|
|
123
|
+
| `list_content` | Browse articles and Q&A with filtering |
|
|
124
|
+
| `list_categories` | List all categories with content counts |
|
|
125
|
+
| `health_check` | Check system status and connectivity |
|
|
126
|
+
|
|
127
|
+
### Knowledge Graph Tools (4)
|
|
128
|
+
|
|
129
|
+
| Tool | Description |
|
|
130
|
+
|------|-------------|
|
|
131
|
+
| `find_related` | Find related articles via shared entities |
|
|
132
|
+
| `get_entities` | Get named entities from an article |
|
|
133
|
+
| `search_entities` | Search entities by name or type |
|
|
134
|
+
| `get_knowledge_graph` | Get graph structure for visualization |
|
|
135
|
+
|
|
136
|
+
### Episodic Memory Tools (5)
|
|
137
|
+
|
|
138
|
+
| Tool | Description |
|
|
139
|
+
|------|-------------|
|
|
140
|
+
| `start_conversation` | Begin tracking a conversation session |
|
|
141
|
+
| `add_message` | Add message with automatic fact extraction |
|
|
142
|
+
| `get_conversation` | Retrieve conversation with all messages |
|
|
143
|
+
| `list_conversations` | List recent conversations |
|
|
144
|
+
| `get_session_context` | Get context for follow-up questions |
|
|
145
|
+
|
|
146
|
+
### Q&A Tools (1)
|
|
85
147
|
|
|
86
148
|
| Tool | Description |
|
|
87
149
|
|------|-------------|
|
|
88
|
-
| `
|
|
89
|
-
| `answer_question` | Get AI-powered answers with sources (generates articles if needed) |
|
|
90
|
-
| `get_article` | Retrieve complete article content by ID |
|
|
91
|
-
| `list_articles` | Browse recent articles with optional category filter |
|
|
92
|
-
| `list_categories` | List all categories with article counts |
|
|
93
|
-
| `log_unanswered` | Flag questions as unanswered for gap analysis |
|
|
94
|
-
| `health_check` | Check system status, database connectivity, and current mode |
|
|
150
|
+
| `add_question` | Add question for later expert answering |
|
|
95
151
|
|
|
96
|
-
### Full Mode Tools
|
|
152
|
+
### Full Mode Tools (4)
|
|
97
153
|
|
|
98
154
|
| Tool | Description |
|
|
99
155
|
|------|-------------|
|
|
100
|
-
| `add_article` | Create a new article with automatic embedding
|
|
156
|
+
| `add_article` | Create a new article with automatic embedding |
|
|
101
157
|
| `edit_article` | Update article content, metadata, or category |
|
|
102
|
-
| `remove_article` | Soft-delete an article
|
|
103
|
-
| `rate_answer` | Rate previous answers: -1
|
|
158
|
+
| `remove_article` | Soft-delete an article |
|
|
159
|
+
| `rate_answer` | Rate previous answers: -1, 0, or 1 |
|
|
160
|
+
|
|
161
|
+
## MCP Resources
|
|
162
|
+
|
|
163
|
+
| URI | Description |
|
|
164
|
+
|-----|-------------|
|
|
165
|
+
| `pgmemory://categories` | List all knowledge base categories |
|
|
166
|
+
| `pgmemory://stats` | System statistics (article count, gaps, latency) |
|
|
167
|
+
| `pgmemory://recent` | Recently added/updated articles |
|
|
168
|
+
|
|
169
|
+
## MCP Prompts
|
|
170
|
+
|
|
171
|
+
| Prompt | Description |
|
|
172
|
+
|--------|-------------|
|
|
173
|
+
| `system-context` | Knowledge base context for AI assistants |
|
|
174
|
+
| `search-tips` | Guidelines for effective search queries |
|
|
104
175
|
|
|
105
176
|
## How It Works
|
|
106
177
|
|
|
107
|
-
This package runs locally as a stdio MCP server and proxies requests to the remote
|
|
178
|
+
This package runs locally as a stdio MCP server and proxies requests to the remote PG-Memory HTTP server. This allows clients that only support stdio to use the cloud-hosted knowledge base.
|
|
108
179
|
|
|
109
180
|
```
|
|
110
|
-
|
|
181
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
182
|
+
│ Raycast/Local │────▶│ memory-mcp │────▶│ PG-Memory │
|
|
183
|
+
│ (stdio only) │ │ (this package) │ │ HTTP Server │
|
|
184
|
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
185
|
+
stdio proxy HTTP
|
|
111
186
|
```
|
|
112
187
|
|
|
113
|
-
|
|
188
|
+
For full mode to work:
|
|
114
189
|
1. The stdio proxy must have `--full` flag OR `MCP_MODE=full`
|
|
115
190
|
2. The remote server must have `MCP_MODE=full` in its environment
|
|
116
191
|
|
|
192
|
+
## Changelog
|
|
193
|
+
|
|
194
|
+
### v2.2.0
|
|
195
|
+
- Added unified `get_content` and `list_content` tools (replacing `get_article`/`list_articles`)
|
|
196
|
+
- Added Knowledge Graph tools: `find_related`, `get_entities`, `search_entities`, `get_knowledge_graph`
|
|
197
|
+
- Added Episodic Memory tools: `start_conversation`, `add_message`, `get_conversation`, `list_conversations`, `get_session_context`
|
|
198
|
+
- Added Q&A tool: `add_question`
|
|
199
|
+
- Updated to MCP Protocol 2025-11-25
|
|
200
|
+
|
|
201
|
+
### v2.1.1
|
|
202
|
+
- Initial public release with core knowledge tools
|
|
203
|
+
|
|
117
204
|
## License
|
|
118
205
|
|
|
119
206
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ async function initializeRemoteSession() {
|
|
|
75
75
|
capabilities: {},
|
|
76
76
|
clientInfo: {
|
|
77
77
|
name: 'pg-memory-stdio',
|
|
78
|
-
version: '2.
|
|
78
|
+
version: '2.2.0',
|
|
79
79
|
},
|
|
80
80
|
});
|
|
81
81
|
}
|
|
@@ -95,28 +95,30 @@ async function main() {
|
|
|
95
95
|
// Create local MCP server
|
|
96
96
|
const server = new McpServer({
|
|
97
97
|
name: 'pg-memory',
|
|
98
|
-
version: '2.
|
|
98
|
+
version: '2.2.0',
|
|
99
99
|
description: `PG-Memory knowledge base (${modeLabel} mode)`,
|
|
100
100
|
});
|
|
101
101
|
// ============================================================
|
|
102
|
-
//
|
|
102
|
+
// CORE KNOWLEDGE TOOLS (available in all modes)
|
|
103
103
|
// ============================================================
|
|
104
|
-
// Tool 1: Search Knowledge (
|
|
104
|
+
// Tool 1: Search Knowledge (Articles + Q&A)
|
|
105
105
|
server.tool('search_knowledge', `Search the nonresident.tax knowledge base using semantic similarity.
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
**YOU MUST CALL THIS TOOL** before answering any domain-specific question.
|
|
108
|
+
|
|
109
|
+
Topics covered:
|
|
110
110
|
- Tax compliance: Form 5472, Form 1120, ECI rules, penalties, deadlines
|
|
111
111
|
- Company formation: Wyoming LLC, Delaware LLC, single-member LLC
|
|
112
112
|
- Banking: Mercury, Wise, US bank account requirements
|
|
113
|
-
-
|
|
114
|
-
- Dissolution: LLC closure, final filings, IRS compliance
|
|
113
|
+
- Services & pricing: LLC formation, tax filing, registered agent
|
|
115
114
|
|
|
116
|
-
Returns relevant articles with relevance scores.
|
|
115
|
+
Returns relevant articles AND Q&A entries with relevance scores.
|
|
116
|
+
Use get_content with the ID to retrieve full content.`, {
|
|
117
117
|
query: z.string().describe('The question or topic to search for'),
|
|
118
|
-
limit: z.number().optional().describe('Maximum
|
|
119
|
-
threshold: z.number().optional().describe('Minimum similarity
|
|
118
|
+
limit: z.number().optional().describe('Maximum results to return (default: 5)'),
|
|
119
|
+
threshold: z.number().optional().describe('Minimum similarity 0-1 (default: 0.55)'),
|
|
120
|
+
include_articles: z.boolean().optional().describe('Include articles (default: true)'),
|
|
121
|
+
include_qa: z.boolean().optional().describe('Include Q&A entries (default: true)'),
|
|
120
122
|
}, async (args) => {
|
|
121
123
|
const result = await callRemoteServer('tools/call', {
|
|
122
124
|
name: 'search_knowledge',
|
|
@@ -124,44 +126,38 @@ Returns relevant articles with relevance scores. Use get_article to retrieve ful
|
|
|
124
126
|
});
|
|
125
127
|
return result;
|
|
126
128
|
});
|
|
127
|
-
// Tool 2: Get
|
|
128
|
-
server.tool('
|
|
129
|
+
// Tool 2: Get Content (Unified - Articles + Q&A)
|
|
130
|
+
server.tool('get_content', `Retrieve complete content from the knowledge base by ID.
|
|
129
131
|
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
- You want to verify specific details from search results
|
|
132
|
+
Automatically detects content type from ID prefix:
|
|
133
|
+
- 'art_*' → Article (knowledge base article)
|
|
134
|
+
- 'qa_*' → Q&A entry (question and answer)
|
|
134
135
|
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
Use summary_only=true first to check content size, then fetch with max_length if needed.`, {
|
|
137
|
+
content_id: z.string().describe('Content ID (e.g., art_abc123 or qa_xyz789)'),
|
|
138
|
+
max_length: z.number().optional().describe('Max content length in chars (truncates if exceeded)'),
|
|
139
|
+
summary_only: z.boolean().optional().describe('Return only summary/metadata (default: false)'),
|
|
137
140
|
}, async (args) => {
|
|
138
141
|
const result = await callRemoteServer('tools/call', {
|
|
139
|
-
name: '
|
|
142
|
+
name: 'get_content',
|
|
140
143
|
arguments: args,
|
|
141
144
|
});
|
|
142
145
|
return result;
|
|
143
146
|
});
|
|
144
147
|
// 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
|
|
148
|
+
server.tool('answer_question', `Answer a customer question using the knowledge base with full RAG pipeline.
|
|
151
149
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
- Document requirements
|
|
158
|
-
- How onboarding works
|
|
159
|
-
- Any nonresident.tax topic
|
|
150
|
+
How it works:
|
|
151
|
+
1. Searches for relevant articles AND published Q&A
|
|
152
|
+
2. Uses knowledge graph to find related content via entities
|
|
153
|
+
3. Generates a synthesized answer from all sources
|
|
154
|
+
4. Returns source references for transparency
|
|
160
155
|
|
|
161
|
-
|
|
156
|
+
Use this when you need a complete answer with sources, not just search results.`, {
|
|
162
157
|
question: z.string().describe('The customer question to answer'),
|
|
163
|
-
context: z.string().optional().describe('Additional context
|
|
164
|
-
generate_if_not_found: z.boolean().optional().describe('Generate new article if
|
|
158
|
+
context: z.string().optional().describe('Additional context'),
|
|
159
|
+
generate_if_not_found: z.boolean().optional().describe('Generate new article if not found (default: true)'),
|
|
160
|
+
use_graph: z.boolean().optional().describe('Enable graph-augmented retrieval (default: true)'),
|
|
165
161
|
}, async (args) => {
|
|
166
162
|
const result = await callRemoteServer('tools/call', {
|
|
167
163
|
name: 'answer_question',
|
|
@@ -172,19 +168,10 @@ The answer includes source references for transparency.`, {
|
|
|
172
168
|
// Tool 4: Log Unanswered
|
|
173
169
|
server.tool('log_unanswered', `Flag a customer question as unanswered for knowledge gap analysis.
|
|
174
170
|
|
|
175
|
-
|
|
176
|
-
|
|
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`, {
|
|
171
|
+
**CALL THIS** when search_knowledge returns no results or low confidence.
|
|
172
|
+
Flagged questions appear in admin "Knowledge Gaps" section.`, {
|
|
186
173
|
query: z.string().describe('The question that could not be answered'),
|
|
187
|
-
reason: z.string().optional().describe('Reason why
|
|
174
|
+
reason: z.string().optional().describe('Reason why'),
|
|
188
175
|
}, async (args) => {
|
|
189
176
|
const result = await callRemoteServer('tools/call', {
|
|
190
177
|
name: 'log_unanswered',
|
|
@@ -192,31 +179,24 @@ WHY IT MATTERS:
|
|
|
192
179
|
});
|
|
193
180
|
return result;
|
|
194
181
|
});
|
|
195
|
-
// Tool 5: List Articles
|
|
196
|
-
server.tool('
|
|
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.
|
|
182
|
+
// Tool 5: List Content (Unified - Articles + Q&A)
|
|
183
|
+
server.tool('list_content', `Browse content in the knowledge base with optional filtering.
|
|
204
184
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
limit: z.number().optional().describe('Number of articles to return (max 50)'),
|
|
185
|
+
Supports: articles, qa, or all (default).
|
|
186
|
+
Use list_categories first to discover available categories.`, {
|
|
187
|
+
type: z.enum(['articles', 'qa', 'all']).optional().describe('Content type (default: all)'),
|
|
188
|
+
limit: z.number().optional().describe('Number of items (default: 10, max: 50)'),
|
|
210
189
|
category: z.string().optional().describe('Filter by category slug'),
|
|
190
|
+
status: z.string().optional().describe('Filter Q&A by status: pending, draft, published'),
|
|
211
191
|
}, async (args) => {
|
|
212
192
|
const result = await callRemoteServer('tools/call', {
|
|
213
|
-
name: '
|
|
193
|
+
name: 'list_content',
|
|
214
194
|
arguments: args,
|
|
215
195
|
});
|
|
216
196
|
return result;
|
|
217
197
|
});
|
|
218
198
|
// Tool 6: Health Check
|
|
219
|
-
server.tool('health_check', 'Check the health status of the memory system.', {}, async () => {
|
|
199
|
+
server.tool('health_check', 'Check the health status of the memory system including article/Q&A counts.', {}, async () => {
|
|
220
200
|
const result = await callRemoteServer('tools/call', {
|
|
221
201
|
name: 'health_check',
|
|
222
202
|
arguments: {},
|
|
@@ -224,22 +204,8 @@ WORKFLOW:
|
|
|
224
204
|
return result;
|
|
225
205
|
});
|
|
226
206
|
// Tool 7: List Categories
|
|
227
|
-
server.tool('list_categories', `List all categories in the knowledge base with
|
|
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 () => {
|
|
207
|
+
server.tool('list_categories', `List all categories in the knowledge base with content counts.
|
|
208
|
+
Use category slugs with list_content for filtered browsing.`, {}, async () => {
|
|
243
209
|
const result = await callRemoteServer('tools/call', {
|
|
244
210
|
name: 'list_categories',
|
|
245
211
|
arguments: {},
|
|
@@ -247,17 +213,152 @@ RECOMMENDED WORKFLOW:
|
|
|
247
213
|
return result;
|
|
248
214
|
});
|
|
249
215
|
// ============================================================
|
|
250
|
-
//
|
|
216
|
+
// KNOWLEDGE GRAPH TOOLS (available in all modes)
|
|
217
|
+
// ============================================================
|
|
218
|
+
// Tool 8: Find Related Articles
|
|
219
|
+
server.tool('find_related', 'Find articles related through shared entities or explicit relationships. Uses graph traversal.', {
|
|
220
|
+
article_id: z.string().describe('Article ID to find relations for'),
|
|
221
|
+
max_depth: z.number().optional().describe('Relationship hops 1-5 (default: 2)'),
|
|
222
|
+
min_strength: z.number().optional().describe('Min strength 0-1 (default: 0.3)'),
|
|
223
|
+
limit: z.number().optional().describe('Max articles (default: 10)'),
|
|
224
|
+
}, async (args) => {
|
|
225
|
+
const result = await callRemoteServer('tools/call', {
|
|
226
|
+
name: 'find_related',
|
|
227
|
+
arguments: args,
|
|
228
|
+
});
|
|
229
|
+
return result;
|
|
230
|
+
});
|
|
231
|
+
// Tool 9: Get Entities
|
|
232
|
+
server.tool('get_entities', 'Get named entities from an article (people, orgs, forms, states, etc.).', {
|
|
233
|
+
article_id: z.string().describe('Article ID to get entities for'),
|
|
234
|
+
}, async (args) => {
|
|
235
|
+
const result = await callRemoteServer('tools/call', {
|
|
236
|
+
name: 'get_entities',
|
|
237
|
+
arguments: args,
|
|
238
|
+
});
|
|
239
|
+
return result;
|
|
240
|
+
});
|
|
241
|
+
// Tool 10: Search Entities
|
|
242
|
+
server.tool('search_entities', 'Search for entities by name or type across the knowledge base.', {
|
|
243
|
+
query: z.string().optional().describe('Search query for entity name'),
|
|
244
|
+
type: z.string().optional().describe('Filter: PERSON, ORGANIZATION, FORM, STATE, COUNTRY, DATE, AMOUNT, CONCEPT, REGULATION, SERVICE, DOCUMENT'),
|
|
245
|
+
limit: z.number().optional().describe('Max entities (default: 20)'),
|
|
246
|
+
}, async (args) => {
|
|
247
|
+
const result = await callRemoteServer('tools/call', {
|
|
248
|
+
name: 'search_entities',
|
|
249
|
+
arguments: args,
|
|
250
|
+
});
|
|
251
|
+
return result;
|
|
252
|
+
});
|
|
253
|
+
// Tool 11: Get Knowledge Graph
|
|
254
|
+
server.tool('get_knowledge_graph', 'Get the knowledge graph structure for visualization. Returns nodes and edges.', {
|
|
255
|
+
center_article_id: z.string().optional().describe('Center on this article'),
|
|
256
|
+
depth: z.number().optional().describe('Traversal depth 1-5 (default: 2)'),
|
|
257
|
+
include_entities: z.boolean().optional().describe('Include entity nodes (default: true)'),
|
|
258
|
+
include_metadata: z.boolean().optional().describe('Include full metadata (default: false)'),
|
|
259
|
+
limit: z.number().optional().describe('Max nodes (default: 20, max: 100)'),
|
|
260
|
+
}, async (args) => {
|
|
261
|
+
const result = await callRemoteServer('tools/call', {
|
|
262
|
+
name: 'get_knowledge_graph',
|
|
263
|
+
arguments: args,
|
|
264
|
+
});
|
|
265
|
+
return result;
|
|
266
|
+
});
|
|
267
|
+
// ============================================================
|
|
268
|
+
// EPISODIC MEMORY TOOLS (available in all modes)
|
|
269
|
+
// ============================================================
|
|
270
|
+
// Tool 12: Start Conversation
|
|
271
|
+
server.tool('start_conversation', `Start a new conversation for tracking episodic memory.
|
|
272
|
+
Call at the beginning of each session to enable context continuity.`, {
|
|
273
|
+
session_id: z.string().describe('Session ID (e.g., MCP session ID)'),
|
|
274
|
+
title: z.string().optional().describe('Conversation title'),
|
|
275
|
+
user_id: z.string().optional().describe('User ID for multi-user tracking'),
|
|
276
|
+
}, async (args) => {
|
|
277
|
+
const result = await callRemoteServer('tools/call', {
|
|
278
|
+
name: 'start_conversation',
|
|
279
|
+
arguments: args,
|
|
280
|
+
});
|
|
281
|
+
return result;
|
|
282
|
+
});
|
|
283
|
+
// Tool 13: Add Message
|
|
284
|
+
server.tool('add_message', `Add a message to an existing conversation.
|
|
285
|
+
Call after each user message and assistant response.
|
|
286
|
+
User facts are automatically extracted.`, {
|
|
287
|
+
conversation_id: z.string().describe('Conversation ID from start_conversation'),
|
|
288
|
+
role: z.enum(['user', 'assistant', 'system']).describe('Message role'),
|
|
289
|
+
content: z.string().describe('Message content'),
|
|
290
|
+
referenced_articles: z.string().optional().describe('Comma-separated article IDs referenced'),
|
|
291
|
+
}, async (args) => {
|
|
292
|
+
const result = await callRemoteServer('tools/call', {
|
|
293
|
+
name: 'add_message',
|
|
294
|
+
arguments: args,
|
|
295
|
+
});
|
|
296
|
+
return result;
|
|
297
|
+
});
|
|
298
|
+
// Tool 14: Get Conversation
|
|
299
|
+
server.tool('get_conversation', 'Retrieve a conversation with all its messages by ID.', {
|
|
300
|
+
conversation_id: z.string().describe('Conversation ID'),
|
|
301
|
+
}, async (args) => {
|
|
302
|
+
const result = await callRemoteServer('tools/call', {
|
|
303
|
+
name: 'get_conversation',
|
|
304
|
+
arguments: args,
|
|
305
|
+
});
|
|
306
|
+
return result;
|
|
307
|
+
});
|
|
308
|
+
// Tool 15: List Conversations
|
|
309
|
+
server.tool('list_conversations', 'List conversations for a session or user. Returns recent first.', {
|
|
310
|
+
session_id: z.string().optional().describe('Filter by session ID'),
|
|
311
|
+
user_id: z.string().optional().describe('Filter by user ID'),
|
|
312
|
+
limit: z.number().optional().describe('Max conversations (default: 20)'),
|
|
313
|
+
}, async (args) => {
|
|
314
|
+
const result = await callRemoteServer('tools/call', {
|
|
315
|
+
name: 'list_conversations',
|
|
316
|
+
arguments: args,
|
|
317
|
+
});
|
|
318
|
+
return result;
|
|
319
|
+
});
|
|
320
|
+
// Tool 16: Get Session Context
|
|
321
|
+
server.tool('get_session_context', `Get recent conversation context for a session.
|
|
322
|
+
Call when user refers to prior conversation or asks follow-up questions.`, {
|
|
323
|
+
session_id: z.string().describe('Session ID to get context for'),
|
|
324
|
+
message_limit: z.number().optional().describe('Max messages (default: 10)'),
|
|
325
|
+
}, async (args) => {
|
|
326
|
+
const result = await callRemoteServer('tools/call', {
|
|
327
|
+
name: 'get_session_context',
|
|
328
|
+
arguments: args,
|
|
329
|
+
});
|
|
330
|
+
return result;
|
|
331
|
+
});
|
|
332
|
+
// ============================================================
|
|
333
|
+
// Q&A TOOLS (available in all modes)
|
|
334
|
+
// ============================================================
|
|
335
|
+
// Tool 17: Add Question
|
|
336
|
+
server.tool('add_question', `Add a new question to the Q&A store for later answering.
|
|
337
|
+
Use to capture questions from external channels.`, {
|
|
338
|
+
question: z.string().describe('The question text'),
|
|
339
|
+
category: z.string().optional().describe('Category slug'),
|
|
340
|
+
tags: z.string().optional().describe('Comma-separated tags'),
|
|
341
|
+
priority: z.string().optional().describe('Priority: low, medium, high, critical'),
|
|
342
|
+
source: z.string().optional().describe('Source: mcp, email, manual, import'),
|
|
343
|
+
}, async (args) => {
|
|
344
|
+
const result = await callRemoteServer('tools/call', {
|
|
345
|
+
name: 'add_question',
|
|
346
|
+
arguments: args,
|
|
347
|
+
});
|
|
348
|
+
return result;
|
|
349
|
+
});
|
|
350
|
+
// ============================================================
|
|
351
|
+
// FULL MODE TOOLS (only with --full flag or MCP_MODE=full)
|
|
251
352
|
// ============================================================
|
|
252
353
|
if (isFullMode) {
|
|
253
|
-
// Tool
|
|
254
|
-
server.tool('add_article', 'Create a new article in the knowledge base.
|
|
354
|
+
// Tool 18: Add Article
|
|
355
|
+
server.tool('add_article', 'Create a new article in the knowledge base. Generates embedding automatically.', {
|
|
255
356
|
title: z.string().describe('Article title'),
|
|
256
|
-
content: z.string().describe('Article content in markdown
|
|
257
|
-
summary: z.string().optional().describe('Brief summary (auto-generated if
|
|
258
|
-
category: z.string().optional().describe('Category
|
|
357
|
+
content: z.string().describe('Article content in markdown'),
|
|
358
|
+
summary: z.string().optional().describe('Brief summary (auto-generated if omitted)'),
|
|
359
|
+
category: z.string().optional().describe('Category slug'),
|
|
259
360
|
tags: z.string().optional().describe('Comma-separated tags'),
|
|
260
|
-
source: z.string().optional().describe('Source
|
|
361
|
+
source: z.string().optional().describe('Source (e.g., manual, irs.gov)'),
|
|
261
362
|
}, async (args) => {
|
|
262
363
|
const result = await callRemoteServer('tools/call', {
|
|
263
364
|
name: 'add_article',
|
|
@@ -265,7 +366,7 @@ RECOMMENDED WORKFLOW:
|
|
|
265
366
|
});
|
|
266
367
|
return result;
|
|
267
368
|
});
|
|
268
|
-
// Tool
|
|
369
|
+
// Tool 19: Edit Article
|
|
269
370
|
server.tool('edit_article', 'Update an existing article. Re-generates embedding if content changes.', {
|
|
270
371
|
article_id: z.string().describe('Article ID to update'),
|
|
271
372
|
title: z.string().optional().describe('New title'),
|
|
@@ -280,10 +381,10 @@ RECOMMENDED WORKFLOW:
|
|
|
280
381
|
});
|
|
281
382
|
return result;
|
|
282
383
|
});
|
|
283
|
-
// Tool
|
|
284
|
-
server.tool('remove_article', 'Soft-delete an article
|
|
384
|
+
// Tool 20: Remove Article
|
|
385
|
+
server.tool('remove_article', 'Soft-delete an article. Marked as deleted but not permanently removed.', {
|
|
285
386
|
article_id: z.string().describe('Article ID to remove'),
|
|
286
|
-
reason: z.string().optional().describe('Reason for deletion
|
|
387
|
+
reason: z.string().optional().describe('Reason for deletion'),
|
|
287
388
|
}, async (args) => {
|
|
288
389
|
const result = await callRemoteServer('tools/call', {
|
|
289
390
|
name: 'remove_article',
|
|
@@ -291,11 +392,11 @@ RECOMMENDED WORKFLOW:
|
|
|
291
392
|
});
|
|
292
393
|
return result;
|
|
293
394
|
});
|
|
294
|
-
// Tool
|
|
295
|
-
server.tool('rate_answer', 'Rate a previous answer as helpful, unhelpful, or neutral.
|
|
296
|
-
query_id: z.string().describe('Query log ID from
|
|
395
|
+
// Tool 21: Rate Answer
|
|
396
|
+
server.tool('rate_answer', 'Rate a previous answer as helpful, unhelpful, or neutral.', {
|
|
397
|
+
query_id: z.string().describe('Query log ID from answer_question'),
|
|
297
398
|
rating: z.number().describe('Rating: -1 (unhelpful), 0 (neutral), 1 (helpful)'),
|
|
298
|
-
feedback: z.string().optional().describe('Optional text
|
|
399
|
+
feedback: z.string().optional().describe('Optional feedback text'),
|
|
299
400
|
}, async (args) => {
|
|
300
401
|
const result = await callRemoteServer('tools/call', {
|
|
301
402
|
name: 'rate_answer',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pragmatic-growth/memory-mcp",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "Stdio proxy for PG-Memory - connects stdio-based MCP clients (Raycast, local tools) to your PG-Memory HTTP server. PG-Memory supports both HTTP transport (Claude Code, mcp-remote) and stdio transport (this package).",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"memory-mcp": "./dist/index.js"
|
|
@@ -15,12 +15,17 @@
|
|
|
15
15
|
"keywords": [
|
|
16
16
|
"mcp",
|
|
17
17
|
"model-context-protocol",
|
|
18
|
+
"stdio",
|
|
19
|
+
"http",
|
|
18
20
|
"raycast",
|
|
21
|
+
"claude-code",
|
|
19
22
|
"knowledge-base",
|
|
20
23
|
"postgresql",
|
|
21
24
|
"pgvector",
|
|
22
25
|
"ai",
|
|
23
|
-
"rag"
|
|
26
|
+
"rag",
|
|
27
|
+
"semantic-search",
|
|
28
|
+
"memory"
|
|
24
29
|
],
|
|
25
30
|
"author": "Pragmatic Growth",
|
|
26
31
|
"license": "MIT",
|
package/src/index.ts
CHANGED
|
@@ -97,7 +97,7 @@ async function initializeRemoteSession(): Promise<void> {
|
|
|
97
97
|
capabilities: {},
|
|
98
98
|
clientInfo: {
|
|
99
99
|
name: 'pg-memory-stdio',
|
|
100
|
-
version: '2.
|
|
100
|
+
version: '2.2.0',
|
|
101
101
|
},
|
|
102
102
|
});
|
|
103
103
|
}
|
|
@@ -119,33 +119,35 @@ async function main(): Promise<void> {
|
|
|
119
119
|
// Create local MCP server
|
|
120
120
|
const server = new McpServer({
|
|
121
121
|
name: 'pg-memory',
|
|
122
|
-
version: '2.
|
|
122
|
+
version: '2.2.0',
|
|
123
123
|
description: `PG-Memory knowledge base (${modeLabel} mode)`,
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
// ============================================================
|
|
127
|
-
//
|
|
127
|
+
// CORE KNOWLEDGE TOOLS (available in all modes)
|
|
128
128
|
// ============================================================
|
|
129
129
|
|
|
130
|
-
// Tool 1: Search Knowledge (
|
|
130
|
+
// Tool 1: Search Knowledge (Articles + Q&A)
|
|
131
131
|
server.tool(
|
|
132
132
|
'search_knowledge',
|
|
133
133
|
`Search the nonresident.tax knowledge base using semantic similarity.
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
**YOU MUST CALL THIS TOOL** before answering any domain-specific question.
|
|
136
|
+
|
|
137
|
+
Topics covered:
|
|
138
138
|
- Tax compliance: Form 5472, Form 1120, ECI rules, penalties, deadlines
|
|
139
139
|
- Company formation: Wyoming LLC, Delaware LLC, single-member LLC
|
|
140
140
|
- Banking: Mercury, Wise, US bank account requirements
|
|
141
|
-
-
|
|
142
|
-
- Dissolution: LLC closure, final filings, IRS compliance
|
|
141
|
+
- Services & pricing: LLC formation, tax filing, registered agent
|
|
143
142
|
|
|
144
|
-
Returns relevant articles with relevance scores.
|
|
143
|
+
Returns relevant articles AND Q&A entries with relevance scores.
|
|
144
|
+
Use get_content with the ID to retrieve full content.`,
|
|
145
145
|
{
|
|
146
146
|
query: z.string().describe('The question or topic to search for'),
|
|
147
|
-
limit: z.number().optional().describe('Maximum
|
|
148
|
-
threshold: z.number().optional().describe('Minimum similarity
|
|
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
|
+
include_articles: z.boolean().optional().describe('Include articles (default: true)'),
|
|
150
|
+
include_qa: z.boolean().optional().describe('Include Q&A entries (default: true)'),
|
|
149
151
|
},
|
|
150
152
|
async (args) => {
|
|
151
153
|
const result = await callRemoteServer('tools/call', {
|
|
@@ -156,23 +158,24 @@ Returns relevant articles with relevance scores. Use get_article to retrieve ful
|
|
|
156
158
|
}
|
|
157
159
|
);
|
|
158
160
|
|
|
159
|
-
// Tool 2: Get
|
|
161
|
+
// Tool 2: Get Content (Unified - Articles + Q&A)
|
|
160
162
|
server.tool(
|
|
161
|
-
'
|
|
162
|
-
`Retrieve
|
|
163
|
+
'get_content',
|
|
164
|
+
`Retrieve complete content from the knowledge base by ID.
|
|
163
165
|
|
|
164
|
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
- You want to verify specific details from search results
|
|
166
|
+
Automatically detects content type from ID prefix:
|
|
167
|
+
- 'art_*' → Article (knowledge base article)
|
|
168
|
+
- 'qa_*' → Q&A entry (question and answer)
|
|
168
169
|
|
|
169
|
-
|
|
170
|
+
Use summary_only=true first to check content size, then fetch with max_length if needed.`,
|
|
170
171
|
{
|
|
171
|
-
|
|
172
|
+
content_id: z.string().describe('Content ID (e.g., art_abc123 or qa_xyz789)'),
|
|
173
|
+
max_length: z.number().optional().describe('Max content length in chars (truncates if exceeded)'),
|
|
174
|
+
summary_only: z.boolean().optional().describe('Return only summary/metadata (default: false)'),
|
|
172
175
|
},
|
|
173
176
|
async (args) => {
|
|
174
177
|
const result = await callRemoteServer('tools/call', {
|
|
175
|
-
name: '
|
|
178
|
+
name: 'get_content',
|
|
176
179
|
arguments: args,
|
|
177
180
|
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
178
181
|
return result;
|
|
@@ -182,27 +185,20 @@ Returns: Full article with title, content, summary, category, tags, version, and
|
|
|
182
185
|
// Tool 3: Answer Question (Full RAG)
|
|
183
186
|
server.tool(
|
|
184
187
|
'answer_question',
|
|
185
|
-
`Answer a customer question using the knowledge base with full RAG.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
1. Searches for relevant articles
|
|
189
|
-
2.
|
|
190
|
-
3.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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.`,
|
|
188
|
+
`Answer a customer question using the knowledge base with full RAG pipeline.
|
|
189
|
+
|
|
190
|
+
How it works:
|
|
191
|
+
1. Searches for relevant articles AND published Q&A
|
|
192
|
+
2. Uses knowledge graph to find related content via entities
|
|
193
|
+
3. Generates a synthesized answer from all sources
|
|
194
|
+
4. Returns source references for transparency
|
|
195
|
+
|
|
196
|
+
Use this when you need a complete answer with sources, not just search results.`,
|
|
202
197
|
{
|
|
203
198
|
question: z.string().describe('The customer question to answer'),
|
|
204
|
-
context: z.string().optional().describe('Additional context
|
|
205
|
-
generate_if_not_found: z.boolean().optional().describe('Generate new article if
|
|
199
|
+
context: z.string().optional().describe('Additional context'),
|
|
200
|
+
generate_if_not_found: z.boolean().optional().describe('Generate new article if not found (default: true)'),
|
|
201
|
+
use_graph: z.boolean().optional().describe('Enable graph-augmented retrieval (default: true)'),
|
|
206
202
|
},
|
|
207
203
|
async (args) => {
|
|
208
204
|
const result = await callRemoteServer('tools/call', {
|
|
@@ -218,20 +214,11 @@ The answer includes source references for transparency.`,
|
|
|
218
214
|
'log_unanswered',
|
|
219
215
|
`Flag a customer question as unanswered for knowledge gap analysis.
|
|
220
216
|
|
|
221
|
-
|
|
222
|
-
|
|
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`,
|
|
217
|
+
**CALL THIS** when search_knowledge returns no results or low confidence.
|
|
218
|
+
Flagged questions appear in admin "Knowledge Gaps" section.`,
|
|
232
219
|
{
|
|
233
220
|
query: z.string().describe('The question that could not be answered'),
|
|
234
|
-
reason: z.string().optional().describe('Reason why
|
|
221
|
+
reason: z.string().optional().describe('Reason why'),
|
|
235
222
|
},
|
|
236
223
|
async (args) => {
|
|
237
224
|
const result = await callRemoteServer('tools/call', {
|
|
@@ -242,29 +229,22 @@ WHY IT MATTERS:
|
|
|
242
229
|
}
|
|
243
230
|
);
|
|
244
231
|
|
|
245
|
-
// Tool 5: List Articles
|
|
232
|
+
// Tool 5: List Content (Unified - Articles + Q&A)
|
|
246
233
|
server.tool(
|
|
247
|
-
'
|
|
248
|
-
`Browse
|
|
249
|
-
|
|
250
|
-
Parameters:
|
|
251
|
-
- limit: Number of articles (default 10, max 50)
|
|
252
|
-
- category: Filter by category slug (use list_categories first)
|
|
234
|
+
'list_content',
|
|
235
|
+
`Browse content in the knowledge base with optional filtering.
|
|
253
236
|
|
|
254
|
-
|
|
255
|
-
Use
|
|
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`,
|
|
237
|
+
Supports: articles, qa, or all (default).
|
|
238
|
+
Use list_categories first to discover available categories.`,
|
|
261
239
|
{
|
|
262
|
-
|
|
240
|
+
type: z.enum(['articles', 'qa', 'all']).optional().describe('Content type (default: all)'),
|
|
241
|
+
limit: z.number().optional().describe('Number of items (default: 10, max: 50)'),
|
|
263
242
|
category: z.string().optional().describe('Filter by category slug'),
|
|
243
|
+
status: z.string().optional().describe('Filter Q&A by status: pending, draft, published'),
|
|
264
244
|
},
|
|
265
245
|
async (args) => {
|
|
266
246
|
const result = await callRemoteServer('tools/call', {
|
|
267
|
-
name: '
|
|
247
|
+
name: 'list_content',
|
|
268
248
|
arguments: args,
|
|
269
249
|
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
270
250
|
return result;
|
|
@@ -274,7 +254,7 @@ WORKFLOW:
|
|
|
274
254
|
// Tool 6: Health Check
|
|
275
255
|
server.tool(
|
|
276
256
|
'health_check',
|
|
277
|
-
'Check the health status of the memory system.',
|
|
257
|
+
'Check the health status of the memory system including article/Q&A counts.',
|
|
278
258
|
{},
|
|
279
259
|
async () => {
|
|
280
260
|
const result = await callRemoteServer('tools/call', {
|
|
@@ -288,22 +268,8 @@ WORKFLOW:
|
|
|
288
268
|
// Tool 7: List Categories
|
|
289
269
|
server.tool(
|
|
290
270
|
'list_categories',
|
|
291
|
-
`List all categories in the knowledge base with
|
|
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`,
|
|
271
|
+
`List all categories in the knowledge base with content counts.
|
|
272
|
+
Use category slugs with list_content for filtered browsing.`,
|
|
307
273
|
{},
|
|
308
274
|
async () => {
|
|
309
275
|
const result = await callRemoteServer('tools/call', {
|
|
@@ -315,21 +281,219 @@ RECOMMENDED WORKFLOW:
|
|
|
315
281
|
);
|
|
316
282
|
|
|
317
283
|
// ============================================================
|
|
318
|
-
//
|
|
284
|
+
// KNOWLEDGE GRAPH TOOLS (available in all modes)
|
|
285
|
+
// ============================================================
|
|
286
|
+
|
|
287
|
+
// Tool 8: Find Related Articles
|
|
288
|
+
server.tool(
|
|
289
|
+
'find_related',
|
|
290
|
+
'Find articles related through shared entities or explicit relationships. Uses graph traversal.',
|
|
291
|
+
{
|
|
292
|
+
article_id: z.string().describe('Article ID to find relations for'),
|
|
293
|
+
max_depth: z.number().optional().describe('Relationship hops 1-5 (default: 2)'),
|
|
294
|
+
min_strength: z.number().optional().describe('Min strength 0-1 (default: 0.3)'),
|
|
295
|
+
limit: z.number().optional().describe('Max articles (default: 10)'),
|
|
296
|
+
},
|
|
297
|
+
async (args) => {
|
|
298
|
+
const result = await callRemoteServer('tools/call', {
|
|
299
|
+
name: 'find_related',
|
|
300
|
+
arguments: args,
|
|
301
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
// Tool 9: Get Entities
|
|
307
|
+
server.tool(
|
|
308
|
+
'get_entities',
|
|
309
|
+
'Get named entities from an article (people, orgs, forms, states, etc.).',
|
|
310
|
+
{
|
|
311
|
+
article_id: z.string().describe('Article ID to get entities for'),
|
|
312
|
+
},
|
|
313
|
+
async (args) => {
|
|
314
|
+
const result = await callRemoteServer('tools/call', {
|
|
315
|
+
name: 'get_entities',
|
|
316
|
+
arguments: args,
|
|
317
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Tool 10: Search Entities
|
|
323
|
+
server.tool(
|
|
324
|
+
'search_entities',
|
|
325
|
+
'Search for entities by name or type across the knowledge base.',
|
|
326
|
+
{
|
|
327
|
+
query: z.string().optional().describe('Search query for entity name'),
|
|
328
|
+
type: z.string().optional().describe('Filter: PERSON, ORGANIZATION, FORM, STATE, COUNTRY, DATE, AMOUNT, CONCEPT, REGULATION, SERVICE, DOCUMENT'),
|
|
329
|
+
limit: z.number().optional().describe('Max entities (default: 20)'),
|
|
330
|
+
},
|
|
331
|
+
async (args) => {
|
|
332
|
+
const result = await callRemoteServer('tools/call', {
|
|
333
|
+
name: 'search_entities',
|
|
334
|
+
arguments: args,
|
|
335
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Tool 11: Get Knowledge Graph
|
|
341
|
+
server.tool(
|
|
342
|
+
'get_knowledge_graph',
|
|
343
|
+
'Get the knowledge graph structure for visualization. Returns nodes and edges.',
|
|
344
|
+
{
|
|
345
|
+
center_article_id: z.string().optional().describe('Center on this article'),
|
|
346
|
+
depth: z.number().optional().describe('Traversal depth 1-5 (default: 2)'),
|
|
347
|
+
include_entities: z.boolean().optional().describe('Include entity nodes (default: true)'),
|
|
348
|
+
include_metadata: z.boolean().optional().describe('Include full metadata (default: false)'),
|
|
349
|
+
limit: z.number().optional().describe('Max nodes (default: 20, max: 100)'),
|
|
350
|
+
},
|
|
351
|
+
async (args) => {
|
|
352
|
+
const result = await callRemoteServer('tools/call', {
|
|
353
|
+
name: 'get_knowledge_graph',
|
|
354
|
+
arguments: args,
|
|
355
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// ============================================================
|
|
361
|
+
// EPISODIC MEMORY TOOLS (available in all modes)
|
|
362
|
+
// ============================================================
|
|
363
|
+
|
|
364
|
+
// Tool 12: Start Conversation
|
|
365
|
+
server.tool(
|
|
366
|
+
'start_conversation',
|
|
367
|
+
`Start a new conversation for tracking episodic memory.
|
|
368
|
+
Call at the beginning of each session to enable context continuity.`,
|
|
369
|
+
{
|
|
370
|
+
session_id: z.string().describe('Session ID (e.g., MCP session ID)'),
|
|
371
|
+
title: z.string().optional().describe('Conversation title'),
|
|
372
|
+
user_id: z.string().optional().describe('User ID for multi-user tracking'),
|
|
373
|
+
},
|
|
374
|
+
async (args) => {
|
|
375
|
+
const result = await callRemoteServer('tools/call', {
|
|
376
|
+
name: 'start_conversation',
|
|
377
|
+
arguments: args,
|
|
378
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Tool 13: Add Message
|
|
384
|
+
server.tool(
|
|
385
|
+
'add_message',
|
|
386
|
+
`Add a message to an existing conversation.
|
|
387
|
+
Call after each user message and assistant response.
|
|
388
|
+
User facts are automatically extracted.`,
|
|
389
|
+
{
|
|
390
|
+
conversation_id: z.string().describe('Conversation ID from start_conversation'),
|
|
391
|
+
role: z.enum(['user', 'assistant', 'system']).describe('Message role'),
|
|
392
|
+
content: z.string().describe('Message content'),
|
|
393
|
+
referenced_articles: z.string().optional().describe('Comma-separated article IDs referenced'),
|
|
394
|
+
},
|
|
395
|
+
async (args) => {
|
|
396
|
+
const result = await callRemoteServer('tools/call', {
|
|
397
|
+
name: 'add_message',
|
|
398
|
+
arguments: args,
|
|
399
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// Tool 14: Get Conversation
|
|
405
|
+
server.tool(
|
|
406
|
+
'get_conversation',
|
|
407
|
+
'Retrieve a conversation with all its messages by ID.',
|
|
408
|
+
{
|
|
409
|
+
conversation_id: z.string().describe('Conversation ID'),
|
|
410
|
+
},
|
|
411
|
+
async (args) => {
|
|
412
|
+
const result = await callRemoteServer('tools/call', {
|
|
413
|
+
name: 'get_conversation',
|
|
414
|
+
arguments: args,
|
|
415
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
416
|
+
return result;
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
// Tool 15: List Conversations
|
|
421
|
+
server.tool(
|
|
422
|
+
'list_conversations',
|
|
423
|
+
'List conversations for a session or user. Returns recent first.',
|
|
424
|
+
{
|
|
425
|
+
session_id: z.string().optional().describe('Filter by session ID'),
|
|
426
|
+
user_id: z.string().optional().describe('Filter by user ID'),
|
|
427
|
+
limit: z.number().optional().describe('Max conversations (default: 20)'),
|
|
428
|
+
},
|
|
429
|
+
async (args) => {
|
|
430
|
+
const result = await callRemoteServer('tools/call', {
|
|
431
|
+
name: 'list_conversations',
|
|
432
|
+
arguments: args,
|
|
433
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
434
|
+
return result;
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
// Tool 16: Get Session Context
|
|
439
|
+
server.tool(
|
|
440
|
+
'get_session_context',
|
|
441
|
+
`Get recent conversation context for a session.
|
|
442
|
+
Call when user refers to prior conversation or asks follow-up questions.`,
|
|
443
|
+
{
|
|
444
|
+
session_id: z.string().describe('Session ID to get context for'),
|
|
445
|
+
message_limit: z.number().optional().describe('Max messages (default: 10)'),
|
|
446
|
+
},
|
|
447
|
+
async (args) => {
|
|
448
|
+
const result = await callRemoteServer('tools/call', {
|
|
449
|
+
name: 'get_session_context',
|
|
450
|
+
arguments: args,
|
|
451
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
// ============================================================
|
|
457
|
+
// Q&A TOOLS (available in all modes)
|
|
458
|
+
// ============================================================
|
|
459
|
+
|
|
460
|
+
// Tool 17: Add Question
|
|
461
|
+
server.tool(
|
|
462
|
+
'add_question',
|
|
463
|
+
`Add a new question to the Q&A store for later answering.
|
|
464
|
+
Use to capture questions from external channels.`,
|
|
465
|
+
{
|
|
466
|
+
question: z.string().describe('The question text'),
|
|
467
|
+
category: z.string().optional().describe('Category slug'),
|
|
468
|
+
tags: z.string().optional().describe('Comma-separated tags'),
|
|
469
|
+
priority: z.string().optional().describe('Priority: low, medium, high, critical'),
|
|
470
|
+
source: z.string().optional().describe('Source: mcp, email, manual, import'),
|
|
471
|
+
},
|
|
472
|
+
async (args) => {
|
|
473
|
+
const result = await callRemoteServer('tools/call', {
|
|
474
|
+
name: 'add_question',
|
|
475
|
+
arguments: args,
|
|
476
|
+
}) as { content: Array<{ type: 'text'; text: string }> };
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// ============================================================
|
|
482
|
+
// FULL MODE TOOLS (only with --full flag or MCP_MODE=full)
|
|
319
483
|
// ============================================================
|
|
320
484
|
|
|
321
485
|
if (isFullMode) {
|
|
322
|
-
// Tool
|
|
486
|
+
// Tool 18: Add Article
|
|
323
487
|
server.tool(
|
|
324
488
|
'add_article',
|
|
325
|
-
'Create a new article in the knowledge base.
|
|
489
|
+
'Create a new article in the knowledge base. Generates embedding automatically.',
|
|
326
490
|
{
|
|
327
491
|
title: z.string().describe('Article title'),
|
|
328
|
-
content: z.string().describe('Article content in markdown
|
|
329
|
-
summary: z.string().optional().describe('Brief summary (auto-generated if
|
|
330
|
-
category: z.string().optional().describe('Category
|
|
492
|
+
content: z.string().describe('Article content in markdown'),
|
|
493
|
+
summary: z.string().optional().describe('Brief summary (auto-generated if omitted)'),
|
|
494
|
+
category: z.string().optional().describe('Category slug'),
|
|
331
495
|
tags: z.string().optional().describe('Comma-separated tags'),
|
|
332
|
-
source: z.string().optional().describe('Source
|
|
496
|
+
source: z.string().optional().describe('Source (e.g., manual, irs.gov)'),
|
|
333
497
|
},
|
|
334
498
|
async (args) => {
|
|
335
499
|
const result = await callRemoteServer('tools/call', {
|
|
@@ -340,7 +504,7 @@ RECOMMENDED WORKFLOW:
|
|
|
340
504
|
}
|
|
341
505
|
);
|
|
342
506
|
|
|
343
|
-
// Tool
|
|
507
|
+
// Tool 19: Edit Article
|
|
344
508
|
server.tool(
|
|
345
509
|
'edit_article',
|
|
346
510
|
'Update an existing article. Re-generates embedding if content changes.',
|
|
@@ -361,13 +525,13 @@ RECOMMENDED WORKFLOW:
|
|
|
361
525
|
}
|
|
362
526
|
);
|
|
363
527
|
|
|
364
|
-
// Tool
|
|
528
|
+
// Tool 20: Remove Article
|
|
365
529
|
server.tool(
|
|
366
530
|
'remove_article',
|
|
367
|
-
'Soft-delete an article
|
|
531
|
+
'Soft-delete an article. Marked as deleted but not permanently removed.',
|
|
368
532
|
{
|
|
369
533
|
article_id: z.string().describe('Article ID to remove'),
|
|
370
|
-
reason: z.string().optional().describe('Reason for deletion
|
|
534
|
+
reason: z.string().optional().describe('Reason for deletion'),
|
|
371
535
|
},
|
|
372
536
|
async (args) => {
|
|
373
537
|
const result = await callRemoteServer('tools/call', {
|
|
@@ -378,14 +542,14 @@ RECOMMENDED WORKFLOW:
|
|
|
378
542
|
}
|
|
379
543
|
);
|
|
380
544
|
|
|
381
|
-
// Tool
|
|
545
|
+
// Tool 21: Rate Answer
|
|
382
546
|
server.tool(
|
|
383
547
|
'rate_answer',
|
|
384
|
-
'Rate a previous answer as helpful, unhelpful, or neutral.
|
|
548
|
+
'Rate a previous answer as helpful, unhelpful, or neutral.',
|
|
385
549
|
{
|
|
386
|
-
query_id: z.string().describe('Query log ID from
|
|
550
|
+
query_id: z.string().describe('Query log ID from answer_question'),
|
|
387
551
|
rating: z.number().describe('Rating: -1 (unhelpful), 0 (neutral), 1 (helpful)'),
|
|
388
|
-
feedback: z.string().optional().describe('Optional text
|
|
552
|
+
feedback: z.string().optional().describe('Optional feedback text'),
|
|
389
553
|
},
|
|
390
554
|
async (args) => {
|
|
391
555
|
const result = await callRemoteServer('tools/call', {
|
package/.npmrc.bak
DELETED