@scitrera/memorylayer-mcp-server 0.0.3

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 ADDED
@@ -0,0 +1,350 @@
1
+ # @scitrera/memorylayer-mcp-server
2
+
3
+ TypeScript MCP (Model Context Protocol) server for [MemoryLayer.ai](https://memorylayer.ai).
4
+
5
+ Provides 16 memory tools for LLM agents to store, recall, synthesize, and manage information across sessions.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @scitrera/memorylayer-mcp-server
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### As a Standalone MCP Server
16
+
17
+ ```bash
18
+ # Set environment variables
19
+ export MEMORYLAYER_URL=http://localhost:61001
20
+ export MEMORYLAYER_WORKSPACE_ID=my-workspace
21
+
22
+ # Run the server
23
+ npx memorylayer-mcp
24
+ ```
25
+
26
+ ### Claude Code Configuration (Recommended)
27
+
28
+ > **Detailed setup guide:** See [CLAUDE_CODE_SETUP.md](../docs/CLAUDE_CODE_SETUP.md) for step-by-step instructions.
29
+
30
+ Claude Code runs MCP servers from the project directory, so our server auto-detects the workspace from your git repo or folder name. Add `.mcp.json` to your project root:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "memorylayer": {
36
+ "command": "npx",
37
+ "args": ["@scitrera/memorylayer-mcp-server"],
38
+ "env": {
39
+ "MEMORYLAYER_URL": "http://localhost:61001"
40
+ }
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ **Auto-workspace detection**: The server uses your git repo name (or directory name) as the workspace ID. Each project gets isolated memory storage automatically.
47
+
48
+ **Override options:**
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "memorylayer": {
53
+ "command": "npx",
54
+ "args": ["@scitrera/memorylayer-mcp-server"],
55
+ "env": {
56
+ "MEMORYLAYER_URL": "http://localhost:61001",
57
+ "MEMORYLAYER_WORKSPACE_ID": "${WORKSPACE_ID:-my-project}"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ Or via CLI:
65
+ ```bash
66
+ claude mcp add --transport stdio memorylayer \
67
+ --env MEMORYLAYER_URL=http://localhost:61001 \
68
+ -- npx @scitrera/memorylayer-mcp-server
69
+ ```
70
+
71
+ ### Claude Desktop Configuration
72
+
73
+ Add to your Claude Desktop config file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "memorylayer": {
79
+ "command": "npx",
80
+ "args": ["@scitrera/memorylayer-mcp-server"],
81
+ "env": {
82
+ "MEMORYLAYER_URL": "http://localhost:61001",
83
+ "MEMORYLAYER_WORKSPACE_ID": "my-project"
84
+ }
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ **Note**: Claude Desktop doesn't change directories per-project, so you should set `MEMORYLAYER_WORKSPACE_ID` explicitly for each project entry.
91
+
92
+ ### Programmatic Usage
93
+
94
+ ```typescript
95
+ import { MemoryLayerClient, createServer } from "@scitrera/memorylayer-mcp-server";
96
+
97
+ // Create client (wraps the @scitrera/memorylayer-sdk)
98
+ const client = new MemoryLayerClient({
99
+ baseUrl: "http://localhost:61001",
100
+ workspaceId: "my-workspace",
101
+ apiKey: "optional-api-key"
102
+ });
103
+
104
+ // Create MCP server
105
+ const server = await createServer(client);
106
+
107
+ // Run server on stdio transport
108
+ await server.run();
109
+ ```
110
+
111
+ ## Available Tools
112
+
113
+ ### Core Memory Tools (5)
114
+
115
+ #### 1. `memory_remember`
116
+ Store a new memory for later recall.
117
+
118
+ ```typescript
119
+ {
120
+ content: "User prefers TypeScript for new projects",
121
+ type: "semantic", // episodic, semantic, procedural, working
122
+ importance: 0.8, // 0.0 - 1.0
123
+ tags: ["preference", "typescript"],
124
+ subtype: "Preference" // Optional domain classification
125
+ }
126
+ ```
127
+
128
+ #### 2. `memory_recall`
129
+ Search memories by semantic query.
130
+
131
+ ```typescript
132
+ {
133
+ query: "What are the user's coding preferences?",
134
+ limit: 10,
135
+ min_relevance: 0.5,
136
+ types: ["semantic"], // Optional filter
137
+ tags: ["preference"] // Optional filter (AND logic)
138
+ }
139
+ ```
140
+
141
+ #### 3. `memory_reflect`
142
+ Synthesize insights across multiple memories.
143
+
144
+ ```typescript
145
+ {
146
+ query: "What patterns have we seen with database performance?",
147
+ max_tokens: 500,
148
+ include_sources: true,
149
+ depth: 2 // Association traversal depth
150
+ }
151
+ ```
152
+
153
+ #### 4. `memory_forget`
154
+ Delete or decay outdated information.
155
+
156
+ ```typescript
157
+ {
158
+ memory_id: "mem_abc123",
159
+ reason: "Outdated information",
160
+ hard: false // true = permanent delete
161
+ }
162
+ ```
163
+
164
+ #### 5. `memory_associate`
165
+ Link memories with typed relationships.
166
+
167
+ ```typescript
168
+ {
169
+ source_id: "mem_problem",
170
+ target_id: "mem_solution",
171
+ relationship: "solves", // 60+ relationship types available
172
+ strength: 0.9 // 0.0 - 1.0
173
+ }
174
+ ```
175
+
176
+ ### Extended Memory Tools (4)
177
+
178
+ #### 6. `memory_briefing`
179
+ Get a session briefing with recent context.
180
+
181
+ ```typescript
182
+ {
183
+ lookback_hours: 24,
184
+ include_contradictions: true
185
+ }
186
+ ```
187
+
188
+ #### 7. `memory_statistics`
189
+ Get workspace analytics and memory usage.
190
+
191
+ ```typescript
192
+ {
193
+ include_breakdown: true // Include breakdown by type/subtype
194
+ }
195
+ ```
196
+
197
+ #### 8. `memory_graph_query`
198
+ Multi-hop graph traversal for causal chains.
199
+
200
+ ```typescript
201
+ {
202
+ start_memory_id: "mem_abc123",
203
+ relationship_types: ["causes", "triggers"],
204
+ max_depth: 3,
205
+ direction: "both", // outgoing, incoming, both
206
+ max_paths: 50
207
+ }
208
+ ```
209
+
210
+ #### 9. `memory_audit`
211
+ Find contradictions and inconsistencies.
212
+
213
+ ```typescript
214
+ {
215
+ memory_id: "mem_abc123", // Optional - omit to audit entire workspace
216
+ auto_resolve: false // Auto-prefer newer contradicting memories
217
+ }
218
+ ```
219
+
220
+ ### Session Management Tools (3)
221
+
222
+ These tools enable working memory that persists across tool calls within a session.
223
+
224
+ #### 10. `memory_session_start`
225
+ Start a new session for working memory tracking.
226
+
227
+ ```typescript
228
+ {
229
+ metadata: { task: "debugging" } // Optional metadata
230
+ }
231
+ ```
232
+
233
+ #### 11. `memory_session_end`
234
+ End the current session and optionally commit working memory.
235
+
236
+ ```typescript
237
+ {
238
+ commit: true, // Commit to long-term storage
239
+ importance_threshold: 0.5 // Min importance for extracted memories
240
+ }
241
+ ```
242
+
243
+ #### 12. `memory_session_status`
244
+ Get current session status including working memory summary.
245
+
246
+ ```typescript
247
+ {} // No parameters required
248
+ ```
249
+
250
+ ## Environment Variables
251
+
252
+ | Variable | Description | Default |
253
+ |----------|-------------|---------|
254
+ | `MEMORYLAYER_URL` | Base URL for MemoryLayer API | `http://localhost:61001` |
255
+ | `MEMORYLAYER_API_KEY` | API key for authentication | (none) |
256
+ | `MEMORYLAYER_WORKSPACE_ID` | Workspace ID (overrides auto-detection) | (auto-detected) |
257
+ | `MEMORYLAYER_AUTO_WORKSPACE` | Set to `false` to disable auto-detection | `true` |
258
+
259
+ ## Memory Types
260
+
261
+ - **Episodic**: Specific events/interactions
262
+ - **Semantic**: Facts, concepts, relationships
263
+ - **Procedural**: How-to knowledge
264
+ - **Working**: Current task context (session-scoped)
265
+
266
+ ## Relationship Types (60+)
267
+
268
+ Organized into 11 categories:
269
+
270
+ **Hierarchical**: parent_of, child_of, part_of, contains, instance_of, subtype_of
271
+ **Causal**: causes, triggers, leads_to, prevents
272
+ **Temporal**: precedes, concurrent_with, follows_temporally
273
+ **Similarity**: similar_to, variant_of, related_to
274
+ **Learning**: builds_on, contradicts, confirms, supersedes
275
+ **Refinement**: refines, abstracts, specializes, generalizes
276
+ **Reference**: references, referenced_by
277
+ **Solution**: solves, addresses, alternative_to, improves
278
+ **Context**: occurs_in, applies_to, works_with, requires
279
+ **Workflow**: follows, depends_on, enables, blocks
280
+ **Quality**: effective_for, preferred_over, deprecated_by
281
+
282
+ ## Architecture
283
+
284
+ The MCP server wraps the `@scitrera/memorylayer-sdk` TypeScript SDK, providing an MCP-compatible interface for LLM agents.
285
+
286
+ ```
287
+ memorylayer-mcp-typescript/
288
+ ├── src/
289
+ │ ├── types.ts # TypeScript types for MCP tools
290
+ │ ├── tools.ts # MCP tool definitions (12 tools)
291
+ │ ├── client.ts # Wrapper around @scitrera/memorylayer-sdk
292
+ │ ├── session.ts # Local session state management
293
+ │ ├── handlers.ts # Tool handler implementations
294
+ │ ├── server.ts # MCP server using @modelcontextprotocol/sdk
295
+ │ └── index.ts # Main exports
296
+ ├── bin/
297
+ │ └── memorylayer-mcp.ts # CLI entry point
298
+ ├── package.json
299
+ ├── tsconfig.json
300
+ └── README.md
301
+ ```
302
+
303
+ ## Development
304
+
305
+ ```bash
306
+ # Install dependencies
307
+ npm install
308
+
309
+ # Build
310
+ npm run build
311
+
312
+ # Watch mode
313
+ npm run dev
314
+
315
+ # Run locally
316
+ npm start
317
+ ```
318
+
319
+ ## Using the SDK Client Directly
320
+
321
+ The MCP server's client is a thin wrapper around the TypeScript SDK. For direct SDK usage without MCP, install `@scitrera/memorylayer-sdk`:
322
+
323
+ ```bash
324
+ npm install @scitrera/memorylayer-sdk
325
+ ```
326
+
327
+ ```typescript
328
+ import { MemoryLayerClient } from "@scitrera/memorylayer-sdk";
329
+
330
+ const client = new MemoryLayerClient({
331
+ baseUrl: "http://localhost:61001",
332
+ workspaceId: "my-workspace"
333
+ });
334
+
335
+ const memory = await client.remember("Important fact", {
336
+ type: "semantic",
337
+ importance: 0.8
338
+ });
339
+ ```
340
+
341
+ ## License
342
+
343
+ Apache 2.0 License - see LICENSE file for details.
344
+
345
+ ## Links
346
+
347
+ - [MemoryLayer.ai](https://memorylayer.ai)
348
+ - [Documentation](https://docs.memorylayer.ai)
349
+ - [TypeScript SDK](https://www.npmjs.com/package/@scitrera/memorylayer-sdk)
350
+ - [Model Context Protocol](https://modelcontextprotocol.io)
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for MemoryLayer Claude Code hooks
4
+ *
5
+ * Usage:
6
+ * memorylayer-hook <hook-type>
7
+ *
8
+ * Reads HookInput JSON from stdin, writes HookOutput JSON to stdout.
9
+ *
10
+ * Hook types:
11
+ * SessionStart - Called at session start
12
+ * UserPromptSubmit - Called when user submits a prompt
13
+ * PreToolUse - Called before a tool is used
14
+ * PostToolUse - Called after a tool is used
15
+ * PreCompact - Called before context compaction
16
+ * Stop - Called when session ends
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=memorylayer-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memorylayer-hook.d.ts","sourceRoot":"","sources":["../../bin/memorylayer-hook.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for MemoryLayer Claude Code hooks
4
+ *
5
+ * Usage:
6
+ * memorylayer-hook <hook-type>
7
+ *
8
+ * Reads HookInput JSON from stdin, writes HookOutput JSON to stdout.
9
+ *
10
+ * Hook types:
11
+ * SessionStart - Called at session start
12
+ * UserPromptSubmit - Called when user submits a prompt
13
+ * PreToolUse - Called before a tool is used
14
+ * PostToolUse - Called after a tool is used
15
+ * PreCompact - Called before context compaction
16
+ * Stop - Called when session ends
17
+ */
18
+ import { appendFileSync } from "fs";
19
+ import { HookEvent } from "../src/hooks/types.js";
20
+ import { handleSessionStart } from "../src/hooks/handlers/session-start.js";
21
+ import { handleUserPromptSubmit } from "../src/hooks/handlers/user-prompt.js";
22
+ import { handlePreToolUse } from "../src/hooks/handlers/pre-tool.js";
23
+ import { handlePostToolUse } from "../src/hooks/handlers/post-tool.js";
24
+ import { handleStop } from "../src/hooks/handlers/stop.js";
25
+ import { resetRecallStatus, updateSessionInfo } from "../src/hooks/state.js";
26
+ import { getClient } from "../src/hooks/client.js";
27
+ /**
28
+ * Read all input from stdin
29
+ */
30
+ async function readStdin() {
31
+ const chunks = [];
32
+ return new Promise((resolve, reject) => {
33
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
34
+ process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
35
+ process.stdin.on("error", reject);
36
+ // Timeout after 5 seconds if no input
37
+ setTimeout(() => {
38
+ if (chunks.length === 0) {
39
+ resolve("{}");
40
+ }
41
+ }, 5000);
42
+ });
43
+ }
44
+ /**
45
+ * Parse hook type from command line
46
+ */
47
+ function parseHookType(arg) {
48
+ if (!arg)
49
+ return null;
50
+ const normalized = arg.toLowerCase();
51
+ switch (normalized) {
52
+ case "sessionstart":
53
+ return HookEvent.SessionStart;
54
+ case "userpromptsubmit":
55
+ return HookEvent.UserPromptSubmit;
56
+ case "pretooluse":
57
+ return HookEvent.PreToolUse;
58
+ case "posttooluse":
59
+ return HookEvent.PostToolUse;
60
+ case "precompact":
61
+ return HookEvent.PreCompact;
62
+ case "stop":
63
+ return HookEvent.Stop;
64
+ default:
65
+ return null;
66
+ }
67
+ }
68
+ /** Hooks that support hookSpecificOutput with additionalContext */
69
+ const HOOKS_WITH_ADDITIONAL_CONTEXT = new Set([
70
+ HookEvent.SessionStart, // Works empirically even though not in schema
71
+ HookEvent.PreToolUse,
72
+ HookEvent.PostToolUse,
73
+ HookEvent.UserPromptSubmit,
74
+ ]);
75
+ /**
76
+ * Build properly formatted hook output
77
+ */
78
+ function buildOutput(hookType, additionalContext, block = false, reason) {
79
+ const output = {
80
+ continue: !block, // Always continue unless blocking
81
+ };
82
+ // For PreToolUse, we can block
83
+ if (hookType === HookEvent.PreToolUse && block) {
84
+ output.continue = false;
85
+ output.decision = "block";
86
+ output.reason = reason || "Blocked by MemoryLayer hook";
87
+ }
88
+ // Only add hookSpecificOutput for hooks that support it
89
+ // SessionStart, Stop, PreCompact do NOT support hookSpecificOutput
90
+ if (additionalContext && HOOKS_WITH_ADDITIONAL_CONTEXT.has(hookType)) {
91
+ output.hookSpecificOutput = {
92
+ hookEventName: hookType,
93
+ additionalContext,
94
+ };
95
+ }
96
+ return output;
97
+ }
98
+ /**
99
+ * Build error output
100
+ */
101
+ function buildErrorOutput(hookType, error) {
102
+ return {
103
+ hookSpecificOutput: {
104
+ hookEventName: hookType,
105
+ additionalContext: `MemoryLayer hook error: ${error}`,
106
+ },
107
+ };
108
+ }
109
+ /**
110
+ * Convert legacy HookOutput to HandlerResult
111
+ */
112
+ function legacyToResult(output) {
113
+ // Handle new format (has hookSpecificOutput)
114
+ if (output.hookSpecificOutput) {
115
+ return {
116
+ additionalContext: output.hookSpecificOutput.additionalContext,
117
+ block: output.decision === "block",
118
+ reason: output.reason,
119
+ };
120
+ }
121
+ // Handle legacy format (has additionalContext at top level)
122
+ return {
123
+ additionalContext: output.additionalContext,
124
+ block: output.block,
125
+ reason: output.blockReason,
126
+ error: output.error,
127
+ };
128
+ }
129
+ /**
130
+ * Dispatch to appropriate handler
131
+ */
132
+ async function dispatch(hookType, input) {
133
+ switch (hookType) {
134
+ case HookEvent.SessionStart: {
135
+ // Reset recall status at session start
136
+ resetRecallStatus();
137
+ // Write workspace ID and session ID to CLAUDE_ENV_FILE for subsequent bash commands
138
+ const envFile = process.env.CLAUDE_ENV_FILE;
139
+ if (envFile) {
140
+ try {
141
+ const client = getClient();
142
+ const workspaceId = client.getWorkspaceId();
143
+ appendFileSync(envFile, `export MEMORYLAYER_WORKSPACE_ID="${workspaceId}"\n`);
144
+ // Start a server-side session and export its ID
145
+ const sessionResult = await client.startSession({ ttl_seconds: 3600 });
146
+ appendFileSync(envFile, `export MEMORYLAYER_SESSION_ID="${sessionResult.session_id}"\n`);
147
+ // Also save to hook state so the Stop handler can access it
148
+ updateSessionInfo(workspaceId, sessionResult.session_id);
149
+ }
150
+ catch {
151
+ // Ignore errors - memory features will still work via MCP tools
152
+ }
153
+ }
154
+ return legacyToResult(await handleSessionStart(input));
155
+ }
156
+ case HookEvent.UserPromptSubmit:
157
+ // Reset recall status for new user turn
158
+ resetRecallStatus();
159
+ return legacyToResult(await handleUserPromptSubmit(input));
160
+ case HookEvent.PreToolUse:
161
+ return legacyToResult(await handlePreToolUse(input));
162
+ case HookEvent.PostToolUse:
163
+ return legacyToResult(await handlePostToolUse(input));
164
+ case HookEvent.PreCompact:
165
+ // PreCompact: Can't inject instructions at this point, just continue
166
+ return {};
167
+ case HookEvent.Stop:
168
+ // Stop: commit working memory and end session (side effects only, no context injection)
169
+ return legacyToResult(await handleStop());
170
+ default:
171
+ return {};
172
+ }
173
+ }
174
+ /**
175
+ * Main entry point
176
+ */
177
+ async function main() {
178
+ const hookTypeArg = process.argv[2];
179
+ const hookType = parseHookType(hookTypeArg);
180
+ if (!hookType) {
181
+ const output = {
182
+ error: `Invalid or missing hook type. Usage: memorylayer-hook <hook-type>\nValid types: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PreCompact, Stop`,
183
+ };
184
+ console.log(JSON.stringify(output));
185
+ process.exit(1);
186
+ }
187
+ try {
188
+ // Read input from stdin
189
+ const stdinData = await readStdin();
190
+ let input;
191
+ try {
192
+ input = stdinData.trim() ? JSON.parse(stdinData) : { hook_type: hookType };
193
+ }
194
+ catch {
195
+ input = { hook_type: hookType };
196
+ }
197
+ // Add hook type to input
198
+ input.hook_type = hookType;
199
+ // Dispatch to handler
200
+ const result = await dispatch(hookType, input);
201
+ // Handle errors from handlers
202
+ if (result.error) {
203
+ const output = buildErrorOutput(hookType, result.error);
204
+ console.log(JSON.stringify(output));
205
+ return;
206
+ }
207
+ // Build properly formatted output
208
+ const output = buildOutput(hookType, result.additionalContext, result.block, result.reason);
209
+ // Write output to stdout
210
+ console.log(JSON.stringify(output));
211
+ }
212
+ catch (error) {
213
+ const output = buildErrorOutput(hookType, error instanceof Error ? error.message : String(error));
214
+ console.log(JSON.stringify(output));
215
+ process.exit(1);
216
+ }
217
+ }
218
+ // Run
219
+ main().catch((error) => {
220
+ console.error("Fatal error:", error);
221
+ process.exit(1);
222
+ });
223
+ //# sourceMappingURL=memorylayer-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memorylayer-hook.js","sourceRoot":"","sources":["../../bin/memorylayer-hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for MemoryLayer MCP server
4
+ *
5
+ * Usage:
6
+ * memorylayer-mcp
7
+ *
8
+ * Environment variables:
9
+ * MEMORYLAYER_URL - Base URL for MemoryLayer API (default: http://localhost:61001)
10
+ * MEMORYLAYER_API_KEY - API key for authentication (optional)
11
+ * MEMORYLAYER_WORKSPACE_ID - Workspace ID (default: auto-detected from directory)
12
+ * MEMORYLAYER_AUTO_WORKSPACE - Set to "false" to disable auto-detection
13
+ * MEMORYLAYER_SESSION_MODE - Set to "false" to disable session/working memory (default: true)
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=memorylayer-mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memorylayer-mcp.d.ts","sourceRoot":"","sources":["../../bin/memorylayer-mcp.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for MemoryLayer MCP server
4
+ *
5
+ * Usage:
6
+ * memorylayer-mcp
7
+ *
8
+ * Environment variables:
9
+ * MEMORYLAYER_URL - Base URL for MemoryLayer API (default: http://localhost:61001)
10
+ * MEMORYLAYER_API_KEY - API key for authentication (optional)
11
+ * MEMORYLAYER_WORKSPACE_ID - Workspace ID (default: auto-detected from directory)
12
+ * MEMORYLAYER_AUTO_WORKSPACE - Set to "false" to disable auto-detection
13
+ * MEMORYLAYER_SESSION_MODE - Set to "false" to disable session/working memory (default: true)
14
+ */
15
+ import { MemoryLayerClient } from "../src/client.js";
16
+ import { createServer } from "../src/server.js";
17
+ import { detectWorkspaceId } from "../src/workspace.js";
18
+ async function main() {
19
+ // Parse environment variables
20
+ const baseUrl = process.env.MEMORYLAYER_URL || "http://localhost:61001";
21
+ const apiKey = process.env.MEMORYLAYER_API_KEY;
22
+ const autoWorkspace = process.env.MEMORYLAYER_AUTO_WORKSPACE !== "false";
23
+ const sessionMode = process.env.MEMORYLAYER_SESSION_MODE !== "false";
24
+ // Log working directory for debugging workspace detection issues
25
+ console.error(`MCP server starting in directory: ${process.cwd()}`);
26
+ // Determine workspace ID
27
+ let workspaceId;
28
+ if (process.env.MEMORYLAYER_WORKSPACE_ID) {
29
+ workspaceId = process.env.MEMORYLAYER_WORKSPACE_ID;
30
+ console.error(`Using explicit workspace from env: ${workspaceId}`);
31
+ }
32
+ else if (autoWorkspace) {
33
+ workspaceId = detectWorkspaceId();
34
+ console.error(`Auto-detected workspace: ${workspaceId}`);
35
+ }
36
+ else {
37
+ workspaceId = "default";
38
+ console.error(`Using default workspace: ${workspaceId}`);
39
+ }
40
+ // Create client
41
+ const client = new MemoryLayerClient({
42
+ baseUrl,
43
+ apiKey,
44
+ workspaceId
45
+ });
46
+ // Create and run server
47
+ const server = await createServer(client, { workspaceId, sessionMode });
48
+ console.error("MemoryLayer MCP Server Manifest:", JSON.stringify(server.getManifest(), null, 2));
49
+ await server.run();
50
+ }
51
+ // Handle errors and signals
52
+ process.on("SIGINT", () => {
53
+ console.error("Received SIGINT, shutting down gracefully");
54
+ process.exit(0);
55
+ });
56
+ process.on("SIGTERM", () => {
57
+ console.error("Received SIGTERM, shutting down gracefully");
58
+ process.exit(0);
59
+ });
60
+ process.on("uncaughtException", (error) => {
61
+ console.error("Uncaught exception:", error);
62
+ process.exit(1);
63
+ });
64
+ process.on("unhandledRejection", (reason) => {
65
+ console.error("Unhandled rejection:", reason);
66
+ process.exit(1);
67
+ });
68
+ main().catch((error) => {
69
+ console.error("Fatal error:", error);
70
+ process.exit(1);
71
+ });
72
+ //# sourceMappingURL=memorylayer-mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memorylayer-mcp.js","sourceRoot":"","sources":["../../bin/memorylayer-mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Example usage of the MemoryLayer client
3
+ */
4
+
5
+ // @ts-ignore
6
+ import { MemoryLayerClient, MemoryType, RelationshipType } from "@scitrera/memorylayer-mcp-server";
7
+
8
+ async function main() {
9
+ // Create client instance
10
+ const client = new MemoryLayerClient({
11
+ baseUrl: "http://localhost:61001",
12
+ workspaceId: "example-workspace"
13
+ });
14
+
15
+ // 1. Store some memories
16
+ console.log("1. Storing memories...");
17
+
18
+ const problemMemory = await client.remember({
19
+ content: "User encountered authentication timeout errors when API response takes >30s",
20
+ type: MemoryType.EPISODIC,
21
+ importance: 0.7,
22
+ tags: ["authentication", "timeout", "problem"]
23
+ });
24
+ console.log(`Stored problem memory: ${problemMemory.id}`);
25
+
26
+ const solutionMemory = await client.remember({
27
+ content: "Increased authentication timeout to 60s and added retry logic with exponential backoff",
28
+ type: MemoryType.PROCEDURAL,
29
+ importance: 0.9,
30
+ tags: ["authentication", "timeout", "solution"]
31
+ });
32
+ console.log(`Stored solution memory: ${solutionMemory.id}`);
33
+
34
+ // 2. Create an association between problem and solution
35
+ console.log("\n2. Creating association...");
36
+
37
+ const association = await client.associate({
38
+ source_id: solutionMemory.id,
39
+ target_id: problemMemory.id,
40
+ relationship: RelationshipType.SOLVES,
41
+ strength: 0.95
42
+ });
43
+ console.log(`Created association: ${association.id}`);
44
+
45
+ // 3. Recall memories about authentication
46
+ console.log("\n3. Recalling memories about authentication...");
47
+
48
+ const recallResult = await client.recall({
49
+ query: "authentication timeout issues",
50
+ limit: 5,
51
+ min_relevance: 0.5
52
+ });
53
+
54
+ console.log(`Found ${recallResult.memories.length} memories:`);
55
+ for (const memory of recallResult.memories) {
56
+ console.log(` - [${memory.type}] ${memory.content.substring(0, 80)}...`);
57
+ }
58
+
59
+ // 4. Reflect on authentication patterns
60
+ console.log("\n4. Reflecting on authentication patterns...");
61
+
62
+ const reflectResult = await client.reflect({
63
+ query: "What have we learned about handling authentication timeouts?",
64
+ max_tokens: 300,
65
+ include_sources: true
66
+ });
67
+
68
+ console.log("Reflection:");
69
+ console.log(reflectResult.reflection);
70
+ console.log(`\nBased on ${reflectResult.source_memories.length} source memories`);
71
+
72
+ // 5. Get workspace statistics
73
+ console.log("\n5. Getting workspace statistics...");
74
+
75
+ const stats = await client.getStatistics(true);
76
+ console.log(`Total memories: ${stats.total_memories}`);
77
+ console.log(`Total associations: ${stats.total_associations}`);
78
+
79
+ if (stats.breakdown) {
80
+ console.log("\nMemories by type:");
81
+ for (const [type, count] of Object.entries(stats.breakdown.by_type)) {
82
+ console.log(` ${type}: ${count}`);
83
+ }
84
+ }
85
+
86
+ // 6. Get a briefing
87
+ console.log("\n6. Getting session briefing...");
88
+
89
+ const briefing = await client.getBriefing(24, true);
90
+ console.log(briefing.briefing);
91
+ }
92
+
93
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@scitrera/memorylayer-mcp-server",
3
+ "version": "0.0.3",
4
+ "description": "MCP (Model Context Protocol) server for MemoryLayer.ai",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "memorylayer-mcp": "dist/bin/memorylayer-mcp.js",
10
+ "memorylayer-hook": "dist/bin/memorylayer-hook.js"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "start": "node dist/bin/memorylayer-mcp.js",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "test:coverage": "vitest run --coverage",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "memory",
25
+ "ai",
26
+ "llm",
27
+ "agent"
28
+ ],
29
+ "author": "Scitrera LLC <> (https://scitrera.ai)",
30
+ "license": "Apache-2.0",
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.0.0",
33
+ "@scitrera/memorylayer-sdk": "^0.0.3"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.0.0",
37
+ "typescript": "^5.3.0",
38
+ "vitest": "^1.0.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/scitrera/memorylayer"
46
+ }
47
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Tests for MCP Server
3
+ */
4
+
5
+ import { describe, it, expect, vi, beforeEach } from "vitest";
6
+ import { MCPServer, createServer } from "../src/server.js";
7
+ import { MemoryLayerClient } from "../src/client.js";
8
+ import { TOOLS, SESSION_TOOLS, CONTEXT_ENVIRONMENT_TOOLS } from "../src/tools.js";
9
+
10
+ // Mock the SDK client
11
+ vi.mock("../src/client.js", () => {
12
+ return {
13
+ MemoryLayerClient: vi.fn().mockImplementation(() => ({
14
+ getWorkspaceId: vi.fn().mockReturnValue("test-workspace"),
15
+ remember: vi.fn().mockResolvedValue({ id: "mem-123", type: "semantic", importance: 0.5, tags: [] }),
16
+ recall: vi.fn().mockResolvedValue({ memories: [], total_count: 0, search_latency_ms: 10, mode_used: "semantic" }),
17
+ startSession: vi.fn().mockResolvedValue({ session_id: "server-session-123" }),
18
+ endSession: vi.fn().mockResolvedValue({ memories_extracted: 2 }),
19
+ })),
20
+ };
21
+ });
22
+
23
+ describe("MCPServer", () => {
24
+ let client: MemoryLayerClient;
25
+
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ client = new MemoryLayerClient({ baseUrl: "http://localhost:61001" });
29
+ });
30
+
31
+ describe("constructor", () => {
32
+ it("should create server with default options", () => {
33
+ const server = new MCPServer(client);
34
+ expect(server).toBeDefined();
35
+ });
36
+
37
+ it("should enable session mode by default", () => {
38
+ const server = new MCPServer(client);
39
+ const manifest = server.getManifest();
40
+ expect(manifest.name).toBe("memorylayer");
41
+ });
42
+
43
+ it("should accept custom options", () => {
44
+ const server = new MCPServer(client, {
45
+ workspaceId: "custom-workspace",
46
+ sessionMode: false,
47
+ });
48
+ expect(server).toBeDefined();
49
+ });
50
+ });
51
+
52
+ describe("getManifest", () => {
53
+ it("should return server manifest", () => {
54
+ const server = new MCPServer(client);
55
+ const manifest = server.getManifest();
56
+
57
+ expect(manifest.name).toBe("memorylayer");
58
+ expect(manifest.version).toBe("0.1.0");
59
+ expect(manifest.description).toContain("MemoryLayer.ai");
60
+ expect(manifest.capabilities).toBeDefined();
61
+ });
62
+
63
+ it("should list core tools in capabilities", () => {
64
+ const server = new MCPServer(client);
65
+ const manifest = server.getManifest();
66
+ const toolNames = manifest.capabilities?.tools as string[];
67
+
68
+ expect(toolNames).toContain("memory_remember");
69
+ expect(toolNames).toContain("memory_recall");
70
+ });
71
+ });
72
+
73
+ describe("tool listing", () => {
74
+ it("should include session tools when session mode enabled", () => {
75
+ const server = new MCPServer(client, { sessionMode: true });
76
+ const manifest = server.getManifest();
77
+
78
+ // The manifest only shows TOOLS, but the handler includes SESSION_TOOLS
79
+ // This tests that core tools are present
80
+ const toolNames = manifest.capabilities?.tools as string[];
81
+ expect(toolNames.length).toBeGreaterThan(0);
82
+ });
83
+ });
84
+ });
85
+
86
+ describe("createServer", () => {
87
+ let client: MemoryLayerClient;
88
+
89
+ beforeEach(() => {
90
+ vi.clearAllMocks();
91
+ client = new MemoryLayerClient({ baseUrl: "http://localhost:61001" });
92
+ });
93
+
94
+ it("should create server instance", async () => {
95
+ const server = await createServer(client);
96
+ expect(server).toBeInstanceOf(MCPServer);
97
+ });
98
+
99
+ it("should pass options to server", async () => {
100
+ const server = await createServer(client, {
101
+ workspaceId: "my-workspace",
102
+ sessionMode: false,
103
+ });
104
+ expect(server).toBeInstanceOf(MCPServer);
105
+ });
106
+ });
107
+
108
+ describe("Tool counts", () => {
109
+ it("should have expected number of core tools", () => {
110
+ // memory_remember, memory_recall, memory_reflect, memory_forget,
111
+ // memory_associate, memory_briefing, memory_statistics, memory_graph_query, memory_audit
112
+ expect(TOOLS.length).toBe(9);
113
+ });
114
+
115
+ it("should have expected number of session tools", () => {
116
+ // memory_session_start, memory_session_end, memory_session_commit, memory_session_status
117
+ expect(SESSION_TOOLS.length).toBe(4);
118
+ });
119
+
120
+ it("should have expected number of context environment tools", () => {
121
+ // memory_context_exec, memory_context_inspect, memory_context_load,
122
+ // memory_context_inject, memory_context_query, memory_context_rlm,
123
+ // memory_context_status, memory_context_checkpoint
124
+ expect(CONTEXT_ENVIRONMENT_TOOLS.length).toBe(8);
125
+ });
126
+
127
+ it("should have 21 total tools when combined", () => {
128
+ expect(TOOLS.length + SESSION_TOOLS.length + CONTEXT_ENVIRONMENT_TOOLS.length).toBe(21);
129
+ });
130
+ });
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Tests for SessionManager
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from "vitest";
6
+ import { SessionManager } from "../src/session.js";
7
+
8
+ describe("SessionManager", () => {
9
+ let manager: SessionManager;
10
+
11
+ beforeEach(() => {
12
+ manager = new SessionManager();
13
+ });
14
+
15
+ describe("constructor", () => {
16
+ it("should be enabled by default", () => {
17
+ expect(manager.isEnabled).toBe(true);
18
+ });
19
+
20
+ it("should respect enabled config", () => {
21
+ const disabled = new SessionManager({ enabled: false });
22
+ expect(disabled.isEnabled).toBe(false);
23
+ });
24
+
25
+ it("should have no active session initially", () => {
26
+ expect(manager.hasActiveSession).toBe(false);
27
+ expect(manager.currentSession).toBeNull();
28
+ });
29
+ });
30
+
31
+ describe("startSession", () => {
32
+ it("should create a new session", () => {
33
+ const session = manager.startSession("test-workspace");
34
+
35
+ expect(session).toBeDefined();
36
+ expect(session.id).toMatch(/^local_\d+_[a-z0-9]+$/);
37
+ expect(session.workspaceId).toBe("test-workspace");
38
+ expect(session.committed).toBe(false);
39
+ expect(session.workingMemory.size).toBe(0);
40
+ });
41
+
42
+ it("should set hasActiveSession to true", () => {
43
+ manager.startSession("test-workspace");
44
+ expect(manager.hasActiveSession).toBe(true);
45
+ });
46
+
47
+ it("should store server session ID if provided", () => {
48
+ const session = manager.startSession("test-workspace", "server-123");
49
+ expect(session.serverSessionId).toBe("server-123");
50
+ });
51
+
52
+ it("should replace existing session", () => {
53
+ const first = manager.startSession("workspace-1");
54
+ const second = manager.startSession("workspace-2");
55
+
56
+ expect(manager.currentSession?.id).toBe(second.id);
57
+ expect(manager.currentSession?.workspaceId).toBe("workspace-2");
58
+ });
59
+ });
60
+
61
+ describe("endSession", () => {
62
+ it("should return null if no active session", () => {
63
+ const result = manager.endSession();
64
+ expect(result).toBeNull();
65
+ });
66
+
67
+ it("should return the ended session", () => {
68
+ const started = manager.startSession("test-workspace");
69
+ const ended = manager.endSession();
70
+
71
+ expect(ended?.id).toBe(started.id);
72
+ });
73
+
74
+ it("should clear the active session", () => {
75
+ manager.startSession("test-workspace");
76
+ manager.endSession();
77
+
78
+ expect(manager.hasActiveSession).toBe(false);
79
+ expect(manager.currentSession).toBeNull();
80
+ });
81
+ });
82
+
83
+ describe("markCommitted", () => {
84
+ it("should mark session as committed", () => {
85
+ manager.startSession("test-workspace");
86
+ manager.markCommitted();
87
+
88
+ expect(manager.currentSession?.committed).toBe(true);
89
+ });
90
+
91
+ it("should do nothing if no active session", () => {
92
+ // Should not throw
93
+ manager.markCommitted();
94
+ });
95
+ });
96
+
97
+ describe("working memory", () => {
98
+ beforeEach(() => {
99
+ manager.startSession("test-workspace");
100
+ });
101
+
102
+ describe("getAllWorkingMemory", () => {
103
+ it("should return empty array for new session", () => {
104
+ const entries = manager.getAllWorkingMemory();
105
+ expect(entries).toEqual([]);
106
+ });
107
+
108
+ it("should return empty array if no session", () => {
109
+ manager.endSession();
110
+ const entries = manager.getAllWorkingMemory();
111
+ expect(entries).toEqual([]);
112
+ });
113
+ });
114
+
115
+ describe("clearWorkingMemory", () => {
116
+ it("should not throw on empty working memory", () => {
117
+ expect(() => manager.clearWorkingMemory()).not.toThrow();
118
+ expect(manager.getAllWorkingMemory()).toHaveLength(0);
119
+ });
120
+ });
121
+ });
122
+
123
+ describe("getSessionSummary", () => {
124
+ it("should return inactive status when no session", () => {
125
+ const summary = manager.getSessionSummary();
126
+ expect(summary.active).toBe(false);
127
+ });
128
+
129
+ it("should return session info when active", () => {
130
+ manager.startSession("test-workspace", "server-123");
131
+
132
+ const summary = manager.getSessionSummary();
133
+
134
+ expect(summary.active).toBe(true);
135
+ expect(summary.workspaceId).toBe("test-workspace");
136
+ expect(summary.serverSessionId).toBe("server-123");
137
+ expect(summary.workingMemoryCount).toBe(0);
138
+ expect(summary.committed).toBe(false);
139
+ });
140
+ });
141
+
142
+ describe("getTtlSeconds", () => {
143
+ it("should return default TTL", () => {
144
+ expect(manager.getTtlSeconds()).toBe(3600);
145
+ });
146
+
147
+ it("should return custom TTL", () => {
148
+ const custom = new SessionManager({ ttlSeconds: 7200 });
149
+ expect(custom.getTtlSeconds()).toBe(7200);
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Tests for MCP tool definitions
3
+ */
4
+
5
+ import { describe, it, expect } from "vitest";
6
+ import { TOOLS, SESSION_TOOLS, CONTEXT_ENVIRONMENT_TOOLS, CORE_TOOLS, EXTENDED_TOOLS } from "../src/tools.js";
7
+
8
+ describe("Tool Definitions", () => {
9
+ describe("TOOLS", () => {
10
+ it("should export core memory tools", () => {
11
+ const toolNames = TOOLS.map(t => t.name);
12
+
13
+ expect(toolNames).toContain("memory_remember");
14
+ expect(toolNames).toContain("memory_recall");
15
+ expect(toolNames).toContain("memory_reflect");
16
+ expect(toolNames).toContain("memory_forget");
17
+ expect(toolNames).toContain("memory_associate");
18
+ expect(toolNames).toContain("memory_briefing");
19
+ expect(toolNames).toContain("memory_statistics");
20
+ expect(toolNames).toContain("memory_graph_query");
21
+ expect(toolNames).toContain("memory_audit");
22
+ });
23
+
24
+ it("should have valid input schemas", () => {
25
+ for (const tool of TOOLS) {
26
+ expect(tool.inputSchema).toBeDefined();
27
+ expect(tool.inputSchema.type).toBe("object");
28
+ expect(tool.inputSchema.properties).toBeDefined();
29
+ }
30
+ });
31
+
32
+ it("should have descriptions for all tools", () => {
33
+ for (const tool of TOOLS) {
34
+ expect(tool.description).toBeDefined();
35
+ expect(tool.description.length).toBeGreaterThan(10);
36
+ }
37
+ });
38
+ });
39
+
40
+ describe("SESSION_TOOLS", () => {
41
+ it("should export session management tools", () => {
42
+ const toolNames = SESSION_TOOLS.map(t => t.name);
43
+
44
+ expect(toolNames).toContain("memory_session_start");
45
+ expect(toolNames).toContain("memory_session_end");
46
+ expect(toolNames).toContain("memory_session_commit");
47
+ expect(toolNames).toContain("memory_session_status");
48
+ });
49
+
50
+ it("should have valid input schemas", () => {
51
+ for (const tool of SESSION_TOOLS) {
52
+ expect(tool.inputSchema).toBeDefined();
53
+ expect(tool.inputSchema.type).toBe("object");
54
+ }
55
+ });
56
+
57
+ it("should have descriptions for all tools", () => {
58
+ for (const tool of SESSION_TOOLS) {
59
+ expect(tool.description).toBeDefined();
60
+ expect(tool.description.length).toBeGreaterThan(10);
61
+ }
62
+ });
63
+ });
64
+
65
+ describe("CONTEXT_ENVIRONMENT_TOOLS", () => {
66
+ it("should export context environment tools", () => {
67
+ const toolNames = CONTEXT_ENVIRONMENT_TOOLS.map(t => t.name);
68
+
69
+ expect(toolNames).toContain("memory_context_exec");
70
+ expect(toolNames).toContain("memory_context_inspect");
71
+ expect(toolNames).toContain("memory_context_load");
72
+ expect(toolNames).toContain("memory_context_inject");
73
+ expect(toolNames).toContain("memory_context_query");
74
+ expect(toolNames).toContain("memory_context_rlm");
75
+ expect(toolNames).toContain("memory_context_status");
76
+ expect(toolNames).toContain("memory_context_checkpoint");
77
+ });
78
+
79
+ it("should have valid input schemas", () => {
80
+ for (const tool of CONTEXT_ENVIRONMENT_TOOLS) {
81
+ expect(tool.inputSchema).toBeDefined();
82
+ expect(tool.inputSchema.type).toBe("object");
83
+ }
84
+ });
85
+
86
+ it("should have descriptions for all tools", () => {
87
+ for (const tool of CONTEXT_ENVIRONMENT_TOOLS) {
88
+ expect(tool.description).toBeDefined();
89
+ expect(tool.description.length).toBeGreaterThan(10);
90
+ }
91
+ });
92
+ });
93
+
94
+ describe("memory_remember tool", () => {
95
+ const tool = TOOLS.find(t => t.name === "memory_remember");
96
+
97
+ it("should require content", () => {
98
+ expect(tool?.inputSchema.required).toContain("content");
99
+ });
100
+
101
+ it("should have memory type enum", () => {
102
+ const typeProperty = tool?.inputSchema.properties?.type;
103
+ expect(typeProperty?.enum).toContain("episodic");
104
+ expect(typeProperty?.enum).toContain("semantic");
105
+ expect(typeProperty?.enum).toContain("procedural");
106
+ expect(typeProperty?.enum).toContain("working");
107
+ });
108
+
109
+ it("should have importance range", () => {
110
+ const importance = tool?.inputSchema.properties?.importance;
111
+ expect(importance?.minimum).toBe(0);
112
+ expect(importance?.maximum).toBe(1);
113
+ });
114
+ });
115
+
116
+ describe("memory_recall tool", () => {
117
+ const tool = TOOLS.find(t => t.name === "memory_recall");
118
+
119
+ it("should require query", () => {
120
+ expect(tool?.inputSchema.required).toContain("query");
121
+ });
122
+
123
+ it("should have limit constraints", () => {
124
+ const limit = tool?.inputSchema.properties?.limit;
125
+ expect(limit?.minimum).toBe(1);
126
+ expect(limit?.maximum).toBe(100);
127
+ });
128
+ });
129
+
130
+ describe("memory_associate tool", () => {
131
+ const tool = TOOLS.find(t => t.name === "memory_associate");
132
+
133
+ it("should require source_id, target_id, and relationship", () => {
134
+ expect(tool?.inputSchema.required).toContain("source_id");
135
+ expect(tool?.inputSchema.required).toContain("target_id");
136
+ expect(tool?.inputSchema.required).toContain("relationship");
137
+ });
138
+
139
+ it("should have relationship description", () => {
140
+ const relationship = tool?.inputSchema.properties?.relationship;
141
+ expect(relationship?.type).toBe("string");
142
+ expect(relationship?.description).toBeDefined();
143
+ });
144
+ });
145
+
146
+ describe("memory_context_exec tool", () => {
147
+ const tool = CONTEXT_ENVIRONMENT_TOOLS.find(t => t.name === "memory_context_exec");
148
+
149
+ it("should require code", () => {
150
+ expect(tool?.inputSchema.required).toContain("code");
151
+ });
152
+
153
+ it("should have return_result option", () => {
154
+ const returnResult = tool?.inputSchema.properties?.return_result;
155
+ expect(returnResult?.type).toBe("boolean");
156
+ });
157
+ });
158
+
159
+ describe("memory_session_end tool", () => {
160
+ const tool = SESSION_TOOLS.find(t => t.name === "memory_session_end");
161
+
162
+ it("should have commit option", () => {
163
+ expect(tool?.inputSchema.properties?.commit).toBeDefined();
164
+ expect(tool?.inputSchema.properties?.commit?.type).toBe("boolean");
165
+ });
166
+
167
+ it("should have importance_threshold option", () => {
168
+ const threshold = tool?.inputSchema.properties?.importance_threshold;
169
+ expect(threshold?.minimum).toBe(0);
170
+ expect(threshold?.maximum).toBe(1);
171
+ });
172
+ });
173
+
174
+ describe("Legacy exports", () => {
175
+ it("should export CORE_TOOLS as filtered subset of TOOLS", () => {
176
+ const coreNames = CORE_TOOLS.map(t => t.name);
177
+ expect(coreNames).toEqual([
178
+ "memory_remember", "memory_recall", "memory_reflect",
179
+ "memory_forget", "memory_associate"
180
+ ]);
181
+ });
182
+
183
+ it("should export EXTENDED_TOOLS as filtered subset of TOOLS", () => {
184
+ const extNames = EXTENDED_TOOLS.map(t => t.name);
185
+ expect(extNames).toEqual([
186
+ "memory_briefing", "memory_statistics",
187
+ "memory_graph_query", "memory_audit"
188
+ ]);
189
+ });
190
+ });
191
+ });
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: "node",
7
+ include: ["tests/**/*.test.ts"],
8
+ coverage: {
9
+ provider: "v8",
10
+ reporter: ["text", "json", "html"],
11
+ include: ["src/**/*.ts"],
12
+ exclude: ["src/index.ts", "src/types.ts"],
13
+ },
14
+ },
15
+ });