Skip to content

feat(mcp): add tree.tag write tool (completes Phase 3 #2269)#2316

Merged
senamakel merged 10 commits into
tinyhumansai:mainfrom
justinhsu1477:feat/mcp-tree-tag-write-tool
May 22, 2026
Merged

feat(mcp): add tree.tag write tool (completes Phase 3 #2269)#2316
senamakel merged 10 commits into
tinyhumansai:mainfrom
justinhsu1477:feat/mcp-tree-tag-write-tool

Conversation

@justinhsu1477
Copy link
Copy Markdown
Contributor

@justinhsu1477 justinhsu1477 commented May 20, 2026

Summary

Adds the third MCP write tool from the Phase 3 RFC (#2269) on top of @Liohtml's memory.store + memory.note scaffolding in #2306, so the RFC closes out fully.

  • New tree.tag MCP tool: attach one or more category labels to an existing memory chunk.
  • Routes through the existing openhuman.memory_doc_put RPC.
  • Reuses enforce_write_policy + dispatch_write_tool from feat(mcp): add memory.store and memory.note write tools #2306 unchanged — inherits the same policy gate and audit-log tracing.
  • Same source_type = "mcp" provenance convention as memory.store / memory.note.

Depends on #2306

This PR is layered directly on top of #2306. The diff here only adds the new tool surface; if #2306 changes during review (e.g. CodeRabbit's open CHANGES_REQUESTED items), I'll rebase this branch.

What tree.tag does

{
  "name": "tree.tag",
  "arguments": {
    "chunk_id": "chunk-42",
    "tags": ["todo", "q3-planning"]
  }
}

→ Creates / upserts a tag-record document keyed on mcp-tag-<chunk_id> in the mcp namespace, with:

  • tags = ["todo", "q3-planning"] on the top-level document tags index (queryable downstream via doc_list / search filters).
  • metadata.tags_for_chunk_id = "chunk-42" — back-reference so consumers reading the metadata view know which chunk the tag-record points at.
  • metadata.applied_tags = ["todo", "q3-planning"] — mirror of the tag list for callers that read the metadata blob without joining against the top-level field.

How it differs from memory.note

memory.note tree.tag
Payload shape Free-form text (note_text) Typed categorical list (tags[])
Re-call semantic Upsert by chunk_id (silently overwrites — see #2306 for the open contract discussion) Upsert: re-tagging replaces the prior tag set
Queryable index Opaque content Top-level tags index (filterable)
Key derivation mcp-note-<chunk_id> mcp-tag-<chunk_id>

Both upsert by chunk_id. As graycyrus noted in #2306, memory.note overwrites on re-call — whether that should change to a content-hash key (true append) or stay as set-semantic is the open contract discussion happening on the parent PR. tree.tag is unambiguously replace-on-re-call: supply the complete desired set each time.

Implementation footprint

src/openhuman/mcp_server/protocol.rs    |   1 +
src/openhuman/mcp_server/tools.rs       | 242 +++++++++++++++++++++++++-
2 files changed, 242 insertions(+), 1 deletion(-)

Plus a small reusable helper required_non_empty_string_array added next to its sibling optional_string_array — used to reject tags: [] up-front at the MCP layer rather than letting an empty tag-set propagate to the document RPC where the failure mode would be silent.

Submission Checklist

  • Tests added or updated — 8 new unit tests cover: missing chunk_id, missing tags, empty tags array, all-blank tags, non-string tag entries, happy path, blank-trim with non-empty residue, empty chunk_id rejection, unknown-argument rejection.
  • Diff coverage ≥ 80% — new code is overwhelmingly build_rpc_params validation + schema, fully exercised by the new unit tests; helper function paths covered by the same tests.
  • N/A: Coverage matrix updated — extends the same MCP write tool surface row added in feat(mcp): add memory.store and memory.note write tools #2306; no new matrix feature row.
  • N/A: All affected feature IDs from the matrix are listed — extends an existing capability.
  • No new external network dependencies introduced — pure RPC routing through existing memory_doc_put.
  • N/A: Manual smoke checklist updated — no release-cut surface changes.
  • Linked issue closed via Closes #2269 in ## Related (transitively — feat(mcp): add memory.store and memory.note write tools #2306 already declares Closes #2269; this PR completes the RFC's three-tool surface so it's logically the closing piece).

Impact

  • Runtime/platform impact: desktop core only (Rust). No Tauri shell, no frontend.
  • Compatibility impact: strictly additive. Schema for the existing tools unchanged. memory.store / memory.note behavior unchanged.
  • Performance impact: zero on existing flows; new path is one extra dispatch arm and one extra match branch in build_rpc_params.
  • Security impact: inherits enforce_write_policy gate from feat(mcp): add memory.store and memory.note write tools #2306 — no new attack surface relative to that PR.

Related


AI Authored PR Metadata

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

Validation Run

  • N/A: pnpm --filter openhuman-app format:check — Rust-only change.
  • N/A: pnpm typecheck — Rust-only change.
  • Focused tests: cargo test --lib tree_tag (8/8 pass); cargo test --lib mcp_server (59/59 pass, no regression on memory.store / memory.note from feat(mcp): add memory.store and memory.note write tools #2306).
  • Rust fmt/check: cargo fmt --check clean; cargo check --lib clean (pre-existing warnings only); cargo clippy --lib --no-deps no new warnings in mcp_server/tools.rs.
  • N/A: Tauri fmt/check — no app/src-tauri/src/** changes.

Validation Blocked

  • N/A

Behavior Changes

  • Intended behavior change: MCP clients gain a third write tool — tree.tag — that attaches typed category labels to existing memory chunks.
  • User-visible effect: an LLM conversation in (e.g.) Claude Desktop can now categorize chunks of OpenHuman's memory tree with structured tags that are queryable downstream, completing the read-and-write loop the RFC proposed.

Parity Contract

Duplicate / Superseded PR Handling

Summary by CodeRabbit

  • New Features

    • Added three tools for memory storage, note creation, and content tagging. They include enforced write security, deterministic keying and slugging for stored items, sensible defaults (e.g., default namespace), strict tag limits/trimming, and robust parameter validation to ensure consistent persistence and tagging behavior.
  • Tests

    • Expanded coverage for tool inclusion, parameter validation/rejection, key/content generation, tag semantics and limits, slug generation, and write-path auditing.

Review Change Stack

Liohtml and others added 4 commits May 20, 2026 10:04
Expose two new MCP write tools that route to the existing doc_put RPC:

- memory.store: create a memory document from content (title, content,
  namespace, optional tags) with source_type=mcp provenance
- memory.note: append an annotation to an existing chunk by ID, stored
  as a linked document with metadata.annotates_chunk_id

Adds enforce_write_policy() for write-specific security gating (uses
the Act operation gate, with a distinct log path for audit clarity),
dispatch_write_tool() with structured tracing::info! audit logging,
and 11 unit tests covering param validation, defaults, and slugging.

Closes tinyhumansai#2269

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address CodeRabbit review: slug_from now returns "untitled" when the
title contains only non-alphanumeric characters, preventing key
collisions on the mcp-store- prefix. Also adds the missing
memory.store and memory.note entries to the protocol integration test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses CodeRabbit review: titles with no ASCII-alphanumeric chars
(e.g. "会议记录", "Протокол", emoji) now produce "untitled-<sha256_hex>"
instead of a constant "untitled", ensuring distinct stable slugs.
Adds 2 regression tests for uniqueness and stability across calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes tinyhumansai#2269 by adding the third MCP write tool from the RFC, on top of
the memory.store + memory.note scaffolding already in tinyhumansai#2306.

## What this adds

A new `tree.tag` MCP tool that attaches one or more category labels to
an existing memory chunk:

  - Input: `chunk_id` (string) + `tags` (non-empty array of strings).
  - Routes through the existing `openhuman.memory_doc_put` RPC using
    `enforce_write_policy` + `dispatch_write_tool` from tinyhumansai#2306, so it
    inherits the same write-policy gate and audit-log tracing as
    `memory.store` / `memory.note`.
  - Source provenance: `source_type = "mcp"` (matches tinyhumansai#2306).
  - Deterministic document key `mcp-tag-<chunk_id>` so re-tagging the
    same chunk upserts the prior tag-record rather than accumulating
    duplicate annotations. The RFC's intent is that callers supply the
    complete desired tag set on each call.

Distinguishes from `memory.note` in two ways:
  1. The payload is a categorical label list (typed `tags`), not free
     text. The top-level `tags` field flows to the document tags index
     so callers can filter by tag downstream.
  2. The upsert semantic means re-tagging *replaces* the tag set,
     where `memory.note` is intended for additive annotations.

Metadata carries a back-reference (`tags_for_chunk_id`) plus a mirror
of the applied tag list (`applied_tags`) so consumers reading the
metadata view don't need to also join against the top-level `tags`
field.

## Coverage

- 8 new unit tests covering: missing chunk_id, missing tags, empty
  tags array, all-blank tags, non-string tag entries, happy path,
  blank-trim semantics with non-empty residue, empty chunk_id
  rejection, unknown-argument rejection.
- All 59 existing `mcp_server` tests still pass — no regression.

## Validation

- `cargo check --lib` — clean (pre-existing warnings only)
- `cargo test --lib mcp_server` — 59/59 pass
- `cargo test --lib tree_tag` — 8/8 pass
- `cargo fmt --check` — clean
- `cargo clippy --lib --no-deps` — no new warnings in
  `src/openhuman/mcp_server/tools.rs`

Depends on tinyhumansai#2306.
@justinhsu1477 justinhsu1477 requested a review from a team May 20, 2026 08:57
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ca8944b5-00e7-4300-b715-f7f86ea8fc74

📥 Commits

Reviewing files that changed from the base of the PR and between 3c50c97 and dbcd678.

📒 Files selected for processing (1)
  • src/openhuman/mcp_server/tools.rs

📝 Walkthrough

Walkthrough

Adds three MCP write tools—memory.store, memory.note, and tree.tag—with JSON input schemas, write-gated call dispatch to openhuman.memory_doc_put, parameter-building/validation (including slug_from), and unit tests; also updates the tools/list test expectations.

Changes

MCP Write Tools: memory.store, memory.note, tree.tag

Layer / File(s) Summary
Tool catalog, constants, and input schemas
src/openhuman/mcp_server/tools.rs (lines 29–407)
Adds MCP argument-name constants, TREE_TAG_* caps, registers memory.store, memory.note, tree.tag in base_tool_specs, and provides memory_store_schema, memory_note_schema, and tree_tag_schema.
call_tool write dispatch & security
src/openhuman/mcp_server/tools.rs (lines 504–1148)
Routes the three tools through a write path in call_tool, enforces write policy via enforce_write_policy, validates controller params, and centralizes write RPC invocation with dispatch_write_tool.
Parameter building, validation, slug helper
src/openhuman/mcp_server/tools.rs (lines 690–1357)
Adds build_rpc_params branches for the new tools with argument validation, namespace defaulting, deterministic key generation (slug_from), content/metadata formatting, tag trimming/limits, and required_non_empty_string_array.
Tests and updated tool-list expectations
src/openhuman/mcp_server/protocol.rs (lines 350–360), src/openhuman/mcp_server/tools.rs (lines 1397–2136)
Updates tools/list test expectations and adds unit tests covering required-field validation, namespace defaults, tag behavior and limits, slug generation/truncation/hash fallback, and document construction semantics.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • senamakel

Poem

🐰 I hop through the code with a carrot and tag,

memory.store nests facts in a soft little bag,
memory.note whispers where annotations belong,
tree.tag strings labels to keep order strong,
Happy hops — the memory garden grows along!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately describes the main change: adding the tree.tag write tool. References the phase completion and issue number for context.
Linked Issues check ✅ Passed PR successfully implements tree.tag write tool from RFC #2269 with required parameter validation, tag caps, security policy enforcement, and comprehensive tests meeting stated objectives.
Out of Scope Changes check ✅ Passed All changes are directly scoped to tree.tag tool implementation and related test updates. No unrelated modifications or feature creep detected.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — tree.tag write tool (Phase 3 completion)

Solid, well-documented PR. The tree.tag tool is cleanly implemented, validation is thorough (especially required_non_empty_string_array), and the 8 new unit tests are comprehensive. Good use of upsert semantics keyed on chunk_id and the tag-index design for queryability.

One semantic issue in memory.note that should be addressed before merge.

Area Files Verdict
MCP tool specs tools.rs (schemas + dispatch) 1 major, 2 minor
Protocol test protocol.rs ✅ Clean

Nice work on the slug fallback for non-ASCII titles — the SHA-256 hash approach is a good call.

reject_unexpected_arguments(&args, MEMORY_NOTE_ARGUMENTS)?;
let chunk_id = required_non_empty_string(&args, "chunk_id")?;
let note_text = required_non_empty_string(&args, "note_text")?;
let key = format!("mcp-note-{chunk_id}");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] memory.note has an upsert-vs-append semantic mismatch.

The key is mcp-note-{chunk_id} and it routes through memory_doc_put — so calling memory.note twice on the same chunk_id silently replaces the first note. But the tool description above says "Append a note" and the PR description table lists the re-call semantic as "Append-style annotation per call."

This is confusing for MCP clients: an LLM that calls memory.note multiple times on the same chunk expecting accumulated annotations will instead get only the last one.

Options:

  1. Change the key to include a nonce/timestamp (e.g. mcp-note-{chunk_id}-{unix_millis}) so each call creates a distinct document — actual append semantics.
  2. Keep upsert but fix the description to say "Upsert a note" and document that re-noting replaces the prior annotation (matching tree.tag's honesty about this).

Either way, the description and behavior need to agree.

tool_name: &str,
params: &Map<String, Value>,
) -> Result<Value, ToolCallError> {
let rpc_method = "openhuman.memory_doc_put";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] rpc_method is hardcoded to "openhuman.memory_doc_put" here, but each tool spec already declares this via rpc_method: Some("openhuman.memory_doc_put"). If a future write tool uses a different RPC method, this function will silently send it to the wrong endpoint.

Consider passing the McpToolSpec (or at least spec.rpc_method) into this function instead of hardcoding.

let namespace =
optional_non_empty_string(&args, "namespace")?.unwrap_or_else(|| "mcp".to_string());
// Generate a deterministic key from the title for upsert dedup.
let key = format!("mcp-store-{}", slug_from(&title));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] slug_from maps non-alphanumeric chars to hyphens, so titles that differ only in punctuation collide silently. E.g. "Hello World!" and "Hello World?" both produce key mcp-store-hello-world, causing one to overwrite the other.

This may be acceptable if title-based dedup is intentional, but it's worth a comment acknowledging the trade-off — or mixing in a short content hash to make collisions less likely.

senamakel added a commit that referenced this pull request May 21, 2026
## Summary

- Adds MCP stdio session state that captures `initialize.params.clientInfo.name` for the lifetime of the session.
- Normalizes known MCP client names into stable source labels such as `mcp:claude-desktop`, `mcp:cursor`, and `mcp:windsurf`.
- Preserves the existing bare `mcp` fallback for missing, empty, whitespace-only, or Unicode-only client names.
- Keeps existing stateless protocol helpers for tests/callers while wiring the stdio loop through the new stateful handler.
- Documents the provenance contract for follow-up write-capable MCP tools.

## Problem

#2317 needs MCP write tools to distinguish which client wrote memory, not only that the write came from MCP. The write-tool PRs are still in flight (#2306 for `memory.store` / `memory.note`, #2316 for `tree.tag`), so implementing the full source-type propagation directly on `main` would duplicate those open PRs.

## Solution

This PR lands the non-duplicative foundation first: the MCP server records the client identity at `initialize` time and exposes a session source label internally. The default remains `mcp`, so older clients and clients without `clientInfo.name` retain current behavior. Once #2306/#2316 merge, the write dispatch path can use `session.source_type()` instead of the placeholder `mcp` source string.

## Submission Checklist

> If a section does not apply to this change, mark the item as `N/A` with a one-line reason. Do not delete items.

- [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement)
- [x] **Diff coverage ≥ 80%** — local coverage not run; CI coverage gate is the source of truth for changed-line coverage on this PR.
- [x] Coverage matrix updated — N/A: MCP protocol/session foundation only; no feature matrix row added/removed/renamed.
- [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — N/A: no feature matrix row applies.
- [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy))
- [x] Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release manual smoke flow changes.
- [x] Linked issue closed via `Closes #NNN` in the `## Related` section — N/A: this prepares #2317 but does not fully close it until write tools consume the session source label.

## Impact

- Runtime/platform impact: CLI stdio MCP server only.
- Compatibility: existing stateless helpers remain available; stdio sessions now retain client provenance across newline-delimited JSON-RPC messages.
- Security/privacy: records only the client name already supplied by the MCP initialize payload; no wire-format mutation and no new persistence.
- Performance: negligible in-memory string normalization during initialize only.

## Related

- Refs #2317
- Depends conceptually on #2306 and #2316 for write-tool consumption.
- Follow-up PR(s)/TODOs: after #2306/#2316 merge, thread `McpSession::source_type()` into `memory.store`, `memory.note`, and `tree.tag` source_type construction.

---

## AI Authored PR Metadata (required for Codex/Linear PRs)

> Keep this section for AI-authored PRs. For human-only PRs, mark each field `N/A`.

### Linear Issue
- Key: N/A
- URL: N/A

### Commit & Branch
- Branch: `feat/mcp-client-provenance`
- Commit SHA: `95ab2dfe`

### Validation Run
- [x] `pnpm --filter openhuman-app format:check` — blocked locally; see Validation Blocked.
- [x] `pnpm typecheck` — not run locally because Node/app dependency environment is blocked; see Validation Blocked.
- [x] Focused tests: `GGML_NATIVE=OFF cargo test --lib mcp_server --manifest-path Cargo.toml` — 47 passed, 0 failed.
- [x] Rust fmt/check (if changed): `cargo fmt --check --manifest-path Cargo.toml` — passed.
- [x] Tauri fmt/check (if changed): N/A, no Tauri shell files changed.
- [x] Additional: `git diff --check` — passed.

### Validation Blocked
- `command:` `pnpm --filter openhuman-app format:check` via pre-push hook
- `error:` local app dependencies are not installed (`prettier: command not found`), and local Node is `v22.14.0` while `openhuman-app` requires `>=24.0.0`.
- `impact:` local JS/Prettier validation could not run in this environment; Rust-focused validation for the touched MCP core files passed. Push used `--no-verify` because the hook failure was local environment/dependency setup, not this change.
- `command:` `GGML_NATIVE=OFF cargo clippy --lib --manifest-path Cargo.toml --no-deps -- -D warnings`
- `error:` blocked by 119 pre-existing lint errors in unrelated files (examples: unused imports in `src/openhuman/inference/local/mod.rs`, duplicate module lints in `src/openhuman/inference/provider/*`, doc/comment lints, and unrelated clippy style lints across memory/tools/wallet).
- `impact:` clippy cannot currently be used as a clean global gate locally; focused MCP tests and Rust formatting passed.

### Behavior Changes
- Intended behavior change: MCP stdio sessions now remember the normalized client source label from `initialize.params.clientInfo.name` and preserve it for the session.
- User-visible effect: none immediately for read-only tools; follow-up write tools can attribute memory writes to `mcp:<client>` while preserving `mcp` fallback.

### Parity Contract
- Legacy behavior preserved: missing/empty/blank/unusable client names continue to produce bare `mcp`; later malformed initialize payloads do not clear already captured session provenance.
- Guard/fallback/dispatch parity checks: existing `handle_json_line` / `handle_json_value` APIs still work; stdio loop uses the new stateful handlers so session data persists between messages.

### Duplicate / Superseded PR Handling
- Duplicate PR(s): #2306 and #2316 are related dependencies, not duplicates.
- Canonical PR: this PR is the canonical non-duplicative foundation for #2317 on `main`.
- Resolution (closed/superseded/updated): N/A.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * MCP server now captures and preserves a client "source" label from initialization, normalizing client names and falling back to a default when absent.

* **Documentation**
  * Added guidance on client provenance, name-normalization rules, and recommended source-label usage for tools.

* **Tests**
  * Added unit tests verifying client name normalization and initialization behavior for captured source labels.

<!-- review_stack_entry_start -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2332?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: 李冠辰 <liguanchen@xiaomi.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
@justinhsu1477
Copy link
Copy Markdown
Contributor Author

@graycyrus — thanks for the careful read. All three inline notes are valid concerns, and all three are in code that landed in #2306 (dispatch_write_tool, slug_from, and the mcp-note-<chunk_id> key shape), not in this PR's new surface. To avoid forking the fix into two PRs:

  1. [major] line 705 — memory.note upsert-vs-append mismatch — Real. The key derivation lives in feat(mcp): add memory.store and memory.note write tools #2306's build_rpc_params("memory.note") arm. Posting a separate technical note on feat(mcp): add memory.store and memory.note write tools #2306 so the fix lands at the source. In parallel I'll update this PR's description (the comparison table calls memory.note's re-call semantic "Append-style") to say "upsert" instead, so the docs match the actual behaviour until feat(mcp): add memory.store and memory.note write tools #2306 settles the contract.

  2. [minor] line 1072 — dispatch_write_tool hardcoded rpc_method — Agreed. Passing spec.rpc_method through would future-proof for any write tool that doesn't route through memory_doc_put. Same function (feat(mcp): add memory.store and memory.note write tools #2306's), best refactored at the source.

  3. [minor] line 686 — slug_from punctuation collisions — Also in feat(mcp): add memory.store and memory.note write tools #2306. A short content-hash mix-in, or an explicit "intentional dedup-on-title" comment, resolves it either way.

tree.tag itself doesn't introduce upsert/append ambiguity — re-tag replaces, stated explicitly in the schema description ("Re-tagging the same chunk replaces the prior tag set; supply the complete desired set on each call") — and doesn't introduce its own slug path. Once these land in #2306 this PR rebases cleanly.

mtkik pushed a commit to mtkik/openhuman-meet that referenced this pull request May 21, 2026
## Summary

- Adds MCP stdio session state that captures `initialize.params.clientInfo.name` for the lifetime of the session.
- Normalizes known MCP client names into stable source labels such as `mcp:claude-desktop`, `mcp:cursor`, and `mcp:windsurf`.
- Preserves the existing bare `mcp` fallback for missing, empty, whitespace-only, or Unicode-only client names.
- Keeps existing stateless protocol helpers for tests/callers while wiring the stdio loop through the new stateful handler.
- Documents the provenance contract for follow-up write-capable MCP tools.

## Problem

tinyhumansai#2317 needs MCP write tools to distinguish which client wrote memory, not only that the write came from MCP. The write-tool PRs are still in flight (tinyhumansai#2306 for `memory.store` / `memory.note`, tinyhumansai#2316 for `tree.tag`), so implementing the full source-type propagation directly on `main` would duplicate those open PRs.

## Solution

This PR lands the non-duplicative foundation first: the MCP server records the client identity at `initialize` time and exposes a session source label internally. The default remains `mcp`, so older clients and clients without `clientInfo.name` retain current behavior. Once tinyhumansai#2306/tinyhumansai#2316 merge, the write dispatch path can use `session.source_type()` instead of the placeholder `mcp` source string.

## Submission Checklist

> If a section does not apply to this change, mark the item as `N/A` with a one-line reason. Do not delete items.

- [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement)
- [x] **Diff coverage ≥ 80%** — local coverage not run; CI coverage gate is the source of truth for changed-line coverage on this PR.
- [x] Coverage matrix updated — N/A: MCP protocol/session foundation only; no feature matrix row added/removed/renamed.
- [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — N/A: no feature matrix row applies.
- [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy))
- [x] Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release manual smoke flow changes.
- [x] Linked issue closed via `Closes #NNN` in the `## Related` section — N/A: this prepares tinyhumansai#2317 but does not fully close it until write tools consume the session source label.

## Impact

- Runtime/platform impact: CLI stdio MCP server only.
- Compatibility: existing stateless helpers remain available; stdio sessions now retain client provenance across newline-delimited JSON-RPC messages.
- Security/privacy: records only the client name already supplied by the MCP initialize payload; no wire-format mutation and no new persistence.
- Performance: negligible in-memory string normalization during initialize only.

## Related

- Refs tinyhumansai#2317
- Depends conceptually on tinyhumansai#2306 and tinyhumansai#2316 for write-tool consumption.
- Follow-up PR(s)/TODOs: after tinyhumansai#2306/tinyhumansai#2316 merge, thread `McpSession::source_type()` into `memory.store`, `memory.note`, and `tree.tag` source_type construction.

---

## AI Authored PR Metadata (required for Codex/Linear PRs)

> Keep this section for AI-authored PRs. For human-only PRs, mark each field `N/A`.

### Linear Issue
- Key: N/A
- URL: N/A

### Commit & Branch
- Branch: `feat/mcp-client-provenance`
- Commit SHA: `95ab2dfe`

### Validation Run
- [x] `pnpm --filter openhuman-app format:check` — blocked locally; see Validation Blocked.
- [x] `pnpm typecheck` — not run locally because Node/app dependency environment is blocked; see Validation Blocked.
- [x] Focused tests: `GGML_NATIVE=OFF cargo test --lib mcp_server --manifest-path Cargo.toml` — 47 passed, 0 failed.
- [x] Rust fmt/check (if changed): `cargo fmt --check --manifest-path Cargo.toml` — passed.
- [x] Tauri fmt/check (if changed): N/A, no Tauri shell files changed.
- [x] Additional: `git diff --check` — passed.

### Validation Blocked
- `command:` `pnpm --filter openhuman-app format:check` via pre-push hook
- `error:` local app dependencies are not installed (`prettier: command not found`), and local Node is `v22.14.0` while `openhuman-app` requires `>=24.0.0`.
- `impact:` local JS/Prettier validation could not run in this environment; Rust-focused validation for the touched MCP core files passed. Push used `--no-verify` because the hook failure was local environment/dependency setup, not this change.
- `command:` `GGML_NATIVE=OFF cargo clippy --lib --manifest-path Cargo.toml --no-deps -- -D warnings`
- `error:` blocked by 119 pre-existing lint errors in unrelated files (examples: unused imports in `src/openhuman/inference/local/mod.rs`, duplicate module lints in `src/openhuman/inference/provider/*`, doc/comment lints, and unrelated clippy style lints across memory/tools/wallet).
- `impact:` clippy cannot currently be used as a clean global gate locally; focused MCP tests and Rust formatting passed.

### Behavior Changes
- Intended behavior change: MCP stdio sessions now remember the normalized client source label from `initialize.params.clientInfo.name` and preserve it for the session.
- User-visible effect: none immediately for read-only tools; follow-up write tools can attribute memory writes to `mcp:<client>` while preserving `mcp` fallback.

### Parity Contract
- Legacy behavior preserved: missing/empty/blank/unusable client names continue to produce bare `mcp`; later malformed initialize payloads do not clear already captured session provenance.
- Guard/fallback/dispatch parity checks: existing `handle_json_line` / `handle_json_value` APIs still work; stdio loop uses the new stateful handlers so session data persists between messages.

### Duplicate / Superseded PR Handling
- Duplicate PR(s): tinyhumansai#2306 and tinyhumansai#2316 are related dependencies, not duplicates.
- Canonical PR: this PR is the canonical non-duplicative foundation for tinyhumansai#2317 on `main`.
- Resolution (closed/superseded/updated): N/A.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * MCP server now captures and preserves a client "source" label from initialization, normalizing client names and falling back to a default when absent.

* **Documentation**
  * Added guidance on client provenance, name-normalization rules, and recommended source-label usage for tools.

* **Tests**
  * Added unit tests verifying client name normalization and initialization behavior for captured source labels.

<!-- review_stack_entry_start -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2332?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: 李冠辰 <liguanchen@xiaomi.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
@graycyrus
Copy link
Copy Markdown
Contributor

Security & Robustness Review — Final Verdict

Clean, well-scoped PR. The tree.tag implementation is tight — required_non_empty_string_array catches empty/blank tag arrays at the MCP layer, the upsert-replace semantic is explicitly documented in the schema description, and the 8 unit tests cover every validation path. Good call reusing enforce_write_policy + dispatch_write_tool from #2306 without modification — keeps the security surface consistent.

Overall: this is good to merge (once #2306 lands and you rebase).

Two items worth noting, both minor:

Tag-specific concerns

  1. Tag value sanitization — Tags are trimmed and blank entries are filtered, which is good. But there's no max-length or max-count cap on the tags array. A client could send 1000 tags or a single tag that's 10KB of text. A lightweight cap (e.g. max 50 tags, max 128 chars per tag) would match the "explicit rejection" pattern used elsewhere in the MCP layer and prevent index bloat.

  2. Silent tag replacement — The replace-on-re-call semantic is the right design and it's clearly documented. Just flagging that from a user's perspective, if an LLM re-tags a chunk, the prior tag set is gone with no undo. This ties into the broader "UI notification for MCP writes" recommendation on feat(mcp): add memory.store and memory.note write tools #2306 — users should ideally see when their tags get modified by an external client.

Already covered by #2306's foundation

The content size limit, ToolOperation::Write gate, per-write notification, and multi-process concerns I raised on #2306 apply equally here but are inherited from that PR's scaffolding — no action needed on this side.

Nice work closing out the Phase 3 RFC. The three-tool write surface (memory.store, memory.note, tree.tag) is a well-structured, incremental addition. 👍

Addresses graycyrus's tinyhumansai#2316 review minor tinyhumansai#1 (tag-array sanitization).

Adds two upper bounds in `build_rpc_params("tree.tag")`:

  TREE_TAG_MAX_TAGS       = 50
  TREE_TAG_MAX_TAG_LENGTH = 128

Both reject up-front via `ToolCallError::InvalidParams` rather than
silently truncating — same "explicit rejection over silent clamping"
discipline already used by `required_non_empty_string_array` for
empty/blank entries.

Error messages match the existing argument-rejection style:
  "argument `tags` accepts at most 50 entries (got N)"
  "argument `tags` entry exceeds 128 characters (got N chars)"

The caps prevent two specific misuse patterns:
  - A misbehaving MCP client flooding a single chunk's tag-record
    document with hundreds of categorical labels (would bloat the
    document tags index disproportionately).
  - A single oversize tag that's actually free-form text — that
    payload belongs in `memory.note`, not the categorical tag
    surface. Rejecting up-front surfaces the misuse rather than
    silently writing a giant token into the queryable `tags` index.

Three new unit tests cover:
  - Oversize array rejection (TREE_TAG_MAX_TAGS + 1 entries).
  - Oversize single-entry rejection (TREE_TAG_MAX_TAG_LENGTH + 1 chars).
  - Boundary acceptance (exactly TREE_TAG_MAX_TAGS entries with
    each entry at exactly TREE_TAG_MAX_TAG_LENGTH chars) so a future
    off-by-one refactor fails this test, not user calls.

Tests: 11 tree_tag unit tests pass (was 8; +3 new). All 59 existing
`mcp_server` tests still pass — no regression.

Verified locally:
  - `cargo check --lib` clean
  - `cargo test --lib tree_tag` 11/11 pass
  - `cargo fmt --check` clean
  - `cargo clippy --lib --no-deps` no new warnings
@justinhsu1477
Copy link
Copy Markdown
Contributor Author

@graycyrus thanks for the thorough review.

Minor #1 (tag-array sanitization) — addressed in 3c50c975. Added TREE_TAG_MAX_TAGS = 50 and TREE_TAG_MAX_TAG_LENGTH = 128 caps in build_rpc_params("tree.tag"), both rejecting up-front via ToolCallError::InvalidParams (same explicit-rejection style as required_non_empty_string_array). Three new tests cover:

  • Oversize array (MAX_TAGS + 1 entries) → rejected.
  • Oversize single entry (MAX_TAG_LENGTH + 1 chars) → rejected.
  • Boundary acceptance (exactly MAX_TAGS entries × MAX_TAG_LENGTH chars) → succeeds, locking in inclusive bounds against a future off-by-one refactor.

11 tree_tag unit tests pass (was 8; +3 new), all 62 mcp_server tests still pass — no regression.

Minor #2 (silent tag replacement → UI notification) — agreed this lives at the broader "MCP write surface UI" follow-up theme you raised on #2306 rather than being tree.tag-specific. Will file a separate issue once the foundation PRs (#2306, #2316) merge so the UX scope can span all three write tools at once. Not blocking this PR.

Ready for re-review.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/openhuman/mcp_server/tools.rs (1)

752-756: 💤 Low value

Byte length vs character count terminology.

String::len() returns byte length, but the error message says "characters". For ASCII tags (typical for categorical labels) this is equivalent, but for Unicode input (e.g., "会议" is 2 characters but 6 bytes) the reported count would be misleading.

Consider either changing the message to say "bytes" or using .chars().count() if true character-count semantics are intended. Given tags are categorical labels (expected ASCII), this is a minor documentation nit.

Option A: Update terminology (simpler)
             if let Some(oversize) = tags.iter().find(|t| t.len() > TREE_TAG_MAX_TAG_LENGTH) {
                 return Err(ToolCallError::InvalidParams(format!(
-                    "argument `tags` entry exceeds {TREE_TAG_MAX_TAG_LENGTH} characters (got {} chars)",
+                    "argument `tags` entry exceeds {TREE_TAG_MAX_TAG_LENGTH} bytes (got {} bytes)",
                     oversize.len()
                 )));
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/mcp_server/tools.rs` around lines 752 - 756, The error message
reports "characters" but uses String::len() (byte length); update the code that
constructs the ToolCallError in the oversize branch (the tags.iter().find(...)
check that returns ToolCallError::InvalidParams) to either report "bytes"
instead of "characters" to match oversize.len(), or replace oversize.len() with
oversize.chars().count() if you want true character counts; keep the referenced
constant TREE_TAG_MAX_TAG_LENGTH unchanged and ensure the formatted value
reflects the chosen unit (bytes or characters).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/openhuman/mcp_server/tools.rs`:
- Around line 752-756: The error message reports "characters" but uses
String::len() (byte length); update the code that constructs the ToolCallError
in the oversize branch (the tags.iter().find(...) check that returns
ToolCallError::InvalidParams) to either report "bytes" instead of "characters"
to match oversize.len(), or replace oversize.len() with oversize.chars().count()
if you want true character counts; keep the referenced constant
TREE_TAG_MAX_TAG_LENGTH unchanged and ensure the formatted value reflects the
chosen unit (bytes or characters).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 444a0ba3-d376-4b30-b4d9-06c337374de9

📥 Commits

Reviewing files that changed from the base of the PR and between 19a8694 and 3c50c97.

📒 Files selected for processing (1)
  • src/openhuman/mcp_server/tools.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 22, 2026
CI flagged a fmt diff on `3c50c975` — the three new tag-cap tests
had multi-line `build_rpc_params(...)` / `assert!(...)` invocations
that fmt wants collapsed onto single lines.

Locally `cargo fmt --check` had run against the wrong branch when
the commit was prepared, so the diff slipped through. Re-ran fmt
on this branch and the formatter applied:

  - `let err = build_rpc_params(...)` calls collapsed to single line
  - `assert!(...)` calls with short bodies collapsed to single line
  - `let params = build_rpc_params(...)` collapsed to single line

No behaviour change — purely the formatter's preferred width-based
collapse. All 11 tree_tag unit tests still pass.
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 22, 2026
@justinhsu1477
Copy link
Copy Markdown
Contributor Author

Quick CI status note for anyone scanning this surface:

Plan: once #2470 lands and @Liohtml rebases #2306 onto new main, I'll do the matching rebase here and add annotations: … to the tree.tag initializer. No action needed in the meantime.

@YOMXXX
Copy link
Copy Markdown
Contributor

YOMXXX commented May 22, 2026

Status link for the dependency chain: I prepared and validated a #2306 sync branch at YOMXXX:fix/2306-review-sync, but cannot push directly to Liohtml:feat/mcp-write-tools from the current credentials. Details are on #2306.

This PR still has three unresolved review threads pointing at src/openhuman/mcp_server/tools.rs from the #2306 foundation layer (memory.note, dispatch_write_tool, slug_from). I would keep #2316 unchanged until #2306 advances, then rebase #2316 so those inherited threads become stale/resolved and CI can rerun against current main.

CodeGhost21 pushed a commit to CodeGhost21/openhuman that referenced this pull request May 22, 2026
## Summary

- Adds MCP stdio session state that captures `initialize.params.clientInfo.name` for the lifetime of the session.
- Normalizes known MCP client names into stable source labels such as `mcp:claude-desktop`, `mcp:cursor`, and `mcp:windsurf`.
- Preserves the existing bare `mcp` fallback for missing, empty, whitespace-only, or Unicode-only client names.
- Keeps existing stateless protocol helpers for tests/callers while wiring the stdio loop through the new stateful handler.
- Documents the provenance contract for follow-up write-capable MCP tools.

## Problem

tinyhumansai#2317 needs MCP write tools to distinguish which client wrote memory, not only that the write came from MCP. The write-tool PRs are still in flight (tinyhumansai#2306 for `memory.store` / `memory.note`, tinyhumansai#2316 for `tree.tag`), so implementing the full source-type propagation directly on `main` would duplicate those open PRs.

## Solution

This PR lands the non-duplicative foundation first: the MCP server records the client identity at `initialize` time and exposes a session source label internally. The default remains `mcp`, so older clients and clients without `clientInfo.name` retain current behavior. Once tinyhumansai#2306/tinyhumansai#2316 merge, the write dispatch path can use `session.source_type()` instead of the placeholder `mcp` source string.

## Submission Checklist

> If a section does not apply to this change, mark the item as `N/A` with a one-line reason. Do not delete items.

- [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement)
- [x] **Diff coverage ≥ 80%** — local coverage not run; CI coverage gate is the source of truth for changed-line coverage on this PR.
- [x] Coverage matrix updated — N/A: MCP protocol/session foundation only; no feature matrix row added/removed/renamed.
- [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — N/A: no feature matrix row applies.
- [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy))
- [x] Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release manual smoke flow changes.
- [x] Linked issue closed via `Closes #NNN` in the `## Related` section — N/A: this prepares tinyhumansai#2317 but does not fully close it until write tools consume the session source label.

## Impact

- Runtime/platform impact: CLI stdio MCP server only.
- Compatibility: existing stateless helpers remain available; stdio sessions now retain client provenance across newline-delimited JSON-RPC messages.
- Security/privacy: records only the client name already supplied by the MCP initialize payload; no wire-format mutation and no new persistence.
- Performance: negligible in-memory string normalization during initialize only.

## Related

- Refs tinyhumansai#2317
- Depends conceptually on tinyhumansai#2306 and tinyhumansai#2316 for write-tool consumption.
- Follow-up PR(s)/TODOs: after tinyhumansai#2306/tinyhumansai#2316 merge, thread `McpSession::source_type()` into `memory.store`, `memory.note`, and `tree.tag` source_type construction.

---

## AI Authored PR Metadata (required for Codex/Linear PRs)

> Keep this section for AI-authored PRs. For human-only PRs, mark each field `N/A`.

### Linear Issue
- Key: N/A
- URL: N/A

### Commit & Branch
- Branch: `feat/mcp-client-provenance`
- Commit SHA: `95ab2dfe`

### Validation Run
- [x] `pnpm --filter openhuman-app format:check` — blocked locally; see Validation Blocked.
- [x] `pnpm typecheck` — not run locally because Node/app dependency environment is blocked; see Validation Blocked.
- [x] Focused tests: `GGML_NATIVE=OFF cargo test --lib mcp_server --manifest-path Cargo.toml` — 47 passed, 0 failed.
- [x] Rust fmt/check (if changed): `cargo fmt --check --manifest-path Cargo.toml` — passed.
- [x] Tauri fmt/check (if changed): N/A, no Tauri shell files changed.
- [x] Additional: `git diff --check` — passed.

### Validation Blocked
- `command:` `pnpm --filter openhuman-app format:check` via pre-push hook
- `error:` local app dependencies are not installed (`prettier: command not found`), and local Node is `v22.14.0` while `openhuman-app` requires `>=24.0.0`.
- `impact:` local JS/Prettier validation could not run in this environment; Rust-focused validation for the touched MCP core files passed. Push used `--no-verify` because the hook failure was local environment/dependency setup, not this change.
- `command:` `GGML_NATIVE=OFF cargo clippy --lib --manifest-path Cargo.toml --no-deps -- -D warnings`
- `error:` blocked by 119 pre-existing lint errors in unrelated files (examples: unused imports in `src/openhuman/inference/local/mod.rs`, duplicate module lints in `src/openhuman/inference/provider/*`, doc/comment lints, and unrelated clippy style lints across memory/tools/wallet).
- `impact:` clippy cannot currently be used as a clean global gate locally; focused MCP tests and Rust formatting passed.

### Behavior Changes
- Intended behavior change: MCP stdio sessions now remember the normalized client source label from `initialize.params.clientInfo.name` and preserve it for the session.
- User-visible effect: none immediately for read-only tools; follow-up write tools can attribute memory writes to `mcp:<client>` while preserving `mcp` fallback.

### Parity Contract
- Legacy behavior preserved: missing/empty/blank/unusable client names continue to produce bare `mcp`; later malformed initialize payloads do not clear already captured session provenance.
- Guard/fallback/dispatch parity checks: existing `handle_json_line` / `handle_json_value` APIs still work; stdio loop uses the new stateful handlers so session data persists between messages.

### Duplicate / Superseded PR Handling
- Duplicate PR(s): tinyhumansai#2306 and tinyhumansai#2316 are related dependencies, not duplicates.
- Canonical PR: this PR is the canonical non-duplicative foundation for tinyhumansai#2317 on `main`.
- Resolution (closed/superseded/updated): N/A.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * MCP server now captures and preserves a client "source" label from initialization, normalizing client names and falling back to a default when absent.

* **Documentation**
  * Added guidance on client provenance, name-normalization rules, and recommended source-label usage for tools.

* **Tests**
  * Added unit tests verifying client name normalization and initialization behavior for captured source labels.

<!-- review_stack_entry_start -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2332?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: 李冠辰 <liguanchen@xiaomi.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
@senamakel senamakel self-assigned this May 22, 2026
senamakel added 3 commits May 22, 2026 12:52
- Add `annotations` to the new memory.store / memory.note / tree.tag
  McpToolSpec entries (writeable, idempotent upsert, closed-world). This
  unblocks the `Rust Quality (clippy)` CI failure introduced by the
  `annotations` field added in tinyhumansai#2268 — the new specs were forked from
  pre-tinyhumansai#2268 tinyhumansai#2306 scaffolding and were missing the required field on
  rebase against current main.

- Extend the read-only-by-default test exemption (`act_tool_names`) to
  cover the three new write tools, matching their `readOnlyHint: false`.

- Fix CodeRabbit nit (tools.rs:803): the oversize-tag error said
  "characters" but used `String::len()` (byte length). Reword to
  "bytes" so Unicode tag input gets a self-consistent message.

Defers @graycyrus's three inline threads on lines 686/705/1072
(slug_from collisions, memory.note upsert-vs-append, dispatch_write_tool
hardcoded rpc_method) to tinyhumansai#2306 per the author's review-thread reply —
that's where the underlying functions land and where the fix should
originate so this branch can simply inherit on rebase.
@senamakel
Copy link
Copy Markdown
Member

Status update (PR-finisher pass on 2026-05-22T20:02Z)

Pushed 560a4c2 to address what was actionable without forking the #2306 fixes:

  1. CodeRabbit nit (tools.rs:803, characters → bytes) — applied. String::len() returns bytes, so the error message now reads "exceeds {N} bytes (got {} bytes)" for consistency on Unicode tag input.
  2. CI: McpToolSpec.annotations missing on the three new specs — applied. Added write_local_annotations() (readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false) for memory.store / memory.note / tree.tag, and extended act_tool_names in read_only_tools_are_marked_read_only_and_closed_world so the readOnly contract test still passes. cargo check --lib, cargo clippy --lib --no-deps -D warnings, and cargo test --lib mcp_server (78/78) all green locally. Should clear the Rust Quality red.
  3. Frontend / i18n / Coverage / E2E reds — left untouched; per @justinhsu1477's earlier note these are the stale-base German-locale gap @YOMXXX is unblocking via fix(i18n): complete German MCP translations #2470 + feat(mcp): add memory.store and memory.note write tools #2306 rebase. Not in scope for feat(mcp): add tree.tag write tool (completes Phase 3 #2269) #2316.

Deferred per @justinhsu1477's review reply (not addressed here):

  • @graycyrus tools.rs:705 — memory.note upsert-vs-append semantic mismatch.
  • @graycyrus tools.rs:1072 — dispatch_write_tool hardcoded rpc_method.
  • @graycyrus tools.rs:686 — slug_from punctuation-collision risk.

All three live in #2306's foundation code (build_rpc_params("memory.note"), dispatch_write_tool, slug_from) and the author has explicitly chosen to fix them at the source so this branch inherits cleanly on rebase. Flagging here so the threads aren't lost.

@senamakel senamakel merged commit 788087c into tinyhumansai:main May 22, 2026
25 of 26 checks passed
senamakel added a commit to aqilaziz/openhuman that referenced this pull request May 23, 2026
…) (tinyhumansai#2316)

Co-authored-by: Lionel <lionel.machire@gmail.com>
Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants