Skip to content

Keep a remote tool's annotations on McpRemoteTool - #104

Open
senamakel wants to merge 7 commits into
mainfrom
remote-tool-annotations
Open

senamakel wants to merge 7 commits into
mainfrom
remote-tool-annotations

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

McpRemoteTool dropped each listed tool's annotations. That object carries the MCP behaviour hints (readOnlyHint, destructiveHint, …), so a host calling a remote server's tools could not tell one that only reads from one that changes something. A host that asks the user before a write had to treat every tool as a write.

This keeps annotations on McpRemoteTool verbatim (optional, omitted when absent) and adds McpRemoteTool::is_read_only(). Following the protocol's default, a tool without readOnlyHint: true is not read-only. The hints are documented as untrusted: a reason to ask the user, never a reason to skip a check.

Related issue

None.

API or behavior changes

  • Contract 1.24 → 1.25 (additive): new optional field McpRemoteTool::annotations: Option<Value>, and new method McpRemoteTool::is_read_only.
  • Source-level: a struct literal McpRemoteTool { .. } must now name annotations. McpRemoteTool::new and serde decoding are unaffected. OpenHuman has two such literals (mcp/module_client.rs, mcp/module_client_tests.rs), which will get annotations in the gitlink bump.
  • Wire: unchanged when there are no annotations, because the field is skipped when None.

Validation

  • cargo fmt --all -- --check: ok
  • cargo clippy --all-targets --all-features -- -D warnings: ok
  • cargo build --all-targets --all-features: ok
  • cargo test --all-features: all pass
  • RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features: ok

Tests

tinymcp-bus/src/transport/mod_tests.rs:

  • Annotations survive decoding.
  • is_read_only is false without the hint, with it set to false, or with a non-boolean value.
  • Absent annotations stay off the wire.
  • An annotated tool round-trips.

Also updated: the contract version pin, and the one struct literal in the processing tests. The is_read_only doctest covers the example.

Documentation

tinymcp-bus/README.md (the 1.25 entry), the version history comment, and rustdoc on the field and method.

Checklist

  • The change is focused on one logical change
  • No new #[allow(...)], #[ignore], or relaxed lints. The new test file uses the same test-only header as other *_tests.rs
  • No secrets, tokens, or .env contents in the diff or the description

senamakel and others added 2 commits October 15, 2026 16:55
McpRemoteTool now carries the server's annotations object verbatim and offers
is_read_only() to read the readOnlyHint, treating a missing hint as not
read-only per the protocol default. The contract version moves to 1.25 to
record the added optional field.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Describe the annotations field kept on McpRemoteTool and the is_read_only
helper so hosts can tell read-only tools from ones that change state. The
transport tests were also reformatted to satisfy rustfmt.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@tinysweeper

tinysweeper Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper reviewed this change across 5 lane(s) and found 0 active actionable finding(s). The change keeps a listed remote tool's `annotations` object on `McpRemoteTool` as an optional serde field, adds `McpRemoteTool::is_read_only()`, bumps the contract version to 1.25, and scrubs annotations in `StaticScrubTools`. Detailed lane evidence and any incomplete work are listed below.

State: Ready for maintainer review
Priority: none
Reviewed head: b20a7d13bfab
Updated: 2026-10-15T13:23:07Z

Review snapshot

Change surface Files Review signal Count
Production 4 Active findings 0
Tests 4 Noted findings 0
Documentation 1 Resolved findings 0
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

Adds an optional `annotations` field to `McpRemoteTool` in crates/tinymcp-bus/src/transport/types.rs, verbatim behaviour hints such as `readOnlyHint`, omitted from the wire when absent; adds `McpRemoteTool::is_read_only()` which returns true only when `annotations.readOnlyHint` is boolean true, following the protocol default that an absent hint is not read-only. Bumps `CONTRACT_VERSION` to (1, 25) in crates/tinymcp-bus/src/version/mod.rs and pins it in crates/tinymcp-bus/src/version/mod_tests.rs. Scrubs the annotations object alongside `_meta` in `scrub_static_tools` at crates/tinymcp/src/processing/mod.rs. Documents the behaviour as Contract 1.25 in crates/tinymcp-bus/README.md and adds a test module for the transport types via crates/tinymcp-bus/src/transport/mod.rs.

Features

  • Added — Optional `annotations` field on `McpRemoteTool`: A listed tool's `annotations` object (e.g. `readOnlyHint`, `destructiveHint`) is now retained on the DTO and round-trips through the wire form, omitted only when absent, so a host can inspect the server's behaviour hints. (crates/tinymcp-bus/src/transport/types.rs#pub struct McpRemoteTool {, crates/tinymcp-bus/src/transport/mod_tests.rs)
  • Added — `McpRemoteTool::is_read_only()`: Provides a `#[must_use]` accessor reporting whether the server marks the tool as changing nothing; a tool without the hint, with an empty annotations object, or with a non-boolean `readOnlyHint` is reported as not read-only, matching the protocol default. (crates/tinymcp-bus/src/transport/types.rs#impl McpRemoteTool {, crates/tinymcp-bus/src/transport/mod_tests.rs)
  • Modified — Contract version bump to 1.25: `CONTRACT_VERSION` becomes (1, 25) and the pinning test asserts the new value, signalling the added optional `annotations` member to hosts binding to a module. (crates/tinymcp-bus/src/version/mod.rs, crates/tinymcp-bus/src/version/mod_tests.rs#use super::{CONTRACT_VERSION, binds, is_compatible};)
  • Modified — Annotations scrubbed in `scrub_static_tools`: The configured-credential scrubber now also runs over a tool's `annotations` object, so credentials accidentally placed in that metadata are redacted before the bus returns it, consistent with the existing `_meta` scrubbing. (crates/tinymcp/src/processing/mod.rs#pub fn scrub_static_tools(, crates/tinymcp-bus/src/processing/mod_tests.rs#fn processing_requests_pin_enum_tags_and_optional_render_defaults() {)
  • Modified — `transport/mod.rs` test module wiring: Adds a `#[cfg(test)]` `tests` module to the transport module so the new `mod_tests.rs` covering the annotations wire form is compiled. (crates/tinymcp-bus/src/transport/mod.rs#pub use types::{)
  • Modified — Documentation for Contract 1.25 annotations: The bus README now explains that a listed tool's `annotations` are kept on `McpRemoteTool`, that the field is optional and omitted when absent, and that `StaticScrubTools` scrubs it. (crates/tinymcp-bus/README.md#Contract 1.24 carries each tool's `_meta` on `McpTool`, so `ListTools`,)

Tests

  • unit — Deserializes a tool JSON value carrying `annotations` (`readOnlyHint` true, `openWorldHint` false) and asserts the annotations are kept verbatim and that `is_read_only()` reports true.: Covers the new persistence of annotations and the positive read-only path. (crates/tinymcp-bus/src/transport/mod_tests.rs)
  • unit — Iterates over absent annotations, an empty object, `readOnlyHint` false, and a non-boolean `readOnlyHint` value, asserting `is_read_only()` is false in each case.: Covers the protocol-default and non-boolean edge cases for the read-only predicate. (crates/tinymcp-bus/src/transport/mod_tests.rs)
  • unit — Serializes a tool without annotations and asserts the annotations key stays off the wire, then serializes an annotated tool, asserts the annotations value on the wire, and asserts a round-trip deserialization equals the original.: Covers the omit-when-absent serde behaviour and round-trip fidelity of the new field. (crates/tinymcp-bus/src/transport/mod_tests.rs)
  • unit — Asserts the shipped `CONTRACT_VERSION` is pinned to (1, 25).: Keeps the contract version pin in step with the new optional member. (crates/tinymcp-bus/src/version/mod_tests.rs#use super::{CONTRACT_VERSION, binds, is_compatible};)
  • integration — Extends the static-tool metadata test fixture with an `annotations` object containing a credential-like title and a `readOnlyHint`, then asserts the annotation title is `[redacted]` while `readOnlyHint` stays true.: Covers that annotations are scrubbed for credentials without destroying non-secret hint values. (crates/tinymcp/src/tinybus_module/mod_tests.rs#async fn static_tool_metadata_redacts_configured_credentials_before_bus_return())
  • unit — Updates the static processing fixture to include `annotations: None` in the tool struct literal.: Keeps an existing processing test compiling against the widened DTO. (crates/tinymcp-bus/src/processing/mod_tests.rs#fn processing_requests_pin_enum_tags_and_optional_render_defaults() {)

Findings

No active actionable findings.

Before merge

None.

How this fits together

flowchart LR
  n0["...in_enum_tags_and_optional_render_defaults<br/>changed"]:::changed
  n1["McpRemoteTool<br/>changed"]:::changed
  n2["scrub_static_tools<br/>changed"]:::changed
  n3["..._configured_credentials_before_bus_return<br/>changed"]:::changed
  n4["bounded"]:::impacted
  n5["json"]:::impacted
  n6["limit_error"]:::impacted
  n7["render_bounded"]:::impacted
  n8["invalid_argument"]:::impacted
  n9["transform_text"]:::impacted
  n0 -->|calls| n5
  n0 -->|tests| n5
  n2 -->|uses| n1
  n2 -->|calls| n4
  n3 -->|calls| n5
  n3 -->|tests| n5
  n4 -->|calls| n6
  n6 -->|calls| n8
  n7 -->|calls| n6
  n7 -->|calls| n8
  n9 -->|calls| n4
  n9 -->|calls| n6
  n9 -->|calls| n8
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 9 files; 0 findings. _The code index is behind this pull request (indexed at `296c0b01fa2e`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 8 files; 0 findings. 1 file was not security-reviewed: crates/tinymcp-bus/README.md (prose or tabular data). _The code index is behind this pull request (indexed at `296c0b01fa2e`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: _The code index is behind this pull request (indexed at `296c0b01fa2e`), so retrieved context may be out of date._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The description matches the diff: it keeps tool `annotations` on `McpRemoteTool` verbatim as an optional serde field, adds `is_read_only()`, bumps the contract to 1.25, scrubs annotations in `StaticScrubTools`, and covers all of it with tests and docs. One thing the description never mentions: `crates/tinymcp/src/processing/mod.rs` also now scrubs `annotations` (only the bus crate's README mentions scrubbing), which is a small omission but the stated scrubbing behaviour is otherwise accurate. I have looked up `scrub_value` and the DTO definitions; `is_read_only` correctly follows the protocol default and the serde attribute mirrors the sibling `meta` field. No functional defect found — a compile check of the workspace is verified by the reported `cargo build`/`clippy` runs, which the diff's struct-literal updates are consistent with. _The code index is behind this pull request (indexed at `296c0b01fa2e`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.000676
  • Tokens: 479897 input · 7641 output · 65280 cached · 393 embedding
Head State Pass summary
0a2cb52fed84 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-15T11:32:33Z)
9dbb3f6321c5 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-15T12:29:07Z)
9dbb3f6321c5 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-15T12:45:38Z)
b20a7d13bfab ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-15T13:23:07Z)

tinysweeper 0.1.2

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T12:25:40.848873Z 9dbb3f6 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8e220ae6-9164-4698-a8b1-73bf3d90d11c
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a2cb52fed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/tinymcp-bus/src/transport/types.rs

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper found nothing blocking, but could not review everything, so this is not an approval: crates/tinymcp-bus/README.md, crates/tinymcp-bus/src/processing/mod_tests.rs, crates/tinymcp-bus/src/transport/mod.rs, crates/tinymcp-bus/src/transport/mod_tests.rs, crates/tinymcp-bus/src/transport/types.rs, crates/tinymcp-bus/src/version/mod.rs, crates/tinymcp-bus/src/version/mod_tests.rs.

             $0.0015 · 159,772 in / 5,469 out · 13,440 cached (8%) · ladder/vectors-oai3, glm-5.3-flash · 335 embedded
critique:    $0.0006 · 59,155 in  / 1,767 out · 4,480 cached (8%)  · glm-5.3-flash
security:    $0.0006 · 65,812 in  / 1,042 out · 8,960 cached (14%) · glm-5.3-flash
tests:       $0.0001 · 14,455 in  / 550 out   · 0 cached (0%)      · glm-5.3-flash
description: $0.0002 · 15,661 in  / 358 out   · 0 cached (0%)      · glm-5.3-flash

senamakel and others added 3 commits October 15, 2026 17:42
Extend the static tool metadata redaction test to include an annotations
object with a credential-bearing title and a readOnlyHint, asserting that
the title is redacted while the boolean hint is preserved.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Tool annotations were left untouched when scrubbing static tools, so any
sensitive values they carried could leak through. They are now passed
through the scrubber alongside the rest of the tool metadata.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Clarify in the README that the optional annotations field on McpRemoteTool is scrubbed by StaticScrubTools along with the rest of a tool's metadata.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2279761381

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/tinymcp-bus/src/transport/types.rs
Comment thread crates/tinymcp-bus/src/transport/mod_tests.rs Outdated
Adds a test module for the bus transport, covering the behaviour that was previously unverified.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper found nothing blocking, but could not review everything, so this is not an approval: crates/tinymcp/src/tinybus_module/mod_tests.rs.

             $0.0018 · 167,823 in / 8,086 out · 4,480 cached (3%) · ladder/vectors-oai3, glm-5.3-flash · 393 embedded
critique:    $0.0006 · 68,932 in  / 503 out   · 4,480 cached (6%) · glm-5.3-flash
security:    $0.0007 · 67,669 in  / 2,477 out · 0 cached (0%)     · glm-5.3-flash
tests:       $0.0001 · 14,886 in  / 12 out    · 0 cached (0%)     · glm-5.3-flash
description: $0.0002 · 7,439 in   / 4,668 out · 0 cached (0%)     · glm-5.3-flash

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper found nothing blocking, but could not review everything, so this is not an approval: crates/tinymcp-bus/src/transport/mod.rs, crates/tinymcp-bus/src/transport/mod_tests.rs, crates/tinymcp-bus/src/transport/types.rs, crates/tinymcp/src/tinybus_module/mod_tests.rs.

             $0.0046 · 292,557 in / 8,748 out · 0 cached (0%) · ladder/vectors-oai3, glm-5.3-flash,  · 393 embedded
critique:    $0.0031 · 147,931 in / 3,737 out · 0 cached (0%) · glm-5.3-flash
security:    $0.0011 · 109,808 in / 3,659 out · 0 cached (0%) · glm-5.3-flash,
tests:       $0.0001 · 15,153 in  / 461 out   · 0 cached (0%) · glm-5.3-flash
description: $0.0002 · 19,665 in  / 891 out   · 0 cached (0%) · glm-5.3-flash

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper found nothing blocking. Approving.

             $0.0007 · 479,897 in / 7,641 out · 65,280 cached (14%) · ladder/vectors-oai3, deepseek-v4.1-flash · 393 embedded
critique:    $0.0004 · 310,925 in / 4,425 out · 41,856 cached (13%) · deepseek-v4.1-flash
security:    $0.0001 · 128,257 in / 1,063 out · 16,384 cached (13%) · deepseek-v4.1-flash
tests:       $0.0000 · 15,970 in  / 8 out     · 2,176 cached (14%)  · deepseek-v4.1-flash
description: $0.0000 · 20,164 in  / 522 out   · 4,864 cached (24%)  · deepseek-v4.1-flash

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants