Skip to content

feat: add seekmoon as a plugin install target - #20

Open
chaijie2018 wants to merge 1 commit into
EverMind-AI:mainfrom
chaijie2018:seekmoon-plugin
Open

chaijie2018 wants to merge 1 commit into
EverMind-AI:mainfrom
chaijie2018:seekmoon-plugin

Conversation

@chaijie2018

Copy link
Copy Markdown

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-memory entry with a freshly minted agent token.

⚠️ Blocked on the backend: seekmoon must join known_platform

The client side is finished: plugin list detects the host, plugin uninstall seekmoon
removes our entry, and the writer and its tests are in place — but plugin install
currently stops at the EverMe backend, which validates platform against 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 seekmoon added to known_platform in the managed
backend, so this should ship together with that release. One decision belongs in
review before the enum is touched: the platform id — seekmoon (proposed, matching the
app 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
    --platform and reported in data.platforms[].platform.
  • cli/internal/plugin/seekmoon.go — a detector (runtime directory per OS, the macOS
    bundle, seekmoon/openseek on PATH) and a writer that wraps the shared
    mcpWriter. No new upsert, backup, TOCTOU or atomic-write code.
  • cli/internal/plugin/registry.go, registry_test.go — register the platform in both
    the 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 — the
    install command, the host table, and the "takes effect on the next conversation" note.

What the install writes, and where:

OS File
macOS ~/Library/Application Support/SeekMoon/host-mcp.json
Windows %APPDATA%\SeekMoon\host-mcp.json
Linux ~/.config/SeekMoon/host-mcp.json
  • Upserts mcpServers.everme-memory: npx -y @everme/memory-mcp@latest plus
    EVERME_API_BASE, EVERME_AGENT_ID, EVERME_AGENT_TOKEN — through the shared writer,
    so the .bak backup, the TOCTOU check and the atomic rename all apply.
  • Leaves everything else in the file alone. SeekMoon publishes its own seekmoon
    entry 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.
  • Tightens the file to 0600, because the entry carries a live evt_ token.
  • Tells the user when the server actually appears: SeekMoon connects its MCP servers once
    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 next
    conversation, or the next app launch — never by one that is already open.

Test Plan

go1.27.3, darwin/arm64, commit under review:

go build ./...                                          exit 0
gofmt -l internal/plugin/                               (no output)
go test ./internal/plugin/ -run 'SeekMoon|Registry' -v  PASS
go test ./internal/plugin/                              ok  21.3s
  • CLI tests pass, if cli/ changed — go test ./internal/plugin/ above, neither
    cached nor filtered
  • Plugin workspace tests pass, if plugins/ changed — not applicable, this PR does
    not touch plugins/
  • Manual verification completed, if behavior changed — on a real macOS machine with
    SeekMoon installed: detection reports the right target, and launching exactly what
    the written entry names (npx -y @everme/memory-mcp@latest) with its environment
    completes the MCP handshake (serverInfo: everme-memory-mcp, protocol
    2025-06-18) and lists the mem_* tools

The 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
(seekmoon and a hand-added server both survive, and the new entry carries the token),
the next-conversation message, 0600 on a file carrying the token, and idempotent
removal that keeps the desktop's entry. On that machine plugin list reported:

{ "platform": "seekmoon", "displayName": "SeekMoon", "installed": true,
  "configPath": "/Users/<user>/Library/Application Support/SeekMoon/host-mcp.json",
  "hasEverMeEntry": false }

hasEverMeEntry: false is the before state — no EverMe entry existed yet, which is
exactly the state plugin install acts on; a successful install flips it to true.

Security

  • No real emk_*, evt_*, cookies, private URLs, or production logs are included —
    the new tests use the dummy value test-token, and the writer sets 0600 on a
    config it writes a live token into.

Deliberately out of scope

A conversation importer for SeekMoon history. evercli import conversations has no
SeekMoon reader yet. SeekMoon keeps one JSONL transcript per conversation at
<workspace>/.openseek/sessions/<session-id>/openseek_session-<session-id>.jsonl: a
header line (version, id, system_prompt), then one {sequence, ts, item: {kind, payload}} record per line, where kind is user, assistant or tool_result plus a
few app-only kinds (runtime_notice, terminal). A reader is the same shape of work as
the 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.

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>
@chaijie2018

Copy link
Copy Markdown
Author

First contribution to this repo, so the CI run is sitting in action_required — could a maintainer approve it when convenient? Nothing else is blocking it from my side: locally the same commands CI runs all pass (go build ./..., go vet ./..., gofmt -l ., go mod tidy -diff, go test -race -count=1 ./...).

One thing worth flagging while it waits: evercli plugin install seekmoon cannot mint an agent yet, because the backend validates platform against a closed enum:

{"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 seekmoon added to known_platform in the managed backend — ideally shipped together with this change.

That also settles a small decision before the enum is touched: the platform id. This PR uses seekmoon (matching the app name); if you would rather use openseek (the repository name), I will rename it — it is one line plus the registry entries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant