Skip to content

fix: auto-parse params when LLM serializes as JSON string#5

Open
Jasper-1222 wants to merge 4 commits into
agent-network-protocol:mainfrom
Jasper-1222:fix/params-json-string-handling
Open

fix: auto-parse params when LLM serializes as JSON string#5
Jasper-1222 wants to merge 4 commits into
agent-network-protocol:mainfrom
Jasper-1222:fix/params-json-string-handling

Conversation

@Jasper-1222
Copy link
Copy Markdown

@Jasper-1222 Jasper-1222 commented May 29, 2026

Summary

修复 anp.invokeOpenRPCparams 参数无法处理 JSON 字符串输入的问题。

现象

Claude Desktop / Hermes 等 MCP 客户端在调用 anp.invokeOpenRPC 时,LLM 有时会将 params 参数序列化为 JSON 字符串(如 "{\"question\":\"test\"}")而非原生 JSON 对象。这导致 Pydantic 校验失败,错误信息为 Input should be a valid dictionary [input_type=str]

根因

这不是 mcp2anp 自身的 bug——字符串化来自 LLM 的工具调用生成行为。MCP 生态系统的 FastMCP 库已经在 pre_parse_json(func_metadata.py)中记录了同样的现象并实现了兼容处理。mcp2anp 的 server.py 使用底层 mcp.server.Server 类,绕过了 FastMCP 的 pre_parse_json

修复

  • InvokeOpenRPCRequest.params 字段上添加 @field_validator(mode="before"),自动检测 JSON 字符串并解析
  • 覆盖边缘情况:None、空字符串、纯空格、"null"、合法 JSON dict/list
  • 自动修复时输出 structlog 警告日志,方便运维排查
  • 更新 inputSchema 添加 anyOf: [object, array, null] 类型约束,减少 LLM 序列化的概率

Verification

  • uv run pytest 所有测试通过(8/8 MCP bridge tests)
  • 8 个 params 输入边缘情况全部验证通过
  • Hermes + mcp2anp + Expert Agent 端到端对话验证通过
  • ruff / black 代码风格检查通过

Risk

低风险。field_validator 仅对字符串类型的 params 做 JSON 解析,正确的 dict/list 值直接透传。

Claude Desktop and other MCP clients sometimes serialize the `params`
argument as a JSON-encoded string (e.g. `"{\"question\":\"test\"}"`)
instead of a native JSON object. This caused Pydantic validation to
fail with `Input should be a valid dictionary [input_type=str]`.

Changes:
- Add `@field_validator` to `InvokeOpenRPCRequest.params` that auto-
  detects JSON strings and parses them before Pydantic validation.
  Handles edge cases: None, empty string, whitespace-only string,
  JSON null literal, valid JSON objects, and valid JSON arrays.
- Add structured warning log when auto-repair occurs to help
  operators identify client-side serialization issues.
- Update `inputSchema` for `anp.invokeOpenRPC` to include explicit
  `anyOf: [object, array, null]` type constraints. This gives the
  LLM a clear schema hint that params should not be a string.

This is a defensive fix — the stringification originates from LLM
tool-call generation, not from mcp2anp itself. FastMCP already
implements similar handling in `pre_parse_json` (func_metadata.py).

Refs: agent-network-protocol/mcp2anp
The `setup_logging` function configured `StreamHandler(sys.stdout)`,
which interleaves log lines with MCP JSON-RPC messages on stdout.
This breaks the MCP stdio transport as the client expects only valid
JSON-RPC frames on stdout.

Changed `StreamHandler(sys.stdout)` to `StreamHandler(sys.stderr)`
so that structured logs go to stderr while stdout remains clean for
the MCP protocol wire format.
Address pre-existing ruff lint issues in logging.py.
@Jasper-1222 Jasper-1222 force-pushed the fix/params-json-string-handling branch from b5bd062 to 537deac Compare May 29, 2026 14:47
The anyOf type constraint on params caused MCP client-side validation
to reject JSON strings before they reached the field_validator, defeating
the purpose of the fix. Remove the constraint and keep only the
field_validator in models.py as the defensive layer.

Root cause: MCP clients validate tool arguments against inputSchema
before sending them to the server. When an LLM serializes params as a
JSON string, the client-side schema check rejects it because string is
not in [object, array, null]. Our server-side fix never executes.
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