@mmnto/mcp 1.97.0 → 1.98.1

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
@@ -1,6 +1,6 @@
1
1
  # @mmnto/mcp
2
2
 
3
- MCP (Model Context Protocol) server for [Totem](https://github.com/mmnto-ai/totem) a local-first, file-anchored substrate that makes AI-agent work queryable, enforceable, and derivable in your codebase. A stdio-based server that exposes a Totem project's knowledge index to MCP-compatible agents. Installs the `totem-mcp` binary.
3
+ MCP (Model Context Protocol) server for [Totem](https://github.com/mmnto-ai/totem), the local-first toolkit that keeps AI-agent work queryable, enforceable, and derivable as plain files in your codebase. A stdio-based server that exposes a Totem project's knowledge index to MCP-compatible agents. Installs the `totem-mcp` binary.
4
4
 
5
5
  ## Setup
6
6
 
@@ -8,9 +8,11 @@ describe('package self-description parity (mmnto-ai/totem#2336 D1 family alignme
8
8
  const pkgUrl = new URL('../package.json', import.meta.url);
9
9
  const pkg = JSON.parse(readFileSync(pkgUrl, 'utf-8'));
10
10
  expect(pkg.name).toBe('@mmnto/mcp');
11
- expect(pkg.description).toContain('a local-first, file-anchored substrate that makes AI-agent work queryable, enforceable, and derivable in your codebase');
12
- // Guards against a regression to the retired category.
11
+ expect(pkg.description).toContain('local-first toolkit that keeps AI-agent work queryable, enforceable, and derivable as plain files in your codebase');
12
+ // Guards against regressions to retired vocabulary.
13
13
  expect(pkg.description).not.toContain('persistent memory and context layer');
14
+ expect(pkg.description).not.toContain('substrate');
15
+ expect(pkg.description).not.toContain('—');
14
16
  });
15
17
  });
16
18
  //# sourceMappingURL=description.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"description.test.js","sourceRoot":"","sources":["../src/description.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,wFAAwF;AACxF,sFAAsF;AACtF,sEAAsE;AACtE,QAAQ,CAAC,2EAA2E,EAAE,GAAG,EAAE;IACzF,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAGnD,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,SAAS,CAC/B,wHAAwH,CACzH,CAAC;QACF,uDAAuD;QACvD,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"description.test.js","sourceRoot":"","sources":["../src/description.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,wFAAwF;AACxF,sFAAsF;AACtF,sEAAsE;AACtE,QAAQ,CAAC,2EAA2E,EAAE,GAAG,EAAE;IACzF,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAGnD,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,SAAS,CAC/B,oHAAoH,CACrH,CAAC;QACF,oDAAoD;QACpD,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAC;QAC7E,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -7,7 +7,54 @@ export interface SearchLogEntry {
7
7
  durationMs: number;
8
8
  topScore: number | null;
9
9
  error?: string;
10
+ /**
11
+ * A.3.a schema extension (ADR-029 flight-readiness note; ruled in on
12
+ * mmnto-ai/totem#2362). Env-derived at log time; the ~420 pre-extension
13
+ * entries stay permanently unattributed (no retro-inference — Tenet 20).
14
+ *
15
+ * Agent runtime attribution per ADR-078 § Decision item 2, STRICTLY
16
+ * orthogonal to any emitter/subsystem identifier. Derived from
17
+ * `TOTEM_SELF_AGENT` — the one env-carried agent identity in this cohort
18
+ * (packages/core/src/orchestration-resolver.ts § resolveSelfAgents,
19
+ * highest-precedence layer). `null` (stamped explicitly, never guessed —
20
+ * Tenet 4) when the env var is absent, so the reader can partition legacy +
21
+ * hookless events into an explicit "unattributed" bucket rather than a
22
+ * fabricated seat. The ADR-029 compliance reader renders this field as
23
+ * attribution COVERAGE (per-seat entry counts); per-seat compliance rates
24
+ * wait on a commit-side identity primitive, because commits carry no seat
25
+ * identity to join against (ruled on mmnto-ai/totem#2362).
26
+ */
27
+ agent_source?: string | null;
28
+ /** A.3.a: explicit session id passed through from `TOTEM_SESSION_ID` if present, else null (never guessed). Reserved forward primitive for the ADR-078 commit-side session join; deliberately inert in the current repo-wide windowing. */
29
+ session_id?: string | null;
30
+ /** A.3.a: trace-correlation id passed through from `TOTEM_CORRELATION_ID` if present, else null (never guessed). Reserved for the A.3.c end-to-end correlation pass; carried now so the trio lands in one producer touch. */
31
+ correlation_id?: string | null;
10
32
  }
33
+ /**
34
+ * The A.3.a attribution trio stamped onto every `SearchLogEntry` at log time.
35
+ * Every field is `string | null` — a present value or an explicit `null`
36
+ * (Tenet 4: absence is stamped, never guessed).
37
+ */
38
+ export interface SearchLogAttribution {
39
+ agent_source: string | null;
40
+ session_id: string | null;
41
+ correlation_id: string | null;
42
+ }
43
+ /**
44
+ * Derive the A.3.a attribution trio from the environment (default
45
+ * `process.env`). Pure — no I/O, no side effects — so the producer stamp is
46
+ * testable without touching the filesystem.
47
+ *
48
+ * - `agent_source`: from `TOTEM_SELF_AGENT`, reusing the same read + comma
49
+ * parsing precedent as `resolveSelfAgents` (orchestration-resolver.ts). The
50
+ * MCP server is spawned under a single seat, so the first non-empty
51
+ * comma-separated entry is that seat; a bare value passes through unchanged.
52
+ * - `session_id` / `correlation_id`: straight pass-through from
53
+ * `TOTEM_SESSION_ID` / `TOTEM_CORRELATION_ID`.
54
+ *
55
+ * Any field whose env var is absent or blank is stamped `null`.
56
+ */
57
+ export declare function deriveSearchLogAttribution(env?: NodeJS.ProcessEnv): SearchLogAttribution;
11
58
  export interface SearchStats {
12
59
  totalCalls: number;
13
60
  avgDuration: number;
@@ -21,12 +68,19 @@ export declare function setLogDir(totemDir: string): void;
21
68
  /**
22
69
  * Record a search call.
23
70
  *
71
+ * - Stamps the A.3.a attribution trio (`agent_source` / `session_id` /
72
+ * `correlation_id`) from the environment at log time — one producer touch
73
+ * shared by every call site. A caller-supplied value (none today) wins over
74
+ * the env derivation.
24
75
  * - Always appends to the in-memory array.
25
76
  * - Best-effort appends a single JSON line to `{totemDir}/.search-log.jsonl`.
26
77
  * File writes are fire-and-forget — failures are silently swallowed because
27
78
  * writing to stdout/stderr would corrupt the MCP stdio transport.
79
+ *
80
+ * Returns the stamped entry so the attribution stamp is observable in tests
81
+ * without touching the filesystem.
28
82
  */
29
- export declare function logSearch(entry: SearchLogEntry): void;
83
+ export declare function logSearch(entry: SearchLogEntry): SearchLogEntry;
30
84
  /**
31
85
  * Compute aggregate stats from the in-memory search log.
32
86
  */
@@ -1 +1 @@
1
- {"version":3,"file":"search-log.d.ts","sourceRoot":"","sources":["../src/search-log.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAQD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAgBrD;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAc5C"}
1
+ {"version":3,"file":"search-log.d.ts","sourceRoot":"","sources":["../src/search-log.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,2OAA2O;IAC3O,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,6NAA6N;IAC7N,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAaD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,oBAAoB,CAgBtB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAQD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAsB/D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAc5C"}
@@ -1,5 +1,45 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
+ /**
4
+ * Read one env var, trim it, and normalize empty/whitespace-only to `null`.
5
+ * `null` is the explicit "not present" stamp (Tenet 4) — never a guessed value.
6
+ */
7
+ function envOrNull(env, key) {
8
+ const raw = env[key];
9
+ if (typeof raw !== 'string')
10
+ return null;
11
+ const trimmed = raw.trim();
12
+ return trimmed.length > 0 ? trimmed : null;
13
+ }
14
+ /**
15
+ * Derive the A.3.a attribution trio from the environment (default
16
+ * `process.env`). Pure — no I/O, no side effects — so the producer stamp is
17
+ * testable without touching the filesystem.
18
+ *
19
+ * - `agent_source`: from `TOTEM_SELF_AGENT`, reusing the same read + comma
20
+ * parsing precedent as `resolveSelfAgents` (orchestration-resolver.ts). The
21
+ * MCP server is spawned under a single seat, so the first non-empty
22
+ * comma-separated entry is that seat; a bare value passes through unchanged.
23
+ * - `session_id` / `correlation_id`: straight pass-through from
24
+ * `TOTEM_SESSION_ID` / `TOTEM_CORRELATION_ID`.
25
+ *
26
+ * Any field whose env var is absent or blank is stamped `null`.
27
+ */
28
+ export function deriveSearchLogAttribution(env = process.env) {
29
+ const selfAgentRaw = envOrNull(env, 'TOTEM_SELF_AGENT');
30
+ const agent_source = selfAgentRaw !== null
31
+ ? // Same comma-split as resolveSelfAgents; take the first non-empty seat.
32
+ (selfAgentRaw
33
+ .split(',')
34
+ .map((s) => s.trim())
35
+ .find((s) => s.length > 0) ?? null)
36
+ : null;
37
+ return {
38
+ agent_source,
39
+ session_id: envOrNull(env, 'TOTEM_SESSION_ID'),
40
+ correlation_id: envOrNull(env, 'TOTEM_CORRELATION_ID'),
41
+ };
42
+ }
3
43
  /** In-memory log of search calls for the current server session. */
4
44
  const entries = [];
5
45
  /** Resolved path to the JSONL log file, set on first logSearch call. */
@@ -14,17 +54,28 @@ export function setLogDir(totemDir) {
14
54
  /**
15
55
  * Record a search call.
16
56
  *
57
+ * - Stamps the A.3.a attribution trio (`agent_source` / `session_id` /
58
+ * `correlation_id`) from the environment at log time — one producer touch
59
+ * shared by every call site. A caller-supplied value (none today) wins over
60
+ * the env derivation.
17
61
  * - Always appends to the in-memory array.
18
62
  * - Best-effort appends a single JSON line to `{totemDir}/.search-log.jsonl`.
19
63
  * File writes are fire-and-forget — failures are silently swallowed because
20
64
  * writing to stdout/stderr would corrupt the MCP stdio transport.
65
+ *
66
+ * Returns the stamped entry so the attribution stamp is observable in tests
67
+ * without touching the filesystem.
21
68
  */
22
69
  export function logSearch(entry) {
23
- entries.push(entry);
70
+ // Env stamp LAST: the attribution trio is governance telemetry, so the
71
+ // environment-derived values are authoritative — a caller-supplied (or
72
+ // spread-truthy `undefined`) trio field must never displace the stamp.
73
+ const stamped = { ...entry, ...deriveSearchLogAttribution() };
74
+ entries.push(stamped);
24
75
  // Best-effort, non-blocking file append
25
76
  if (logFilePath) {
26
77
  try {
27
- const line = JSON.stringify(entry) + '\n';
78
+ const line = JSON.stringify(stamped) + '\n';
28
79
  // fs.promises.appendFile returns a promise — we intentionally do NOT await it.
29
80
  // The .catch() swallows any write error silently (no stdout/stderr writes).
30
81
  fs.promises.appendFile(logFilePath, line, 'utf-8').catch(() => {
@@ -35,6 +86,7 @@ export function logSearch(entry) {
35
86
  // Intentionally empty — synchronous errors from JSON.stringify etc.
36
87
  }
37
88
  }
89
+ return stamped;
38
90
  }
39
91
  /**
40
92
  * Compute aggregate stats from the in-memory search log.
@@ -1 +1 @@
1
- {"version":3,"file":"search-log.js","sourceRoot":"","sources":["../src/search-log.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAmBlC,oEAAoE;AACpE,MAAM,OAAO,GAAqB,EAAE,CAAC;AAErC,wEAAwE;AACxE,IAAI,WAA+B,CAAC;AAEpC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEpB,wCAAwC;IACxC,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;YAC1C,+EAA+E;YAC/E,4EAA4E;YAC5E,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC5D,8EAA8E;YAChF,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAElC,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC;IAEnF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC;IAC1D,MAAM,WAAW,GACf,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAS,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"search-log.js","sourceRoot":"","sources":["../src/search-log.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AA8ClC;;;GAGG;AACH,SAAS,SAAS,CAAC,GAAsB,EAAE,GAAW;IACpD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,YAAY,GAAG,SAAS,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IACxD,MAAM,YAAY,GAChB,YAAY,KAAK,IAAI;QACnB,CAAC,CAAC,wEAAwE;YACxE,CAAC,YAAY;iBACV,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBACpB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;QACvC,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO;QACL,YAAY;QACZ,UAAU,EAAE,SAAS,CAAC,GAAG,EAAE,kBAAkB,CAAC;QAC9C,cAAc,EAAE,SAAS,CAAC,GAAG,EAAE,sBAAsB,CAAC;KACvD,CAAC;AACJ,CAAC;AAQD,oEAAoE;AACpE,MAAM,OAAO,GAAqB,EAAE,CAAC;AAErC,wEAAwE;AACxE,IAAI,WAA+B,CAAC;AAEpC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,uEAAuE;IACvE,uEAAuE;IACvE,uEAAuE;IACvE,MAAM,OAAO,GAAmB,EAAE,GAAG,KAAK,EAAE,GAAG,0BAA0B,EAAE,EAAE,CAAC;IAC9E,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAEtB,wCAAwC;IACxC,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;YAC5C,+EAA+E;YAC/E,4EAA4E;YAC5E,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC5D,8EAA8E;YAChF,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAElC,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC;IAEnF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC;IAC1D,MAAM,WAAW,GACf,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAS,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=search-log.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-log.test.d.ts","sourceRoot":"","sources":["../src/search-log.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
+ import { deriveSearchLogAttribution, logSearch } from './search-log.js';
3
+ // ─── deriveSearchLogAttribution (pure — env in, trio out) ───
4
+ describe('deriveSearchLogAttribution', () => {
5
+ it('stamps all three fields from the environment when present', () => {
6
+ const attribution = deriveSearchLogAttribution({
7
+ TOTEM_SELF_AGENT: 'totem-claude',
8
+ TOTEM_SESSION_ID: 'sess-123',
9
+ TOTEM_CORRELATION_ID: 'corr-abc',
10
+ });
11
+ expect(attribution).toEqual({
12
+ agent_source: 'totem-claude',
13
+ session_id: 'sess-123',
14
+ correlation_id: 'corr-abc',
15
+ });
16
+ });
17
+ it('stamps null for every field when the env is absent (Tenet 4 — never guess)', () => {
18
+ expect(deriveSearchLogAttribution({})).toEqual({
19
+ agent_source: null,
20
+ session_id: null,
21
+ correlation_id: null,
22
+ });
23
+ });
24
+ it('stamps null per-field independently (partial env)', () => {
25
+ const attribution = deriveSearchLogAttribution({ TOTEM_SESSION_ID: 'only-session' });
26
+ expect(attribution.agent_source).toBeNull();
27
+ expect(attribution.session_id).toBe('only-session');
28
+ expect(attribution.correlation_id).toBeNull();
29
+ });
30
+ it('treats whitespace-only env values as absent → null', () => {
31
+ const attribution = deriveSearchLogAttribution({
32
+ TOTEM_SELF_AGENT: ' ',
33
+ TOTEM_SESSION_ID: '\t',
34
+ TOTEM_CORRELATION_ID: '',
35
+ });
36
+ expect(attribution).toEqual({
37
+ agent_source: null,
38
+ session_id: null,
39
+ correlation_id: null,
40
+ });
41
+ });
42
+ it('takes the first non-empty seat from a comma-separated TOTEM_SELF_AGENT', () => {
43
+ // Mirrors resolveSelfAgents' comma parsing — the MCP server runs under a
44
+ // single seat, so the first entry is that seat.
45
+ expect(deriveSearchLogAttribution({ TOTEM_SELF_AGENT: ' , totem-gemini , extra' }).agent_source).toBe('totem-gemini');
46
+ });
47
+ });
48
+ // ─── logSearch stamping (no setLogDir → in-memory only, no fs) ───
49
+ describe('logSearch attribution stamp', () => {
50
+ const ENV_KEYS = ['TOTEM_SELF_AGENT', 'TOTEM_SESSION_ID', 'TOTEM_CORRELATION_ID'];
51
+ const saved = {};
52
+ const baseEntry = {
53
+ timestamp: '2026-07-15T00:00:00.000Z',
54
+ query: 'q',
55
+ resultCount: 0,
56
+ durationMs: 1,
57
+ topScore: null,
58
+ };
59
+ beforeEach(() => {
60
+ for (const k of ENV_KEYS) {
61
+ saved[k] = process.env[k];
62
+ delete process.env[k];
63
+ }
64
+ });
65
+ afterEach(() => {
66
+ for (const k of ENV_KEYS) {
67
+ if (saved[k] === undefined)
68
+ delete process.env[k];
69
+ else
70
+ process.env[k] = saved[k];
71
+ }
72
+ });
73
+ it('stamps the trio from process.env onto the returned entry', () => {
74
+ process.env.TOTEM_SELF_AGENT = 'totem-claude';
75
+ process.env.TOTEM_SESSION_ID = 'sess-9';
76
+ process.env.TOTEM_CORRELATION_ID = 'corr-9';
77
+ const stamped = logSearch({ ...baseEntry });
78
+ expect(stamped.agent_source).toBe('totem-claude');
79
+ expect(stamped.session_id).toBe('sess-9');
80
+ expect(stamped.correlation_id).toBe('corr-9');
81
+ });
82
+ it('stamps explicit null on every field when the env is absent', () => {
83
+ const stamped = logSearch({ ...baseEntry });
84
+ expect(stamped.agent_source).toBeNull();
85
+ expect(stamped.session_id).toBeNull();
86
+ expect(stamped.correlation_id).toBeNull();
87
+ });
88
+ });
89
+ //# sourceMappingURL=search-log.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-log.test.js","sourceRoot":"","sources":["../src/search-log.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mmnto/mcp",
3
- "version": "1.97.0",
4
- "description": "MCP server for Totem a local-first, file-anchored substrate that makes AI-agent work queryable, enforceable, and derivable in your codebase",
3
+ "version": "1.98.1",
4
+ "description": "MCP server for Totem, the local-first toolkit that keeps AI-agent work queryable, enforceable, and derivable as plain files in your codebase",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=24"
@@ -22,7 +22,7 @@
22
22
  "dotenv": "^16.4.0",
23
23
  "jiti": "^2.4.0",
24
24
  "zod": "^3.24.0",
25
- "@mmnto/totem": "1.97.0"
25
+ "@mmnto/totem": "1.98.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.0.0",