wontopos-mcp 1.0.10 → 1.0.12

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.
Files changed (3) hide show
  1. package/README.md +33 -6
  2. package/index.mjs +54 -34
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -40,12 +40,15 @@ claude mcp add wontopos --env WONTOPOS_API_KEY=wos-live-... -- npx -y wontopos-m
40
40
  `WONTOPOS_USER_ID` picks the store this tool remembers into (optional, default
41
41
  `"default"`) - one per project, per person, or per agent.
42
42
 
43
- Store ids are folded: lowercased, and anything outside `[a-z0-9_]` becomes `_`.
44
- So `My-Project`, `my_project` and `MY.PROJECT` are **one store** sharing one set
45
- of memories, and an id with no latin alphanumerics at all (`선우`, `田中`) folds to
46
- underscores, which every such id on the key also folds to. The server prints the
47
- folded id at boot whenever it differs from what you configured. Pick ids that stay
48
- distinct after the fold if the stores must stay apart.
43
+ Store ids are folded: lowercased, and anything outside `[a-z0-9_]` becomes `_`, so
44
+ `My-Project`, `my_project` and `MY.PROJECT` all name **one store**. The boot line
45
+ prints the folded id, which is the one that actually holds the memories. Creating a
46
+ second id that folds onto an existing one is refused with `409 conflict_error`
47
+ rather than silently sharing it, so pick ids that stay distinct after the fold.
48
+
49
+ The id must also match `[A-Za-z0-9][A-Za-z0-9._-]{0,63}`. An id outside that (`선우`,
50
+ `田中`, an email address) cannot be used at all: creating it answers 400 and every
51
+ other call 404s. Romanize it, or key the store on an internal id.
49
52
 
50
53
  Create a key in the [console](https://wontopos.com). Optional env:
51
54
  `WONTOPOS_USER_ID` (default store, default `"default"`), `WONTOPOS_MODEL`
@@ -114,6 +117,30 @@ automatic retries, redirect refusal, response caps, and key hygiene apply as-is.
114
117
 
115
118
  ## Changelog
116
119
 
120
+ - **1.0.12** - a server that could not answer anything reported itself ready. A store id
121
+ outside `[A-Za-z0-9][A-Za-z0-9._-]{0,63}` (`선우`, `田中`, an email) cannot be created
122
+ and cannot be reached, so every tool call 404s - and 1.0.11 warned about it and then
123
+ served anyway. Worse, the boot check told the agent to fix it with `create_store`,
124
+ which answers 400 on that same id. A bad API key has always exited loudly; an id that
125
+ nothing at runtime can repair is the same situation, so it now exits too. The exception
126
+ is `WONTOPOS_ALLOW_STORE_OVERRIDE=1`, where the agent names a store per call and the
127
+ configured one is only a default - there the server still has work to do, so it warns
128
+ and serves.
129
+ - **1.0.11** - fixes a regression 1.0.10 introduced, published the same day. 1.0.10 was
130
+ right that the folded id should be *announced* and wrong to also *send* it. The API
131
+ records the raw id next to the folded one on purpose: that is how it tells a genuine
132
+ collision (`alice-smith` vs `alice.smith`, two people, one collection) from the same id
133
+ typed again. Sending the pre-folded form destroyed the signal, so a store created as
134
+ `My-Project` answered a later `create_store` for `my_project` with **409
135
+ conflict_error** - the store reported colliding with itself. Reads and writes were
136
+ never affected (the API folds those on lookup either way). Now the raw id travels and
137
+ only the messages speak in folded terms. Also corrected: 1.0.10 warned that an id with
138
+ no latin alphanumerics (`선우`, `田中`) makes every such id share one store. Measured
139
+ against the live rule, that is not what happens - store ids must match
140
+ `[A-Za-z0-9][A-Za-z0-9._-]{0,63}`, so creating one answers 400 and every other call
141
+ 404s. It is unusable, not shared, and the warning now says so. And the general fold
142
+ warning was dropped from this package: the SDK emits its own on the boot call, and two
143
+ near-identical lines in a host log is worse than one.
117
144
  - **1.0.10** - the store id is announced as the id that actually holds the memories.
118
145
  1.0.8 fixed this for whitespace and stopped there: the API also lowercases an id and
119
146
  turns anything outside `[a-z0-9_]` into `_`, so `WONTOPOS_USER_ID=My-Project` read and
package/index.mjs CHANGED
@@ -47,25 +47,31 @@ const ALLOW_STORE_OVERRIDE = flag(process.env.WONTOPOS_ALLOW_STORE_OVERRIDE);
47
47
  // untouched. Say it out loud instead.
48
48
  const STORE_FROM_ENV = (process.env.WONTOPOS_USER_ID || "").trim();
49
49
  const STORE_AS_CONFIGURED = STORE_FROM_ENV || "default";
50
- /** The API's store-id rule, mirrored: lowercase, anything outside [a-z0-9_] → "_".
50
+ /** The API's store-id fold, mirrored: lowercase, anything outside [a-z0-9_] → "_".
51
+ * The collection that holds the memories is named from this, so it is the honest
52
+ * answer to "which store am I on" — WONTOPOS_USER_ID=My-Project reads and writes
53
+ * `my_project`, while the ready line, the boot check and create_store all announced
54
+ * "My-Project", handing the operator their own spelling back as confirmation.
55
+ * 1.0.8 fixed exactly that lie for whitespace and stopped there.
51
56
  *
52
- * Mirrored here NOT to change where memories go — the API folds the id either way,
53
- * so the same collection is reached — but so that everything this process SAYS names
54
- * the store that actually holds them. 1.0.8 fixed exactly this lie for whitespace and
55
- * stopped there; case and punctuation split the message from the store the same way.
56
- * WONTOPOS_USER_ID=My-Project reads and writes `my_project` while the ready line, the
57
- * boot check and create_store all announced "My-Project".
58
- *
59
- * The collision it implies is the heavier half: ids differing only by case or
60
- * punctuation are ONE store and therefore one set of memories. `Alice.Smith`,
61
- * `alice-smith` and `alice_smith` are the same person as far as memory is concerned. */
57
+ * Used for MESSAGES ONLY. 1.0.10 also sent it, and that was wrong: the API records
58
+ * the RAW id alongside the folded one precisely so it can tell a real collision
59
+ * ("alice-smith" vs "alice.smith", different people, one collection) from the same
60
+ * id typed again. Sending the already-folded form destroys that signal — a store
61
+ * created as `My-Project` answered a later create for `my_project` with
62
+ * 409 conflict_error, claiming the store collided with itself. The raw id goes to
63
+ * the API, which owns this mapping; we only stop lying about the result. */
62
64
  const canonicalStoreId = (s) => s.toLowerCase().replace(/[^a-z0-9_]/g, "_");
63
- const STORE = canonicalStoreId(STORE_AS_CONFIGURED);
64
- /** True when the configured id survives the fold with nothing distinguishing left —
65
- * a purely non-Latin id ("선우", "田中") becomes all underscores, so every such id in
66
- * the account lands in the SAME store. Worth its own line: it is not a cosmetic
67
- * difference like a dash, it is two different people sharing one memory. */
68
- const STORE_HAS_NO_ASCII = !/[a-z0-9]/.test(STORE);
65
+ const STORE_FOLDED = canonicalStoreId(STORE_AS_CONFIGURED);
66
+ /** What every request carries: the id as configured, trimmed. The API folds it. */
67
+ const STORE = STORE_AS_CONFIGURED;
68
+ /** Store ids are code-level identifiers — the API requires
69
+ * `[A-Za-z0-9][A-Za-z0-9._-]{0,63}`, so an id with no latin alphanumerics at all
70
+ * ("선우", "田中") is not merely folded, it is unusable: creation answers 400 and
71
+ * every other call 404s, because the `__` it folds to can never be created either.
72
+ * Measured against the live rule, not inferred. */
73
+ const STORE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
74
+ const STORE_ID_UNUSABLE = !STORE_ID_RE.test(STORE);
69
75
  // Cap what a tool result may inject into the model's context. The SDK's 64MB cap
70
76
  // protects the process; this protects the conversation (a 2MB memory must not
71
77
  // become 2MB of context). Measured in UTF-8 BYTES, not JS chars: `str.length`
@@ -559,24 +565,35 @@ await server.connect(transport);
559
565
  };
560
566
  }
561
567
  console.error(
562
- `wontopos-mcp ${VERSION} ready (store: ${STORE}${STORE_FROM_ENV ? "" : " — FALLBACK: WONTOPOS_USER_ID is not set"}${READ_ONLY ? ", read-only" : ""})`
568
+ `wontopos-mcp ${VERSION} ready (store: ${STORE_FOLDED}${STORE_FROM_ENV ? "" : " — FALLBACK: WONTOPOS_USER_ID is not set"}${READ_ONLY ? ", read-only" : ""})`
563
569
  );
564
570
  // Say it at boot, once, when the configured id is not the id that holds the memories.
565
571
  // The operator wrote this value by hand in a host config file and has no other way to
566
572
  // learn it was folded — and the ready line above, which used to repeat their spelling
567
573
  // back to them, was the very thing that made it look fine.
568
- if (STORE !== STORE_AS_CONFIGURED) {
574
+ // The fold itself is NOT announced here: now that the raw id is what travels, the
575
+ // SDK's own warning (2.2.27) fires on the boot check and says the same thing. Two
576
+ // near-identical lines in a host log is worse than one. The ready line above already
577
+ // names the folded store, which is the part the SDK does not cover.
578
+ //
579
+ // This one has no SDK equivalent — the SDK knows the fold, not the creation rule.
580
+ // A key that can do nothing exits(1). An id that can do nothing used to WARN and then
581
+ // serve — so the host reported a healthy MCP server whose every call 404'd, and the
582
+ // boot check's own advice ("create_store") was impossible: creating this id answers
583
+ // 400 by the same rule. Nothing the agent does at runtime can repair it, so it belongs
584
+ // on the same side as the bad key.
585
+ //
586
+ // Unless the store is not pinned: under ALLOW_STORE_OVERRIDE the agent supplies an id
587
+ // per call and the configured one is only a default, so the server still has work.
588
+ if (STORE_ID_UNUSABLE) {
569
589
  console.error(
570
- `wontopos-mcp: WONTOPOS_USER_ID ${JSON.stringify(STORE_AS_CONFIGURED)} is stored as ${JSON.stringify(STORE)} ` +
571
- "(lowercased, and anything outside [a-z0-9_] becomes \"_\"). Ids that differ only by case or punctuation " +
572
- "are ONE store and share one set of memories."
590
+ `wontopos-mcp: WONTOPOS_USER_ID ${JSON.stringify(STORE)} is not a usable store id. The API requires English ` +
591
+ "letters, digits and . _ - (1-64 chars, starting with a letter or digit), so creating it answers 400 and " +
592
+ "every other call 404s. Romanize it or pick a latin id."
573
593
  );
574
- if (STORE_HAS_NO_ASCII) {
575
- console.error(
576
- `wontopos-mcp: nothing distinguishing survives the fold — ${JSON.stringify(STORE_AS_CONFIGURED)} became ` +
577
- `${JSON.stringify(STORE)}, so EVERY id on this key with no a-z0-9 characters lands in this same store. ` +
578
- "Use a latin-alphanumeric id (romanize it, or add a suffix) if these stores must stay apart."
579
- );
594
+ if (!ALLOW_STORE_OVERRIDE) {
595
+ console.error("wontopos-mcp: every tool call would fail and no tool could repair it. Exiting.");
596
+ process.exit(1);
580
597
  }
581
598
  }
582
599
 
@@ -585,7 +602,7 @@ if (STORE !== STORE_AS_CONFIGURED) {
585
602
  // A missing store is recoverable (the agent can create_store) → warn and serve.
586
603
  mem.stats().then(
587
604
  (s) => {
588
- console.error(`wontopos-mcp: store "${STORE}" verified (${s?.total_memories ?? "?"} memories).`);
605
+ console.error(`wontopos-mcp: store "${STORE_FOLDED}" verified (${s?.total_memories ?? "?"} memories).`);
589
606
  // Verified, but on the fallback store — the case a misconfiguration hides in.
590
607
  // The seeded `default` always exists, so this check passes and the operator has
591
608
  // no other signal that the store they configured is not the one in use.
@@ -603,15 +620,18 @@ mem.stats().then(
603
620
  process.exit(1);
604
621
  } else if (status === 404) {
605
622
  console.error(
606
- `wontopos-mcp: the key works but store "${STORE}" does not exist — ` +
607
- (READ_ONLY
608
- ? "fix WONTOPOS_USER_ID (read-only mode cannot create stores)."
609
- : "ask the agent to create it (create_store) or fix WONTOPOS_USER_ID.")
623
+ `wontopos-mcp: the key works but store "${STORE_FOLDED}" does not exist — ` +
624
+ (STORE_ID_UNUSABLE
625
+ // create_store answers 400 on this id by the same rule, so do not send them there.
626
+ ? "and it cannot be created under this id. Set WONTOPOS_USER_ID to a latin-alphanumeric id."
627
+ : READ_ONLY
628
+ ? "fix WONTOPOS_USER_ID (read-only mode cannot create stores)."
629
+ : "ask the agent to create it (create_store) or fix WONTOPOS_USER_ID.")
610
630
  );
611
631
  } else if (status === 402) {
612
632
  console.error("wontopos-mcp: the key works but the account has no card or balance (402) — top up at https://wontopos.com/console.");
613
633
  } else {
614
- console.error(`wontopos-mcp: could not verify store "${STORE}" (${e?.message ?? e}) — continuing anyway.`);
634
+ console.error(`wontopos-mcp: could not verify store "${STORE_FOLDED}" (${e?.message ?? e}) — continuing anyway.`);
615
635
  }
616
636
  }
617
637
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos-mcp",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "mcpName": "com.wontopos/mcp",
5
5
  "description": "(beta) Wontopos (WOS) long-term memory as an MCP server — one memory across Claude Code, Claude Desktop, Cursor, ChatGPT, and your own agents.",
6
6
  "keywords": [