@kalera/munin-runtime 1.2.6 → 1.2.9

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @kalera/munin-runtime@1.2.6 build /home/runner/work/munin-for-agents/munin-for-agents/packages/runtime
2
+ > @kalera/munin-runtime@1.2.9 build /home/runner/work/munin-for-agents/munin-for-agents/packages/runtime
3
3
  > tsc -p tsconfig.json
4
4
 
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @kalera/munin-runtime
2
+
3
+ MCP (Model Context Protocol) server runtime for Munin Context Core. Works with any MCP-compatible client (Claude Code, Cursor, etc.).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @kalera/munin-runtime
9
+ ```
10
+
11
+ ## Available Tools
12
+
13
+ | Tool | Description |
14
+ |------|-------------|
15
+ | `munin_store_memory` | Store or update a memory |
16
+ | `munin_retrieve_memory` | Retrieve a memory by key |
17
+ | `munin_search_memories` | Semantic/keyword search |
18
+ | `munin_list_memories` | List all memories (paginated) |
19
+ | `munin_recent_memories` | Most recently updated memories |
20
+ | `munin_share_memory` | Share memories across projects |
21
+ | `munin_get_project_info` | Get project metadata and E2EE status |
22
+
23
+ ## Environment Variables
24
+
25
+ | Variable | Required | Description |
26
+ |----------|----------|-------------|
27
+ | `MUNIN_API_KEY` | Yes | Your Munin API key |
28
+ | `MUNIN_PROJECT` | Yes* | Active project ID (*or pass as tool arg) |
29
+ | `MUNIN_BASE_URL` | No | Default: `https://munin.kalera.dev` |
30
+ | `MUNIN_TIMEOUT_MS` | No | Request timeout (default: 15000) |
31
+ | `MUNIN_ENCRYPTION_KEY` | No | Encryption key for E2EE projects |
32
+
33
+ ## Usage as MCP Server
34
+
35
+ ```bash
36
+ MUNIN_API_KEY=your-key MUNIN_PROJECT=your-project npx @kalera/munin-runtime
37
+ ```
38
+
39
+ ## Claude Code Integration
40
+
41
+ Configure in your `~/.claude/settings.json`:
42
+
43
+ ```json
44
+ {
45
+ "pluginConfigs": {
46
+ "munin": {
47
+ "command": "npx",
48
+ "args": ["@kalera/munin-runtime"]
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ Set required env vars in your project's `.env` or `.env.local`.
55
+
56
+ ## License
57
+
58
+ MIT
package/dist/env.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export { parseCliArgs, loadCliEnv, resolveProjectId, executeWithRetry, safeError, } from "./index.js";
2
+ export type { ParsedCliArgs, CliEnv } from "./index.js";
1
3
  export interface EnvVar {
2
4
  key: string;
3
5
  value: string;
@@ -13,25 +15,3 @@ export declare function resolveEncryptionKey(): string | undefined;
13
15
  * - Keys are matched via string split (not regex) to prevent injection.
14
16
  */
15
17
  export declare function writeEnvFile(filename: string, vars: EnvVar[], cwd?: string): void;
16
- /**
17
- * Resolve MUNIN_PROJECT from multiple sources, in priority order:
18
- * 1. Explicit environment variable
19
- * 2. .env.local in any ancestor directory (walk-up from CWD)
20
- * 3. .env in any ancestor directory (walk-up from CWD)
21
- */
22
- export declare function resolveProjectId(): string | undefined;
23
- export interface CliEnv {
24
- baseUrl: string;
25
- apiKey: string | undefined;
26
- timeoutMs: number;
27
- retries: number;
28
- backoffMs: number;
29
- }
30
- export declare function loadCliEnv(): CliEnv;
31
- export declare function executeWithRetry<T>(task: () => Promise<T>, retries: number, backoffMs: number): Promise<T>;
32
- export declare function safeError(error: unknown): Record<string, unknown>;
33
- export declare function parseCliArgs(argv: string[], usage: string): {
34
- action: string;
35
- payload: Record<string, unknown>;
36
- };
37
- export * from "./mcp-server.js";
package/dist/env.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
- const REDACT_KEYS = ["apiKey", "authorization", "token", "secret", "password"];
3
+ // Re-export all shared utilities from index.ts (single source of truth)
4
+ export { parseCliArgs, loadCliEnv, resolveProjectId, executeWithRetry, safeError, } from "./index.js";
4
5
  /**
5
6
  * Resolve the encryption key for E2EE projects from MUNIN_ENCRYPTION_KEY env var.
6
7
  */
@@ -62,140 +63,3 @@ export function writeEnvFile(filename, vars, cwd = process.cwd()) {
62
63
  }
63
64
  fs.writeFileSync(filePath, lines.join("\n"), "utf8");
64
65
  }
65
- /**
66
- * Safe numeric env parser: returns defaultVal if value is missing or NaN.
67
- */
68
- function safeParseInt(envVal, defaultVal) {
69
- if (envVal === undefined)
70
- return defaultVal;
71
- const parsed = Number(envVal);
72
- if (isNaN(parsed))
73
- return defaultVal;
74
- return parsed;
75
- }
76
- /**
77
- * Walk up the directory tree from `startDir` (default CWD) toward root,
78
- * searching each ancestor for `filename`. Returns the value of the first match.
79
- * Stops at filesystem root or when a `.git` dir is found (project boundary).
80
- */
81
- function resolveEnvFileUpward(filename, startDir) {
82
- let current = startDir ?? process.cwd();
83
- let last = "";
84
- while (current !== last) {
85
- last = current;
86
- const filePath = path.join(current, filename);
87
- try {
88
- if (fs.existsSync(filePath)) {
89
- const content = fs.readFileSync(filePath, "utf8");
90
- for (const line of content.split("\n")) {
91
- const trimmed = line.trim();
92
- if (!trimmed.startsWith("MUNIN_PROJECT="))
93
- continue;
94
- return trimmed.slice("MUNIN_PROJECT=".length).trim();
95
- }
96
- }
97
- }
98
- catch (error) {
99
- if (error.code !== "ENOENT") {
100
- console.error(`[munin-runtime] Failed to read ${filePath}:`, error);
101
- }
102
- }
103
- // Stop at git root (project boundary)
104
- if (fs.existsSync(path.join(current, ".git")))
105
- break;
106
- current = path.dirname(current);
107
- }
108
- return undefined;
109
- }
110
- /**
111
- * Resolve MUNIN_PROJECT from multiple sources, in priority order:
112
- * 1. Explicit environment variable
113
- * 2. .env.local in any ancestor directory (walk-up from CWD)
114
- * 3. .env in any ancestor directory (walk-up from CWD)
115
- */
116
- export function resolveProjectId() {
117
- // 1. Explicit env var (highest priority)
118
- if (process.env.MUNIN_PROJECT) {
119
- return process.env.MUNIN_PROJECT;
120
- }
121
- // 2. Walk upward from CWD — find .env.local in any ancestor dir
122
- const fromLocal = resolveEnvFileUpward(".env.local");
123
- if (fromLocal)
124
- return fromLocal;
125
- // 3. Walk upward from CWD — find .env in any ancestor dir
126
- const fromEnv = resolveEnvFileUpward(".env");
127
- if (fromEnv)
128
- return fromEnv;
129
- return undefined;
130
- }
131
- export function loadCliEnv() {
132
- return {
133
- baseUrl: process.env.MUNIN_BASE_URL || "https://munin.kalera.dev",
134
- apiKey: process.env.MUNIN_API_KEY,
135
- timeoutMs: safeParseInt(process.env.MUNIN_TIMEOUT_MS, 15_000),
136
- retries: safeParseInt(process.env.MUNIN_RETRIES, 3),
137
- backoffMs: safeParseInt(process.env.MUNIN_BACKOFF_MS, 300),
138
- };
139
- }
140
- export async function executeWithRetry(task, retries, backoffMs) {
141
- let attempt = 0;
142
- let lastError;
143
- while (attempt < retries) {
144
- try {
145
- return await task();
146
- }
147
- catch (error) {
148
- lastError = error;
149
- attempt += 1;
150
- if (attempt >= retries)
151
- break;
152
- const jitter = Math.floor(Math.random() * 100);
153
- await sleep(backoffMs * 2 ** attempt + jitter);
154
- }
155
- }
156
- throw lastError;
157
- }
158
- export function safeError(error) {
159
- if (error instanceof Error) {
160
- return {
161
- name: error.name,
162
- message: redactText(error.message),
163
- };
164
- }
165
- return { message: redactText(String(error)) };
166
- }
167
- function redactText(text) {
168
- return REDACT_KEYS.reduce((acc, key) => {
169
- // Replace key=value patterns with key=[REDACTED] using simple string split
170
- let result = acc;
171
- let idx = result.indexOf(`${key}=`);
172
- while (idx !== -1) {
173
- const endIdx = result.indexOf(/[\s,;]/.test(result[idx + key.length + 1] ?? "")
174
- ? result[idx + key.length + 1]
175
- : "");
176
- const end = endIdx === -1 ? result.length : endIdx;
177
- const before = result.slice(0, idx + key.length + 1);
178
- const after = result.slice(end);
179
- result = `${before}[REDACTED]${after}`;
180
- idx = result.indexOf(`${key}=`, idx + key.length + 1);
181
- }
182
- return result;
183
- }, text);
184
- }
185
- function sleep(ms) {
186
- return new Promise((resolve) => setTimeout(resolve, ms));
187
- }
188
- export function parseCliArgs(argv, usage) {
189
- const [action, payloadRaw] = argv;
190
- if (!action)
191
- throw new Error(usage);
192
- if (!payloadRaw)
193
- return { action, payload: {} };
194
- try {
195
- return { action, payload: JSON.parse(payloadRaw) };
196
- }
197
- catch {
198
- throw new Error("Payload must be valid JSON");
199
- }
200
- }
201
- export * from "./mcp-server.js";
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
1
3
  const REDACT_KEYS = ["apiKey", "authorization", "token", "secret", "password"];
2
4
  export function parseCliArgs(argv, usage) {
3
5
  const [action, payloadRaw] = argv;
@@ -38,8 +40,6 @@ function safeParseInt(envVal, defaultVal) {
38
40
  * Stops at filesystem root or when a `.git` dir is found (project boundary).
39
41
  */
40
42
  function resolveEnvFileUpward(filename, startDir) {
41
- const fs = require("fs");
42
- const path = require("path");
43
43
  let current = startDir ?? process.cwd();
44
44
  let last = "";
45
45
  while (current !== last) {
@@ -14,7 +14,7 @@ export function createMcpServerInstance(env, opts) {
14
14
  });
15
15
  const server = new Server({
16
16
  name: "munin-mcp-server",
17
- version: "1.0.0",
17
+ version: "1.2.9",
18
18
  }, {
19
19
  capabilities: {
20
20
  tools: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kalera/munin-runtime",
3
- "version": "1.2.6",
3
+ "version": "1.2.9",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@modelcontextprotocol/sdk": "^1.27.1",
15
- "@kalera/munin-sdk": "1.2.6"
15
+ "@kalera/munin-sdk": "1.2.9"
16
16
  },
17
17
  "scripts": {
18
18
  "build": "tsc -p tsconfig.json",
package/src/env.ts CHANGED
@@ -1,13 +1,22 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
3
 
4
+ // Re-export all shared utilities from index.ts (single source of truth)
5
+ export {
6
+ parseCliArgs,
7
+ loadCliEnv,
8
+ resolveProjectId,
9
+ executeWithRetry,
10
+ safeError,
11
+ } from "./index.js";
12
+
13
+ export type { ParsedCliArgs, CliEnv } from "./index.js";
14
+
4
15
  export interface EnvVar {
5
16
  key: string;
6
17
  value: string;
7
18
  }
8
19
 
9
- const REDACT_KEYS = ["apiKey", "authorization", "token", "secret", "password"];
10
-
11
20
  /**
12
21
  * Resolve the encryption key for E2EE projects from MUNIN_ENCRYPTION_KEY env var.
13
22
  */
@@ -79,158 +88,3 @@ export function writeEnvFile(
79
88
 
80
89
  fs.writeFileSync(filePath, lines.join("\n"), "utf8");
81
90
  }
82
-
83
- /**
84
- * Safe numeric env parser: returns defaultVal if value is missing or NaN.
85
- */
86
- function safeParseInt(envVal: string | undefined, defaultVal: number): number {
87
- if (envVal === undefined) return defaultVal;
88
- const parsed = Number(envVal);
89
- if (isNaN(parsed)) return defaultVal;
90
- return parsed;
91
- }
92
-
93
- /**
94
- * Walk up the directory tree from `startDir` (default CWD) toward root,
95
- * searching each ancestor for `filename`. Returns the value of the first match.
96
- * Stops at filesystem root or when a `.git` dir is found (project boundary).
97
- */
98
- function resolveEnvFileUpward(filename: string, startDir?: string): string | undefined {
99
- let current = startDir ?? process.cwd();
100
- let last = "";
101
-
102
- while (current !== last) {
103
- last = current;
104
- const filePath = path.join(current, filename);
105
- try {
106
- if (fs.existsSync(filePath)) {
107
- const content = fs.readFileSync(filePath, "utf8");
108
- for (const line of content.split("\n")) {
109
- const trimmed = line.trim();
110
- if (!trimmed.startsWith("MUNIN_PROJECT=")) continue;
111
- return trimmed.slice("MUNIN_PROJECT=".length).trim();
112
- }
113
- }
114
- } catch (error) {
115
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
116
- console.error(`[munin-runtime] Failed to read ${filePath}:`, error);
117
- }
118
- }
119
-
120
- // Stop at git root (project boundary)
121
- if (fs.existsSync(path.join(current, ".git"))) break;
122
-
123
- current = path.dirname(current);
124
- }
125
- return undefined;
126
- }
127
-
128
- /**
129
- * Resolve MUNIN_PROJECT from multiple sources, in priority order:
130
- * 1. Explicit environment variable
131
- * 2. .env.local in any ancestor directory (walk-up from CWD)
132
- * 3. .env in any ancestor directory (walk-up from CWD)
133
- */
134
- export function resolveProjectId(): string | undefined {
135
- // 1. Explicit env var (highest priority)
136
- if (process.env.MUNIN_PROJECT) {
137
- return process.env.MUNIN_PROJECT;
138
- }
139
-
140
- // 2. Walk upward from CWD — find .env.local in any ancestor dir
141
- const fromLocal = resolveEnvFileUpward(".env.local");
142
- if (fromLocal) return fromLocal;
143
-
144
- // 3. Walk upward from CWD — find .env in any ancestor dir
145
- const fromEnv = resolveEnvFileUpward(".env");
146
- if (fromEnv) return fromEnv;
147
-
148
- return undefined;
149
- }
150
-
151
- export interface CliEnv {
152
- baseUrl: string;
153
- apiKey: string | undefined;
154
- timeoutMs: number;
155
- retries: number;
156
- backoffMs: number;
157
- }
158
-
159
- export function loadCliEnv(): CliEnv {
160
- return {
161
- baseUrl: process.env.MUNIN_BASE_URL || "https://munin.kalera.dev",
162
- apiKey: process.env.MUNIN_API_KEY,
163
- timeoutMs: safeParseInt(process.env.MUNIN_TIMEOUT_MS, 15_000),
164
- retries: safeParseInt(process.env.MUNIN_RETRIES, 3),
165
- backoffMs: safeParseInt(process.env.MUNIN_BACKOFF_MS, 300),
166
- };
167
- }
168
-
169
- export async function executeWithRetry<T>(
170
- task: () => Promise<T>,
171
- retries: number,
172
- backoffMs: number,
173
- ): Promise<T> {
174
- let attempt = 0;
175
- let lastError: unknown;
176
-
177
- while (attempt < retries) {
178
- try {
179
- return await task();
180
- } catch (error) {
181
- lastError = error;
182
- attempt += 1;
183
- if (attempt >= retries) break;
184
- const jitter = Math.floor(Math.random() * 100);
185
- await sleep(backoffMs * 2 ** attempt + jitter);
186
- }
187
- }
188
-
189
- throw lastError;
190
- }
191
-
192
- export function safeError(error: unknown): Record<string, unknown> {
193
- if (error instanceof Error) {
194
- return {
195
- name: error.name,
196
- message: redactText(error.message),
197
- };
198
- }
199
- return { message: redactText(String(error)) };
200
- }
201
-
202
- function redactText(text: string): string {
203
- return REDACT_KEYS.reduce((acc, key) => {
204
- // Replace key=value patterns with key=[REDACTED] using simple string split
205
- let result = acc;
206
- let idx = result.indexOf(`${key}=`);
207
- while (idx !== -1) {
208
- const endIdx = result.indexOf(/[\s,;]/.test(result[idx + key.length + 1] ?? "")
209
- ? result[idx + key.length + 1]
210
- : "");
211
- const end = endIdx === -1 ? result.length : endIdx;
212
- const before = result.slice(0, idx + key.length + 1);
213
- const after = result.slice(end);
214
- result = `${before}[REDACTED]${after}`;
215
- idx = result.indexOf(`${key}=`, idx + key.length + 1);
216
- }
217
- return result;
218
- }, text);
219
- }
220
-
221
- function sleep(ms: number): Promise<void> {
222
- return new Promise((resolve) => setTimeout(resolve, ms));
223
- }
224
-
225
- export function parseCliArgs(argv: string[], usage: string): { action: string; payload: Record<string, unknown> } {
226
- const [action, payloadRaw] = argv;
227
- if (!action) throw new Error(usage);
228
- if (!payloadRaw) return { action, payload: {} };
229
- try {
230
- return { action, payload: JSON.parse(payloadRaw) as Record<string, unknown> };
231
- } catch {
232
- throw new Error("Payload must be valid JSON");
233
- }
234
- }
235
-
236
- export * from "./mcp-server.js";
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
1
4
  export interface ParsedCliArgs {
2
5
  action: string;
3
6
  payload: Record<string, unknown>;
@@ -55,9 +58,6 @@ function safeParseInt(envVal: string | undefined, defaultVal: number): number {
55
58
  * Stops at filesystem root or when a `.git` dir is found (project boundary).
56
59
  */
57
60
  function resolveEnvFileUpward(filename: string, startDir?: string): string | undefined {
58
- const fs = require("fs") as typeof import("fs");
59
- const path = require("path") as typeof import("path");
60
-
61
61
  let current = startDir ?? process.cwd();
62
62
  let last = "";
63
63
 
@@ -110,7 +110,6 @@ export function resolveProjectId(): string | undefined {
110
110
  return undefined;
111
111
  }
112
112
 
113
-
114
113
  export async function executeWithRetry<T>(
115
114
  task: () => Promise<T>,
116
115
  retries: number,
@@ -155,5 +154,6 @@ function redactText(text: string): string {
155
154
  function sleep(ms: number): Promise<void> {
156
155
  return new Promise((resolve) => setTimeout(resolve, ms));
157
156
  }
157
+
158
158
  export * from "./mcp-server.js";
159
159
  export * from "./env.js";
package/src/mcp-server.ts CHANGED
@@ -26,7 +26,7 @@ export function createMcpServerInstance(
26
26
  const server = new Server(
27
27
  {
28
28
  name: "munin-mcp-server",
29
- version: "1.0.0",
29
+ version: "1.2.9",
30
30
  },
31
31
  {
32
32
  capabilities: {