memory-journal-mcp 4.4.2 → 4.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/.github/workflows/lint-and-test.yml +1 -1
- package/.github/workflows/security-update.yml +1 -1
- package/CHANGELOG.md +81 -1
- package/DOCKER_README.md +57 -7
- package/Dockerfile +17 -17
- package/README.md +65 -6
- package/SECURITY.md +27 -35
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +5 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +137 -83
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/database/SqliteAdapter.d.ts +2 -1
- package/dist/database/SqliteAdapter.d.ts.map +1 -1
- package/dist/database/SqliteAdapter.js +15 -8
- package/dist/database/SqliteAdapter.js.map +1 -1
- package/dist/handlers/resources/index.d.ts +3 -1
- package/dist/handlers/resources/index.d.ts.map +1 -1
- package/dist/handlers/resources/index.js +5 -2
- 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 +63 -16
- 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 +43 -2
- package/dist/server/McpServer.js.map +1 -1
- package/dist/server/Scheduler.d.ts +91 -0
- package/dist/server/Scheduler.d.ts.map +1 -0
- package/dist/server/Scheduler.js +201 -0
- package/dist/server/Scheduler.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/security-utils.d.ts +0 -21
- package/dist/utils/security-utils.d.ts.map +1 -1
- package/dist/utils/security-utils.js +0 -47
- package/dist/utils/security-utils.js.map +1 -1
- package/hooks/README.md +107 -0
- package/hooks/cursor/hooks.json +10 -0
- package/hooks/cursor/memory-journal.mdc +22 -0
- package/hooks/cursor/session-end.sh +19 -0
- package/hooks/kilo-code/session-end-mode.json +11 -0
- package/hooks/kiro/session-end.md +13 -0
- package/package.json +8 -8
- package/releases/v4.5.0.md +116 -0
- package/scripts/generate-server-instructions.ts +176 -0
- package/scripts/server-instructions-function-body.ts +77 -0
- package/server.json +3 -3
- package/src/cli.ts +26 -0
- package/src/constants/ServerInstructions.ts +137 -83
- package/src/constants/server-instructions.md +262 -0
- package/src/database/SqliteAdapter.ts +22 -8
- package/src/handlers/resources/index.ts +8 -2
- package/src/handlers/tools/index.ts +70 -20
- package/src/server/McpServer.ts +60 -2
- package/src/server/Scheduler.ts +278 -0
- package/src/utils/logger.ts +6 -3
- package/src/utils/security-utils.ts +0 -52
- package/tests/constants/server-instructions.test.ts +26 -0
- package/tests/database/sqlite-adapter.test.ts +84 -0
- package/tests/filtering/tool-filter.test.ts +46 -0
- package/tests/handlers/github-resource-handlers.test.ts +453 -0
- package/tests/handlers/github-tool-handlers.test.ts +899 -0
- package/tests/handlers/prompt-handlers.test.ts +40 -0
- package/tests/handlers/resource-handlers.test.ts +32 -0
- package/tests/handlers/tool-handlers.test.ts +13 -2
- package/tests/security/sql-injection.test.ts +3 -54
- package/tests/server/mcp-server.test.ts +491 -5
- package/tests/server/scheduler.test.ts +400 -0
- package/tests/vector/vector-search-manager.test.ts +60 -0
- package/.vscode/settings.json +0 -84
package/hooks/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# IDE Hook & Rule Configurations
|
|
2
|
+
|
|
3
|
+
Ready-to-use configurations for automatic session management with memory-journal-mcp.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
Memory Journal bridges AI sessions with two behaviors:
|
|
8
|
+
|
|
9
|
+
- **Session start** — Agent reads `memory://briefing` and shows the user a project context summary
|
|
10
|
+
- **Session end** — Agent creates a `retrospective` entry tagged `session-summary` capturing what was done, what's pending, and context for the next session
|
|
11
|
+
|
|
12
|
+
The next session's briefing automatically includes the previous session's summary.
|
|
13
|
+
|
|
14
|
+
## Progressive Enhancement
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────────────────────────┐
|
|
18
|
+
│ Cursor Rule (most reliable for agent behavior) │
|
|
19
|
+
│ .cursor/rules/memory-journal.mdc │
|
|
20
|
+
│ ↓ always-apply rule instructs agent directly │
|
|
21
|
+
├─────────────────────────────────────────────────────┤
|
|
22
|
+
│ Server Instructions (fallback for any MCP client) │
|
|
23
|
+
│ Embedded in MCP server initialization │
|
|
24
|
+
│ ↓ agent follows Session Start / Session End steps │
|
|
25
|
+
├─────────────────────────────────────────────────────┤
|
|
26
|
+
│ IDE Hooks (audit & logging) │
|
|
27
|
+
│ Cursor sessionEnd · Kiro hooks · Kilo Code modes │
|
|
28
|
+
│ ↓ fire-and-forget observation on session close │
|
|
29
|
+
├─────────────────────────────────────────────────────┤
|
|
30
|
+
│ User opt-out │
|
|
31
|
+
│ User says "skip the summary" → agent skips │
|
|
32
|
+
└─────────────────────────────────────────────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Setup by IDE
|
|
36
|
+
|
|
37
|
+
### Cursor
|
|
38
|
+
|
|
39
|
+
#### Step 1: Cursor Rule (handles session start + end behavior)
|
|
40
|
+
|
|
41
|
+
Copy `cursor/memory-journal.mdc` to your project's `.cursor/rules/`:
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
# From your project root
|
|
45
|
+
mkdir -p .cursor/rules
|
|
46
|
+
cp <path-to-memory-journal-mcp>/hooks/cursor/memory-journal.mdc .cursor/rules/memory-journal.mdc
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This `alwaysApply` rule instructs the agent to:
|
|
50
|
+
|
|
51
|
+
- Read `memory://briefing` and show project context at session start
|
|
52
|
+
- Create a session summary entry when the conversation wraps up
|
|
53
|
+
|
|
54
|
+
**Requirements**: Cursor with Rules support (`.cursor/rules/` directory).
|
|
55
|
+
|
|
56
|
+
#### Step 2: sessionEnd Hook (optional — audit logging)
|
|
57
|
+
|
|
58
|
+
Copy `cursor/hooks.json` and `cursor/session-end.sh` to your project's `.cursor/`:
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
cp <path-to-memory-journal-mcp>/hooks/cursor/hooks.json .cursor/hooks.json
|
|
62
|
+
mkdir -p .cursor/hooks
|
|
63
|
+
cp <path-to-memory-journal-mcp>/hooks/cursor/session-end.sh .cursor/hooks/session-end.sh
|
|
64
|
+
chmod +x .cursor/hooks/session-end.sh
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The `sessionEnd` hook is **fire-and-forget** — Cursor does not use its output. It logs session metadata to `/tmp/memory-journal-sessions.log`. Customize the script for your own auditing needs.
|
|
68
|
+
|
|
69
|
+
> **Note:** Cursor's `sessionEnd` hook cannot inject messages or trigger agent actions. Session summary creation is handled by the Cursor rule and server instructions, not the hook.
|
|
70
|
+
|
|
71
|
+
**Requirements**: Cursor v1.7+ with Hooks enabled.
|
|
72
|
+
|
|
73
|
+
### Kiro (AWS)
|
|
74
|
+
|
|
75
|
+
Copy `kiro/session-end.md` to your project's `.kiro/hooks/` directory:
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
mkdir -p .kiro/hooks
|
|
79
|
+
cp <path-to-memory-journal-mcp>/hooks/kiro/session-end.md .kiro/hooks/
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Kiro hooks use markdown files with YAML frontmatter. The `manual` trigger means you activate it from the Kiro hook panel when ending a session.
|
|
83
|
+
|
|
84
|
+
### Kilo Code
|
|
85
|
+
|
|
86
|
+
Copy `kilo-code/session-end-mode.json` to your Kilo Code settings:
|
|
87
|
+
|
|
88
|
+
```powershell
|
|
89
|
+
# Import via Kilo Code settings UI, or add to your modes configuration
|
|
90
|
+
cp <path-to-memory-journal-mcp>/hooks/kilo-code/session-end-mode.json ~/.kilocode/modes/
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This creates a `session-end` custom mode. Switch to it at the end of a session and Kilo Code will create the summary entry using MCP tools.
|
|
94
|
+
|
|
95
|
+
### VS Code + GitHub Copilot
|
|
96
|
+
|
|
97
|
+
Agent hooks are in **preview** (early 2026). When stabilized, a configuration will be added here. In the meantime, the server instructions fallback handles session management.
|
|
98
|
+
|
|
99
|
+
### AntiGravity
|
|
100
|
+
|
|
101
|
+
Does not currently support hooks or rules. The server instructions fallback handles session management automatically. For session-start behavior, add to your user rules: "At session start, read `memory://briefing` from memory-journal-mcp."
|
|
102
|
+
|
|
103
|
+
## No Rules or Hooks? No Problem
|
|
104
|
+
|
|
105
|
+
The `server-instructions.md` in this project includes **Session Start** and **Session End** behavior sections that instruct any MCP-connected agent to manage sessions automatically. This works in **every** MCP client, regardless of rule or hook support — though reliability varies by client.
|
|
106
|
+
|
|
107
|
+
To **disable** session-end entries, tell the agent: "Skip the session summary" or "Don't create an end-of-session entry."
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Memory Journal MCP - session start briefing and session end summary
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Journal MCP
|
|
7
|
+
|
|
8
|
+
## Session Start
|
|
9
|
+
|
|
10
|
+
Before responding to the first user message in every conversation:
|
|
11
|
+
|
|
12
|
+
1. Fetch `memory://briefing` from the memory-journal-mcp server
|
|
13
|
+
2. Show the `userMessage` field to the user (it contains a formatted project context summary)
|
|
14
|
+
3. Then proceed with the user's request
|
|
15
|
+
|
|
16
|
+
## Session End
|
|
17
|
+
|
|
18
|
+
When the conversation is wrapping up (user says "thanks," final deliverable complete, no more work planned):
|
|
19
|
+
|
|
20
|
+
1. Create a journal entry summarizing: what was accomplished, what's unfinished, and context for the next session
|
|
21
|
+
2. Use `entry_type: "retrospective"` and tag with `session-summary`
|
|
22
|
+
3. This is opt-out — ALWAYS create the entry, even for short/trivial sessions, unless the user explicitly says to skip it
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# session-end.sh — Fire-and-forget sessionEnd hook for memory-journal-mcp
|
|
4
|
+
#
|
|
5
|
+
# Cursor's sessionEnd hook is observational only (output is not used).
|
|
6
|
+
# Session summary creation is handled by the Cursor rule + server instructions,
|
|
7
|
+
# not this hook. Customize this script for logging or cleanup.
|
|
8
|
+
#
|
|
9
|
+
# Input (JSON via stdin): session_id, reason, duration_ms, is_background_agent, etc.
|
|
10
|
+
|
|
11
|
+
input=$(cat)
|
|
12
|
+
|
|
13
|
+
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
14
|
+
session_id=$(echo "$input" | grep -o '"session_id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
15
|
+
reason=$(echo "$input" | grep -o '"reason":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
16
|
+
|
|
17
|
+
echo "[$timestamp] session=$session_id reason=$reason" >> /tmp/memory-journal-sessions.log 2>/dev/null
|
|
18
|
+
|
|
19
|
+
exit 0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"customModes": [
|
|
3
|
+
{
|
|
4
|
+
"slug": "session-end",
|
|
5
|
+
"name": "Session End",
|
|
6
|
+
"roleDefinition": "You are wrapping up a coding session. Create a memory-journal-mcp entry summarizing the session.",
|
|
7
|
+
"customInstructions": "Create a memory-journal-mcp entry summarizing this session:\n- What was accomplished (key changes, decisions, files modified)\n- What's unfinished or blocked (pending items, open questions)\n- Context for next session (relevant entry IDs, branch names, PR numbers)\n\nUse entry_type 'retrospective' and tag with 'session-summary'.",
|
|
8
|
+
"groups": ["mcp"]
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Session End Journal Entry
|
|
3
|
+
description: Creates a session summary journal entry via memory-journal-mcp
|
|
4
|
+
trigger: manual
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Create a memory-journal-mcp entry summarizing this session:
|
|
8
|
+
|
|
9
|
+
- **What was accomplished** (key changes, decisions, files modified)
|
|
10
|
+
- **What's unfinished or blocked** (pending items, open questions)
|
|
11
|
+
- **Context for next session** (relevant entry IDs, branch names, PR numbers)
|
|
12
|
+
|
|
13
|
+
Use `entry_type: "retrospective"` and tag with `session-summary`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory-journal-mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"description": "Project context management for AI-assisted development - Persistent knowledge graphs and intelligent context recall across fragmented AI threads",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"test": "vitest run",
|
|
19
19
|
"test:watch": "vitest",
|
|
20
20
|
"test:coverage": "vitest run --coverage",
|
|
21
|
-
"bench": "vitest bench --run"
|
|
21
|
+
"bench": "vitest bench --run",
|
|
22
|
+
"generate:instructions": "node scripts/generate-server-instructions.ts"
|
|
22
23
|
},
|
|
23
24
|
"keywords": [
|
|
24
25
|
"mcp",
|
|
@@ -55,8 +56,8 @@
|
|
|
55
56
|
"@octokit/rest": "^22.0.1",
|
|
56
57
|
"@xenova/transformers": "^2.17.2",
|
|
57
58
|
"commander": "^14.0.3",
|
|
58
|
-
"cors": "^2.8.6",
|
|
59
59
|
"express": "^5.2.1",
|
|
60
|
+
"express-rate-limit": "^8.2.1",
|
|
60
61
|
"simple-git": "^3.32.3",
|
|
61
62
|
"sql.js": "^1.14.0",
|
|
62
63
|
"vectra": "^0.12.3",
|
|
@@ -64,12 +65,11 @@
|
|
|
64
65
|
},
|
|
65
66
|
"devDependencies": {
|
|
66
67
|
"@eslint/js": "^10.0.1",
|
|
67
|
-
"@types/cors": "^2.8.19",
|
|
68
68
|
"@types/express": "^5.0.6",
|
|
69
|
-
"@types/node": "^25.3.
|
|
69
|
+
"@types/node": "^25.3.3",
|
|
70
70
|
"@vitest/coverage-v8": "^4.0.18",
|
|
71
71
|
"eslint": "^10.0.2",
|
|
72
|
-
"globals": "^17.
|
|
72
|
+
"globals": "^17.4.0",
|
|
73
73
|
"typescript": "^5.9.3",
|
|
74
74
|
"typescript-eslint": "^8.56.1",
|
|
75
75
|
"vitest": "^4.0.17"
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"axios": "^1.13.5",
|
|
79
79
|
"brace-expansion": "^2.0.2",
|
|
80
80
|
"glob": "^11.1.0",
|
|
81
|
-
"minimatch": "^10.2.
|
|
82
|
-
"tar": "^7.5.
|
|
81
|
+
"minimatch": "^10.2.4",
|
|
82
|
+
"tar": "^7.5.9",
|
|
83
83
|
"tmp": "^0.2.4"
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# v4.5.0 - Automated Scheduling, Security Hardening & Quality Improvements
|
|
2
|
+
|
|
3
|
+
**Released: March 2, 2026**
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
### ⏰ Automated Scheduler (HTTP/SSE Only)
|
|
8
|
+
|
|
9
|
+
New in-process scheduler runs periodic maintenance jobs for long-running HTTP/SSE server processes:
|
|
10
|
+
|
|
11
|
+
- `--backup-interval <minutes>` — Automated database backups with cleanup
|
|
12
|
+
- `--keep-backups <count>` — Max backups to retain (default: 5)
|
|
13
|
+
- `--vacuum-interval <minutes>` — Database optimization (`PRAGMA optimize`)
|
|
14
|
+
- `--rebuild-index-interval <minutes>` — Full vector index rebuild
|
|
15
|
+
|
|
16
|
+
Each job is error-isolated. Status visible via `memory://health`.
|
|
17
|
+
|
|
18
|
+
### 🔒 Security Hardening
|
|
19
|
+
|
|
20
|
+
Comprehensive security improvements across the entire stack:
|
|
21
|
+
|
|
22
|
+
- **HTTP Transport** — Rate limiting (100 req/min), CSP headers, Cache-Control, Referrer-Policy, CORS wildcard warning
|
|
23
|
+
- **Input Validation** — `entry_type` and `significance_type` now constrained to Zod enums; date format validation via regex
|
|
24
|
+
- **Dead Code Wiring** — `sanitizeSearchQuery()` and `assertNoPathTraversal()` now active in code paths
|
|
25
|
+
- **Foreign Keys** — `PRAGMA foreign_keys = ON` enforced at database initialization
|
|
26
|
+
- **Path Traversal** — `exportToFile()` now protected with `assertNoPathTraversal()`
|
|
27
|
+
- **Logger Hardening** — `LOG_LEVEL` validated; `setLevel()` guarded against invalid values
|
|
28
|
+
- **Removed dead code** — SQL injection detection functions that provided false sense of security
|
|
29
|
+
- **CI** — Blocking `npm audit` and TruffleHog steps; Node.js test matrix aligned to `>=24.0.0`
|
|
30
|
+
|
|
31
|
+
### ✅ Test Coverage → 92%
|
|
32
|
+
|
|
33
|
+
Expanded test suite from 549 → 590 tests, raising line coverage from 88.59% → 92.06%:
|
|
34
|
+
|
|
35
|
+
- SIGINT shutdown handlers for all three transport modes
|
|
36
|
+
- Prompt handlers with proper arguments
|
|
37
|
+
- `SqliteAdapter` backup edge cases
|
|
38
|
+
- GitHub integration error paths
|
|
39
|
+
|
|
40
|
+
### 📝 Cursor Rule for Session Management
|
|
41
|
+
|
|
42
|
+
Added `hooks/cursor/memory-journal.mdc` — an `alwaysApply` Cursor rule that instructs agents to read `memory://briefing` at session start and create a retrospective at session end. This is the most reliable mechanism for session behavior in Cursor.
|
|
43
|
+
|
|
44
|
+
## Added
|
|
45
|
+
|
|
46
|
+
- **Automated Scheduler** — `Scheduler.ts` module with CLI flags for backup, vacuum, and index rebuild intervals
|
|
47
|
+
- **Cursor Rule** — `hooks/cursor/memory-journal.mdc` for reliable session management
|
|
48
|
+
- **Cursor `sessionEnd` Hook** — `hooks/cursor/hooks.json` + `session-end.sh` audit script
|
|
49
|
+
|
|
50
|
+
## Improved
|
|
51
|
+
|
|
52
|
+
- **Test Coverage** — 88.59% → 92.06% line coverage (549 → 590 tests)
|
|
53
|
+
- **Database I/O** — Debounced `scheduleSave()` reduces disk writes on rapid mutations
|
|
54
|
+
- **Vector Index Rebuild** — Paginated fetching (200/page) + parallel batch embedding (5 at a time)
|
|
55
|
+
- **Server Startup** — Eliminated duplicate `getTools()` call
|
|
56
|
+
- **GitHub API** — TTL response cache (5 min) with automatic invalidation on mutations
|
|
57
|
+
|
|
58
|
+
## Fixed
|
|
59
|
+
|
|
60
|
+
- **Session Start briefing in Cursor** — Added `user-memory-journal-mcp` server name for Cursor compatibility
|
|
61
|
+
- **`deleteOldBackups` Test Isolation** — Fixed flaky test by cleaning up pre-existing backups
|
|
62
|
+
- **`share_with_team` Not Setting `isPersonal`** — `create_entry` with `share_with_team: true` now correctly sets `isPersonal: false`
|
|
63
|
+
- **Path Traversal Test Assertion** — Updated to assert `PathTraversalError` type
|
|
64
|
+
- **Tool Handler Test Fix** — Updated to use valid `entry_type` enum value
|
|
65
|
+
|
|
66
|
+
## Security
|
|
67
|
+
|
|
68
|
+
- Wire dead-code security utilities (F-001, F-002)
|
|
69
|
+
- HTTP security headers: CSP, Cache-Control, Referrer-Policy (F-003)
|
|
70
|
+
- `PRAGMA foreign_keys = ON` (F-005)
|
|
71
|
+
- CORS wildcard warning (F-006)
|
|
72
|
+
- `entry_type` / `significance_type` enum constraints
|
|
73
|
+
- Date format validation on all date string fields
|
|
74
|
+
- HTTP rate limiting (100 req/min per IP)
|
|
75
|
+
- Remove dead SQL injection detection code
|
|
76
|
+
- `exportToFile()` path traversal protection
|
|
77
|
+
- `getRawDb()` safety documentation
|
|
78
|
+
- Logger `LOG_LEVEL` validation (L1) and `setLevel()` guard (L2)
|
|
79
|
+
- CI `security-scan` Node version alignment (L3)
|
|
80
|
+
|
|
81
|
+
## Changed
|
|
82
|
+
|
|
83
|
+
- `@types/node`: 25.3.2 → 25.3.3 (patch)
|
|
84
|
+
- `globals`: 17.3.0 → 17.4.0 (minor)
|
|
85
|
+
- `minimatch` override: 10.2.3 → 10.2.4 (patch)
|
|
86
|
+
- `tar` override: 7.5.8 → 7.5.9 (patch)
|
|
87
|
+
|
|
88
|
+
## Removed
|
|
89
|
+
|
|
90
|
+
- **Unused `cors` dependency** — CORS handled by custom middleware
|
|
91
|
+
|
|
92
|
+
## CI/CD
|
|
93
|
+
|
|
94
|
+
- Removed Dependabot auto-merge workflow
|
|
95
|
+
- Trivy Action updated to 0.34.0
|
|
96
|
+
- Node.js test matrix aligned: `[24.x, 25.x]`
|
|
97
|
+
- Blocking `npm audit` in CI pipeline
|
|
98
|
+
- Blocking TruffleHog secret scanning
|
|
99
|
+
|
|
100
|
+
## Documentation
|
|
101
|
+
|
|
102
|
+
- Revised `hooks/README.md` with progressive enhancement model
|
|
103
|
+
- Updated Session Management in README.md and DOCKER_README.md
|
|
104
|
+
- SECURITY.md rewrite for TypeScript era
|
|
105
|
+
- Team collaboration in READMEs with wiki links
|
|
106
|
+
- Rate limiting documentation
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# npm
|
|
112
|
+
npm install -g memory-journal-mcp@4.5.0
|
|
113
|
+
|
|
114
|
+
# Docker
|
|
115
|
+
docker pull writenotenow/memory-journal-mcp:v4.5.0
|
|
116
|
+
```
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates src/constants/ServerInstructions.ts from src/constants/server-instructions.md
|
|
3
|
+
*
|
|
4
|
+
* Reads the markdown source file, splits it by section delimiters,
|
|
5
|
+
* escapes for template literals, and produces the full TypeScript module
|
|
6
|
+
* with types, constants, and the generateInstructions function.
|
|
7
|
+
*
|
|
8
|
+
* Usage: node scripts/generate-server-instructions.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
12
|
+
import { dirname, resolve } from 'node:path'
|
|
13
|
+
import { fileURLToPath } from 'node:url'
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
16
|
+
const projectRoot = resolve(__dirname, '..')
|
|
17
|
+
|
|
18
|
+
const mdPath = resolve(projectRoot, 'src/constants/server-instructions.md')
|
|
19
|
+
const tsPath = resolve(projectRoot, 'src/constants/ServerInstructions.ts')
|
|
20
|
+
|
|
21
|
+
// Read plain markdown source
|
|
22
|
+
const markdown = readFileSync(mdPath, 'utf-8')
|
|
23
|
+
|
|
24
|
+
// Section names in order
|
|
25
|
+
const SECTION_NAMES = ['ESSENTIAL', 'GITHUB', 'SERVER_ACCESS', 'TOOL_PARAMETER_REFERENCE']
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse sections from markdown using <!-- SECTION:NAME --> delimiters
|
|
29
|
+
*/
|
|
30
|
+
function parseSections(content) {
|
|
31
|
+
const sections = {}
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < SECTION_NAMES.length; i++) {
|
|
34
|
+
const name = SECTION_NAMES[i]
|
|
35
|
+
const startMarker = '<!-- SECTION:' + name + ' -->'
|
|
36
|
+
const startIdx = content.indexOf(startMarker)
|
|
37
|
+
|
|
38
|
+
if (startIdx === -1) {
|
|
39
|
+
throw new Error('Missing section marker: ' + startMarker)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const contentStart = startIdx + startMarker.length
|
|
43
|
+
|
|
44
|
+
// Find next section marker or end of file
|
|
45
|
+
let contentEnd
|
|
46
|
+
if (i + 1 < SECTION_NAMES.length) {
|
|
47
|
+
const nextMarker = '<!-- SECTION:' + SECTION_NAMES[i + 1] + ' -->'
|
|
48
|
+
contentEnd = content.indexOf(nextMarker)
|
|
49
|
+
if (contentEnd === -1) {
|
|
50
|
+
throw new Error('Missing section marker: ' + nextMarker)
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
contentEnd = content.length
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Trim leading/trailing whitespace but preserve internal structure
|
|
57
|
+
sections[name] = content.slice(contentStart, contentEnd).trim()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return sections
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Escape content for use inside a JS/TS template literal
|
|
65
|
+
*/
|
|
66
|
+
function escapeForTemplateLiteral(content) {
|
|
67
|
+
return content.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const sections = parseSections(markdown)
|
|
71
|
+
|
|
72
|
+
// Read the function body template (static TypeScript code after the constants)
|
|
73
|
+
const FUNCTION_BODY = readFileSync(
|
|
74
|
+
resolve(projectRoot, 'scripts/server-instructions-function-body.ts'),
|
|
75
|
+
'utf-8'
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
// Build the TypeScript file using string concatenation to avoid nested escaping
|
|
79
|
+
const lines = []
|
|
80
|
+
|
|
81
|
+
lines.push('/**')
|
|
82
|
+
lines.push(' * Server instructions for Memory Journal MCP.')
|
|
83
|
+
lines.push(' *')
|
|
84
|
+
lines.push(' * ⚠️ AUTO-GENERATED — DO NOT EDIT THIS FILE DIRECTLY')
|
|
85
|
+
lines.push(' * Edit src/constants/server-instructions.md instead,')
|
|
86
|
+
lines.push(' * then run: npm run generate:instructions')
|
|
87
|
+
lines.push(' *')
|
|
88
|
+
lines.push(' * These instructions are automatically sent to MCP clients during initialization,')
|
|
89
|
+
lines.push(' * providing guidance for AI agents on tool usage.')
|
|
90
|
+
lines.push(' *')
|
|
91
|
+
lines.push(' * Optimized for token efficiency with tiered instruction levels.')
|
|
92
|
+
lines.push(' */')
|
|
93
|
+
lines.push('')
|
|
94
|
+
lines.push("import type { ToolGroup } from '../types/index.js'")
|
|
95
|
+
lines.push("import { TOOL_GROUPS } from '../filtering/ToolFilter.js'")
|
|
96
|
+
lines.push('')
|
|
97
|
+
lines.push('/**')
|
|
98
|
+
lines.push(' * Resource definition for instruction generation')
|
|
99
|
+
lines.push(' */')
|
|
100
|
+
lines.push('export interface ResourceDefinition {')
|
|
101
|
+
lines.push(' uri: string')
|
|
102
|
+
lines.push(' name: string')
|
|
103
|
+
lines.push(' description?: string')
|
|
104
|
+
lines.push('}')
|
|
105
|
+
lines.push('')
|
|
106
|
+
lines.push('/**')
|
|
107
|
+
lines.push(' * Prompt definition for instruction generation')
|
|
108
|
+
lines.push(' */')
|
|
109
|
+
lines.push('export interface PromptDefinition {')
|
|
110
|
+
lines.push(' name: string')
|
|
111
|
+
lines.push(' description?: string')
|
|
112
|
+
lines.push('}')
|
|
113
|
+
lines.push('')
|
|
114
|
+
lines.push('/**')
|
|
115
|
+
lines.push(' * Latest entry snapshot for initial briefing')
|
|
116
|
+
lines.push(' */')
|
|
117
|
+
lines.push('export interface LatestEntrySnapshot {')
|
|
118
|
+
lines.push(' id: number')
|
|
119
|
+
lines.push(' timestamp: string')
|
|
120
|
+
lines.push(' entryType: string')
|
|
121
|
+
lines.push(' content: string')
|
|
122
|
+
lines.push('}')
|
|
123
|
+
lines.push('')
|
|
124
|
+
lines.push('/**')
|
|
125
|
+
lines.push(' * Instruction detail level for token efficiency')
|
|
126
|
+
lines.push(' * - essential: ~200 tokens - Core behaviors only (for token-constrained clients)')
|
|
127
|
+
lines.push(' * - standard: ~400 tokens - + GitHub patterns (default)')
|
|
128
|
+
lines.push(' * - full: ~600 tokens - + tool/resource listings')
|
|
129
|
+
lines.push(' */')
|
|
130
|
+
lines.push("export type InstructionLevel = 'essential' | 'standard' | 'full'")
|
|
131
|
+
lines.push('')
|
|
132
|
+
lines.push('/**')
|
|
133
|
+
lines.push(' * Essential behavioral guidance (~200 tokens)')
|
|
134
|
+
lines.push(' * Core patterns every AI agent should follow.')
|
|
135
|
+
lines.push(' */')
|
|
136
|
+
lines.push(
|
|
137
|
+
'const ESSENTIAL_INSTRUCTIONS = `' + escapeForTemplateLiteral(sections.ESSENTIAL) + '\n`'
|
|
138
|
+
)
|
|
139
|
+
lines.push('')
|
|
140
|
+
lines.push('/**')
|
|
141
|
+
lines.push(' * GitHub integration patterns (~150 additional tokens)')
|
|
142
|
+
lines.push(' */')
|
|
143
|
+
lines.push('const GITHUB_INSTRUCTIONS = `\n' + escapeForTemplateLiteral(sections.GITHUB) + '\n`')
|
|
144
|
+
lines.push('')
|
|
145
|
+
lines.push('/**')
|
|
146
|
+
lines.push(' * Server access instructions - critical for AI agents to call tools correctly')
|
|
147
|
+
lines.push(' */')
|
|
148
|
+
lines.push(
|
|
149
|
+
'const SERVER_ACCESS_INSTRUCTIONS = `\n' +
|
|
150
|
+
escapeForTemplateLiteral(sections.SERVER_ACCESS) +
|
|
151
|
+
'\n`'
|
|
152
|
+
)
|
|
153
|
+
lines.push('')
|
|
154
|
+
lines.push('/**')
|
|
155
|
+
lines.push(' * Tool parameter reference - essential for correct tool invocation')
|
|
156
|
+
lines.push(' */')
|
|
157
|
+
lines.push(
|
|
158
|
+
'const TOOL_PARAMETER_REFERENCE = `\n' +
|
|
159
|
+
escapeForTemplateLiteral(sections.TOOL_PARAMETER_REFERENCE) +
|
|
160
|
+
'\n`'
|
|
161
|
+
)
|
|
162
|
+
lines.push('')
|
|
163
|
+
lines.push(FUNCTION_BODY)
|
|
164
|
+
lines.push('')
|
|
165
|
+
|
|
166
|
+
const tsContent = lines.join('\n')
|
|
167
|
+
|
|
168
|
+
writeFileSync(tsPath, tsContent, 'utf-8')
|
|
169
|
+
|
|
170
|
+
process.stderr.write(
|
|
171
|
+
'✅ Generated ServerInstructions.ts (' +
|
|
172
|
+
tsContent.length.toLocaleString() +
|
|
173
|
+
' bytes) from server-instructions.md (' +
|
|
174
|
+
markdown.length.toLocaleString() +
|
|
175
|
+
' bytes)\n'
|
|
176
|
+
)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate dynamic instructions based on enabled tools, resources, prompts, and latest entry
|
|
3
|
+
*
|
|
4
|
+
* @param enabledTools - Set of enabled tool names
|
|
5
|
+
* @param resources - Available resource definitions
|
|
6
|
+
* @param prompts - Available prompt definitions
|
|
7
|
+
* @param latestEntry - Optional latest entry for context snapshot
|
|
8
|
+
* @param level - Instruction detail level (default: 'standard')
|
|
9
|
+
*/
|
|
10
|
+
export function generateInstructions(
|
|
11
|
+
enabledTools: Set<string>,
|
|
12
|
+
_resources: ResourceDefinition[],
|
|
13
|
+
prompts: PromptDefinition[],
|
|
14
|
+
latestEntry?: LatestEntrySnapshot,
|
|
15
|
+
level: InstructionLevel = 'standard'
|
|
16
|
+
): string {
|
|
17
|
+
let instructions = ESSENTIAL_INSTRUCTIONS
|
|
18
|
+
|
|
19
|
+
// Add latest entry snapshot for immediate context (compact format)
|
|
20
|
+
if (latestEntry) {
|
|
21
|
+
const preview = latestEntry.content.slice(0, 120)
|
|
22
|
+
instructions += `\n**Latest**: #${String(latestEntry.id)} (${latestEntry.timestamp}) ${latestEntry.entryType}\n> ${preview}${latestEntry.content.length > 120 ? '...' : ''}\n`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Standard and full levels include GitHub patterns
|
|
26
|
+
if (level === 'standard' || level === 'full') {
|
|
27
|
+
instructions += GITHUB_INSTRUCTIONS
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Full level includes server access instructions and tool parameter reference
|
|
31
|
+
if (level === 'full') {
|
|
32
|
+
instructions += SERVER_ACCESS_INSTRUCTIONS
|
|
33
|
+
instructions += TOOL_PARAMETER_REFERENCE
|
|
34
|
+
|
|
35
|
+
// Add active tools summary
|
|
36
|
+
const activeGroups = getActiveToolGroups(enabledTools)
|
|
37
|
+
if (activeGroups.length > 0) {
|
|
38
|
+
instructions += `\n## Active Tools (${String(enabledTools.size)})\n`
|
|
39
|
+
for (const { group, tools } of activeGroups) {
|
|
40
|
+
instructions += `**${group}**: ${tools.map((t) => `\`${t}\``).join(', ')}\n`
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Add prompts section
|
|
45
|
+
if (prompts.length > 0) {
|
|
46
|
+
instructions += `\n## Prompts (${String(prompts.length)})\n`
|
|
47
|
+
instructions += 'Pre-built templates and guided workflows:\n'
|
|
48
|
+
for (const prompt of prompts) {
|
|
49
|
+
instructions += `- \`${prompt.name}\` - ${prompt.description ?? ''}\n`
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return instructions
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get active tool groups with their enabled tools
|
|
59
|
+
*/
|
|
60
|
+
function getActiveToolGroups(enabledTools: Set<string>): { group: ToolGroup; tools: string[] }[] {
|
|
61
|
+
const activeGroups: { group: ToolGroup; tools: string[] }[] = []
|
|
62
|
+
|
|
63
|
+
for (const [group, allTools] of Object.entries(TOOL_GROUPS) as [ToolGroup, string[]][]) {
|
|
64
|
+
const enabledInGroup = allTools.filter((tool) => enabledTools.has(tool))
|
|
65
|
+
if (enabledInGroup.length > 0) {
|
|
66
|
+
activeGroups.push({ group, tools: enabledInGroup })
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return activeGroups
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Static instructions for backward compatibility
|
|
75
|
+
* @deprecated Use generateInstructions() instead for dynamic content
|
|
76
|
+
*/
|
|
77
|
+
export const SERVER_INSTRUCTIONS = ESSENTIAL_INSTRUCTIONS + GITHUB_INSTRUCTIONS
|
package/server.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"name": "io.github.neverinfamous/memory-journal-mcp",
|
|
4
4
|
"title": "Memory Journal MCP",
|
|
5
5
|
"description": "MCP server– Project memory system with GitHub-aware context, knowledge graphs, and CI/PR timelines",
|
|
6
|
-
"version": "4.
|
|
6
|
+
"version": "4.5.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "oci",
|
|
10
|
-
"identifier": "docker.io/writenotenow/memory-journal-mcp:v4.
|
|
11
|
-
"version": "4.
|
|
10
|
+
"identifier": "docker.io/writenotenow/memory-journal-mcp:v4.5.0",
|
|
11
|
+
"version": "4.5.0",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
package/src/cli.ts
CHANGED
|
@@ -23,6 +23,22 @@ program
|
|
|
23
23
|
.option('--auto-rebuild-index', 'Rebuild vector index on server startup')
|
|
24
24
|
.option('--cors-origin <origin>', 'CORS allowed origin for HTTP transport (default: *)')
|
|
25
25
|
.option('--log-level <level>', 'Log level: debug, info, warning, error', 'info')
|
|
26
|
+
.option(
|
|
27
|
+
'--backup-interval <minutes>',
|
|
28
|
+
'Automated backup interval in minutes, HTTP only (0 = disabled)',
|
|
29
|
+
'0'
|
|
30
|
+
)
|
|
31
|
+
.option('--keep-backups <count>', 'Max backups to retain during automated cleanup', '5')
|
|
32
|
+
.option(
|
|
33
|
+
'--vacuum-interval <minutes>',
|
|
34
|
+
'Database optimize interval in minutes, HTTP only (0 = disabled)',
|
|
35
|
+
'0'
|
|
36
|
+
)
|
|
37
|
+
.option(
|
|
38
|
+
'--rebuild-index-interval <minutes>',
|
|
39
|
+
'Vector index rebuild interval in minutes, HTTP only (0 = disabled)',
|
|
40
|
+
'0'
|
|
41
|
+
)
|
|
26
42
|
.action(
|
|
27
43
|
async (options: {
|
|
28
44
|
transport: string
|
|
@@ -35,6 +51,10 @@ program
|
|
|
35
51
|
autoRebuildIndex?: boolean
|
|
36
52
|
corsOrigin?: string
|
|
37
53
|
logLevel: string
|
|
54
|
+
backupInterval: string
|
|
55
|
+
keepBackups: string
|
|
56
|
+
vacuumInterval: string
|
|
57
|
+
rebuildIndexInterval: string
|
|
38
58
|
}) => {
|
|
39
59
|
// Set log level
|
|
40
60
|
logger.setLevel(options.logLevel as 'debug' | 'info' | 'warning' | 'error')
|
|
@@ -67,6 +87,12 @@ program
|
|
|
67
87
|
autoRebuildIndex:
|
|
68
88
|
options.autoRebuildIndex ?? process.env['AUTO_REBUILD_INDEX'] === 'true',
|
|
69
89
|
corsOrigin: options.corsOrigin,
|
|
90
|
+
scheduler: {
|
|
91
|
+
backupIntervalMinutes: parseInt(options.backupInterval, 10),
|
|
92
|
+
keepBackups: parseInt(options.keepBackups, 10),
|
|
93
|
+
vacuumIntervalMinutes: parseInt(options.vacuumInterval, 10),
|
|
94
|
+
rebuildIndexIntervalMinutes: parseInt(options.rebuildIndexInterval, 10),
|
|
95
|
+
},
|
|
70
96
|
})
|
|
71
97
|
} catch (error) {
|
|
72
98
|
logger.error('Failed to start server', {
|