-
Notifications
You must be signed in to change notification settings - Fork 0
fix(handoff): tolerate omitted completed/remaining in MCP deserialization #548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -181,6 +181,12 @@ pub(crate) struct HandoffRequest { | |
| description = "The work product being handed off (diff, review, document, ...). Prepend the brief so the recipient knows the ask. Attached to the item as an asset." | ||
| )] | ||
| pub(crate) content: String, | ||
| #[schemars(description = "What's done so far — required, part of the structured payload.")] | ||
| #[serde(default)] | ||
| pub(crate) completed: String, | ||
| #[schemars(description = "What's left to do — required, part of the structured payload.")] | ||
| #[serde(default)] | ||
| pub(crate) remaining: String, | ||
|
Comment on lines
+184
to
+189
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
fd -HI 'Cargo.toml' . -x rg -n -C 3 'schemars'
rg -n -C 4 'HandoffRequest|JsonSchema|schema_for' --glob '*.rs' .Repository: getappz/agentflare Length of output: 594 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(Cargo\.toml|types\.rs|handoff\.rs)$|schema|schemas' || true
printf '%s\n' '--- relevant symbols and attributes ---'
rg -n -C 6 'HandoffRequest|completed|remaining|schema_for|JsonSchema|schemars' --glob '*.rs' --glob 'Cargo.toml' --glob 'Cargo.lock' . || true
printf '%s\n' '--- source outline ---'
if command -v ast-grep >/dev/null 2>&1; then
ast-grep outline src/mcp_server/types.rs || true
ast-grep outline src/mcp_server/handoff.rs || true
fiRepository: getappz/agentflare Length of output: 50375 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
for name in ["src/mcp_server/types.rs", "src/mcp_server/handoff.rs", "Cargo.toml", "Cargo.lock"]:
p = Path(name)
if not p.exists():
continue
print(f"--- {name} ---")
lines = p.read_text().splitlines()
if name.endswith("types.rs"):
ranges = [(150, 210)]
elif name.endswith("handoff.rs"):
ranges = [(1, 90)]
else:
ranges = [(1, len(lines))]
for lo, hi in ranges:
for i in range(lo, min(hi, len(lines)) + 1):
print(f"{i:4}: {lines[i-1]}")
PYRepository: getappz/agentflare Length of output: 50375 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- exact schemars lock entries ---'
awk '
/^\[\[package\]\]/ { keep=0 }
/^name = "schemars"$/ { keep=1 }
keep { print }
' Cargo.lock
printf '%s\n' '--- HandoffRequest references ---'
rg -n -C 8 'HandoffRequest' src crates --glob '*.rs' || true
printf '%s\n' '--- schema generation and schemars configuration ---'
rg -n -C 5 'schema_for|schema_name|JsonSchema|schemars\(' src crates --glob '*.rs' || true
printf '%s\n' '--- MCP tool registration around handoff ---'
rg -n -C 12 'handoff_impl|handoff' src/mcp_server --glob '*.rs' || trueRepository: getappz/agentflare Length of output: 50375 🌐 Web query:
💡 Result: In the Schemars library, the Citations:
🌐 Web query:
💡 Result: In Schemars, the attributes #[serde(default)] and #[schemars(default)] serve to define a default value for a field during deserialization [1][2]. When applied, the field is excluded from the JSON schema's required properties list because it is no longer strictly required to be present in the input JSON [1][2]. Regarding the specific combination of attributes: 1. #[schemars(required)]: This attribute is primarily used for validation (e.g., when integrated with validation crates like garde or validator) or to force an Option field to be treated as required in the generated schema, effectively overriding default optionality [1][2]. 2. Interaction with #[serde(default)]: If you mark a field with both #[serde(default)] and #[schemars(required)], you create a contradiction between the Serde behavior (which allows the field to be missing by providing a default) and the Schemars schema generation (which marks the field as required in the JSON schema) [1][2]. Typically, if you want a field to have a default value in your Rust code/Serde deserialization but also ensure it is documented as required in your JSON schema, you cannot achieve this purely by relying on #[serde(default)] because that attribute inherently signals to Schemars that the field is optional [1][2]. To force a field to appear as required in the schema, you would generally need to omit the default attribute or manually manipulate the schema [1][2]. Note that in version 1.2.1, there was a specific fix addressing unintended changes in behavior when combining default and required attributes, ensuring that the library behaves consistently according to its established attribute processing logic [3]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- focused schema/assertion patterns ---'
rg -n -C 4 'to_value|schema_for|input_schema|required|properties|HandoffRequest|JsonSchema' src/mcp_server crates/gateway-registry --glob '*.rs' --glob '!**/tests/live_github.rs' || true
printf '%s\n' '--- local dependency source availability ---'
find "${CARGO_HOME:-$HOME/.cargo}" -path '*schemars*' -type f \( -name '*.rs' -o -name '*.md' \) -print 2>/dev/null | head -n 40 || trueRepository: getappz/agentflare Length of output: 50375 Keep Schemars 1.2.1 excludes 🤖 Prompt for AI Agents |
||
| #[schemars( | ||
| description = "html | markdown | mermaid | diagram | text (default: markdown) — picks the attached asset's extension/mime type" | ||
| )] | ||
|
|
@@ -235,10 +241,6 @@ pub(crate) struct HandoffRequest { | |
| )] | ||
| #[serde(default)] | ||
| pub(crate) last_commit: Option<String>, | ||
| #[schemars(description = "What's done so far — required, part of the structured payload.")] | ||
| pub(crate) completed: String, | ||
| #[schemars(description = "What's left to do — required, part of the structured payload.")] | ||
| pub(crate) remaining: String, | ||
| #[schemars(description = "Known blockers, if any.")] | ||
| #[serde(default)] | ||
| pub(crate) blockers: Option<Vec<String>>, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: getappz/agentflare
Length of output: 50375
🏁 Script executed:
Repository: getappz/agentflare
Length of output: 17328
🏁 Script executed:
Repository: getappz/agentflare
Length of output: 308
🌐 Web query:
rmcp 1.8.0 Parameters<T> deserialization implementation💡 Result:
In the rmcp library, Parameters is a wrapper used primarily in tool and prompt handlers to facilitate the automatic deserialization of arguments provided in incoming requests [1][2]. The implementation details for Parameters in version 1.8.0 and subsequent versions are as follows: Structure and Purpose The Parameters struct is defined as a transparent wrapper: pub struct Parameters
(pub P) [1]. By utilizing the #[serde(transparent)] attribute, the wrapper ensures that it does not introduce an additional layer in the JSON structure; instead, it directly delegates serialization and deserialization processes to the inner type P [1]. Deserialization Mechanism When used as a parameter in tool or prompt handler functions, the framework automatically extracts and deserializes the JSON arguments from the request into the type P [1][2]. Specifically: - For tools, it parses the arguments field from tool call requests [1]. - For prompts, it parses the arguments field from prompt requests [1]. - The procedural macros (e.g., #[tool], #[prompt]) automate the wiring, allowing developers to define handler functions that accept Parameters directly [2][3]. Error Handling The framework handles deserialization errors by returning appropriate error responses [1]. Internal router logic includes a specific error prefix, TOOL_ARGUMENT_DESERIALIZATION_ERROR_PREFIX ("failed to deserialize parameters:"), which is used to identify and report issues when input arguments fail to match the expected schema defined by T [4][5]. Developers should ensure that the inner type T implements serde::Deserialize and (typically) schemars::JsonSchema to support automatic schema generation and runtime deserialization [1][3].
Citations:
Cover both omitted progress fields in the regression test.
This payload omits only
completed. Omit both fields, or add a case that omitsremaining, and assert that each field defaults to""before validation returns the friendly error. The test also callsserde_json::from_valuedirectly, while production extractsParameters<HandoffRequest>; exercise that boundary if it is the reported failure point.🤖 Prompt for AI Agents