@memoraone/mcp 0.1.21 → 0.1.22

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/dist/cli.cjs CHANGED
@@ -30,7 +30,7 @@ var require_package = __commonJS({
30
30
  "package.json"(exports2, module2) {
31
31
  module2.exports = {
32
32
  name: "@memoraone/mcp",
33
- version: "0.1.21",
33
+ version: "0.1.22",
34
34
  type: "module",
35
35
  main: "dist/index.cjs",
36
36
  bin: {
@@ -88,6 +88,13 @@ function ensureBaseDir() {
88
88
  var fs2 = __toESM(require("fs/promises"), 1);
89
89
  var path2 = __toESM(require("path"), 1);
90
90
  var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
91
+ function normalizeEnvironment(raw) {
92
+ if (raw === void 0 || raw === null || typeof raw !== "string") {
93
+ return void 0;
94
+ }
95
+ const trimmed = raw.trim();
96
+ return trimmed === "" ? void 0 : trimmed;
97
+ }
91
98
  function parseAndValidateM1(content, markerPath) {
92
99
  let parsed;
93
100
  try {
@@ -104,7 +111,8 @@ function parseAndValidateM1(content, markerPath) {
104
111
  }
105
112
  const apiKeyRaw = parsed?.MEMORAONE_API_KEY ?? parsed?.api_key;
106
113
  const apiKey = apiKeyRaw !== void 0 && apiKeyRaw !== null && typeof apiKeyRaw === "string" && apiKeyRaw.trim() !== "" ? apiKeyRaw.trim() : null;
107
- return { projectId: projectId.trim(), apiKey };
114
+ const environment = normalizeEnvironment(parsed?.environment);
115
+ return environment === void 0 ? { projectId: projectId.trim(), apiKey } : { projectId: projectId.trim(), apiKey, environment };
108
116
  }
109
117
  async function resolveProjectIdFromExplicitM1Path() {
110
118
  const raw = process.env.MEMORAONE_M1_PATH;
@@ -114,8 +122,8 @@ async function resolveProjectIdFromExplicitM1Path() {
114
122
  const markerPath = path2.resolve(raw);
115
123
  try {
116
124
  const content = await fs2.readFile(markerPath, "utf8");
117
- const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
118
- return { projectId, apiKey, foundAt: markerPath };
125
+ const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
126
+ return environment === void 0 ? { projectId, apiKey, foundAt: markerPath } : { projectId, apiKey, environment, foundAt: markerPath };
119
127
  } catch (err) {
120
128
  if (err?.code === "ENOENT") {
121
129
  return null;
@@ -129,9 +137,9 @@ async function findM1WalkingUp(workspaceRoot) {
129
137
  const markerPath = path2.join(current, "memoraone.m1");
130
138
  try {
131
139
  const content = await fs2.readFile(markerPath, "utf8");
132
- const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
140
+ const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
133
141
  const repoRoot = path2.dirname(markerPath);
134
- return { projectId, apiKey, repoRoot, markerPath };
142
+ return environment === void 0 ? { projectId, apiKey, repoRoot, markerPath } : { projectId, apiKey, environment, repoRoot, markerPath };
135
143
  } catch (err) {
136
144
  if (err?.code !== "ENOENT") {
137
145
  throw err;
@@ -191,6 +199,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
191
199
  workspaceRoot: path2.dirname(explicitBinding.foundAt),
192
200
  m1Path: explicitBinding.foundAt,
193
201
  apiKey: resolved.apiKey,
202
+ ...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
194
203
  bindingSource: "explicit-m1-path",
195
204
  apiKeySource: resolved.apiKeySource
196
205
  };
@@ -208,6 +217,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
208
217
  workspaceRoot: binding.repoRoot,
209
218
  m1Path: binding.markerPath,
210
219
  apiKey: resolved.apiKey,
220
+ ...binding.environment !== void 0 ? { environment: binding.environment } : {},
211
221
  bindingSource: "workspace-search",
212
222
  apiKeySource: resolved.apiKeySource
213
223
  };
@@ -306,9 +316,12 @@ This repository uses **MemoraOne** via the MCP server named **user-memoraone** (
306
316
  ### Tools
307
317
 
308
318
  - Before answering questions about **prior decisions**, **remembered facts**, **identity or personal recall**, **preferences**, **repo history**, or **what to do next**, call **\`memora_ask_with_memory\`** so replies stay aligned with MemoraOne memory.
309
- - After **meaningful repository changes** (decisions, fixes, new endpoints or schema, migrations, meaningful wiring), call **\`memora_post_event\`** to append a concise timeline note.
319
+ - Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. \`cursor\`), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
320
+ - Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
310
321
 
311
- Keep tool usage proportional to the task; do not spam events for trivial edits.
322
+ Do not spam events for trivial edits, formatting-only changes, or temporary WIP. Prefer concise, fact-promotable statements.
323
+
324
+ Example \`content.body\`: "MemoraOne Studio added a live dashboard summary, replacing the static dashboard placeholder with authenticated project and memory activity data."
312
325
  `;
313
326
  }
314
327
  function copilotAndJetBrainsBody(title) {
@@ -321,9 +334,12 @@ This repo is set up to use **MemoraOne** through MCP where your editor exposes i
321
334
  ## Behavior
322
335
 
323
336
  - For questions about **earlier decisions**, **stored facts**, **personal or identity recall**, **preferences**, **project history**, or **recommended next steps**, use MemoraOne memory tools (e.g. **\`memora_ask_with_memory\`**) when available before answering.
324
- - After **substantive changes** to this codebase (feature work, bugfixes, API/schema changes, migrations, meaningful integration steps), record a short timeline note with **\`memora_post_event\`** when that tool is available.
337
+ - Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. your agent name), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
338
+ - Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
339
+
340
+ Do not spam events for trivial edits, formatting-only changes, or temporary WIP. Prefer concise, fact-promotable statements.
325
341
 
326
- Use judgment: skip logging for trivial typo-only edits.
342
+ Example \`content.body\`: "MemoraOne Studio added a live dashboard summary, replacing the static dashboard placeholder with authenticated project and memory activity data."
327
343
  `;
328
344
  }
329
345
  function mcpJsonHeader() {
@@ -601,8 +617,9 @@ if (args[0] === "setup-ide-files") {
601
617
  ensureBaseDir();
602
618
  const binding = await resolveBinding();
603
619
  const socketPath = getSocketPath(binding.projectId);
620
+ const environmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
604
621
  log(
605
- `authoritative binding project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}`
622
+ `authoritative binding project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}${environmentLog}`
606
623
  );
607
624
  let socket;
608
625
  try {
package/dist/daemon.cjs CHANGED
@@ -53,6 +53,13 @@ function ensureBaseDir() {
53
53
  var fs2 = __toESM(require("fs/promises"), 1);
54
54
  var path2 = __toESM(require("path"), 1);
55
55
  var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
56
+ function normalizeEnvironment(raw) {
57
+ if (raw === void 0 || raw === null || typeof raw !== "string") {
58
+ return void 0;
59
+ }
60
+ const trimmed = raw.trim();
61
+ return trimmed === "" ? void 0 : trimmed;
62
+ }
56
63
  function parseAndValidateM1(content, markerPath) {
57
64
  let parsed2;
58
65
  try {
@@ -69,7 +76,8 @@ function parseAndValidateM1(content, markerPath) {
69
76
  }
70
77
  const apiKeyRaw = parsed2?.MEMORAONE_API_KEY ?? parsed2?.api_key;
71
78
  const apiKey = apiKeyRaw !== void 0 && apiKeyRaw !== null && typeof apiKeyRaw === "string" && apiKeyRaw.trim() !== "" ? apiKeyRaw.trim() : null;
72
- return { projectId: projectId.trim(), apiKey };
79
+ const environment = normalizeEnvironment(parsed2?.environment);
80
+ return environment === void 0 ? { projectId: projectId.trim(), apiKey } : { projectId: projectId.trim(), apiKey, environment };
73
81
  }
74
82
  async function resolveProjectIdFromExplicitM1Path() {
75
83
  const raw = process.env.MEMORAONE_M1_PATH;
@@ -79,8 +87,8 @@ async function resolveProjectIdFromExplicitM1Path() {
79
87
  const markerPath = path2.resolve(raw);
80
88
  try {
81
89
  const content = await fs2.readFile(markerPath, "utf8");
82
- const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
83
- return { projectId, apiKey, foundAt: markerPath };
90
+ const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
91
+ return environment === void 0 ? { projectId, apiKey, foundAt: markerPath } : { projectId, apiKey, environment, foundAt: markerPath };
84
92
  } catch (err) {
85
93
  if (err?.code === "ENOENT") {
86
94
  return null;
@@ -94,9 +102,9 @@ async function findM1WalkingUp(workspaceRoot) {
94
102
  const markerPath = path2.join(current, "memoraone.m1");
95
103
  try {
96
104
  const content = await fs2.readFile(markerPath, "utf8");
97
- const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
105
+ const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
98
106
  const repoRoot = path2.dirname(markerPath);
99
- return { projectId, apiKey, repoRoot, markerPath };
107
+ return environment === void 0 ? { projectId, apiKey, repoRoot, markerPath } : { projectId, apiKey, environment, repoRoot, markerPath };
100
108
  } catch (err) {
101
109
  if (err?.code !== "ENOENT") {
102
110
  throw err;
@@ -156,6 +164,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
156
164
  workspaceRoot: path2.dirname(explicitBinding.foundAt),
157
165
  m1Path: explicitBinding.foundAt,
158
166
  apiKey: resolved.apiKey,
167
+ ...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
159
168
  bindingSource: "explicit-m1-path",
160
169
  apiKeySource: resolved.apiKeySource
161
170
  };
@@ -173,6 +182,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
173
182
  workspaceRoot: binding.repoRoot,
174
183
  m1Path: binding.markerPath,
175
184
  apiKey: resolved.apiKey,
185
+ ...binding.environment !== void 0 ? { environment: binding.environment } : {},
176
186
  bindingSource: "workspace-search",
177
187
  apiKeySource: resolved.apiKeySource
178
188
  };
@@ -194,6 +204,7 @@ function decodeResolvedBinding(value) {
194
204
  const workspaceRoot = parsed2?.workspaceRoot;
195
205
  const m1Path = parsed2?.m1Path;
196
206
  const apiKey = parsed2?.apiKey;
207
+ const environment = normalizeEnvironment(parsed2?.environment);
197
208
  const bindingSource = parsed2?.bindingSource;
198
209
  const apiKeySource = parsed2?.apiKeySource;
199
210
  if (!projectId || typeof projectId !== "string" || !uuidRegex.test(projectId.trim())) {
@@ -219,6 +230,7 @@ function decodeResolvedBinding(value) {
219
230
  workspaceRoot,
220
231
  m1Path,
221
232
  apiKey: typeof apiKey === "string" && apiKey.trim() !== "" ? apiKey.trim() : null,
233
+ ...environment !== void 0 ? { environment } : {},
222
234
  bindingSource,
223
235
  apiKeySource
224
236
  };
@@ -541,6 +553,7 @@ async function registerRepoSource(client, projectId, repoPath, ideType) {
541
553
 
542
554
  // src/tools/postEvent.ts
543
555
  var import_v42 = require("zod/v4");
556
+ var postEventDescription = 'Append a durable project-change note to the MemoraOne timeline. Use after meaningful repository or project changes: decisions, fixes, new endpoints, schema changes, migrations, important wiring, or durable product behavior changes \u2014 not for trivial edits, formatting-only changes, or temporary WIP. Recommended shape: kind "note"; content.title (concise title); content.body (one durable, fact-promotable project-change statement); metadata.source (agent name, e.g. "cursor"); metadata.purpose "dev-log"; metadata.schema "v1".';
544
557
  var postEventShape = {
545
558
  kind: import_v42.z.string().min(1),
546
559
  actor: import_v42.z.object({
@@ -649,6 +662,9 @@ var setProjectShape = {
649
662
  projectId: import_v411.z.string().min(1).optional()
650
663
  };
651
664
 
665
+ // src/tools/bindingStatus.ts
666
+ var bindingStatusShape = {};
667
+
652
668
  // src/tools/handlers/postEvent.ts
653
669
  var import_v412 = require("zod/v4");
654
670
  var crypto3 = __toESM(require("crypto"), 1);
@@ -721,6 +737,32 @@ var postEventInputSchema = import_v412.z.object({
721
737
  content: import_v412.z.record(import_v412.z.string(), import_v412.z.any()),
722
738
  metadata: import_v412.z.record(import_v412.z.string(), import_v412.z.any()).optional()
723
739
  });
740
+ function buildPostEventContentFields(content) {
741
+ if (typeof content.message === "string") {
742
+ return { message: content.message };
743
+ }
744
+ if (typeof content.text === "string") {
745
+ return { message: content.text };
746
+ }
747
+ const title = typeof content.title === "string" ? content.title : void 0;
748
+ const body = typeof content.body === "string" ? content.body : void 0;
749
+ if (title !== void 0 || body !== void 0) {
750
+ const message = body ?? title ?? "";
751
+ const structuredContent = {};
752
+ for (const [key, value] of Object.entries(content)) {
753
+ if (key === "message" || key === "text") continue;
754
+ structuredContent[key] = value;
755
+ }
756
+ const new_value = {
757
+ message,
758
+ ...title !== void 0 ? { title } : {},
759
+ ...body !== void 0 ? { body } : {},
760
+ content: structuredContent
761
+ };
762
+ return { message, new_value };
763
+ }
764
+ return { message: JSON.stringify(content) };
765
+ }
724
766
  async function handlePostEvent(client, args) {
725
767
  const nonce = crypto3.randomBytes(8).toString("hex");
726
768
  console.error(
@@ -732,7 +774,7 @@ async function handlePostEvent(client, args) {
732
774
  throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
733
775
  }
734
776
  const content = parsed2.content ?? {};
735
- const message = typeof content.message === "string" ? content.message : typeof content.text === "string" ? content.text : JSON.stringify(content);
777
+ const { message, new_value } = buildPostEventContentFields(content);
736
778
  const body = {
737
779
  kind: parsed2.kind,
738
780
  message,
@@ -742,6 +784,7 @@ async function handlePostEvent(client, args) {
742
784
  identifier: config2.agentName,
743
785
  ...parsed2.actor.id ? { id: parsed2.actor.id } : {}
744
786
  },
787
+ ...new_value ? { new_value } : {},
745
788
  ...parsed2.metadata ? { metadata: parsed2.metadata } : {}
746
789
  };
747
790
  try {
@@ -926,7 +969,8 @@ async function handleAskWithMemory(client, args) {
926
969
  }
927
970
  return {
928
971
  answer: res.answer,
929
- used: res.used ?? {}
972
+ used: res.used ?? {},
973
+ ...res.retrieval !== void 0 ? { retrieval: res.retrieval } : {}
930
974
  };
931
975
  }
932
976
 
@@ -1471,6 +1515,27 @@ async function handleSetProject(args) {
1471
1515
  return { ok: true, projectKey: resolvedProjectKey };
1472
1516
  }
1473
1517
 
1518
+ // src/tools/handlers/bindingStatus.ts
1519
+ function buildBindingStatus(binding) {
1520
+ const status = {
1521
+ projectId: binding.projectId,
1522
+ workspaceRoot: binding.workspaceRoot,
1523
+ m1Path: binding.m1Path,
1524
+ bindingSource: binding.bindingSource,
1525
+ apiKeySource: binding.apiKeySource
1526
+ };
1527
+ if (binding.environment !== void 0) {
1528
+ status.environment = binding.environment;
1529
+ }
1530
+ return status;
1531
+ }
1532
+ function handleBindingStatus(binding) {
1533
+ if (!binding) {
1534
+ throw new Error("[memoraone-mcp] Binding status unavailable (not initialized)");
1535
+ }
1536
+ return buildBindingStatus(binding);
1537
+ }
1538
+
1474
1539
  // src/index.ts
1475
1540
  var notInitializedResult = {
1476
1541
  content: [
@@ -1654,6 +1719,7 @@ async function main(opts = {}) {
1654
1719
  projectId: null,
1655
1720
  apiKeySource: null,
1656
1721
  apiKeyFingerprint: null,
1722
+ authoritativeBinding: opts.authoritativeBinding ?? null,
1657
1723
  ideType: void 0
1658
1724
  };
1659
1725
  let workspaceRoot;
@@ -1703,7 +1769,7 @@ async function main(opts = {}) {
1703
1769
  const registeredToolNames = [];
1704
1770
  server.tool(
1705
1771
  "memora_post_event",
1706
- "Forward an event to MemoraOne timeline",
1772
+ postEventDescription,
1707
1773
  postEventShape,
1708
1774
  async (args) => runWithSessionContext(sessionContext, async () => {
1709
1775
  if (!runtime.client || !runtime.projectId) return notInitializedResult;
@@ -1779,6 +1845,19 @@ async function main(opts = {}) {
1779
1845
  })
1780
1846
  );
1781
1847
  registeredToolNames.push("memora_set_project");
1848
+ server.tool(
1849
+ "memora_status",
1850
+ "Return non-secret project binding metadata for this MCP session",
1851
+ bindingStatusShape,
1852
+ async () => runWithSessionContext(sessionContext, async () => {
1853
+ if (!runtime.authoritativeBinding) return notInitializedResult;
1854
+ const result = handleBindingStatus(runtime.authoritativeBinding);
1855
+ return {
1856
+ content: [{ type: "text", text: JSON.stringify(result) }]
1857
+ };
1858
+ })
1859
+ );
1860
+ registeredToolNames.push("memora_status");
1782
1861
  registerToolWithWorklog(
1783
1862
  server,
1784
1863
  runtime,
@@ -1929,10 +2008,12 @@ async function main(opts = {}) {
1929
2008
  runtime.projectId = projectId;
1930
2009
  runtime.apiKeySource = binding.apiKeySource;
1931
2010
  runtime.apiKeyFingerprint = fingerprintApiKey(apiKeyToUse);
2011
+ runtime.authoritativeBinding = binding;
1932
2012
  runtime.client = new memoraClient_default(config2, projectId, apiKeyToUse);
1933
2013
  workspaceRoot = binding.workspaceRoot;
2014
+ const environmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
1934
2015
  process.stderr.write(
1935
- `[memoraone-mcp] registering workspace source bindingSource=${binding.bindingSource} workspaceRoot=${workspaceRoot ?? "(unset)"} m1Path=${binding.m1Path}
2016
+ `[memoraone-mcp] registering workspace source bindingSource=${binding.bindingSource} workspaceRoot=${workspaceRoot ?? "(unset)"} m1Path=${binding.m1Path}${environmentLog}
1936
2017
  `
1937
2018
  );
1938
2019
  await registerRepoSource(
@@ -1946,8 +2027,9 @@ async function main(opts = {}) {
1946
2027
  console.error("[memoraone-mcp][auth] project_id:", projectId);
1947
2028
  console.error("[memoraone-mcp][auth] api_key source:", binding.apiKeySource);
1948
2029
  }
2030
+ const bindingEnvironmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
1949
2031
  console.error(
1950
- `[memoraone-mcp] ${sessionLabel} authoritative binding: project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}`
2032
+ `[memoraone-mcp] ${sessionLabel} authoritative binding: project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}${bindingEnvironmentLog}`
1951
2033
  );
1952
2034
  bindingReadyResolve?.(runtime.client);
1953
2035
  return server.server._oninitialize(request);
package/dist/index.cjs CHANGED
@@ -281,6 +281,13 @@ var memoraClient_default = MemoraClient;
281
281
  var fs2 = __toESM(require("fs/promises"), 1);
282
282
  var path2 = __toESM(require("path"), 1);
283
283
  var uuidRegex2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
284
+ function normalizeEnvironment(raw) {
285
+ if (raw === void 0 || raw === null || typeof raw !== "string") {
286
+ return void 0;
287
+ }
288
+ const trimmed = raw.trim();
289
+ return trimmed === "" ? void 0 : trimmed;
290
+ }
284
291
  function parseAndValidateM1(content, markerPath) {
285
292
  let parsed2;
286
293
  try {
@@ -297,7 +304,8 @@ function parseAndValidateM1(content, markerPath) {
297
304
  }
298
305
  const apiKeyRaw = parsed2?.MEMORAONE_API_KEY ?? parsed2?.api_key;
299
306
  const apiKey = apiKeyRaw !== void 0 && apiKeyRaw !== null && typeof apiKeyRaw === "string" && apiKeyRaw.trim() !== "" ? apiKeyRaw.trim() : null;
300
- return { projectId: projectId.trim(), apiKey };
307
+ const environment = normalizeEnvironment(parsed2?.environment);
308
+ return environment === void 0 ? { projectId: projectId.trim(), apiKey } : { projectId: projectId.trim(), apiKey, environment };
301
309
  }
302
310
  async function resolveProjectIdFromExplicitM1Path() {
303
311
  const raw = process.env.MEMORAONE_M1_PATH;
@@ -307,8 +315,8 @@ async function resolveProjectIdFromExplicitM1Path() {
307
315
  const markerPath = path2.resolve(raw);
308
316
  try {
309
317
  const content = await fs2.readFile(markerPath, "utf8");
310
- const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
311
- return { projectId, apiKey, foundAt: markerPath };
318
+ const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
319
+ return environment === void 0 ? { projectId, apiKey, foundAt: markerPath } : { projectId, apiKey, environment, foundAt: markerPath };
312
320
  } catch (err) {
313
321
  if (err?.code === "ENOENT") {
314
322
  return null;
@@ -322,9 +330,9 @@ async function findM1WalkingUp(workspaceRoot) {
322
330
  const markerPath = path2.join(current, "memoraone.m1");
323
331
  try {
324
332
  const content = await fs2.readFile(markerPath, "utf8");
325
- const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
333
+ const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
326
334
  const repoRoot = path2.dirname(markerPath);
327
- return { projectId, apiKey, repoRoot, markerPath };
335
+ return environment === void 0 ? { projectId, apiKey, repoRoot, markerPath } : { projectId, apiKey, environment, repoRoot, markerPath };
328
336
  } catch (err) {
329
337
  if (err?.code !== "ENOENT") {
330
338
  throw err;
@@ -384,6 +392,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
384
392
  workspaceRoot: path2.dirname(explicitBinding.foundAt),
385
393
  m1Path: explicitBinding.foundAt,
386
394
  apiKey: resolved.apiKey,
395
+ ...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
387
396
  bindingSource: "explicit-m1-path",
388
397
  apiKeySource: resolved.apiKeySource
389
398
  };
@@ -401,6 +410,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
401
410
  workspaceRoot: binding.repoRoot,
402
411
  m1Path: binding.markerPath,
403
412
  apiKey: resolved.apiKey,
413
+ ...binding.environment !== void 0 ? { environment: binding.environment } : {},
404
414
  bindingSource: "workspace-search",
405
415
  apiKeySource: resolved.apiKeySource
406
416
  };
@@ -481,6 +491,7 @@ async function registerRepoSource(client, projectId, repoPath, ideType) {
481
491
 
482
492
  // src/tools/postEvent.ts
483
493
  var import_v42 = require("zod/v4");
494
+ var postEventDescription = 'Append a durable project-change note to the MemoraOne timeline. Use after meaningful repository or project changes: decisions, fixes, new endpoints, schema changes, migrations, important wiring, or durable product behavior changes \u2014 not for trivial edits, formatting-only changes, or temporary WIP. Recommended shape: kind "note"; content.title (concise title); content.body (one durable, fact-promotable project-change statement); metadata.source (agent name, e.g. "cursor"); metadata.purpose "dev-log"; metadata.schema "v1".';
484
495
  var postEventShape = {
485
496
  kind: import_v42.z.string().min(1),
486
497
  actor: import_v42.z.object({
@@ -589,6 +600,9 @@ var setProjectShape = {
589
600
  projectId: import_v411.z.string().min(1).optional()
590
601
  };
591
602
 
603
+ // src/tools/bindingStatus.ts
604
+ var bindingStatusShape = {};
605
+
592
606
  // src/tools/handlers/postEvent.ts
593
607
  var import_v412 = require("zod/v4");
594
608
  var crypto3 = __toESM(require("crypto"), 1);
@@ -661,6 +675,32 @@ var postEventInputSchema = import_v412.z.object({
661
675
  content: import_v412.z.record(import_v412.z.string(), import_v412.z.any()),
662
676
  metadata: import_v412.z.record(import_v412.z.string(), import_v412.z.any()).optional()
663
677
  });
678
+ function buildPostEventContentFields(content) {
679
+ if (typeof content.message === "string") {
680
+ return { message: content.message };
681
+ }
682
+ if (typeof content.text === "string") {
683
+ return { message: content.text };
684
+ }
685
+ const title = typeof content.title === "string" ? content.title : void 0;
686
+ const body = typeof content.body === "string" ? content.body : void 0;
687
+ if (title !== void 0 || body !== void 0) {
688
+ const message = body ?? title ?? "";
689
+ const structuredContent = {};
690
+ for (const [key, value] of Object.entries(content)) {
691
+ if (key === "message" || key === "text") continue;
692
+ structuredContent[key] = value;
693
+ }
694
+ const new_value = {
695
+ message,
696
+ ...title !== void 0 ? { title } : {},
697
+ ...body !== void 0 ? { body } : {},
698
+ content: structuredContent
699
+ };
700
+ return { message, new_value };
701
+ }
702
+ return { message: JSON.stringify(content) };
703
+ }
664
704
  async function handlePostEvent(client, args) {
665
705
  const nonce = crypto3.randomBytes(8).toString("hex");
666
706
  console.error(
@@ -672,7 +712,7 @@ async function handlePostEvent(client, args) {
672
712
  throw new Error("No project selected. Use memora_list_projects and memora_set_project to select a project.");
673
713
  }
674
714
  const content = parsed2.content ?? {};
675
- const message = typeof content.message === "string" ? content.message : typeof content.text === "string" ? content.text : JSON.stringify(content);
715
+ const { message, new_value } = buildPostEventContentFields(content);
676
716
  const body = {
677
717
  kind: parsed2.kind,
678
718
  message,
@@ -682,6 +722,7 @@ async function handlePostEvent(client, args) {
682
722
  identifier: config2.agentName,
683
723
  ...parsed2.actor.id ? { id: parsed2.actor.id } : {}
684
724
  },
725
+ ...new_value ? { new_value } : {},
685
726
  ...parsed2.metadata ? { metadata: parsed2.metadata } : {}
686
727
  };
687
728
  try {
@@ -866,7 +907,8 @@ async function handleAskWithMemory(client, args) {
866
907
  }
867
908
  return {
868
909
  answer: res.answer,
869
- used: res.used ?? {}
910
+ used: res.used ?? {},
911
+ ...res.retrieval !== void 0 ? { retrieval: res.retrieval } : {}
870
912
  };
871
913
  }
872
914
 
@@ -1411,6 +1453,27 @@ async function handleSetProject(args) {
1411
1453
  return { ok: true, projectKey: resolvedProjectKey };
1412
1454
  }
1413
1455
 
1456
+ // src/tools/handlers/bindingStatus.ts
1457
+ function buildBindingStatus(binding) {
1458
+ const status = {
1459
+ projectId: binding.projectId,
1460
+ workspaceRoot: binding.workspaceRoot,
1461
+ m1Path: binding.m1Path,
1462
+ bindingSource: binding.bindingSource,
1463
+ apiKeySource: binding.apiKeySource
1464
+ };
1465
+ if (binding.environment !== void 0) {
1466
+ status.environment = binding.environment;
1467
+ }
1468
+ return status;
1469
+ }
1470
+ function handleBindingStatus(binding) {
1471
+ if (!binding) {
1472
+ throw new Error("[memoraone-mcp] Binding status unavailable (not initialized)");
1473
+ }
1474
+ return buildBindingStatus(binding);
1475
+ }
1476
+
1414
1477
  // src/index.ts
1415
1478
  var notInitializedResult = {
1416
1479
  content: [
@@ -1594,6 +1657,7 @@ async function main(opts = {}) {
1594
1657
  projectId: null,
1595
1658
  apiKeySource: null,
1596
1659
  apiKeyFingerprint: null,
1660
+ authoritativeBinding: opts.authoritativeBinding ?? null,
1597
1661
  ideType: void 0
1598
1662
  };
1599
1663
  let workspaceRoot;
@@ -1643,7 +1707,7 @@ async function main(opts = {}) {
1643
1707
  const registeredToolNames = [];
1644
1708
  server.tool(
1645
1709
  "memora_post_event",
1646
- "Forward an event to MemoraOne timeline",
1710
+ postEventDescription,
1647
1711
  postEventShape,
1648
1712
  async (args) => runWithSessionContext(sessionContext, async () => {
1649
1713
  if (!runtime.client || !runtime.projectId) return notInitializedResult;
@@ -1719,6 +1783,19 @@ async function main(opts = {}) {
1719
1783
  })
1720
1784
  );
1721
1785
  registeredToolNames.push("memora_set_project");
1786
+ server.tool(
1787
+ "memora_status",
1788
+ "Return non-secret project binding metadata for this MCP session",
1789
+ bindingStatusShape,
1790
+ async () => runWithSessionContext(sessionContext, async () => {
1791
+ if (!runtime.authoritativeBinding) return notInitializedResult;
1792
+ const result = handleBindingStatus(runtime.authoritativeBinding);
1793
+ return {
1794
+ content: [{ type: "text", text: JSON.stringify(result) }]
1795
+ };
1796
+ })
1797
+ );
1798
+ registeredToolNames.push("memora_status");
1722
1799
  registerToolWithWorklog(
1723
1800
  server,
1724
1801
  runtime,
@@ -1869,10 +1946,12 @@ async function main(opts = {}) {
1869
1946
  runtime.projectId = projectId;
1870
1947
  runtime.apiKeySource = binding.apiKeySource;
1871
1948
  runtime.apiKeyFingerprint = fingerprintApiKey(apiKeyToUse);
1949
+ runtime.authoritativeBinding = binding;
1872
1950
  runtime.client = new memoraClient_default(config2, projectId, apiKeyToUse);
1873
1951
  workspaceRoot = binding.workspaceRoot;
1952
+ const environmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
1874
1953
  process.stderr.write(
1875
- `[memoraone-mcp] registering workspace source bindingSource=${binding.bindingSource} workspaceRoot=${workspaceRoot ?? "(unset)"} m1Path=${binding.m1Path}
1954
+ `[memoraone-mcp] registering workspace source bindingSource=${binding.bindingSource} workspaceRoot=${workspaceRoot ?? "(unset)"} m1Path=${binding.m1Path}${environmentLog}
1876
1955
  `
1877
1956
  );
1878
1957
  await registerRepoSource(
@@ -1886,8 +1965,9 @@ async function main(opts = {}) {
1886
1965
  console.error("[memoraone-mcp][auth] project_id:", projectId);
1887
1966
  console.error("[memoraone-mcp][auth] api_key source:", binding.apiKeySource);
1888
1967
  }
1968
+ const bindingEnvironmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
1889
1969
  console.error(
1890
- `[memoraone-mcp] ${sessionLabel} authoritative binding: project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}`
1970
+ `[memoraone-mcp] ${sessionLabel} authoritative binding: project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}${bindingEnvironmentLog}`
1891
1971
  );
1892
1972
  bindingReadyResolve?.(runtime.client);
1893
1973
  return server.server._oninitialize(request);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoraone/mcp",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {
@@ -13,15 +13,6 @@
13
13
  "publishConfig": {
14
14
  "access": "public"
15
15
  },
16
- "scripts": {
17
- "build": "tsup && node scripts/writeBinWrapper.cjs",
18
- "prepublishOnly": "pnpm run build",
19
- "dev": "tsx src/cli.ts",
20
- "lint": "eslint .",
21
- "lint:contracts": "node scripts/lint-contracts.cjs",
22
- "test": "pnpm run lint:contracts && node --import=tsx --test test/*.test.js",
23
- "validate:auth": "node --import=tsx --test test/memoraClient.test.js"
24
- },
25
16
  "dependencies": {
26
17
  "@modelcontextprotocol/sdk": "^1.25.1",
27
18
  "dotenv": "^16.4.5",
@@ -31,5 +22,13 @@
31
22
  "tsx": "^4.21.0",
32
23
  "tsup": "^8.5.1",
33
24
  "typescript": "^5.9.2"
25
+ },
26
+ "scripts": {
27
+ "build": "tsup && node scripts/writeBinWrapper.cjs",
28
+ "dev": "tsx src/cli.ts",
29
+ "lint": "eslint .",
30
+ "lint:contracts": "node scripts/lint-contracts.cjs",
31
+ "test": "pnpm run lint:contracts && node --import=tsx --test test/*.test.js",
32
+ "validate:auth": "node --import=tsx --test test/memoraClient.test.js"
34
33
  }
35
- }
34
+ }