Skip to content

fix(mcp): set_fields rejects reserved keys; add top-level description#158

Open
avrabe wants to merge 1 commit intomainfrom
fix/mcp-set-fields-scope
Open

fix(mcp): set_fields rejects reserved keys; add top-level description#158
avrabe wants to merge 1 commit intomainfrom
fix/mcp-set-fields-scope

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented Apr 21, 2026

Two related MCP bugs

Bug 1 — `yaml_edit.rs::modify_artifact_yaml` unconditionally nested every `set_fields` key under `fields:` using `format!("{key}: {value}")`. Passing `description=...` wrote `fields: {description: ...}` instead of touching top-level, and raw-interpolation corrupted YAML when the value contained backticks or newlines.

Bug 2 — MCP `ModifyParams` exposed only `status`, `title`, `add_tags`, `remove_tags`, `set_fields`. Top-level `description` (and `source-file`, `provenance`, full tag replacement) were unreachable via MCP at all.

Fix

  • `rivet-core/src/mutate.rs` — new `RESERVED_TOP_LEVEL_KEYS` const (id, type, title, description, status, tags, links, fields, provenance, source-file). `validate_modify` rejects reserved keys in `set_fields` with a targeted hint. `ModifyParams` gains `set_description: Option`.
  • `rivet-core/src/yaml_edit.rs` — new helpers for YAML-safe scalar quoting: `yaml_quote_inline_scalar`, `yaml_double_quote`, `yaml_render_scalar_value`. Double-quoted for single-line YAML-significant chars, block-literal `|-` for multi-line with proper continuation indent. `set_field` splits rendered output on `\n` for block-literal continuation. `set_title`/`set_status` also quote safely now.
  • `rivet-cli/src/mcp.rs` — `ModifyParams` gains `description: Option` sibling of `status`/`title`. No new tool; existing `rivet_modify` surface just got larger.

Test plan

  • 4 MCP stdio integration tests (`rivet-cli/tests/mcp_integration.rs`)
  • 1 mutate integration test (`rivet-core/tests/mutate_integration.rs`)
  • 4 yaml_edit unit tests
  • All green: 22/22 + 20/20 + 29/29
  • CI green

Depends-on: #157 for clean main.rs merge.

🤖 Generated with Claude Code

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Rivet Criterion Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.

Benchmark suite Current: 00e997b Previous: 60d728a Ratio
store_lookup/1000 25029 ns/iter (± 222) 19280 ns/iter (± 48) 1.30
diff/1000 717685 ns/iter (± 3324) 584274 ns/iter (± 2706) 1.23
query/100 773 ns/iter (± 3) 619 ns/iter (± 1) 1.25
query/1000 6826 ns/iter (± 20) 5174 ns/iter (± 14) 1.32

This comment was automatically generated by workflow using github-action-benchmark.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

❌ Patch coverage is 83.81503% with 28 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rivet-core/src/yaml_edit.rs 82.50% 28 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe force-pushed the fix/mcp-set-fields-scope branch from 9701cf9 to 00e997b Compare April 21, 2026 18:59
…tion` setter

Two related MCP correctness bugs in `rivet_modify`:

1. `set_fields` silently wrote reserved top-level keys (description, title,
   status, ...) under the `fields:` sub-map — corrupting the artifact's
   shape. Values containing backticks or newlines additionally broke the
   YAML emitter, which used unquoted `format!("{key}: {value}")` lines.

2. There was no way to set the top-level `description` (or other top-level
   metadata) via MCP — `set_fields` was the only "generic setter" and, by
   design, targets only the domain `fields:` map.

Design: keep `set_fields` scoped to the `fields:` sub-map and expose
dedicated setters for top-level metadata. `validate_modify` now rejects
any `set_fields` key listed in `RESERVED_TOP_LEVEL_KEYS` (id, type, title,
description, status, tags, links, fields, provenance, source-file) with a
hint pointing at the right parameter. A new `description` parameter on
`rivet_modify` routes through `ModifyParams::set_description`, which
emits YAML-safe scalars — multi-line values become block-literal (`|-`)
scalars, single-line values with YAML-significant characters are
double-quoted with proper escapes. `set_field` in the editor was extended
to splice multi-line values into the line buffer so block scalars stay
well-formed.

Tests (added failing first, now green):
- `test_set_fields_rejects_reserved_description` / `_all_reserved_top_level_keys`
- `test_modify_sets_top_level_description`
- `test_modify_description_with_backticks_and_newlines`
- `test_validate_modify_rejects_reserved_top_level_in_set_fields`
- `test_yaml_quote_inline_scalar_*`, `test_set_field_writes_*_as_*_scalar`

Fixes: REQ-002
@avrabe avrabe force-pushed the fix/mcp-set-fields-scope branch from 00e997b to c2e56f7 Compare April 21, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant