Skip to content

fix(mcp): accept a single conversation file as a convos mine source - #2282

Open
rubicon wants to merge 3 commits into
MemPalace:developfrom
rubicon:dev/2281-hub-mine-file
Open

fix(mcp): accept a single conversation file as a convos mine source#2282
rubicon wants to merge 3 commits into
MemPalace:developfrom
rubicon:dev/2281-hub-mine-file

Conversation

@rubicon

@rubicon rubicon commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Closes #2281

What was wrong

cli.py documents the mine source as:

"dir", help="Directory to mine, or one conversation file with --mode convos"

mcp_server.tool_mine validated os.path.isdir(src) regardless of mode. Since cmd_mine forwards to the hub whenever one is registered and healthy, the documented single-file form was unreachable in the configuration most users run.

hooks_cli._ingest_transcript submits exactly one .jsonl — it returns early unless path.is_file() — so with a hub live, every Stop and every PreCompact transcript ingest failed with source directory not found.

Why it went unnoticed

hook_precompact returns _output({}) on the success path and on this path alike. A compaction that captured nothing is indistinguishable from one that captured everything; the only evidence is a stderr line in hook_state/hook.log. On the machine where this was found, the newest mined chunk for a session file predated the running hub by five days, across continuous use.

The change

convos accepts a file or a directory. The tree-walking modes keep the directory requirement. The error string no longer asserts "directory" for a mode where a file is valid.

Tests

Two added to tests/test_mcp_mine.py:

  • test_convos_mode_accepts_a_single_file — mines one file and asserts drawers actually land, rather than asserting the call merely returned success.
  • test_projects_mode_still_rejects_a_file — pins the other side, so the relaxation cannot widen into "any mode, any path".

Both were confirmed non-vacuous by mutation rather than by inspection:

Mutation Result
revert to os.path.isdir(src) test_convos_mode_accepts_a_single_file fails
widen to isdir(src) or isfile(src) for every mode test_projects_mode_still_rejects_a_file fails

Each mutation is killed by the test whose name describes it, and neither test passes on the mutated code it exists to catch.

Full suite: 4302 passed, 31 skipped.

Not in scope

The palace write lock held by a long-lived MCP server (#2024, #1924, #2172) blocks the diary checkpoint on the same hook runs. That is a separate failure with a separate cause; this precondition fails before any lock is taken.

cli.py documents the mine source as "Directory to mine, or one conversation
file with --mode convos", and hooks_cli._ingest_transcript submits exactly one
.jsonl -- it returns early unless path.is_file(). The MCP tool validated
os.path.isdir unconditionally, and cmd_mine forwards to the hub whenever one is
registered and healthy, so the documented single-file form was unreachable in
the configuration most users run and every Stop and PreCompact transcript
ingest failed with "source directory not found".

The failure was silent: hook_precompact returns the same empty object on the
success path, so a compaction that captured nothing looked exactly like one
that captured everything. The only trace was a stderr line in hook.log.

convos now accepts a file or a directory. The tree-walking modes keep the
directory requirement, and a test pins that so the relaxation cannot widen into
"any mode, any path".

Closes MemPalace#2281
The tool schema and the generated reference both still described
mempalace_mine.source as a directory only, which stopped being true for
mode='convos'. The implementation was right and the published contract was
stale, so a caller reading either would not know the single-file form exists.

Covers the MCP tool description and its source parameter, the website MCP tools
reference, and the openclaw skill listing.
Only CHANGELOG.md conflicted. v3.8.0 was tagged while this branch was open,
so the MemPalace#2281 entry no longer belongs in the 3.8.0 section it was originally
written into. It moves verbatim into a new [Unreleased] section above it,
restoring the layout ebebb2b removed at the 3.8.0 promotion. The nine 3.8.0
Bug Fixes entries that develop added are kept unchanged.

mcp_server.py, integrations/openclaw/SKILL.md and website/reference/mcp-tools.md
merged cleanly and still carry the fix and its documentation. Full suite:
4470 passed, 31 skipped. ruff check and ruff format --check both clean.
@igorls

igorls commented Aug 24, 2026

Copy link
Copy Markdown
Member

Thanks for this contribution! Enabling single conversation file ingest via MCP matches the CLI behavior and is very helpful for transcript hooks.

There are currently merge conflicts with the latest develop branch. Could you please rebase this PR against develop? Once resolved and CI passes, we'll merge it in.

@igorls igorls added bug Something isn't working area/mcp MCP server and tools area/mining File and conversation mining needs-rebase PR has merge conflicts with develop and needs rebase labels Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/mcp MCP server and tools area/mining File and conversation mining bug Something isn't working needs-rebase PR has merge conflicts with develop and needs rebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hub mine rejects a single conversation file, so every hook transcript ingest fails while a hub is running

2 participants