wontopos-mcp 1.0.10 → 1.0.11

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 +24 -6
  2. package/index.mjs +37 -32
  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,21 @@ automatic retries, redirect refusal, response caps, and key hygiene apply as-is.
114
117
 
115
118
  ## Changelog
116
119
 
120
+ - **1.0.11** - fixes a regression 1.0.10 introduced, published the same day. 1.0.10 was
121
+ right that the folded id should be *announced* and wrong to also *send* it. The API
122
+ records the raw id next to the folded one on purpose: that is how it tells a genuine
123
+ collision (`alice-smith` vs `alice.smith`, two people, one collection) from the same id
124
+ typed again. Sending the pre-folded form destroyed the signal, so a store created as
125
+ `My-Project` answered a later `create_store` for `my_project` with **409
126
+ conflict_error** - the store reported colliding with itself. Reads and writes were
127
+ never affected (the API folds those on lookup either way). Now the raw id travels and
128
+ only the messages speak in folded terms. Also corrected: 1.0.10 warned that an id with
129
+ no latin alphanumerics (`선우`, `田中`) makes every such id share one store. Measured
130
+ against the live rule, that is not what happens - store ids must match
131
+ `[A-Za-z0-9][A-Za-z0-9._-]{0,63}`, so creating one answers 400 and every other call
132
+ 404s. It is unusable, not shared, and the warning now says so. And the general fold
133
+ warning was dropped from this package: the SDK emits its own on the boot call, and two
134
+ near-identical lines in a host log is worse than one.
117
135
  - **1.0.10** - the store id is announced as the id that actually holds the memories.
118
136
  1.0.8 fixed this for whitespace and stopped there: the API also lowercases an id and
119
137
  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,25 +565,24 @@ 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
+ if (STORE_ID_UNUSABLE) {
569
581
  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."
582
+ `wontopos-mcp: WONTOPOS_USER_ID ${JSON.stringify(STORE)} is not a usable store id. The API requires English ` +
583
+ "letters, digits and . _ - (1-64 chars, starting with a letter or digit), so creating it answers 400 and " +
584
+ "every other call 404s. Romanize it or pick a latin id."
573
585
  );
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
- );
580
- }
581
586
  }
582
587
 
583
588
  // Boot check (one metered stats call, after connect so host startup never waits):
@@ -585,7 +590,7 @@ if (STORE !== STORE_AS_CONFIGURED) {
585
590
  // A missing store is recoverable (the agent can create_store) → warn and serve.
586
591
  mem.stats().then(
587
592
  (s) => {
588
- console.error(`wontopos-mcp: store "${STORE}" verified (${s?.total_memories ?? "?"} memories).`);
593
+ console.error(`wontopos-mcp: store "${STORE_FOLDED}" verified (${s?.total_memories ?? "?"} memories).`);
589
594
  // Verified, but on the fallback store — the case a misconfiguration hides in.
590
595
  // The seeded `default` always exists, so this check passes and the operator has
591
596
  // no other signal that the store they configured is not the one in use.
@@ -603,7 +608,7 @@ mem.stats().then(
603
608
  process.exit(1);
604
609
  } else if (status === 404) {
605
610
  console.error(
606
- `wontopos-mcp: the key works but store "${STORE}" does not exist — ` +
611
+ `wontopos-mcp: the key works but store "${STORE_FOLDED}" does not exist — ` +
607
612
  (READ_ONLY
608
613
  ? "fix WONTOPOS_USER_ID (read-only mode cannot create stores)."
609
614
  : "ask the agent to create it (create_store) or fix WONTOPOS_USER_ID.")
@@ -611,7 +616,7 @@ mem.stats().then(
611
616
  } else if (status === 402) {
612
617
  console.error("wontopos-mcp: the key works but the account has no card or balance (402) — top up at https://wontopos.com/console.");
613
618
  } else {
614
- console.error(`wontopos-mcp: could not verify store "${STORE}" (${e?.message ?? e}) — continuing anyway.`);
619
+ console.error(`wontopos-mcp: could not verify store "${STORE_FOLDED}" (${e?.message ?? e}) — continuing anyway.`);
615
620
  }
616
621
  }
617
622
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wontopos-mcp",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
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": [