Repository navigation
feat: add seekmoon as a plugin install target - #20
chaijie2018 wants to merge 1 commit into
Conversation
SeekMoon (moonbitlang/openseek) reads MCP servers from `mcpServers.<name>` in `<runtime_dir>/host-mcp.json`, the same shape Claude Desktop uses, so the adapter reuses the shared mcpWriter instead of growing a new writer: macOS ~/Library/Application Support/SeekMoon/host-mcp.json Windows %APPDATA%/SeekMoon/host-mcp.json Linux ~/.config/SeekMoon/host-mcp.json The desktop keeps its own `seekmoon` entry (a per-launch localhost tool server) in that file; the shared upsert only touches `mcpServers.everme-memory`, so the desktop's entry and any hand-added server survive, and uninstall removes only ours. The install result reports the step that actually makes the server live. SeekMoon connects its MCP servers once per engine process and keeps those connections across turns; a desktop conversation runs its own engine, spawned with `--mcp-config=<runtime>/host-mcp.json` (internal/openseek/serve/loop.mbt). A new entry is therefore picked up by the next conversation or the next app launch, never by a conversation that is already open — which is what the NextStep says. Verified locally with go1.27.3: `go build ./...`, `gofmt -l` clean, `go test ./internal/plugin/` green (including the six SeekMoon tests), and `plugin list` detects this machine's SeekMoon at the right config path. `plugin install seekmoon` reaches the backend and is refused there with `platform: seekmoon does not validate as known_platform` (20004) — the platform enum is server-side, so this adapter needs a matching backend release before install can mint an agent. Co-Authored-By: SeekMoon <seekmoon@moonbitlang.com>
|
First contribution to this repo, so the CI run is sitting in One thing worth flagging while it waits: {"error": {"type": "upstream", "code": 20004, "message": "platform: seekmoon does not validate as known_platform"}}So the client side here is complete and tested, but wiring a real install needs That also settles a small decision before the enum is touched: the platform id. This PR uses |
Summary
SeekMoon (moonbitlang/openseek) is a desktop
agent that keeps its MCP servers in one JSON file, using the same
mcpServers.<name>shape Claude Desktop does. This PR makes it an install target, so its memory comes from
EverMe like any other host: a detector that finds the app, and a writer that adds the
everme-memoryentry with a freshly minted agent token.seekmoonmust joinknown_platformThe client side is finished:
plugin listdetects the host,plugin uninstall seekmoonremoves our entry, and the writer and its tests are in place — but
plugin installcurrently stops at the EverMe backend, which validates
platformagainst a closed enum:{ "ok": false, "error": { "type": "conflict", "detail": { "failed": [ { "platform": "seekmoon", "error": { "type": "upstream", "code": 20004, "message": "platform: seekmoon does not validate as known_platform" } } ] } } }Minting an agent therefore needs
seekmoonadded toknown_platformin the managedbackend, so this should ship together with that release. One decision belongs in
review before the enum is touched: the platform id —
seekmoon(proposed, matching theapp name) or
openseek(the repository name). Client and backend have to agree on it.Changes
cli/internal/plugin/types.go—PlatformSeekMoon = "seekmoon", the id used by--platformand reported indata.platforms[].platform.cli/internal/plugin/seekmoon.go— a detector (runtime directory per OS, the macOSbundle,
seekmoon/openseekonPATH) and a writer that wraps the sharedmcpWriter. No new upsert, backup, TOCTOU or atomic-write code.cli/internal/plugin/registry.go,registry_test.go— register the platform in boththe detectors and writers maps, which is what the existing invariant test checks.
cli/internal/plugin/seekmoon_test.go— six new test functions (ten cases).README.md,README.zh.md,cli/README.md,cli/AGENTS.md,CHANGELOG.md— theinstall command, the host table, and the "takes effect on the next conversation" note.
What the install writes, and where:
~/Library/Application Support/SeekMoon/host-mcp.json%APPDATA%\SeekMoon\host-mcp.json~/.config/SeekMoon/host-mcp.jsonmcpServers.everme-memory:npx -y @everme/memory-mcp@latestplusEVERME_API_BASE,EVERME_AGENT_ID,EVERME_AGENT_TOKEN— through the shared writer,so the
.bakbackup, the TOCTOU check and the atomic rename all apply.seekmoonentry there (a per-launch localhost tool server); the upsert only touches our key, so
that entry and any server the user added by hand survive. Uninstall removes only ours,
and a second uninstall is a no-op.
0600, because the entry carries a liveevt_token.per engine process and reuses those connections across turns, and a desktop
conversation runs its own engine spawned with
--mcp-config=<runtime>/host-mcp.json(
internal/openseek/serve/loop.mbt). A new entry is therefore picked up by the nextconversation, or the next app launch — never by one that is already open.
Test Plan
go1.27.3, darwin/arm64, commit under review:
cli/changed —go test ./internal/plugin/above, neithercached nor filtered
plugins/changed — not applicable, this PR doesnot touch
plugins/SeekMoon installed: detection reports the right target, and launching exactly what
the written entry names (
npx -y @everme/memory-mcp@latest) with its environmentcompletes the MCP handshake (
serverInfo: everme-memory-mcp, protocol2025-06-18) and lists themem_*toolsThe six tests cover the config path (override and per-OS default), detection by runtime
directory and by app bundle including an existing entry, sibling preservation on install
(
seekmoonand a hand-added server both survive, and the new entry carries the token),the next-conversation message,
0600on a file carrying the token, and idempotentremoval that keeps the desktop's entry. On that machine
plugin listreported:{ "platform": "seekmoon", "displayName": "SeekMoon", "installed": true, "configPath": "/Users/<user>/Library/Application Support/SeekMoon/host-mcp.json", "hasEverMeEntry": false }hasEverMeEntry: falseis the before state — no EverMe entry existed yet, which isexactly the state
plugin installacts on; a successful install flips it totrue.Security
emk_*,evt_*, cookies, private URLs, or production logs are included —the new tests use the dummy value
test-token, and the writer sets0600on aconfig it writes a live token into.
Deliberately out of scope
A conversation importer for SeekMoon history.
evercli import conversationshas noSeekMoon reader yet. SeekMoon keeps one JSONL transcript per conversation at
<workspace>/.openseek/sessions/<session-id>/openseek_session-<session-id>.jsonl: aheader line (
version,id,system_prompt), then one{sequence, ts, item: {kind, payload}}record per line, wherekindisuser,assistantortool_resultplus afew app-only kinds (
runtime_notice,terminal). A reader is the same shape of work asthe codex and workbuddy ones. Install and uninstall do not depend on it — but SeekMoon
captures nothing on its own (its host has no lifecycle hooks), so importing history is
the main way to seed its memory pool. Happy to send it as a follow-up PR, or to open an
issue if maintainers prefer to track it separately.