feat: AI MindClip device support + 7 MCP tools + cache-leak fixes#67
Open
chenliuyun wants to merge 54 commits into
Open
feat: AI MindClip device support + 7 MCP tools + cache-leak fixes#67chenliuyun wants to merge 54 commits into
chenliuyun wants to merge 54 commits into
Conversation
added 30 commits
June 13, 2026 15:53
fix(hooks): set COMSPEC in pre-commit hook for Windows Git Bash compatibility
…get_summary MCP tools
…gent_todos MCP tools
…y, weekly, urgent-todos) in try/catch
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.
…alias registration
… — registrations land in next commit)
… 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).
…ion in header comments
…_history aliases)
added 24 commits
June 13, 2026 20:57
…aggregate_device_history)
…_history aliases)
…iases (mindclip never shipped under old names)
…ng, comment limit cap
… NO-SUCH-DEVICE arg
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.
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.
Summary
AI MindClipvoice recorder to the catalog (read-only, 5 status fields), a newmindclipCLI 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.clearPrimedCredentials()and wires it together withidempotencyCache.clear()into bothauth loginandconfig set-token;config set-tokenalso gains the missingclearCache()/clearStatusCache()calls.resetcommand now clears the same in-memory caches for long-running processes (MCP, daemon).24 → 31across 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 inCOMMAND_METAasREAD_REMOTE. Version bumped to3.8.0.Also includes: 2 new arg validators (
dateArgforYYYY-MM-DD,weekArgforYYYY-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 locallynpm run test:workspaces— codex (61), claude-code (29), gemini (79), openclaw (16) plugin tests all greennpm run verify:release-gate(run by pre-push hook) — build + smoke installs (pack-install, codex × 4, claude-code, gemini-extension lint) all passswitchbot mindclip --helpshows all 7 subcommandsswitchbot mcp toolslists 31 entries including the 7 mindclip tools