Skip to content

fix: parse JSON-string MCP tool arguments before type check#255

Open
pajitosingh wants to merge 1 commit into
Zoo-Code-Org:mainfrom
pajitosingh:fix/mcp-json-string-arguments
Open

fix: parse JSON-string MCP tool arguments before type check#255
pajitosingh wants to merge 1 commit into
Zoo-Code-Org:mainfrom
pajitosingh:fix/mcp-json-string-arguments

Conversation

@pajitosingh
Copy link
Copy Markdown

@pajitosingh pajitosingh commented May 22, 2026

Problem

Some LLMs (DeepSeek V4 Pro and others) emit MCP tool call arguments as JSON-encoded strings rather than as native objects:

{ "arguments": "{\"headless\": true}" }

instead of:

{ "arguments": { "headless": true } }

The existing validateParams() method in UseMcpToolTool.ts rejects these with "Invalid JSON argument" errors because the type check (typeof params.arguments !== "object") fails on strings.

This is the same bug that plagued Roo Code v3.54.0 (see Roo-Code issues #10919 and the archive discussion).

Fix

Adds a JSON.parse() guard before the existing type check. If arguments arrives as a string, attempt to parse it into an object. If parsing fails (malformed JSON), we fall through silently and the existing code path handles it as before (rejects with the appropriate error).

Safety

  • The guard is safe for all input types:
    • Object arguments (existing correct format): typeof check passes the string guard, goes straight to the object type check → no change
    • String arguments (the bug case): JSON.parse() unwraps the string into an object → type check passes → fix
    • Malformed strings: JSON.parse() throws, catch {} swallows it → falls through to the existing error path → graceful rejection
    • null / undefined / array: Not strings → string guard skipped → existing error path → unchanged

Testing

  • Field-tested on Roo Code v3.54.0 with the identical fix across multiple MCP providers (playwright-stealth etc.)
  • All existing tool call formats continue to work unchanged

Summary by CodeRabbit

  • New Features
    • Arguments now support JSON-encoded string format, with automatic parsing for enhanced flexibility and input compatibility.

Review Change Stack

Some LLMs (DeepSeek V4 Pro, others) emit MCP tool call arguments as
JSON-encoded strings (e.g. '{"headless": true}') rather than as
native objects. This causes validateParams() to reject valid MCP
tool calls with 'Invalid JSON argument' errors.

The fix adds a JSON.parse() guard before the existing type check,
falling through silently if parsing fails. The existing code path
then handles it as before (either accepts the object or rejects
malformed input).

This matches the fix applied to Roo Code v3.54.0 which was field-
tested across multiple MCP providers (playwright-stealth etc).
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8aee4bc3-c9fc-4a14-a8f1-da4d7d1afb68

📥 Commits

Reviewing files that changed from the base of the PR and between 45b239c and 9f9c65f.

📒 Files selected for processing (1)
  • src/core/tools/UseMcpToolTool.ts

📝 Walkthrough

Walkthrough

The PR adds JSON string parsing to the UseMcpToolTool.validateParams method. When params.arguments arrives as a JSON-encoded string, the code attempts to deserialize it. Parse failures leave the value unchanged, preserving backward compatibility.

Changes

Arguments JSON Parsing

Layer / File(s) Summary
JSON string argument deserialization
src/core/tools/UseMcpToolTool.ts
When params.arguments is a string, the code attempts JSON.parse in a try/catch block and falls back to the original string on parse failure, then proceeds with existing type validation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A string wrapped in JSON quotes,
Now unwraps with graceful notes.
Parse it, check it, let it flow—
Arguments dance with just one go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides clear problem context, implementation details, and safety analysis, but lacks the required Related GitHub Issue link and Test Procedure sections from the template. Add the required 'Related GitHub Issue' section (Closes: #) and 'Test Procedure' section with specific testing steps to match the repository's PR template.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding JSON string parsing before type validation for MCP tool arguments.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/core/tools/UseMcpToolTool.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.


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.

Copy link
Copy Markdown
Contributor

@edelauna edelauna left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution! I had one comment around increasing test coverage, but this looks great!

try {
params.arguments = JSON.parse(params.arguments)
} catch {}
}
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.

Could we add a test in useMcpToolTool.spec.ts that passes nativeArgs.arguments as a JSON-encoded string (e.g. '{"headless": true}' as unknown as Record<string, unknown>) and verifies callTool receives the parsed object? This is the primary behavior change and currently has no regression coverage.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 23, 2026

Codecov Report

❌ Patch coverage is 20.00000% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/tools/UseMcpToolTool.ts 20.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

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.

2 participants