memory-journal-mcp 4.3.0 → 4.4.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/.dockerignore +131 -122
- package/.gitattributes +29 -0
- package/.github/workflows/docker-publish.yml +1 -1
- package/.github/workflows/lint-and-test.yml +1 -2
- package/.github/workflows/secrets-scanning.yml +0 -1
- package/.github/workflows/security-update.yml +6 -6
- package/.vscode/settings.json +17 -15
- package/CHANGELOG.md +1065 -11
- package/DOCKER_README.md +51 -33
- package/Dockerfile +14 -12
- package/README.md +68 -33
- package/SECURITY.md +225 -220
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +70 -26
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/constants/icons.d.ts +2 -0
- package/dist/constants/icons.d.ts.map +1 -1
- package/dist/constants/icons.js +6 -0
- package/dist/constants/icons.js.map +1 -1
- package/dist/database/SqliteAdapter.d.ts +51 -10
- package/dist/database/SqliteAdapter.d.ts.map +1 -1
- package/dist/database/SqliteAdapter.js +143 -43
- package/dist/database/SqliteAdapter.js.map +1 -1
- package/dist/filtering/ToolFilter.d.ts +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +7 -1
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/github/GitHubIntegration.d.ts +74 -2
- package/dist/github/GitHubIntegration.d.ts.map +1 -1
- package/dist/github/GitHubIntegration.js +508 -7
- package/dist/github/GitHubIntegration.js.map +1 -1
- package/dist/handlers/prompts/index.js +1 -0
- package/dist/handlers/prompts/index.js.map +1 -1
- package/dist/handlers/resources/index.d.ts.map +1 -1
- package/dist/handlers/resources/index.js +257 -13
- package/dist/handlers/resources/index.js.map +1 -1
- package/dist/handlers/tools/index.d.ts.map +1 -1
- package/dist/handlers/tools/index.js +595 -8
- package/dist/handlers/tools/index.js.map +1 -1
- package/dist/server/McpServer.d.ts +2 -0
- package/dist/server/McpServer.d.ts.map +1 -1
- package/dist/server/McpServer.js +69 -26
- package/dist/server/McpServer.js.map +1 -1
- package/dist/types/index.d.ts +97 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +8 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/progress-utils.d.ts +18 -3
- package/dist/utils/progress-utils.d.ts.map +1 -1
- package/dist/utils/progress-utils.js.map +1 -1
- package/dist/utils/security-utils.d.ts +91 -0
- package/dist/utils/security-utils.d.ts.map +1 -0
- package/dist/utils/security-utils.js +184 -0
- package/dist/utils/security-utils.js.map +1 -0
- package/dist/vector/VectorSearchManager.d.ts +2 -1
- package/dist/vector/VectorSearchManager.d.ts.map +1 -1
- package/dist/vector/VectorSearchManager.js +100 -34
- package/dist/vector/VectorSearchManager.js.map +1 -1
- package/docker-compose.yml +46 -37
- package/mcp-config-example.json +0 -2
- package/package.json +21 -14
- package/releases/v4.3.1.md +69 -0
- package/releases/v4.4.0.md +120 -0
- package/server.json +3 -3
- package/src/cli.ts +11 -0
- package/src/constants/ServerInstructions.ts +70 -26
- package/src/constants/icons.ts +7 -0
- package/src/database/SqliteAdapter.ts +165 -44
- package/src/filtering/ToolFilter.ts +7 -1
- package/src/github/GitHubIntegration.ts +588 -8
- package/src/handlers/prompts/index.ts +1 -0
- package/src/handlers/resources/index.ts +318 -12
- package/src/handlers/tools/index.ts +686 -13
- package/src/server/McpServer.ts +79 -37
- package/src/types/index.ts +98 -0
- package/src/utils/logger.ts +10 -1
- package/src/utils/progress-utils.ts +17 -6
- package/src/utils/security-utils.ts +205 -0
- package/src/vector/VectorSearchManager.ts +110 -39
- package/tests/constants/icons.test.ts +102 -0
- package/tests/constants/server-instructions.test.ts +549 -0
- package/tests/database/sqlite-adapter.bench.ts +63 -0
- package/tests/database/sqlite-adapter.test.ts +555 -0
- package/tests/filtering/tool-filter.test.ts +266 -0
- package/tests/github/github-integration.test.ts +1024 -0
- package/tests/handlers/github-resource-handlers.test.ts +473 -0
- package/tests/handlers/github-tool-handlers.test.ts +556 -0
- package/tests/handlers/prompt-handlers.test.ts +91 -0
- package/tests/handlers/resource-handlers.test.ts +339 -0
- package/tests/handlers/tool-handlers.test.ts +497 -0
- package/tests/handlers/vector-tool-handlers.test.ts +238 -0
- package/tests/security/sql-injection.test.ts +347 -0
- package/tests/server/mcp-server.bench.ts +55 -0
- package/tests/server/mcp-server.test.ts +675 -0
- package/tests/utils/logger.test.ts +180 -0
- package/tests/utils/mcp-logger.test.ts +212 -0
- package/tests/utils/progress-utils.test.ts +156 -0
- package/tests/utils/security-utils.test.ts +82 -0
- package/tests/vector/vector-search-manager.test.ts +335 -0
- package/tests/vector/vector-search.bench.ts +53 -0
- package/vitest.config.ts +15 -0
- package/.github/workflows/DOCKER_DEPLOYMENT_SETUP.md +0 -387
- package/.github/workflows/dependabot-auto-merge.yml +0 -42
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolFilter Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the tool filtering system: groups, meta-groups, parsing, filtering.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, vi, afterEach } from 'vitest'
|
|
8
|
+
import {
|
|
9
|
+
TOOL_GROUPS,
|
|
10
|
+
META_GROUPS,
|
|
11
|
+
getAllToolNames,
|
|
12
|
+
getToolGroup,
|
|
13
|
+
parseToolFilter,
|
|
14
|
+
isToolEnabled,
|
|
15
|
+
filterTools,
|
|
16
|
+
getToolFilterFromEnv,
|
|
17
|
+
calculateTokenSavings,
|
|
18
|
+
getFilterSummary,
|
|
19
|
+
} from '../../src/filtering/ToolFilter.js'
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Constants
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
describe('TOOL_GROUPS', () => {
|
|
26
|
+
it('should contain all 8 groups', () => {
|
|
27
|
+
const groups = Object.keys(TOOL_GROUPS)
|
|
28
|
+
expect(groups).toContain('core')
|
|
29
|
+
expect(groups).toContain('search')
|
|
30
|
+
expect(groups).toContain('analytics')
|
|
31
|
+
expect(groups).toContain('relationships')
|
|
32
|
+
expect(groups).toContain('export')
|
|
33
|
+
expect(groups).toContain('admin')
|
|
34
|
+
expect(groups).toContain('github')
|
|
35
|
+
expect(groups).toContain('backup')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should have no duplicate tools across groups', () => {
|
|
39
|
+
const allTools = Object.values(TOOL_GROUPS).flat()
|
|
40
|
+
const unique = new Set(allTools)
|
|
41
|
+
expect(unique.size).toBe(allTools.length)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('META_GROUPS', () => {
|
|
46
|
+
it('should define starter as core + search', () => {
|
|
47
|
+
expect(META_GROUPS.starter).toEqual(['core', 'search'])
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should define essential as core only', () => {
|
|
51
|
+
expect(META_GROUPS.essential).toEqual(['core'])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should define full with all groups', () => {
|
|
55
|
+
expect(META_GROUPS.full).toContain('core')
|
|
56
|
+
expect(META_GROUPS.full).toContain('github')
|
|
57
|
+
expect(META_GROUPS.full).toContain('backup')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should define readonly without admin, github, backup', () => {
|
|
61
|
+
expect(META_GROUPS.readonly).not.toContain('admin')
|
|
62
|
+
expect(META_GROUPS.readonly).not.toContain('github')
|
|
63
|
+
expect(META_GROUPS.readonly).not.toContain('backup')
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// getAllToolNames
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
describe('getAllToolNames', () => {
|
|
72
|
+
it('should return all tools', () => {
|
|
73
|
+
const names = getAllToolNames()
|
|
74
|
+
expect(names.length).toBeGreaterThan(0)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should contain no duplicates', () => {
|
|
78
|
+
const names = getAllToolNames()
|
|
79
|
+
const unique = new Set(names)
|
|
80
|
+
expect(unique.size).toBe(names.length)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should include known tools', () => {
|
|
84
|
+
const names = getAllToolNames()
|
|
85
|
+
expect(names).toContain('create_entry')
|
|
86
|
+
expect(names).toContain('search_entries')
|
|
87
|
+
expect(names).toContain('backup_journal')
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// getToolGroup
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
describe('getToolGroup', () => {
|
|
96
|
+
it('should return correct group for known tools', () => {
|
|
97
|
+
expect(getToolGroup('create_entry')).toBe('core')
|
|
98
|
+
expect(getToolGroup('search_entries')).toBe('search')
|
|
99
|
+
expect(getToolGroup('backup_journal')).toBe('backup')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should return undefined for unknown tools', () => {
|
|
103
|
+
expect(getToolGroup('nonexistent_tool')).toBeUndefined()
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Type guards
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
describe('group validation via getToolGroup', () => {
|
|
112
|
+
it('should identify valid groups from tool lookups', () => {
|
|
113
|
+
expect(getToolGroup('create_entry')).toBe('core')
|
|
114
|
+
expect(getToolGroup('search_entries')).toBe('search')
|
|
115
|
+
expect(getToolGroup('delete_entry')).toBe('admin')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should return undefined for non-tool strings', () => {
|
|
119
|
+
expect(getToolGroup('starter')).toBeUndefined() // meta-group, not a tool
|
|
120
|
+
expect(getToolGroup('nonexistent')).toBeUndefined()
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// parseToolFilter
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
128
|
+
describe('parseToolFilter', () => {
|
|
129
|
+
it('should parse a single group', () => {
|
|
130
|
+
const config = parseToolFilter('core')
|
|
131
|
+
expect(config.enabledTools.has('create_entry')).toBe(true)
|
|
132
|
+
expect(config.enabledTools.has('backup_journal')).toBe(false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should parse multiple groups', () => {
|
|
136
|
+
const config = parseToolFilter('core,search')
|
|
137
|
+
expect(config.enabledTools.has('create_entry')).toBe(true)
|
|
138
|
+
expect(config.enabledTools.has('search_entries')).toBe(true)
|
|
139
|
+
expect(config.enabledTools.has('backup_journal')).toBe(false)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should parse meta-groups', () => {
|
|
143
|
+
const config = parseToolFilter('starter')
|
|
144
|
+
// starter = core + search
|
|
145
|
+
expect(config.enabledTools.has('create_entry')).toBe(true)
|
|
146
|
+
expect(config.enabledTools.has('search_entries')).toBe(true)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should parse group exclusion', () => {
|
|
150
|
+
const config = parseToolFilter('full,-admin')
|
|
151
|
+
expect(config.enabledTools.has('create_entry')).toBe(true)
|
|
152
|
+
// Admin tools should be excluded
|
|
153
|
+
expect(config.enabledTools.has('delete_entry')).toBe(false)
|
|
154
|
+
expect(config.enabledTools.has('merge_tags')).toBe(false)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should parse tool exclusion', () => {
|
|
158
|
+
const config = parseToolFilter('core,-delete_entry')
|
|
159
|
+
expect(config.enabledTools.has('create_entry')).toBe(true)
|
|
160
|
+
expect(config.enabledTools.has('delete_entry')).toBe(false)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('should parse tool addition with +', () => {
|
|
164
|
+
const config = parseToolFilter('core,+semantic_search')
|
|
165
|
+
expect(config.enabledTools.has('create_entry')).toBe(true)
|
|
166
|
+
expect(config.enabledTools.has('semantic_search')).toBe(true)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should store raw filter string', () => {
|
|
170
|
+
const config = parseToolFilter('starter,-delete_entry')
|
|
171
|
+
expect(config.raw).toBe('starter,-delete_entry')
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// isToolEnabled / filterTools
|
|
177
|
+
// ============================================================================
|
|
178
|
+
|
|
179
|
+
describe('isToolEnabled', () => {
|
|
180
|
+
it('should return true for enabled tools', () => {
|
|
181
|
+
const config = parseToolFilter('core')
|
|
182
|
+
expect(isToolEnabled('create_entry', config)).toBe(true)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should return false for disabled tools', () => {
|
|
186
|
+
const config = parseToolFilter('core')
|
|
187
|
+
expect(isToolEnabled('backup_journal', config)).toBe(false)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('filterTools', () => {
|
|
192
|
+
it('should filter tool objects by name', () => {
|
|
193
|
+
const tools = [
|
|
194
|
+
{ name: 'create_entry', other: 'data' },
|
|
195
|
+
{ name: 'backup_journal', other: 'data' },
|
|
196
|
+
{ name: 'search_entries', other: 'data' },
|
|
197
|
+
]
|
|
198
|
+
const config = parseToolFilter('core')
|
|
199
|
+
const filtered = filterTools(tools, config)
|
|
200
|
+
|
|
201
|
+
expect(filtered.some((t) => t.name === 'create_entry')).toBe(true)
|
|
202
|
+
expect(filtered.some((t) => t.name === 'backup_journal')).toBe(false)
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// ============================================================================
|
|
207
|
+
// getToolFilterFromEnv
|
|
208
|
+
// ============================================================================
|
|
209
|
+
|
|
210
|
+
describe('getToolFilterFromEnv', () => {
|
|
211
|
+
afterEach(() => {
|
|
212
|
+
delete process.env['MEMORY_JOURNAL_MCP_TOOL_FILTER']
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should return null when env var not set', () => {
|
|
216
|
+
delete process.env['MEMORY_JOURNAL_MCP_TOOL_FILTER']
|
|
217
|
+
expect(getToolFilterFromEnv()).toBeNull()
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('should parse env var when set', () => {
|
|
221
|
+
process.env['MEMORY_JOURNAL_MCP_TOOL_FILTER'] = 'starter'
|
|
222
|
+
const config = getToolFilterFromEnv()
|
|
223
|
+
|
|
224
|
+
expect(config).not.toBeNull()
|
|
225
|
+
expect(config?.enabledTools.has('create_entry')).toBe(true)
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// ============================================================================
|
|
230
|
+
// calculateTokenSavings
|
|
231
|
+
// ============================================================================
|
|
232
|
+
|
|
233
|
+
describe('calculateTokenSavings', () => {
|
|
234
|
+
it('should calculate correct reduction percentage', () => {
|
|
235
|
+
const result = calculateTokenSavings(38, 10)
|
|
236
|
+
// (38-10)/38 ≈ 73.68%
|
|
237
|
+
expect(result.reduction).toBeGreaterThan(70)
|
|
238
|
+
expect(result.reduction).toBeLessThan(80)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('should calculate saved tokens', () => {
|
|
242
|
+
const result = calculateTokenSavings(38, 10, 150)
|
|
243
|
+
// (38-10) * 150 = 4200
|
|
244
|
+
expect(result.savedTokens).toBe(4200)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('should return 0 when no tools filtered', () => {
|
|
248
|
+
const result = calculateTokenSavings(38, 38)
|
|
249
|
+
expect(result.reduction).toBe(0)
|
|
250
|
+
expect(result.savedTokens).toBe(0)
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
// ============================================================================
|
|
255
|
+
// getFilterSummary
|
|
256
|
+
// ============================================================================
|
|
257
|
+
|
|
258
|
+
describe('getFilterSummary', () => {
|
|
259
|
+
it('should return human-readable summary', () => {
|
|
260
|
+
const config = parseToolFilter('starter')
|
|
261
|
+
const summary = getFilterSummary(config)
|
|
262
|
+
|
|
263
|
+
expect(typeof summary).toBe('string')
|
|
264
|
+
expect(summary.length).toBeGreaterThan(0)
|
|
265
|
+
})
|
|
266
|
+
})
|