mindkeg-mcp 0.7.0 → 0.7.2

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 CHANGED
@@ -29,7 +29,7 @@ Unlike traditional RAG systems that chunk large documents, Mind Keg stores **pre
29
29
  - Free-form tags and group linking
30
30
  - Three scoping levels: repository-specific, workspace-wide, and global learnings
31
31
  - Dual transport: stdio (local) + HTTP+SSE (remote)
32
- - API key authentication with per-repository access control
32
+ - Auth-free stdio for local use; API key authentication with per-repository access control for HTTP
33
33
  - SQLite storage (zero dependencies, zero config)
34
34
  - Import/export for backup and migration
35
35
  - **Smarter knowledge management**: auto-categorization (KNN voting), conflict detection, smart staleness scoring, access tracking with relevance decay, near-duplicate merging, typed learning relationships
@@ -67,7 +67,7 @@ If you prefer to configure manually, or need HTTP mode:
67
67
  npm install -g mindkeg-mcp
68
68
  ```
69
69
 
70
- #### Create an API key
70
+ #### Create an API key (only needed for HTTP mode)
71
71
 
72
72
  ```bash
73
73
  mindkeg api-key create --name "My Laptop"
@@ -75,6 +75,8 @@ mindkeg api-key create --name "My Laptop"
75
75
  # mk_abc123...
76
76
  ```
77
77
 
78
+ > API keys are only required for HTTP transport. stdio transport (used by Claude Code, Cursor, Windsurf local setups) is auth-free.
79
+
78
80
  #### Connect your AI agent
79
81
 
80
82
  Mind Keg works with any MCP-compatible AI coding agent. Choose your setup:
@@ -86,10 +88,7 @@ Mind Keg works with any MCP-compatible AI coding agent. Choose your setup:
86
88
  "mcpServers": {
87
89
  "mindkeg": {
88
90
  "command": "mindkeg",
89
- "args": ["serve", "--stdio"],
90
- "env": {
91
- "MINDKEG_API_KEY": "mk_your_key_here"
92
- }
91
+ "args": ["serve", "--stdio"]
93
92
  }
94
93
  }
95
94
  }
@@ -102,10 +101,7 @@ Mind Keg works with any MCP-compatible AI coding agent. Choose your setup:
102
101
  "mcpServers": {
103
102
  "mindkeg": {
104
103
  "command": "mindkeg",
105
- "args": ["serve", "--stdio"],
106
- "env": {
107
- "MINDKEG_API_KEY": "mk_your_key_here"
108
- }
104
+ "args": ["serve", "--stdio"]
109
105
  }
110
106
  }
111
107
  }
@@ -118,10 +114,7 @@ Mind Keg works with any MCP-compatible AI coding agent. Choose your setup:
118
114
  "mcpServers": {
119
115
  "mindkeg": {
120
116
  "command": "mindkeg",
121
- "args": ["serve", "--stdio"],
122
- "env": {
123
- "MINDKEG_API_KEY": "mk_your_key_here"
124
- }
117
+ "args": ["serve", "--stdio"]
125
118
  }
126
119
  }
127
120
  }
@@ -162,46 +155,28 @@ Copy `templates/AGENTS.md` to the root of any repository where you want agents t
162
155
 
163
156
  ## MCP Tools
164
157
 
165
- ### Learnings
166
-
167
- | Tool | Description |
168
- |----------------------|------------------------------------------------------|
169
- | `get_context` | Prime an agent session with all relevant learnings ranked, scoped, and budget-controlled |
170
- | `store_learning` | Store a new atomic learning (repo, workspace, or global scope) |
171
- | `search_learnings` | Semantic/keyword search for relevant learnings |
172
- | `update_learning` | Update content, category, or tags |
173
- | `deprecate_learning` | Mark a learning as deprecated |
174
- | `flag_stale` | Flag a learning as potentially outdated |
175
- | `delete_learning` | Permanently delete a learning |
176
- | `merge_learnings` | Merge near-duplicate learnings into a canonical entry |
177
- | `relate_learnings` | Create typed relationships between learnings |
178
- | `list_repositories` | List all repositories with learning counts |
179
- | `list_workspaces` | List all workspaces with learning counts |
180
-
181
- ### Agent Memory Entities
182
-
183
- Structured entity types for capturing decisions, findings, gotchas, and run summaries — richer than atomic learnings, designed for cross-session agent memory.
184
-
185
- | Tool | Description |
186
- |------------------------|------------------------------------------------------|
187
- | `store_decision` | Record an architectural or design decision with rationale |
188
- | `get_decisions` | Retrieve decisions for a repository, optionally filtered by status |
189
- | `supersede_decision` | Mark a decision as superseded by a newer one |
190
- | `store_finding` | Record a bug, issue, or investigation finding |
191
- | `get_open_findings` | Retrieve unresolved findings for a repository |
192
- | `resolve_finding` | Mark a finding as resolved with a resolution summary |
193
- | `store_gotcha` | Record a non-obvious pitfall or gotcha |
194
- | `get_gotchas` | Retrieve gotchas for a repository |
195
- | `get_relevant_context` | Retrieve all entity types relevant to a repository |
196
- | `get_run_history` | Retrieve run summaries for a repository |
197
- | `complete_run` | Record the completion of an agent run with a summary |
158
+ **8 consolidated tools (primary API):**
159
+
160
+ | Tool | Description |
161
+ |---|---|
162
+ | `get_context` | Retrieve relevant knowledge session primer, task-scoped context, or semantic search (replaces `get_context`, `get_relevant_context`, `search_learnings`) |
163
+ | `store` | Save knowledge learning, decision, finding, or gotcha (replaces `store_learning`, `store_decision`, `store_finding`, `store_gotcha`) |
164
+ | `update` | Modify/manage knowledge update, deprecate, flag_stale, delete, or merge (replaces `update_learning`, `deprecate_learning`, `flag_stale`, `delete_learning`, `merge_learnings`) |
165
+ | `resolve` | Close out a decision or finding (replaces `supersede_decision`, `resolve_finding`) |
166
+ | `complete_run` | Record a completed work session |
167
+ | `query` | List knowledge by type decisions, findings, gotchas, or runs (replaces `get_decisions`, `get_open_findings`, `get_gotchas`, `get_run_history`) |
168
+ | `list_scopes` | List repositories and workspaces with counts (replaces `list_repositories`, `list_workspaces`) |
169
+ | `relate_learnings` | Create typed relationships between learnings |
170
+
171
+ **Backwards-compatible aliases:** All 19 old tool names (`store_learning`, `search_learnings`, `update_learning`, `deprecate_learning`, `flag_stale`, `delete_learning`, `merge_learnings`, `store_decision`, `get_decisions`, `supersede_decision`, `store_finding`, `resolve_finding`, `get_open_findings`, `store_gotcha`, `get_gotchas`, `get_run_history`, `get_relevant_context`, `list_repositories`, `list_workspaces`) are registered as aliases that delegate to the same service methods. They will be removed in the next major version.
198
172
 
199
173
  ## CLI Commands
200
174
 
201
175
  ```bash
202
- # Quick setup (auto-detects agent, writes config, copies instructions)
176
+ # Global setup (one-time) writes MCP config, SessionStart hook, runs migrations
203
177
  mindkeg init
204
- mindkeg init --agent cursor
178
+ mindkeg init --agent cursor # Target a specific agent (default: claude-code)
179
+ mindkeg init --project # Per-project setup instead of global (optional)
205
180
 
206
181
  # Database statistics
207
182
  mindkeg stats
@@ -253,7 +228,7 @@ mindkeg backfill-integrity # Compute SHA-256 hashes for legacy learnings
253
228
  | `MINDKEG_HOST` | `127.0.0.1` | HTTP server bind address |
254
229
  | `MINDKEG_PORT` | `52100` | HTTP server port |
255
230
  | `MINDKEG_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
256
- | `MINDKEG_API_KEY` | (none) | API key for stdio transport |
231
+ | `MINDKEG_API_KEY` | (none) | API key for HTTP transport (stdio is auth-free) |
257
232
 
258
233
  ### Embedding providers
259
234
 
@@ -480,9 +455,10 @@ src/
480
455
  audit/ Structured JSON lines audit logger
481
456
  auth/ API key generation + validation middleware
482
457
  crypto/ AES-256-GCM field encryption
458
+ hooks/ Hook script generation (SessionStart auto-retrieval)
483
459
  monitoring/ Prometheus metrics + /health endpoint
484
460
  security/ Content sanitization, integrity hashing, rate limiter
485
- tools/ One file per MCP tool (22 tools) + shared tool-utils
461
+ tools/ MCP tool handlers (8 consolidated + 19 backwards-compatible aliases)
486
462
  services/ LearningService + EmbeddingService + PurgeService + ConflictDetector + StalenessEngine
487
463
  storage/ StorageAdapter interface + SQLite impl
488
464
  models/ Zod schemas + TypeScript types
package/dist/cli/index.js CHANGED
@@ -4298,7 +4298,7 @@ function validateStoreInput(input) {
4298
4298
  function registerConsolidatedStore(server, learningService, entityService, storage, getApiKey, auditLogger) {
4299
4299
  server.tool(
4300
4300
  "store",
4301
- "Save a piece of knowledge. Types: learning (short insight, max 500 chars), decision (architectural choice with rationale), finding (code review issue), gotcha (non-obvious behavior). Before calling this, ask the user if they want to save it and which scope \u2014 this repo, workspace, or global.",
4301
+ 'Save a piece of knowledge when you discover something worth preserving across sessions. Call this proactively when you encounter: a gotcha (non-obvious behavior), an architectural decision with rationale, a code review finding, or a short factual insight (conventions, debugging tips). Always ask the user first \u2014 e.g., "I noticed X, want me to save this to Mind Keg?" \u2014 and confirm scope (this repo / workspace / global) before calling.',
4302
4302
  {
4303
4303
  type: z8.enum(["learning", "decision", "finding", "gotcha"]).describe("Type of knowledge to store."),
4304
4304
  repository: z8.string().optional().nullable().describe("Repository path. Required for decision, finding, gotcha. Optional for learning."),
@@ -4499,7 +4499,7 @@ function validateUpdateInput(input) {
4499
4499
  function registerConsolidatedUpdate(server, learningService, storage, getApiKey, auditLogger) {
4500
4500
  server.tool(
4501
4501
  "update",
4502
- "Modify or manage existing knowledge. Actions: update (change content/tags), deprecate (mark outdated \u2014 preferred over delete), flag_stale (soft flag when unsure), delete (permanent), merge (consolidate duplicates). Use deprecate when you discover a stored learning is wrong. Use flag_stale when you're unsure.",
4502
+ "Modify or manage existing knowledge. Actions: update (change content/tags), deprecate (mark outdated \u2014 preferred over delete), flag_stale (soft flag when unsure), delete (permanent), merge (consolidate duplicates). When you find a stored learning that contradicts current reality, proactively offer to deprecate or flag it. Use deprecate when certain, flag_stale when unsure.",
4503
4503
  {
4504
4504
  action: z9.enum(["update", "deprecate", "flag_stale", "delete", "merge"]).describe("Action to perform."),
4505
4505
  id: z9.string().uuid().describe("Target learning ID."),
@@ -6327,16 +6327,49 @@ function createNoopAuditLogger() {
6327
6327
  }
6328
6328
 
6329
6329
  // src/server.ts
6330
+ var MINDKEG_SERVER_INSTRUCTIONS = `You have access to Mind Keg \u2014 a persistent memory system for knowledge that survives across sessions. Use it to retrieve prior context and proactively preserve new insights.
6331
+
6332
+ # When to retrieve
6333
+
6334
+ At session start, call \`get_context({ repository: "<current repo path>" })\` to load prior knowledge. If the SessionStart hook already injected context (visible as "Mind Keg Persistent Memory" at the top of the conversation), skip this \u2014 the context is already loaded.
6335
+
6336
+ For topic-specific lookups mid-session, call \`get_context({ query: "<topic>", repository: "<path>" })\`.
6337
+
6338
+ # When to store (proactively offer)
6339
+
6340
+ When you discover something non-obvious during work, pause and offer to save it:
6341
+
6342
+ > "I noticed [X]. This looks like a [gotcha/decision/finding]. Want me to save it to Mind Keg?
6343
+ > Scope: this repo, this workspace, or globally?"
6344
+
6345
+ Wait for the user's answer before calling \`store\`.
6346
+
6347
+ Watch for these patterns specifically:
6348
+
6349
+ - **Gotchas** \u2014 non-obvious behaviors, footguns, surprising library quirks \u2192 \`store({ type: "gotcha", ... })\`
6350
+ - **Architectural decisions** with rationale \u2192 \`store({ type: "decision", ... })\`
6351
+ - **Code review findings** that need tracking across sessions \u2192 \`store({ type: "finding", ... })\`
6352
+ - **Short factual insights** (conventions, debugging tips, compact observations) \u2192 \`store({ type: "learning", ... })\`
6353
+
6354
+ # Rules
6355
+
6356
+ - Always ask before storing. Never store silently.
6357
+ - Don't store transient session state, obvious facts, or codebase-specific details that change as code evolves (file paths, function locations). Those belong in project-level memory, not Mind Keg.
6358
+ - Prefer \`update({ action: "deprecate" })\` over delete for wrong knowledge \u2014 preserves audit trail.
6359
+ - Use \`update({ action: "flag_stale" })\` when you suspect something is outdated but aren't sure.
6360
+ - For scope, ask the user; default suggestion: this repo unless the insight clearly applies cross-project.
6361
+ - At session end, if you made multiple discoveries, summarize them and offer to save the ones the user wants to keep.`;
6330
6362
  function createMcpServer(deps) {
6331
6363
  const server = new McpServer(
6332
6364
  {
6333
6365
  name: "mindkeg-mcp",
6334
- version: "0.7.0"
6366
+ version: "0.7.2"
6335
6367
  },
6336
6368
  {
6337
6369
  capabilities: {
6338
6370
  tools: {}
6339
- }
6371
+ },
6372
+ instructions: MINDKEG_SERVER_INSTRUCTIONS
6340
6373
  }
6341
6374
  );
6342
6375
  const learningService = new LearningService(deps.storage, deps.embedding);
@@ -7055,7 +7088,7 @@ function registerImportCommand(program2) {
7055
7088
  }
7056
7089
 
7057
7090
  // cli/commands/init.ts
7058
- import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync } from "fs";
7091
+ import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync, readdirSync } from "fs";
7059
7092
  import { resolve, join as join2, dirname as dirname3 } from "path";
7060
7093
  import { execSync } from "child_process";
7061
7094
  import { fileURLToPath } from "url";
@@ -7071,7 +7104,7 @@ function generateBashHook() {
7071
7104
  REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")
7072
7105
  export MINDKEG_REPO="$REPO_PATH"
7073
7106
 
7074
- node --experimental-sqlite -e "
7107
+ OUTPUT=$(node --experimental-sqlite -e "
7075
7108
  (async () => {
7076
7109
  try {
7077
7110
  const os = await import('node:os');
@@ -7092,63 +7125,21 @@ node --experimental-sqlite -e "
7092
7125
 
7093
7126
  if (rows.length === 0) process.exit(0);
7094
7127
 
7095
- const lines = ['=== Mind Keg: Persistent Memory ===', ''];
7128
+ const lines = ['Mind Keg Persistent Memory (from ~/.mindkeg/brain.db):', ''];
7096
7129
  for (const row of rows) {
7097
7130
  lines.push('- [' + row.category + '] ' + row.content);
7098
7131
  }
7099
- lines.push('', 'Use get_context for full details or store to save new knowledge.');
7132
+ lines.push('', 'These learnings were loaded from Mind Keg. Use get_context for more details or store to save new knowledge.');
7100
7133
  console.log(lines.join('\\n'));
7101
7134
  } catch (e) {
7102
7135
  // Silent failure \u2014 never block session startup
7103
7136
  }
7104
7137
  })();
7105
- " 2>/dev/null
7106
-
7107
- exit 0
7108
- `;
7109
- }
7110
- function generatePowerShellHook() {
7111
- return `# Mind Keg \u2014 SessionStart auto-retrieval hook
7112
- # Generated by: npx mindkeg-mcp init
7113
- # Loads persistent memory at the start of every Claude Code session.
7114
-
7115
- try {
7116
- $repoPath = git rev-parse --show-toplevel 2>$null
7117
- if (-not $repoPath) { $repoPath = $PWD.Path }
7118
- $env:MINDKEG_REPO = $repoPath
7119
-
7120
- node --experimental-sqlite -e "
7121
- (async () => {
7122
- try {
7123
- const os = await import('node:os');
7124
- const path = await import('node:path');
7125
- const { DatabaseSync } = await import('node:sqlite');
7126
-
7127
- const dbPath = path.join(os.homedir(), '.mindkeg', 'brain.db');
7128
- const db = new DatabaseSync(dbPath, { open: true, readOnly: true });
7129
-
7130
- const repo = process.env.MINDKEG_REPO || '';
7131
- const workspace = path.dirname(repo) + '/';
7132
-
7133
- const stmt = db.prepare(
7134
- 'SELECT content, category FROM learnings WHERE status = ? AND (repository = ? OR workspace = ? OR (repository IS NULL AND workspace IS NULL)) ORDER BY updated_at DESC LIMIT 20'
7135
- );
7136
- const rows = stmt.all('active', repo, workspace);
7138
+ " 2>/dev/null)
7137
7139
 
7138
- if (rows.length === 0) process.exit(0);
7139
-
7140
- const lines = ['=== Mind Keg: Persistent Memory ===', ''];
7141
- for (const row of rows) {
7142
- lines.push('- [' + row.category + '] ' + row.content);
7143
- }
7144
- lines.push('', 'Use get_context for full details or store to save new knowledge.');
7145
- console.log(lines.join('\\n'));
7146
- } catch (e) {}
7147
- })();
7148
- " 2>$null
7149
- } catch {
7150
- # Silent failure
7151
- }
7140
+ if [ -n "$OUTPUT" ]; then
7141
+ echo "$OUTPUT"
7142
+ fi
7152
7143
 
7153
7144
  exit 0
7154
7145
  `;
@@ -7297,25 +7288,37 @@ function writeGlobalMcpConfig(homeDir, agent) {
7297
7288
  writeFileSync2(configPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
7298
7289
  return { created: true, path: configPath };
7299
7290
  }
7300
- function writeGlobalHook(homeDir, agent) {
7301
- const config = AGENT_CONFIGS[agent];
7302
- if (!config.globalHookDir || !config.globalSettingsFile) return null;
7303
- const hookDir = join2(homeDir, config.globalHookDir);
7304
- const isWindows = process.platform === "win32";
7305
- const hookFileName = isWindows ? "load-mindkeg.ps1" : "load-mindkeg.sh";
7291
+ function detectClaudeProfiles(homeDir) {
7292
+ const profiles = [];
7293
+ try {
7294
+ const entries = readdirSync(homeDir, { withFileTypes: true });
7295
+ for (const entry of entries) {
7296
+ if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-worktrees") {
7297
+ const settingsPath = join2(homeDir, entry.name, "settings.json");
7298
+ if (existsSync(settingsPath)) {
7299
+ profiles.push(entry.name);
7300
+ }
7301
+ }
7302
+ }
7303
+ } catch {
7304
+ }
7305
+ return profiles;
7306
+ }
7307
+ function writeHookToDir(hookDir, settingsPath) {
7308
+ const hookFileName = "load-mindkeg.sh";
7306
7309
  const hookPath = join2(hookDir, hookFileName);
7307
7310
  if (!existsSync(hookDir)) {
7308
7311
  mkdirSync3(hookDir, { recursive: true });
7309
7312
  }
7310
- const scriptContent = isWindows ? generatePowerShellHook() : generateBashHook();
7313
+ const scriptContent = generateBashHook();
7311
7314
  writeFileSync2(hookPath, scriptContent, "utf-8");
7312
- if (!isWindows) {
7315
+ if (process.platform !== "win32") {
7313
7316
  try {
7314
7317
  execSync(`chmod +x "${hookPath}"`, { stdio: ["pipe", "pipe", "pipe"] });
7315
7318
  } catch {
7316
7319
  }
7317
7320
  }
7318
- const settingsPath = join2(homeDir, config.globalSettingsFile);
7321
+ const hookCommand = "bash " + hookPath.replace(/\\/g, "/");
7319
7322
  let settings = {};
7320
7323
  if (existsSync(settingsPath)) {
7321
7324
  try {
@@ -7323,7 +7326,7 @@ function writeGlobalHook(homeDir, agent) {
7323
7326
  } catch {
7324
7327
  }
7325
7328
  }
7326
- const hookConfig = generateHookConfig(hookPath);
7329
+ const hookConfig = generateHookConfig(hookCommand);
7327
7330
  const existingHooks = settings["hooks"] ?? {};
7328
7331
  const newHooks = hookConfig["hooks"] ?? {};
7329
7332
  const existingSessionStart = existingHooks["SessionStart"] ?? [];
@@ -7340,6 +7343,13 @@ function writeGlobalHook(homeDir, agent) {
7340
7343
  }
7341
7344
  return { created: !alreadyHasMindkeg, path: hookPath };
7342
7345
  }
7346
+ function writeGlobalHook(homeDir, agent) {
7347
+ const config = AGENT_CONFIGS[agent];
7348
+ if (!config.globalHookDir || !config.globalSettingsFile) return null;
7349
+ const hookDir = join2(homeDir, config.globalHookDir);
7350
+ const settingsPath = join2(homeDir, config.globalSettingsFile);
7351
+ return writeHookToDir(hookDir, settingsPath);
7352
+ }
7343
7353
  function ensureDatabase(homeDir) {
7344
7354
  const dbDir = join2(homeDir, ".mindkeg");
7345
7355
  const dbPath = join2(dbDir, "brain.db");
@@ -7445,6 +7455,17 @@ Mind Keg \u2014 Global Setup for ${AGENT_CONFIGS[agent].label}
7445
7455
  } else {
7446
7456
  console.log(` - Hook already registered`);
7447
7457
  }
7458
+ const profiles = detectClaudeProfiles(home);
7459
+ for (const profile of profiles) {
7460
+ const profileHookDir = join2(home, profile, "hooks");
7461
+ const profileSettingsFile = join2(home, profile, "settings.json");
7462
+ const profileResult = writeHookToDir(profileHookDir, profileSettingsFile);
7463
+ if (profileResult.created) {
7464
+ console.log(` \u2713 Wrote ${profileResult.path} (profile: ${profile})`);
7465
+ } else {
7466
+ console.log(` - Hook already registered in ${profile}`);
7467
+ }
7468
+ }
7448
7469
  }
7449
7470
  console.log("\nDatabase:");
7450
7471
  const dbResult = ensureDatabase(home);
@@ -8147,7 +8168,7 @@ Config location: ${settingsDir}
8147
8168
  }
8148
8169
 
8149
8170
  // cli/index.ts
8150
- program.name("mindkeg").description("Mind Keg MCP \u2014 persistent memory for AI coding agents").version("0.5.0");
8171
+ program.name("mindkeg").description("Mind Keg MCP \u2014 persistent memory for AI coding agents").version("0.7.2");
8151
8172
  registerServeCommand(program);
8152
8173
  registerApiKeyCommand(program);
8153
8174
  registerMigrateCommand(program);