Skip to content

feat: AI MindClip device support + 7 MCP tools + cache-leak fixes#67

Open
chenliuyun wants to merge 54 commits into
mainfrom
feat/ai-mindclip
Open

feat: AI MindClip device support + 7 MCP tools + cache-leak fixes#67
chenliuyun wants to merge 54 commits into
mainfrom
feat/ai-mindclip

Conversation

@chenliuyun

Copy link
Copy Markdown
Collaborator

Summary

  • AI MindClip device support — adds the AI MindClip voice recorder to the catalog (read-only, 5 status fields), a new mindclip CLI command group with 7 subcommands (recordings, recording <id>, summary <id>, todos, daily, weekly, urgent-todos), and 7 first-class MCP tools (mindclip_list_recordings, mindclip_get_recording, mindclip_get_summary, mindclip_list_todos, mindclip_daily_recall, mindclip_weekly_summary, mindclip_urgent_todos) so MCP-driven agents (Claude Code / Gemini / OpenClaw) get Zod-typed access without bash fallback.
  • Account-switch cache leak fix — exports clearPrimedCredentials() and wires it together with idempotencyCache.clear() into both auth login and config set-token; config set-token also gains the missing clearCache() / clearStatusCache() calls. reset command now clears the same in-memory caches for long-running processes (MCP, daemon).
  • Plugin doc sync — bumps tool count 24 → 31 across 5 places (gemini-extension manifest, 4 README spots), adds a mindclip Authority-chain row to 3 SKILL.md / GEMINI.md files (codex × 2 sync copies, claude-code, gemini-extension), and registers the 7 commands in COMMAND_META as READ_REMOTE. Version bumped to 3.8.0.

Also includes: 2 new arg validators (dateArg for YYYY-MM-DD, weekArg for YYYY-Www W01–W53), error-handling try/catch wrappers on all 7 mindclip CLI action handlers, and a Windows fix to the pre-push hook (export COMSPEC, mirroring pre-commit) that was blocking pushes from Git Bash.

Test plan

  • npx vitest run — full suite (151 files, 2898 tests) passes locally
  • npm run test:workspaces — codex (61), claude-code (29), gemini (79), openclaw (16) plugin tests all green
  • npm run verify:release-gate (run by pre-push hook) — build + smoke installs (pack-install, codex × 4, claude-code, gemini-extension lint) all pass
  • CI cross-platform (Ubuntu + Windows) green
  • Manually verify switchbot mindclip --help shows all 7 subcommands
  • Manually verify switchbot mcp tools lists 31 entries including the 7 mindclip tools
  • Live smoke (post-merge): hit a real account that has an AI MindClip device and call each of the 7 endpoints

chenliuyun added 30 commits June 13, 2026 15:53
fix(hooks): set COMSPEC in pre-commit hook for Windows Git Bash compatibility
reset.ts: switch to the pure in-memory resetListCache/resetStatusCache
helpers. The previous clearCache/clearStatusCache rerun unlinkSync on
files the data-files loop had already attempted to delete; on a
permission-denied path that re-throw skipped both the in-memory clear
and the result table. Disk deletion now stays the sole responsibility
of the data-files loop where errors are reported into results.

capabilities.ts: derive MCP_TOOLS from TOOL_PROFILES.all instead of
hand-maintaining a separate array. The hardcoded list had drifted to
11 entries while mcp serve registers 31. Add a tool-profiles.test.ts
case that asserts the advertised set equals the names registered by
createSwitchBotMcpServer({ toolProfile: 'all' }) so any future drift
fails CI.
Without a version bump, .github/workflows/publish.yml's check_unpublished
gate would skip these packages on the next release and the README/SKILL/
GEMINI.md edits made for the MindClip work would never reach end users.

- @switchbot/claude-code-plugin 0.1.3 -> 0.1.4 (SKILL.md adds MindClip
  MCP-tool row; README updated to 31 tools)
- @switchbot/codex-plugin 0.1.5 -> 0.1.6 (skills/switchbot/SKILL.md and
  plugins/switchbot/skills/switchbot/SKILL.md add MindClip CLI-command
  row)
- @switchbot/gemini-extension 0.1.0 -> 0.1.1 (GEMINI.md adds MindClip
  MCP-tool row; gemini-extension.json description and README updated
  to 31 tools; gemini-extension.json version bumped in lockstep with
  package.json)

@switchbot/openclaw-skill stays at 0.1.2 (no changes since main).
The MindClip work added 7 MCP tools, bringing the registered total from
24 to 31, but several user-visible strings were missed. This commit
sweeps them so npm pages, CLI help, plugin manifests, and the Gemini
settings.json entry all agree.

- npm package descriptions: claude-code-plugin, codex-plugin,
  gemini-extension package.json all switch "(24 tools, ...)" -> "(31 ...)"
- Claude Code plugin marketplace.json description
- Codex plugin .mcp.json (top-level + plugins/switchbot/.mcp.json)
- gemini-checks.ts MCP server entry written to ~/.gemini/settings.json
- src/commands/mcp.ts: `mcp --help` heading "twenty-four" -> "thirty-one"
  with the 7 mindclip tools added to the bullet list
- src/commands/mcp.ts: `mcp tools --tools <profile>` option help string
  corrected from "(default 13 / readonly 10 / all 24)" to the actual
  "(default 20 / readonly 17 / all 31)" reflected in tool-profiles.ts
… default profile

Three breaking changes that together cut per-session MCP cost:

[B] Consolidate 7 mindclip tools into 3
  - mindclip_recordings (action: list | get | summary)
  - mindclip_list_todos (unchanged)
  - mindclip_recall (period: daily | weekly | urgent_todos)
  Replaces: mindclip_list_recordings, mindclip_get_recording,
  mindclip_get_summary, mindclip_daily_recall, mindclip_weekly_summary,
  mindclip_urgent_todos. Each branch validates its own required params
  in the handler.

[C] Consolidate 3 device-history tools into 1
  - device_history (mode: raw | query | aggregate)
  Replaces: get_device_history, query_device_history,
  aggregate_device_history. Per-mode required-arg validation in handler.

[A] Default plugins to the default tool profile
  Removed --tools all from packages/claude-code-plugin/plugins/switchbot/.mcp.json,
  packages/codex-plugin/.mcp.json, packages/codex-plugin/plugins/switchbot/.mcp.json,
  packages/gemini-extension/gemini-extension.json, src/install/claude-code-checks.ts,
  and src/install/gemini-checks.ts. Admin tools (policy / audit / rules)
  are now opt-in via the user adding --tools all. The CLI default for
  mcp serve is already "default", so removing the arg is a no-op for the CLI.

Profile counts after consolidation: readonly 17 -> 11, default 20 -> 14,
all 31 -> 25.

Tests:
  - tests/mcp/tool-profiles.test.ts: assert new counts (11/14/25)
  - tests/commands/mcp.test.ts: rewrite all mindclip + history tool tests
    to use new names with action/period/mode args; add coverage for the
    handler-level validation errors (missing id, missing deviceId,
    missing metrics).
  - tests/commands/strict-schemas.test.ts: rewrite the strict-key tests
    against device_history's three modes.
  - tests/mcp/tool-schema-completeness.test.ts: device_history input
    schema describes every per-mode field.
  - tests/mcp/tool-meta.test.ts: device_history is read tier.
  - tests/commands/capabilities.test.ts: surfaces.mcp.tools includes
    device_history.
  - tests/install/gemini-checks.test.ts + packages/gemini-extension/
    tests/manifest.test.js: assert the new args without --tools all.

Docs:
  - mcp --help bullet list rewritten with the 25 surviving tools.
  - --tools <profile> help string corrected to (default 14 / readonly 11
    / all 25) at all three call sites (serve, tools, list-tools).
  - README, docs/agent-guide.md, SKILL.md (claude-code), GEMINI.md, and
    each plugin README updated to use the new tool names and counts.
  - All package.json / marketplace.json / .mcp.json / gemini-extension.json
    descriptions reflow as "default 14, --tools all for 25".

Migration: MCP clients calling the old tool names must move to the new
name and pass the discriminator (action | period | mode). Existing
config files that still pass --tools all keep working - the flag is
honored, just no longer the recommended default.
Adds eight groups of test coverage on top of the v3.8.0 consolidation
to harden behavior contracts that were previously implicit.

G1 — discriminator schema validation (6 cases, strict-schemas.test.ts):
  each consolidated tool rejects missing or invalid action/period/mode
  with a -32602 family error, ruling out silent fallback-to-default.

G2/G3 — branch routing + parameter forwarding (~14 cases, mcp.test.ts):
  mindclip_recall period=daily/weekly without their date/week args take
  the server-default branch; period=urgent_todos with date forwards it;
  action=get omits language when caller omits it; partial-filter
  combinations on action=list and mindclip_list_todos forward only the
  fields they were given. device_history mode=raw without deviceId
  returns the devices listing shape; mode=raw with limit bounds the
  history slice; mode=query rejects since combined with from/to;
  mode=aggregate rejects missing deviceId; mode=query happy path
  returns count + records; mode=aggregate forwards bucket and
  maxBucketSamples.

G4 — inputSchema introspection (3 cases, tool-schema-completeness.test.ts):
  mindclip_recordings, mindclip_recall, and device_history each describe
  every documented field, mark the discriminator as required, and
  surface the discriminator's enum values via JSON Schema.

G5 — retired tool name contract (27 cases, retired-tools.test.ts NEW):
  the nine retired names (mindclip_list_recordings,
  mindclip_get_recording, mindclip_get_summary, mindclip_daily_recall,
  mindclip_weekly_summary, mindclip_urgent_todos, get_device_history,
  query_device_history, aggregate_device_history) must (a) be absent
  from createSwitchBotMcpServer({toolProfile:'all'}), (b) be absent
  from listTools() over the wire, and (c) trigger a method-not-found
  / -32601 error from callTool. Guards the breaking-change contract
  in CHANGELOG [Unreleased].

G6 — plugin manifest shape (10 cases across 3 NEW test files):
  packages/claude-code-plugin/tests/manifest.test.js asserts
  plugins/switchbot/.mcp.json args = ['mcp','serve'] and rejects
  --tools/all tokens. packages/codex-plugin/tests/mcp-config.test.js
  asserts the same for both .mcp.json layouts (top-level + nested).
  tests/install/claude-code-checks.test.ts mocks spawnSync and
  asserts registerMcp() invokes `claude mcp add` with default-profile
  args and that `claude mcp list` short-circuits on already-registered.

G7 — boundary tests on filter ranges (12 cases, strict-schemas.test.ts):
  pageSize 0/1/100/101, pageNum 0, completedNum 3 and 0..2,
  category 6, mindclip_recall date regex (slashes rejected), week
  regex (W00/W54 rejected; W01/W53 accepted) — all per the schemas in
  src/commands/mcp.ts.

G8 — cross-branch field leakage (5 cases, mcp.test.ts):
  the consolidated input schemas accept all-optional per-branch
  fields, but the handlers must ignore mismatched-branch fields and
  not forward them to the underlying API. Verified by mocking the
  api client and asserting both the URL chosen and that stray fields
  do NOT appear in params (mindclip side) or that the alternate
  branch was NOT taken (mindclip and device_history sides).

Plus a stale comment fix in src/install/claude-code-checks.ts (the
inline note still referenced --tools all after the v3.8.0 default
flip).

Net: vitest main suite 2904 -> 2972 (+68); workspace tests +7
across two new manifest test files. All green; no production code
changes outside the comment fix.
… 3.x compat

Extract query and aggregate branches into module-level helpers
(runDeviceHistoryQuery, runDeviceHistoryAggregate) so the 3 deprecated
alias tools can delegate without duplicating logic. Register the 3 aliases
with deprecated=true _meta and replacement="device_history". Delete
retired-tools.test.ts; its coverage is now in legacy-aliases.test.ts.
Update mcp.test.ts tool-count assertion (25 -> 28).
chenliuyun added 24 commits June 13, 2026 20:57
…iases (mindclip never shipped under old names)
Both getRecording and getSummary previously interpolated the id directly into the URL path, allowing characters like /, ?, #, .. to escape the recordings prefix or smuggle query params into the request. Wrap the id in encodeURIComponent and add tests covering traversal and query-merge inputs.
…possible dates

- mindclip_recordings/list_todos: optional string filters (deviceID, fileID, language) now require .min(1) so an explicit empty string no longer flows through to '?deviceID=' on the wire.
- mindclip_recall: date now refines through new Date() round-trip, rejecting calendar-impossible inputs like 2026-02-30 or 2026-13-01 that the pure regex previously accepted.
- Export DATE_REGEX/WEEK_REGEX/isCalendarValidDate from arg-parsers so CLI dateArg/weekArg and the MCP zod schemas share one source of truth.
…ek schema

Add isLongISOYear() to arg-parsers (Jan 1 on Thursday, or leap year
starting Wednesday).  weekArg now throws for W53 on any year that only
has 52 ISO weeks.  mindclip_recall week Zod field gains a .refine()
using the same logic.  Tests cover accept/reject for 2026 (long) and
2027/2024 (short).
…andlers

mindclip_recordings, mindclip_list_todos, and mindclip_recall catch
blocks were discarding subKind/retryable/retryAfterMs/hint from API
errors.  Switch to the shared apiErrorToMcpError helper so clients get
the full structured error envelope.
runDeviceHistoryQuery and runDeviceHistoryAggregate already validate all
user args before the try block; any error reaching the catch is a
storage-layer fault (file I/O, corrupt data), not a usage mistake.
Change kind from 'usage' to 'runtime' so callers see the correct
error classification.
Both auth login and config set-token call fs.unlinkSync via clearCache()
which can throw EBUSY on Windows when another process holds the file.
The in-memory portion still clears; wrap only the disk calls in a
try/catch so the success output always reaches the user.
Extract onCredentialChange() helper (try/catch disk clear + in-memory
clear) and call it from auth login, keychain set, keychain delete, and
keychain migrate.  Previously the three keychain subcommands wrote
credentials but left the stale device list and status caches in place,
causing subsequent requests to use data from the previous account.
The device_history raw-mode description advertised max 100 entries but
the Zod schema allowed 10000 with no runtime enforcement.  Add
Math.min(limit ?? 20, 100) for raw mode and update the description to
say "max 100 enforced at runtime" so the contract is unambiguous.
…coded literals

readonly=14, default=17, all=28 (was 11/14/25 — all three were wrong).
Replace literals in mcp.ts --tools option descriptions, gemini-checks.ts
registration description, and all plugin package.json / README /
manifest / .mcp.json files.
…utput

Add DEPRECATED_MCP_TOOLS export to tool-profiles.ts (the three 3.x
backward-compat aliases: get/query/aggregate_device_history).
capabilities --surface mcp now filters them out so only the 25
current tools are advertised, while the MCP server still registers
all 28 for backward compatibility.
search_catalog and describe_device use aliases for fuzzy matching.
Without aliases, queries like "MindClip" or "Mind Clip" returned no
results even though the device is present.
Add optional profile parameter to IdempotencyCache.run() and a new
clearForProfile(profile) method that evicts only entries tagged with
that profile.  sendDeviceCommand now passes getActiveProfile() so that
credential-change clears only the affected profile's dedup window
rather than the entire cache.  auth and config call clearForProfile()
on the active profile instead of clear().
clearPrimedCredentials() could be called while store.get() was still
in flight, causing the resolved value to overwrite the freshly-cleared
cache.  Introduce a generation counter: primeCredentials() captures
the counter before the await and only writes the result when the
counter is unchanged.  clearPrimedCredentials() and __resetPrimedCredentials()
both increment the counter on clear.
…eprecated exclusion

MCP_TOOLS now excludes the 3 deprecated aliases (Commit 10); update the
sync test to verify that MCP_TOOLS contains all non-deprecated registered
tools, that deprecated aliases are registered but not advertised, and
that total counts add up correctly.
13 bug fixes from code-review pass on the AI MindClip branch —
URL injection, schema validation, ISO week calendar check, error
envelope, cache invalidation, tool-count drift, and race conditions.
Full change list in CHANGELOG.md.
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