@mandays/obsidian-memory-mcp 0.1.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.
@@ -0,0 +1,538 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+
3
+ interface Fact {
4
+ type: "fact";
5
+ id?: string;
6
+ entity: string;
7
+ predicate: string;
8
+ value: string;
9
+ valid_from?: string | null;
10
+ valid_to?: string | null;
11
+ recorded_at: string;
12
+ confidence?: "high" | "medium" | "low";
13
+ sources?: string[];
14
+ last_reviewed?: string;
15
+ superseded_by?: string;
16
+ tags?: string[];
17
+ decay?: {
18
+ review_after_days?: number;
19
+ archive_after_valid_to?: boolean;
20
+ pin?: boolean;
21
+ };
22
+ }
23
+ interface ParsedFact {
24
+ data: Fact;
25
+ content: string;
26
+ path: string;
27
+ }
28
+
29
+ interface Event {
30
+ type: "event";
31
+ id?: string;
32
+ occurred_at: string;
33
+ summary: string;
34
+ entities?: string[];
35
+ kind?: "conversation" | "decision" | "ingest" | "action" | "observation";
36
+ sources?: string[];
37
+ derived_facts?: string[];
38
+ }
39
+ interface ParsedEvent {
40
+ data: Event;
41
+ content: string;
42
+ path: string;
43
+ }
44
+
45
+ type OperationType = "create_fact" | "update_fact" | "add_event" | "archive_fact" | "review_fact" | "rename_entity" | "add_entity" | "add_predicate";
46
+ type OperationStatus = "proposed" | "validated" | "applied" | "rejected" | "conflict" | "superseded";
47
+ interface Operation {
48
+ type: "operation";
49
+ operation_id: string;
50
+ op: OperationType;
51
+ agent_id: string;
52
+ created_at: string;
53
+ target_id?: string;
54
+ target_path?: string;
55
+ precondition_hash?: string | null;
56
+ status: OperationStatus;
57
+ reason: string;
58
+ sources?: string[];
59
+ conflict_reason?: string;
60
+ applied_at?: string;
61
+ payload: Record<string, unknown>;
62
+ }
63
+ interface ParsedOperation {
64
+ data: Operation;
65
+ content: string;
66
+ path: string;
67
+ }
68
+
69
+ interface Claim {
70
+ type: "claim";
71
+ target_id: string;
72
+ operation_id: string;
73
+ agent_id: string;
74
+ created_at: string;
75
+ expires_at: string;
76
+ heartbeat_at: string;
77
+ status: "active" | "released" | "broken";
78
+ }
79
+ interface ParsedClaim {
80
+ data: Claim;
81
+ path: string;
82
+ }
83
+
84
+ type EntityKind = "person" | "project" | "org" | "place" | "concept" | "tool";
85
+ interface Entity {
86
+ id: string;
87
+ kind: EntityKind;
88
+ display: string;
89
+ aliases?: string[];
90
+ }
91
+ interface EntityIndex {
92
+ type: "entity-index";
93
+ entities: Entity[];
94
+ }
95
+
96
+ interface Config {
97
+ vaultPath: string;
98
+ transport: "stdio" | "sse";
99
+ agentId: string;
100
+ writeMode: "direct" | "inbox";
101
+ port: number;
102
+ apiKey?: string;
103
+ staleDays: number;
104
+ }
105
+ declare const DEFAULT_CONFIG: Omit<Config, "vaultPath" | "agentId">;
106
+
107
+ interface VaultPaths {
108
+ root: string;
109
+ memory: string;
110
+ facts: string;
111
+ events: string;
112
+ schema: string;
113
+ views: string;
114
+ inbox: string;
115
+ claims: string;
116
+ ops: string;
117
+ opsApplied: string;
118
+ archive: string;
119
+ entities: string;
120
+ predicates: string;
121
+ versionYaml: string;
122
+ sources: string;
123
+ people: string;
124
+ projects: string;
125
+ decisions: string;
126
+ insights: string;
127
+ context: string;
128
+ }
129
+ declare class Vault {
130
+ readonly paths: VaultPaths;
131
+ readonly config: Config;
132
+ constructor(config: Config);
133
+ /**
134
+ * Assert that the vault path exists and contains a valid v3 version marker.
135
+ */
136
+ assertValid(): Promise<void>;
137
+ /**
138
+ * Get the relative path from vault root.
139
+ */
140
+ relativePath(absolutePath: string): string;
141
+ /**
142
+ * Resolve a relative path to absolute from vault root.
143
+ */
144
+ resolvePath(relativePath: string): string;
145
+ /**
146
+ * Get the fact file path for a given entity and predicate.
147
+ */
148
+ factPath(entity: string, predicate: string, suffix?: string): string;
149
+ /**
150
+ * Get the event directory path for a given date.
151
+ */
152
+ eventDir(date: string): string;
153
+ /**
154
+ * Get the inbox ops path for a given agent and operation.
155
+ */
156
+ inboxOpPath(agentId: string, operationId: string): string;
157
+ /**
158
+ * Get the applied operation receipt path.
159
+ */
160
+ appliedOpPath(operationId: string): string;
161
+ /**
162
+ * Get the claim path for a target ID.
163
+ */
164
+ claimPath(targetId: string): string;
165
+ /**
166
+ * Get the archive path for a fact.
167
+ */
168
+ archivePath(year: string, entity: string, predicate: string): string;
169
+ /**
170
+ * Get the view path for a named view.
171
+ */
172
+ viewPath(viewName: string): string;
173
+ /**
174
+ * Get entity-specific view path.
175
+ */
176
+ entityViewPath(entity: string): string;
177
+ }
178
+ declare class VaultError extends Error {
179
+ readonly code: string;
180
+ constructor(message: string, code: string);
181
+ }
182
+
183
+ declare class EntityRegistry {
184
+ private cache;
185
+ private readonly vault;
186
+ constructor(vault: Vault);
187
+ /**
188
+ * Load all entities from entities.md. Uses cache if available.
189
+ */
190
+ getAll(): Promise<Entity[]>;
191
+ /**
192
+ * Get entities filtered by kind.
193
+ */
194
+ getByKind(kind: EntityKind): Promise<Entity[]>;
195
+ /**
196
+ * Find a single entity by ID.
197
+ */
198
+ findById(id: string): Promise<Entity | undefined>;
199
+ /**
200
+ * Check if an entity exists.
201
+ */
202
+ exists(id: string): Promise<boolean>;
203
+ /**
204
+ * Add a new entity. Returns the updated list.
205
+ */
206
+ add(entity: Entity): Promise<Entity[]>;
207
+ /**
208
+ * Invalidate the in-memory cache.
209
+ */
210
+ invalidate(): void;
211
+ private save;
212
+ }
213
+
214
+ declare class PredicateRegistry {
215
+ private cache;
216
+ private readonly vault;
217
+ constructor(vault: Vault);
218
+ /**
219
+ * Load all predicates from predicates.yaml.
220
+ * Supports both formats:
221
+ * - Flat array: ["role", "base", ...]
222
+ * - Object with entries: { predicates: [{id: "role", description: "..."}] }
223
+ */
224
+ getAll(): Promise<string[]>;
225
+ /**
226
+ * Check if a predicate exists.
227
+ */
228
+ exists(predicate: string): Promise<boolean>;
229
+ /**
230
+ * Add a new predicate. Returns the updated list.
231
+ */
232
+ add(predicate: string): Promise<string[]>;
233
+ /**
234
+ * Invalidate the in-memory cache.
235
+ */
236
+ invalidate(): void;
237
+ private save;
238
+ }
239
+
240
+ interface FactFilter {
241
+ entity?: string;
242
+ predicate?: string;
243
+ id?: string;
244
+ onDate?: string;
245
+ confidence?: string;
246
+ tags?: string[];
247
+ query?: string;
248
+ limit?: number;
249
+ }
250
+ interface EventFilter {
251
+ entity?: string;
252
+ since?: string;
253
+ until?: string;
254
+ kind?: string;
255
+ limit?: number;
256
+ }
257
+ declare class QueryEngine {
258
+ private readonly vault;
259
+ constructor(vault: Vault);
260
+ /**
261
+ * Find facts matching the given filters.
262
+ */
263
+ findFacts(filter: FactFilter): Promise<ParsedFact[]>;
264
+ /**
265
+ * Find a fact by its stable ID.
266
+ */
267
+ findFactById(id: string): Promise<ParsedFact | null>;
268
+ /**
269
+ * Find events matching the given filters.
270
+ */
271
+ findEvents(filter: EventFilter): Promise<ParsedEvent[]>;
272
+ /**
273
+ * Find any record (fact, event, operation) by its stable ID.
274
+ */
275
+ findById(id: string): Promise<{
276
+ type: string;
277
+ path: string;
278
+ data: Record<string, unknown>;
279
+ } | null>;
280
+ private matchesFactFilter;
281
+ private matchesEventFilter;
282
+ /**
283
+ * Check if a fact is temporally valid on a given date.
284
+ * valid_from <= date <= valid_to (with null meaning unbounded)
285
+ */
286
+ private isTemporallyValid;
287
+ private applyLimit;
288
+ }
289
+
290
+ interface CreateFactParams {
291
+ entity: string;
292
+ predicate: string;
293
+ value: string;
294
+ confidence?: "high" | "medium" | "low";
295
+ valid_from?: string | null;
296
+ valid_to?: string | null;
297
+ sources?: string[];
298
+ tags?: string[];
299
+ reason: string;
300
+ id?: string;
301
+ }
302
+ interface UpdateFactParams {
303
+ targetId?: string;
304
+ targetPath?: string;
305
+ value?: string;
306
+ valid_to?: string | null;
307
+ confidence?: "high" | "medium" | "low";
308
+ tags?: string[];
309
+ last_reviewed?: string;
310
+ reason: string;
311
+ }
312
+ interface AddEventParams {
313
+ summary: string;
314
+ entities?: string[];
315
+ kind?: "conversation" | "decision" | "ingest" | "action" | "observation";
316
+ sources?: string[];
317
+ body?: string;
318
+ reason?: string;
319
+ }
320
+ interface ArchiveFactParams {
321
+ targetId?: string;
322
+ targetPath?: string;
323
+ reason: string;
324
+ }
325
+ interface CompactResult {
326
+ applied: number;
327
+ conflicts: number;
328
+ archived: number;
329
+ errors: string[];
330
+ }
331
+ declare class OperationManager {
332
+ private readonly vault;
333
+ private readonly agentId;
334
+ constructor(vault: Vault, agentId: string);
335
+ /**
336
+ * Create a fact directly (single-agent mode).
337
+ */
338
+ createFactDirect(params: CreateFactParams): Promise<{
339
+ path: string;
340
+ id: string;
341
+ }>;
342
+ /**
343
+ * Create an operation envelope for creating a fact (inbox mode).
344
+ */
345
+ createFactEnvelope(params: CreateFactParams): Promise<{
346
+ path: string;
347
+ operationId: string;
348
+ }>;
349
+ /**
350
+ * Update a fact directly (single-agent mode).
351
+ */
352
+ updateFactDirect(params: UpdateFactParams): Promise<{
353
+ path: string;
354
+ hash: string;
355
+ }>;
356
+ /**
357
+ * Create an operation envelope for updating a fact (inbox mode).
358
+ */
359
+ updateFactEnvelope(params: UpdateFactParams): Promise<{
360
+ path: string;
361
+ operationId: string;
362
+ }>;
363
+ /**
364
+ * Add an event directly.
365
+ */
366
+ addEventDirect(params: AddEventParams): Promise<{
367
+ path: string;
368
+ id: string;
369
+ }>;
370
+ /**
371
+ * Create an operation envelope for adding an event (inbox mode).
372
+ */
373
+ addEventEnvelope(params: AddEventParams): Promise<{
374
+ path: string;
375
+ operationId: string;
376
+ }>;
377
+ /**
378
+ * Archive a fact directly.
379
+ */
380
+ archiveFactDirect(params: ArchiveFactParams): Promise<{
381
+ archivePath: string;
382
+ }>;
383
+ /**
384
+ * Compact inbox: validate and apply all proposed operations.
385
+ */
386
+ compact(autoApply?: boolean): Promise<CompactResult>;
387
+ private applyOperation;
388
+ private markConflict;
389
+ private moveToApplied;
390
+ private checkClaim;
391
+ private resolveFactPath;
392
+ }
393
+ declare class OperationError extends Error {
394
+ readonly code: string;
395
+ constructor(message: string, code: string);
396
+ }
397
+
398
+ interface LintFinding {
399
+ level: "ERROR" | "WARN";
400
+ path: string;
401
+ message: string;
402
+ }
403
+ declare class SchemaValidator {
404
+ private readonly vault;
405
+ private readonly entities;
406
+ private readonly predicates;
407
+ constructor(vault: Vault, entities: EntityRegistry, predicates: PredicateRegistry);
408
+ /**
409
+ * Run full vault validation. Returns findings array. Empty = valid.
410
+ */
411
+ lint(): Promise<LintFinding[]>;
412
+ private checkVersion;
413
+ private validateFacts;
414
+ private validateEvents;
415
+ private validateOperations;
416
+ private checkDuplicateIds;
417
+ private checkContradictions;
418
+ private temporalOverlap;
419
+ private validateWikilinks;
420
+ }
421
+
422
+ declare class ViewGenerator {
423
+ private readonly vault;
424
+ private readonly staleDays;
425
+ constructor(vault: Vault, staleDays?: number);
426
+ /**
427
+ * Rebuild all materialized views. Returns list of files written.
428
+ */
429
+ rebuildAll(): Promise<string[]>;
430
+ private writeView;
431
+ private loadAllFacts;
432
+ private loadAllEvents;
433
+ private loadAllOperations;
434
+ private loadAllClaims;
435
+ private groupByEntity;
436
+ private renderByEntity;
437
+ private renderById;
438
+ private renderByPredicate;
439
+ private renderTimeline;
440
+ private renderContradictions;
441
+ private renderStale;
442
+ private renderGraph;
443
+ private renderInbox;
444
+ private renderOperations;
445
+ private renderClaims;
446
+ private renderConflicts;
447
+ }
448
+
449
+ /**
450
+ * Create and configure the MCP server with all tools registered.
451
+ */
452
+ declare function createServer(config: Config): McpServer;
453
+
454
+ interface ConfigInput {
455
+ vault?: string;
456
+ transport?: string;
457
+ agentId?: string;
458
+ mode?: string;
459
+ port?: number;
460
+ apiKey?: string;
461
+ staleDays?: number;
462
+ }
463
+ /**
464
+ * Resolve configuration from CLI args, env vars, and defaults.
465
+ */
466
+ declare function resolveConfig(input: ConfigInput): Config;
467
+
468
+ interface ScaffoldOptions {
469
+ vaultPath: string;
470
+ entities?: Entity[];
471
+ predicates?: string[];
472
+ }
473
+ /**
474
+ * Scaffold a new v3 vault with all required directories and schema files.
475
+ */
476
+ declare function scaffoldVault(options: ScaffoldOptions): Promise<string[]>;
477
+
478
+ /**
479
+ * Generate an operation ID matching the pattern: op-{YYYYMMDDTHHMMSSz}-{4-byte-hex}
480
+ */
481
+ declare function generateOperationId(): string;
482
+ /**
483
+ * Generate an agent ID matching the pattern: agent-{slug}-{8-char-hex}
484
+ */
485
+ declare function generateAgentId(name?: string): string;
486
+ /**
487
+ * Generate a fact ID: fact-{entity}-{predicate}
488
+ */
489
+ declare function generateFactId(entity: string, predicate: string): string;
490
+ /**
491
+ * Generate an event ID: event-{YYYY-MM-DD}-{slug}
492
+ */
493
+ declare function generateEventId(date: string, slug: string): string;
494
+ /**
495
+ * Slugify a string for use in filenames and IDs.
496
+ */
497
+ declare function slugify(text: string): string;
498
+
499
+ /**
500
+ * Compute the SHA-256 hash of a file, returning "sha256:{64-hex}" format.
501
+ */
502
+ declare function computeFileHash(filePath: string): Promise<string>;
503
+ /**
504
+ * Compute the SHA-256 hash of a string, returning "sha256:{64-hex}" format.
505
+ */
506
+ declare function computeStringHash(content: string): string;
507
+ /**
508
+ * Validate that a hash string matches the expected pattern.
509
+ */
510
+ declare function isValidHash(hash: string): boolean;
511
+
512
+ interface ParsedMarkdown {
513
+ data: Record<string, unknown>;
514
+ content: string;
515
+ path: string;
516
+ }
517
+ /**
518
+ * Parse a markdown file's YAML frontmatter and body content.
519
+ */
520
+ declare function parseMarkdownFile(filePath: string): Promise<ParsedMarkdown>;
521
+ /**
522
+ * Serialize frontmatter data and optional body into a markdown string.
523
+ */
524
+ declare function serializeMarkdown(data: Record<string, unknown>, body?: string): string;
525
+ /**
526
+ * Write a markdown file atomically (write to .tmp then rename).
527
+ */
528
+ declare function writeMarkdownFile(filePath: string, data: Record<string, unknown>, body?: string): Promise<void>;
529
+ /**
530
+ * Read only the frontmatter of a markdown file without the full body.
531
+ */
532
+ declare function readFrontmatter(filePath: string): Promise<Record<string, unknown>>;
533
+ /**
534
+ * Check if a file exists.
535
+ */
536
+ declare function fileExists(filePath: string): Promise<boolean>;
537
+
538
+ export { type Claim, type Config, type ConfigInput, DEFAULT_CONFIG, type Entity, type EntityIndex, type EntityKind, EntityRegistry, type Event, type Fact, type LintFinding, type Operation, OperationError, OperationManager, type OperationStatus, type OperationType, type ParsedClaim, type ParsedEvent, type ParsedFact, type ParsedMarkdown, type ParsedOperation, PredicateRegistry, QueryEngine, SchemaValidator, Vault, VaultError, ViewGenerator, computeFileHash, computeStringHash, createServer, fileExists, generateAgentId, generateEventId, generateFactId, generateOperationId, isValidHash, parseMarkdownFile, readFrontmatter, resolveConfig, scaffoldVault, serializeMarkdown, slugify, writeMarkdownFile };
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ import {
2
+ EntityRegistry,
3
+ OperationError,
4
+ OperationManager,
5
+ PredicateRegistry,
6
+ QueryEngine,
7
+ SchemaValidator,
8
+ Vault,
9
+ VaultError,
10
+ ViewGenerator,
11
+ computeFileHash,
12
+ computeStringHash,
13
+ createServer,
14
+ fileExists,
15
+ generateAgentId,
16
+ generateEventId,
17
+ generateFactId,
18
+ generateOperationId,
19
+ isValidHash,
20
+ parseMarkdownFile,
21
+ readFrontmatter,
22
+ resolveConfig,
23
+ scaffoldVault,
24
+ serializeMarkdown,
25
+ slugify,
26
+ writeMarkdownFile
27
+ } from "./chunk-5U2LXK3W.js";
28
+ export {
29
+ EntityRegistry,
30
+ OperationError,
31
+ OperationManager,
32
+ PredicateRegistry,
33
+ QueryEngine,
34
+ SchemaValidator,
35
+ Vault,
36
+ VaultError,
37
+ ViewGenerator,
38
+ computeFileHash,
39
+ computeStringHash,
40
+ createServer,
41
+ fileExists,
42
+ generateAgentId,
43
+ generateEventId,
44
+ generateFactId,
45
+ generateOperationId,
46
+ isValidHash,
47
+ parseMarkdownFile,
48
+ readFrontmatter,
49
+ resolveConfig,
50
+ scaffoldVault,
51
+ serializeMarkdown,
52
+ slugify,
53
+ writeMarkdownFile
54
+ };
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@mandays/obsidian-memory-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Obsidian v3 Atomic Markdown Memory vaults",
5
+ "type": "module",
6
+ "bin": {
7
+ "obsidian-memory-mcp": "./dist/cli.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.js",
15
+ "types": "./dist/index.d.ts"
16
+ }
17
+ },
18
+ "engines": {
19
+ "node": ">=20"
20
+ },
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsx bin/cli.ts",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "lint": "eslint src/ bin/",
27
+ "typecheck": "tsc --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "keywords": [
31
+ "mcp",
32
+ "obsidian",
33
+ "memory",
34
+ "ai",
35
+ "model-context-protocol",
36
+ "markdown"
37
+ ],
38
+ "license": "MIT",
39
+ "files": [
40
+ "dist",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
44
+ "dependencies": {
45
+ "@modelcontextprotocol/sdk": "^1.12.0",
46
+ "commander": "^12.1.0",
47
+ "date-fns": "^4.1.0",
48
+ "express": "^5.1.0",
49
+ "glob": "^11.0.0",
50
+ "gray-matter": "^4.0.3",
51
+ "js-yaml": "^4.1.0",
52
+ "zod": "^3.24.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/express": "^5.0.0",
56
+ "@types/js-yaml": "^4.0.9",
57
+ "@types/node": "^22.10.0",
58
+ "eslint": "^9.16.0",
59
+ "tsup": "^8.3.0",
60
+ "tsx": "^4.19.0",
61
+ "typescript": "^5.7.0",
62
+ "vitest": "^2.1.0"
63
+ }
64
+ }