Conversation
ci: update Walrus site package
This reverts commit 7830b32.
…ggregator feat: add seal committee aggregator support
…bility - packages/mcp: new @memwal/mcp npm package — stdio MCP server, browser wallet login flow, auto-reconnect SSE bridge, env presets (--prod/--dev/--staging/--local), auth-required fallback that serves a friendly inline error when ~/.memwal/credentials.json is missing - services/server/scripts/mcp: Node sidecar MCP routes (/mcp/sse + /mcp/messages), 3s heartbeat keepalive, per-session transport map, idempotent cleanup so late close events on replaced transports don't wipe the active session - services/server/src/mcp_proxy.rs: Rust axum reverse proxy with 24h timeout override on SSE GET; the shared http_client carried a 30s timeout that was killing long tool calls (walrus blob writes take 25-30s). Adds anti-buffering response headers (X-Accel-Buffering: no, cache-control no-transform, connection keep-alive) - apps/app/src/pages/ConnectMcp: /connect/mcp consent page with sponsored add_delegate_key tx + localhost callback. Post-login ClientConfigPanel shows paste-ready config snippets for Cursor / Claude Desktop / Claude Code / Antigravity with copy buttons and env-aware CLI flags - services/server/scripts/package.json: bump @mysten/walrus 1.0.3 -> 1.1.7, @mysten/sui 2.5.0 -> 2.16.2, @mysten/seal 1.1.0 -> 1.1.3 to match the current Walrus contract — older versions hit "balance::destroy_zero" abort on remember - plans: design doc covering Phase A package, Phase B login flow, Phase B.5 deferred OAuth-style UX, Phase D dashboard panel
… H4)
C2 — localhost /callback CSRF + DNS rebinding:
- packages/mcp/src/login.ts: generate 32-byte state token, embed in
connectUrl, require callback to echo it back; constant-time compare
(timingSafeEqual). Reject if missing or mismatch.
- Validate Host header against {127.0.0.1:port, localhost:port} —
defeats DNS rebinding to an attacker-controlled name resolving to
loopback.
- Validate Origin header equals webUrl (no wildcard, no missing).
- Replace `Access-Control-Allow-Origin: *` with exact webUrl. Add `Vary: Origin`.
- Assert `Content-Type: application/json` so simple-request cross-origin
POSTs (text/plain, form-urlencoded) hit preflight and get blocked
by the restricted CORS.
- apps/app/src/pages/ConnectMcp.tsx: read `state` from query params,
validate as 64-hex, include in callback POST payload. Treat missing
state as invalid query (forces bridge upgrade in lockstep).
H3 — bridge creds-wipe DoS on 401:
- packages/mcp/src/bridge.ts: stop calling clearCreds() on 401. A 401
from the relayer is evidence of a problem (revoked key, WAF, proxy
MITM, --local malware) but not proof the saved seed is the cause.
Auto-wipe turned any one of those into a permanent outage. Now we
surface a loud error pointing the user at `memwal-mcp login` and
leave credentials.json untouched.
H4 — --relayer silently rewrote saved creds:
- packages/mcp/src/index.ts: --relayer flag now overrides the relayer
in-memory for the current process only; the saved credentials.json
is no longer mutated. Stops malicious config snippets (`--relayer
https://attacker`) from rewriting saved creds so future spawns ship
the seed to the attacker. To rotate the saved relayer, the user
must explicitly `memwal-mcp --logout` then re-login.
Verified locally: SSE handshake unchanged, memwal_remember E2E 27.5s
with blob_id returned, memwal_recall returns saved memories. Build clean.
Remaining audit findings tracked for follow-up:
- C1 (bearer = seed) — needs signed-request migration, larger refactor
- H1, H2, M1-M7, L1-L6 — see plans/2026-05-11-memwal-mcp-package-with-login.md
Resolves conflict in services/server/scripts/sidecar-server.ts (both sides added a new top-level import on the same line — kept both). Additional cleanup while resolving: - services/server/scripts/mcp/index.ts: drop the unused default `import express from "express";` runtime binding. Only the type imports (Express, Request, Response) are used in this file. dev brings in: - seal committee aggregator support (PR #136) - seal threshold + sidecar config extraction to seal-config.ts - vite stale-chunk reload fix in apps/app - doc updates Verified: packages/mcp tsc clean, apps/app tsc clean, cargo check on services/server clean. MCP route mount call site unchanged.
CI installed `zod@4.4.3` per the direct dep in sidecar package.json, then
npm rejected the resolution because `@mysten-incubation/memwal@0.0.3`
declares `peerOptional zod@^3.23.0` — incompatible with major 4 even
though `@modelcontextprotocol/sdk@1.29.0` itself accepts `^3.25 || ^4.0`.
Resolved by pinning the sidecar's zod to `^3.25.0` (lockfile picks
3.25.76). Satisfies:
- @modelcontextprotocol/sdk@1.29.0 → "^3.25 || ^4.0" ✓
- @mysten-incubation/memwal@0.0.3 → "^3.23.0" ✓
Our zod usage (services/server/scripts/mcp/tools/{remember,recall,
analyze,restore}.ts) is limited to `z.string()`, `.min()`, `.optional()`,
`.describe()` — no zod-v4-only surface — so the downgrade is mechanical.
`plans/2026-05-11-mcp-client-testing-guide.md` and `plans/2026-05-11-memwal-mcp-package-with-login.md` were committed in 2e788fe by mistake — they are personal/internal design notes, not intended for the public repo. Also adds `plans/` to .gitignore so future commits never reach in. The content is preserved locally in the author's untracked `.local-plans/` directory.
Step 2 "remember" used to print the HTTP 202 accept envelope and stop:
{ "job_id": "...", "status": "running" }
The relayer continues embedding, encrypting, uploading, and indexing in
the background — but the playground never re-queried so the UI looked
frozen. Users had no way to know whether the memory was actually saved.
Fix: runRemember now runs the accept-then-poll pattern that PR #121
introduced and that the rest of the SDK already follows:
1. memwal.rememberAsync(text)
→ 202 envelope rendered immediately so the async-job model is still
visible (the playground's whole point of demonstration).
2. memwal.waitForRememberJob(job_id, { timeoutMs: 90_000 })
→ signed GET /api/remember/{job_id} polled until terminal. On done,
the result block updates with { id, blob_id, owner, namespace }
and the elapsed time. On failed/timeout, the SDK throws with the
server's error field, surfaced via setRememberError. On timeout
specifically, the error message includes the job_id so the user
can curl the status endpoint manually.
Also updates the displayed code snippet in the step header so it
matches the implementation (was a stale single-call example that the
user could copy-paste and hit the same frozen-UI bug).
No SDK / server changes — rememberAsync + waitForRememberJob already
exist in the SDK and the relayer's /api/remember/{job_id} route already
returns the full state machine.
…→ done) Earlier commit on this branch (df7e019) fixed the frozen-UI bug by chaining rememberAsync + waitForRememberJob, but waitForRememberJob only resolves at terminal — the user still didn't see the intermediate states the relayer's state machine walks through (running → uploaded → done). The job felt monolithic. This commit surfaces each transition. SDK — packages/sdk/src/memwal.ts: - Adds a public `getRememberStatus(jobId)` that wraps signed `GET /api/remember/{job_id}` and returns the full RememberJobStatus (pending | running | uploaded | done | failed | not_found). Mirrors the existing public `getRememberBulkStatus` shape. Callers driving their own polling loop (UI, dashboards) can now read the live state without going through waitForRememberJob (which hides everything before terminal). Playground — apps/app/src/pages/Playground.tsx: - runRemember rewritten to drive its own polling loop on top of the new getRememberStatus. Each poll (1.5s) checks for a status change; on transition we push `{status, elapsed_s}` onto a ladder and re-render. The result block now shows three sections that update live: (1) the 202 accept envelope, (2) the running ladder of state transitions with timestamps, (3) the terminal RememberJobStatus once `done`/`failed`/`not_found`. Strict 90s overall bound; on timeout the error message includes the job_id so the user can curl `/api/remember/{job_id}` themselves. - Imports `RememberJobStatus` type from the SDK. - Updates the displayed code snippet in the step header to show the manual-polling pattern. The earlier snippet referenced waitForRememberJob, which is still the right call for production code that just wants the final result — the playground deliberately picks the more verbose loop so the user can SEE the state machine. No server changes. /api/remember/{job_id} already returned the full state machine — the relayer just needed a UI client willing to look at each value.
Adds `/api/mcp` — a single endpoint that speaks the MCP 2025-06
Streamable HTTP transport. After this lands, users add MemWal to any
MCP client without installing the `@memwal/mcp` package:
claude mcp add --transport http memwal https://relayer.memwal.ai/api/mcp
Architecture matches MailGate / Linear / Figma's MCP servers — one URL,
session id round-trips through the `mcp-session-id` header, auth on
every request via the existing Bearer scheme (no OAuth dance, no JWT,
no .well-known mess).
Sidecar — services/server/scripts/mcp/index.ts:
- Mounts GET/POST/DELETE /mcp via `StreamableHTTPServerTransport` from
`@modelcontextprotocol/sdk` 1.29 (already a dep).
- Per-session transport keyed by `mcp-session-id`. The SDK's
`onsessioninitialized` callback wires the cleanup so a sidecar restart
doesn't leak transports.
- Auth identical to the existing SSE path — same `resolveAuth(...)` lookup,
same Bearer + X-MemWal-Account-Id headers. SSE routes
(/mcp/sse + /mcp/messages) kept alive for the stdio bridge's backward
compatibility.
Rust relayer — services/server/src/mcp_proxy.rs:
- New `streamable_proxy` handler routed at `/api/mcp` (GET/POST/DELETE/
OPTIONS). Method dispatched on inbound, body forwarded verbatim,
response streamed via `Body::from_stream` so both small JSON bodies
and long-lived SSE upgrades work without buffering.
- Header allowlist extended with `mcp-session-id`, `mcp-protocol-version`,
`last-event-id` — the SDK on both ends reads these to route
requests to the right session and resume after disconnects.
- 24h per-request timeout override carried over from the SSE proxy
fix (commit 8990a88) so walrus blob writes don't trip the shared
http_client's 30s ceiling.
Dashboard — apps/app/src/pages/ConnectMcp.tsx:
- `ClientConfigPanel` adds a transport-mode pill (`stdio + package` vs
`http`). Both modes render the JSON snippet inline with copy buttons;
http mode shows the user's accountId and leaves Bearer as a
placeholder (the seed lives in ~/.memwal/credentials.json — never
surfaced to the dashboard for security reasons).
- HTTP hint block tells the user where to pick up the bearer and
reminds them to treat it like a long-lived API token.
Verified locally (Rust + sidecar both restart cleanly):
- POST /api/mcp initialize → 200, mcp-session-id assigned (~73ms)
- tools/list → 4 tools registered
- tools/call memwal_remember → blob_id returned at T+24.4s end-to-end
through the new transport
Tradeoff vs full OAuth 2.1: no native "Connect" button in client UIs,
user pastes a 7-line config block manually. Same UX shape as the
stdio package (which also requires a paste). Phase B.5 OAuth remains
on the follow-up list for when we want the Linear/Figma-style
one-click connect; this commit unblocks the Henry-confirmed simplest
path in the meantime.
Fix/playground poll job status
…session binding
Three reviewer-flagged blockers, plus tests that exercise the fixes
end-to-end via real HTTP.
1. Dockerfile (services/server/Dockerfile)
sidecar-server.ts now imports ./mcp/index.js, but the runtime stage
only copied scripts/*.ts. The MCP module was missing in the image,
so deployed containers would crash at boot with module-not-found.
Added an explicit COPY scripts/mcp/ ./scripts/mcp/.
2. Rate limit before MCP session creation
(services/server/scripts/mcp/rateLimit.ts + index.ts)
resolveAuth() only validates header shape; a flood of forged
bearers + valid-looking accountIds could open long-lived SSE /
streamable-HTTP transports and hold sidecar memory + 24h proxy
streams indefinitely.
New McpRateLimiter caps:
- total concurrent sessions globally
- concurrent active sessions per source IP
- new session opens per source IP per minute (sliding window)
Limiter runs BEFORE resolveAuth() on /mcp/sse and on POST /mcp
for new sessions. Slot is one-shot released on transport close,
on auth failure, and on handleRequest throw.
Limits env-tunable: MCP_MAX_TOTAL_SESSIONS,
MCP_MAX_SESSIONS_PER_IP, MCP_MAX_NEW_SESSIONS_PER_IP_PER_MIN.
Limiter is lazy-initialized so tests can override env before
first request (ESM hoists imports above top-level env writes).
Relayer (services/server/src/mcp_proxy.rs) now injects the real
client IP via x-forwarded-for on all three MCP routes (sse,
messages, streamable), preserving any inbound chain. Without
this the limiter would bucket per loopback.
3. Streamable HTTP session bound to authenticated caller
(services/server/scripts/mcp/index.ts:handleStreamableHttp)
Previously, on a request carrying an existing mcp-session-id,
we dispatched directly without checking the auth matched the
one that opened the session — anyone who learned the UUID
could drive an existing transport under their own (or random)
credentials. Now we reject 403 unless conn.sessionKey ===
auth.sessionKey (delegate:accountId:delegatePubKey). Mismatch
logged as session.auth_mismatch.
Tests
-----
services/server/scripts/mcp/__tests__/rateLimit.test.ts (16 unit
tests) — McpRateLimiter caps, releaseFn idempotency, multi-IP
independence, clientIpFromRequest x-forwarded-for parsing,
loadRateLimitConfigFromEnv defaults + garbage handling.
services/server/scripts/mcp/__tests__/integration.test.ts (6 real-
HTTP integration tests) — spins up mountMcpRoutes on an ephemeral
port and verifies via fetch:
- POST /mcp initialize creates session, returns mcp-session-id
- 3rd open from same IP within window → 429 ip_burst_cap
- separate XFF IPs get independent buckets
- reusing mcp-session-id under different bearer → 403
- reusing under SAME bearer is accepted (sanity)
- malformed bearer → 401 with www-authenticate
services/server/src/mcp_proxy.rs (7 cfg(test) tests) —
inject_forwarded_for (empty/existing/whitespace-only inbound,
IPv6 peer), should_forward (allow/block lists),
build_forwarded_headers (drops cookies/host, keeps authorization
+ x-memwal-*).
services/server/scripts/package.json — wires `npm test` to
node --test --import tsx (no new dep; built-in node:test runner).
Verification
------------
cargo test → 142 passed (135 prior + 7 new)
npm test (sidecar) → 22 passed (16 unit + 6 HTTP integration)
cargo check → clean
…retry classification (MEM-35)
Per Will Bradley (Mysten, 2026-05-12 Slack): coin-object equivocation locks
no longer occur. Collapse the per-wallet Apalis routing layer to a single
wallet, single queue. Classify retry behavior so transient failures get
Apalis backoff and permanent failures (Move abort, object lock) get marked
Dead immediately. Add /metrics/wallet so the simplification can be
validated empirically post-deploy.
Rust changes (services/server/src/):
- types.rs: wallet_storages: Vec<WalletJobStorage> → wallet_storage:
WalletJobStorage. KeyPool::next_index() always Some(0) when a key is
configured; extra keys ignored with a warning at boot.
- main.rs: drop per-wallet spawn loop. Single Apalis worker on queue
'wallet_jobs' with WALLET_JOB_CONCURRENCY env (default 8). Boot logs
'Multi-wallet routing was retired'.
- routes.rs: enqueue_wallet_job uses single storage; wallet_index in
payload retained for audit only.
- jobs.rs: WalletJobError::{Transient, Permanent} + classify_sidecar_error
heuristic on objectlocked/moveabort substrings. Permanent failures emit
tracing::warn!(target: "wallet_job.permanent") for ops alerting. 3 new
unit tests cover classification.
- db.rs: wire migration 007.
- migrations/007_collapse_wallet_queues.sql: idempotent rename of
apalis.jobs.job_type from 'wallet-%' to 'wallet_jobs' for Pending rows.
Guarded by to_regclass IS NOT NULL so brand-new DBs don't fail.
Done/Killed rows keep their historical name as audit trail.
Sidecar changes (services/server/scripts/sidecar-server.ts):
- Keep runExclusiveBySigner mutex. Equivocation locks are gone but the
mutex incidentally fixes Enoki sponsor expiry + SDK default gas-coin
contention under same-wallet concurrency (testnet measured ~75% failure
without it at concurrency=4).
- New submitWalletTransaction wrapper around executeWithEnokiSponsor that
increments counters and classifies errors via regex.
- New GET /metrics/wallet (no auth, before middleware — same posture as
/health) returning walletSubmittedTotal, walletLockErrorsTotal,
walletPermanentFailuresTotal, enokiEnabled, suiNetwork.
Local validation:
- cargo test: 139 passed, 0 failed (incl. 3 new classify_* tests).
- tsc --noEmit on sidecar TS: clean.
- Boot smoke with N=5 keys configured: 'Multi-wallet routing was retired'
fires, single 'wallet_jobs' worker spawned (concurrency=8), migration
007 applied cleanly.
- Migration 007 idempotency: multiple sequential runs on real Postgres
exit 0, no row changes.
- End-to-end testnet via UI (apps/app playground): signed remember →
real Walrus blob created, recall returned stored memories.
- /metrics/wallet after 100+ tx submissions on single wallet:
walletLockErrorsTotal=0.
Operational impact:
- Wallet count 5 → 1.
- Throughput: 5× parallel → 1× serial via mutex. Acceptable per Daniel's
'Walrus uploading is happening on the background' (peak < 10 jobs/s).
- Apalis queues: N → 1.
Migration 007 is non-destructive; rollback is git revert + redeploy.
Post-deploy validation: alert on walletLockErrorsTotal > 0 via
/metrics/wallet scrape.
…hertext-in-redis-after-walrus-upload MEM-37: Cache SEAL ciphertext in Redis
Map permanent wallet failures to terminal Apalis aborts, keep retryable jobs on the collapsed queue during migration, and remove sidecar signer serialization so same-wallet submissions can run concurrently with retries.
…et-queues feat(server): collapse multi-wallet Apalis queues to single wallet
… login command (ENG-1749)
Before this change, first-run setup of the stdio MCP package required two
terminal commands:
codex mcp add memwal -- npx -y @mysten-incubation/memwal-mcp --dev # register
npx -y @mysten-incubation/memwal-mcp login --dev # browser login
Step 2 forced users to leave their MCP client, run a separate CLI command,
then restart the client. PR #141 review (ducnmm) flagged this as a merge
blocker; Henry confirmed on Slack the same friction applies internally.
After this change, only step 1 is needed. The agent calls memwal_login as
a tool inline; the user clicks the URL in chat, approves the wallet, and
the next memwal_* tool call succeeds.
Implementation
--------------
`packages/mcp/src/auth-required.ts`:
- Advertise a 5th tool `memwal_login` in `tools/list` when no creds exist.
- `tools/call memwal_login` returns the dashboard `/connect/mcp` URL
near-instantly (~250ms). The HTTP listener stays alive in the background
for 5 minutes; on user approval, credentials land at
~/.memwal/credentials.json. The tool result text repeats the URL three
times (header, code block, markdown link) so MCP clients that paraphrase
tool output (Claude Code) cannot strip every copy.
- Pass-through `relayerUrl` / `webUrl` / `label` from the entry point so
--dev / --staging / --local correctly route to the matching dashboard
(previously dropped on the auth-required path → always opened prod).
- LOGIN_INSTRUCTION (returned for the other 4 tools when creds missing)
now leads with "call memwal_login from this client" before the CLI
fallback.
`packages/mcp/src/bridge.ts`:
- Same `memwal_login` available in BRIDGE mode (when creds exist) so users
can re-login or switch wallets without removing+re-adding the MCP server.
- New `memwal_logout` tool: clears ~/.memwal/credentials.json and
instructs the user to revoke the on-chain delegate key from the
dashboard if they want full revocation. Local-only, never forwarded.
- `tools/list` responses from the relayer get spliced with the two local
tool definitions on the way back to the client.
- `tools/call memwal_login` / `memwal_logout` intercepted in stdin pump,
handled locally, never sent to the relayer.
`packages/mcp/src/login.ts`:
- New `onUrl` callback option fired as soon as the localhost listener is
bound. Lets callers (the tool wrapper) push the URL inline via the MCP
result before awaiting the callback.
`packages/mcp/src/index.ts`:
- Pass resolved `{relayerUrl, webUrl, label}` through to both
runAuthRequiredServer and runBridge.
`packages/mcp/package.json`:
- Add `engines: { node: ">=20.0.0" }` so npm install warns on older Node.
- Pin all four runtime deps (drop the `^`). For a public stdio package
spawned via `npx -y` (always pulls latest), exact pins eliminate the
supply-chain risk of a transitive dep update introducing a CVE between
publishes. Lockfile updated accordingly.
Local validation
----------------
- pnpm --filter @mysten-incubation/memwal-mcp build: clean (tsc, no errors)
- Direct stdin smoke against fresh HOME, --dev: tools/list returns 5 tools
in auth-required mode (with memwal_login); memwal_login tool call
returns dashboard URL <1s with correct dev.memwal.ai host
- URL routing verified for --prod / --staging / --dev / --local: each
routes to its matching webUrl + relayerUrl (https://memwal.ai,
staging.memwal.ai, dev.memwal.ai, localhost:5173)
- openBrowser disabled in MCP context: prevents macOS `open` from
foregrounding an existing memwal.ai tab instead of navigating to the
full /connect/mcp?... URL. Users click the URL surfaced in chat instead.
Out of scope (separate ticket)
------------------------------
- Native remote MCP OAuth on /api/mcp HTTP transport (ENG-1750). Covers
the second surface (HTTP transport), independent code area, larger
scope. Both flows coexist after both ship.
Closes ENG-1749.
Integrates MEM-35 (multi-wallet → single-wallet collapse) + MEM-37
(SEAL ciphertext Redis cache) + follow-up fixes (single-wallet retry
consistency, concurrent Walrus jobs) from dev.
Conflict in services/server/scripts/sidecar-server.ts:
- HEAD added `mountMcpRoutes(app, ...)` before the shared-secret auth
middleware (this PR's MCP routes).
- dev added `app.get("/metrics/wallet", ...)` in the same pre-auth
position (MEM-35 observability).
Both blocks are independent route mounts that need to coexist — kept
both, MCP routes first then /metrics/wallet, both before the
shared-secret check. No semantic overlap; both serve different
endpoints with different trust models.
Verified:
- cargo test: 149 passed, 0 failed (auto-merged main.rs + routes.rs OK)
- pnpm --filter @mysten-incubation/memwal-mcp build: clean
- tsc on sidecar TS: clean
feat(mcp): @memwal/mcp stdio bridge + browser login
…rage + routes split (#147) Restructures services/server into four focused layers, with zero behavioural regression against dev: - src/engine/ — MemoryEngine trait + WalrusSealEngine (production) and PlaintextEngine (benchmark). Handlers depend on Arc<dyn MemoryEngine> and are mode-blind. - src/services/ — extracts Embedder, Extractor, LlmChat from routes.rs; prompts moved to services/prompts/*.txt versioned text assets bundled via include_str!. - src/storage/ — moves db.rs, seal.rs, sui.rs, walrus.rs from src/ into src/storage/. - src/routes/ — splits the 2,844-line routes.rs into per-endpoint files: analyze.rs, recall.rs, remember.rs, admin.rs, sponsor.rs, mod.rs. Product additions: - BENCHMARK_MODE=true env flag — selects PlaintextEngine so the AI- quality benchmark suite can run without burning Walrus testnet quota. Off by default; loud warning on startup. Auth stays mode-blind. - POST /api/forget — owner-scoped, namespace-wide hard delete on vector_entries (used by the benchmark harness for inter-run cleanup). - POST /api/stats — owner-scoped namespace stats (count + bytes). - GET /health response gains a "mode" field ("production" | "benchmark") — additive, non-breaking. Rebase absorbed three dev-side feature streams that landed in parallel: - MEM-35 — single-wallet Apalis queue collapse + WalletJobError retry classification (dev commit 316cefd, PR #144). - MEM-37 — Redis SEAL ciphertext warm cache + BLOB_CACHE_MAX_BYTES size cap (dev commit 17fc86b, PR #143). Integrated into WalrusSealEngine during conflict resolution so the cap lives next to the cache it bounds. Cache policy factored into pure read_decision / write_decision helpers for unit-testability. - MCP server sidecar + proxy routes (PRs #141, #144). Routes mounted in main.rs; MEMWAL_RELAYER_URL piped to sidecar. Migration-number collision resolved by renaming this PR's benchmark- plaintext migration to 008_*; dev's 007_collapse_wallet_queues.sql is preserved unchanged. Defence-in-depth touch-ups (all in scope of the layer split): - LOW-S1: owner filter on fetch_plaintext_by_blob_id. - LOW-S5: /api/ask body.limit clamped at 100. - F3: upfront SEAL credential probe in /api/ask. Validation: - cargo test — 159/159 pass (+10 new: MEM-37 cache cap policy, /api/ask limit cap, /forget + /stats empty-namespace validation). - cargo check, cargo fmt --check — clean. - cargo clippy --all-targets — 8 warnings (carry-over from dev + 1 expected too_many_arguments on WalrusSealEngine::new after MEM-37 added blob_cache_max_bytes). - AI-quality preserved across rebase: - LOCOMO three-way (BENCHMARK_MODE=true): 54.5 → 54.8 → 53.5 J overall; every category within the ±2-3 J judge-noise floor. - LongMemEval (500 oracle queries): 71.7 J overall vs Mem0 49.0 / Zep 63.8 / Supermemory 85.4. - Four-reviewer panel (architect / backend / security / quality) signed off MERGE READY. Benchmark evidence committed at services/server/review/assessment/ benchmark-runs/2026-05-13-eng1747-quality-validation/ (README + 8 result JSONs). auth.rs is a 1-line namespace-rename diff vs dev; rate_limit.rs is byte-identical. Closes ENG-1747, MEM-40, MEM-41.
fix(relayer): enable TLS for Neon Postgres
Bring the MCP overview in line with the inline-login flow shipped in 1b296b1. Previously the docs told users to run `npx ... login` from a separate terminal and restart their MCP client. After ENG-1749 the stdio package advertises memwal_login and memwal_logout tools, so the agent can sign in or switch accounts without leaving the chat. Changes: - Tools section: split into 4 memory tools + 2 session tools (6 total) - Streamable HTTP setup: keep the CLI as the way to mint a bearer token - stdio setup: lead with the inline memwal_login flow; CLI is now a fallback. Note the 5-minute URL window and the memwal_logout path - New "CLI Flags and Environment Variables" section covering --logout, --label, --web-url, --login, --help, and MEMWAL_MCP_DEBUG - Environment presets: surface the dashboard URL alongside the relayer - Self-hosting: inline the key MCP session-cap env vars instead of punting entirely to the env-vars reference packages/mcp/README.md gets a one-line nudge so the package README also points users at the inline-login tool rather than implying a manual CLI step.
Clean up ENG-1405 follow-ups
docs(mcp): document memwal_login + memwal_logout tools (ENG-1749)
fix(restore): lower default restore limit
chore(release): bump SDK package metadata
harrymove-ctrl
approved these changes
May 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
staging <- dev