@yugabytedb/meko-mcp 1.0.5 → 1.0.6

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.
@@ -374,6 +374,8 @@ async function main() {
374
374
  // Step 1 — connection type. Local Meko only makes sense for users who
375
375
  // have the server repo cloned locally (docker-compose stack on :8000);
376
376
  // npm/npx users have no way to run it, so hide the option for them.
377
+ // When only Cloud is available, skip the prompt entirely — asking to
378
+ // pick between one option is pointless UX.
377
379
  const connectionOptions = [
378
380
  { label: 'Cloud Meko', description: 'Hosted at mekodev.com (requires URL + API key)' },
379
381
  ];
@@ -383,7 +385,10 @@ async function main() {
383
385
  description: 'Local stack at localhost:8000',
384
386
  });
385
387
  }
386
- const connectionType = await askChoice('Connection type:', connectionOptions);
388
+ const connectionType =
389
+ connectionOptions.length === 1
390
+ ? connectionOptions[0].label
391
+ : await askChoice('Connection type:', connectionOptions);
387
392
 
388
393
  if (connectionType === 'Cloud Meko') {
389
394
  // Step 2 — MCP URL
@@ -283,11 +283,17 @@ export async function runMigration(opts) {
283
283
  const [, memRes] = await Promise.all([addMsgPromise, memPromise]);
284
284
  messagesSent++;
285
285
  if (!opts.noExtract) {
286
- if (memRes && memRes.ok) {
286
+ if (memRes && memRes.ok && !isErrorText(memRes.text)) {
287
287
  memoryPairsSent++;
288
288
  prog.counts.memoryPairsSent++;
289
289
  } else if (memRes) {
290
290
  prog.counts.memoryErrors++;
291
+ log.warn("memory_add_failed", {
292
+ agentId: ref.agentId,
293
+ sessionUuid: ref.sessionUuid,
294
+ userUuid: ex.user_uuid,
295
+ detail: errorDetail(memRes),
296
+ });
291
297
  }
292
298
  }
293
299
  lastUuid = ex.user_uuid;
@@ -361,7 +367,7 @@ export async function runMigration(opts) {
361
367
  metadata: JSON.stringify(item.metadata),
362
368
  }, { fireAndForget: true });
363
369
 
364
- if (res.ok) {
370
+ if (res.ok && !isErrorText(res.text)) {
365
371
  state.markMemory(st, item.agentId, item.hash, {
366
372
  memoryId: extractId(res.text) || "",
367
373
  name: item.name,
@@ -370,7 +376,12 @@ export async function runMigration(opts) {
370
376
  prog.counts.memoriesFromMD++;
371
377
  } else {
372
378
  prog.counts.memoryErrors++;
373
- log.warn("memory_md_failed", { agentId: item.agentId, name: item.name, error: res.error?.message });
379
+ log.warn("memory_md_failed", {
380
+ agentId: item.agentId,
381
+ name: item.name,
382
+ error: res.error?.message,
383
+ detail: errorDetail(res),
384
+ });
374
385
  }
375
386
  if (++opsSinceFlush >= FLUSH_EVERY) flushState();
376
387
  writeProgress();
@@ -441,3 +452,34 @@ function extractId(text) {
441
452
  return null;
442
453
  }
443
454
  }
455
+
456
+ /**
457
+ * The MCP transport returns `{ok:true}` whenever the HTTP call itself
458
+ * succeeded, even if the tool's body encoded a failure. memory_add in
459
+ * older server builds returned plain "Error adding memory: ..." strings
460
+ * on exception; newer builds return JSON `{"error":"memory_add_failed",...}`.
461
+ * Either way, the text content is the authoritative success signal.
462
+ */
463
+ function isErrorText(text) {
464
+ if (!text) return false;
465
+ const trimmed = text.trim();
466
+ if (trimmed.startsWith("Error adding memory")) return true;
467
+ if (trimmed.startsWith("{")) {
468
+ try {
469
+ const obj = JSON.parse(trimmed);
470
+ if (obj && typeof obj === "object" && obj.error) return true;
471
+ } catch { /* not JSON; fall through */ }
472
+ }
473
+ return false;
474
+ }
475
+
476
+ function errorDetail(res) {
477
+ if (!res) return null;
478
+ if (res.error?.message) return res.error.message;
479
+ if (!res.text) return null;
480
+ try {
481
+ const obj = JSON.parse(res.text);
482
+ if (obj?.error) return obj.detail || obj.error;
483
+ } catch { /* not JSON */ }
484
+ return res.text.slice(0, 200);
485
+ }
package/lib/prompt.mjs CHANGED
@@ -164,16 +164,21 @@ export async function askText(questionText, defaultValue) {
164
164
  * @returns {Promise<string>}
165
165
  */
166
166
  export async function askSecret(questionText) {
167
+ // Fully close the singleton readline BEFORE printing the prompt or
168
+ // entering raw mode. pause() alone was insufficient — readline left
169
+ // stdin in a state where the terminal echo would still leak keystrokes
170
+ // before our raw-mode handler wrote its `*` mask. close() forcefully
171
+ // releases stdin back to the OS, which lets setRawMode actually
172
+ // suppress echo. getRL() will recreate the interface on the next
173
+ // askText/askYesNo call, so downstream prompts still work.
174
+ if (rl) {
175
+ rl.close();
176
+ rl = null;
177
+ }
178
+
167
179
  // Print the prompt without a trailing newline.
168
180
  process.stdout.write(` ${questionText} `);
169
181
 
170
- // Pause the singleton readline so its data listener doesn't echo each
171
- // keystroke in parallel with our manual "*" write. Without this, users
172
- // see `d*j*f*k*...` (raw char + mask interleaved) and backspace fights
173
- // with readline over cursor position.
174
- const pausedRL = rl;
175
- if (pausedRL) pausedRL.pause();
176
-
177
182
  return new Promise((resolve, reject) => {
178
183
  let secret = "";
179
184
  const stdin = process.stdin;
@@ -185,7 +190,6 @@ export async function askSecret(questionText) {
185
190
  const fallbackRL = createInterface({ input: stdin, output: process.stdout });
186
191
  fallbackRL.question("", (answer) => {
187
192
  fallbackRL.close();
188
- if (pausedRL) pausedRL.resume();
189
193
  resolve(answer.trim());
190
194
  });
191
195
  return;
@@ -200,8 +204,8 @@ export async function askSecret(questionText) {
200
204
  function cleanup() {
201
205
  stdin.setRawMode(wasRaw);
202
206
  stdin.removeListener("data", onData);
207
+ stdin.pause();
203
208
  process.stdout.write("\n");
204
- if (pausedRL) pausedRL.resume();
205
209
  }
206
210
 
207
211
  function onData(ch) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yugabytedb/meko-mcp",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Single-line installer for Meko MCP Server across Claude and Cursor clients",
5
5
  "type": "module",
6
6
  "bin": {