Skip to content

fix(mcp): coerce string-encoded metadata, types, and tags in tool calls#869

Open
octo-patch wants to merge 1 commit intovectorize-io:mainfrom
octo-patch:fix/issue-849-mcp-coerce-string-encoded-args
Open

fix(mcp): coerce string-encoded metadata, types, and tags in tool calls#869
octo-patch wants to merge 1 commit intovectorize-io:mainfrom
octo-patch:fix/issue-849-mcp-coerce-string-encoded-args

Conversation

@octo-patch
Copy link
Copy Markdown
Contributor

Fixes #849

Problem

MCP tool bridges (Claude Code, Cursor, and others) sometimes serialize JSON arrays and objects as strings during transport. This causes Pydantic validation failures on retain and recall calls:

Error: 1 validation error for call[retain]
tags
  Input should be a valid list [type=list_type, input_value='["tag1", "tag2"]', input_type=str]

Error: 1 validation error for call[recall]
types
  Input should be a valid list [type=list_type, input_value='["world"]', input_type=str]

Error: 1 validation error for call[retain]
metadata
  Input should be a valid dictionary [type=dict_type, input_value='{"key": "value"}', input_type=str]

The agent retries with the same broken format, wasting tokens and silently failing to store memories.

Solution

Add defensive coercion at two layers — mirroring the pattern already used for MemoryItem.tags (added in #682):

HTTP API (http.py):

  • RecallRequest: field_validator on types and tags (mode="before") that parses JSON-string arrays back into lists
  • MemoryItem: field_validator on metadata (mode="before") that parses JSON-string objects back into dicts

MCP tools (mcp_tools.py):

  • build_content_dict: coerce metadata from JSON string to dict, matching the existing tags coercion in the same function
  • Both recall() variants (with and without bank_id param): coerce types and tags from JSON strings before passing to the memory engine

All coercions are backward-compatible — correctly-typed native arrays/objects pass through unchanged.

Testing

The existing coercion pattern for tags in MemoryItem (from #682) already has test coverage. The new validators follow the identical logic and can be validated by sending a JSON-string value to the affected fields.

MCP tool bridges (Claude Code, Cursor, etc.) sometimes serialize JSON
arrays and objects as strings during transport, causing Pydantic to
reject the input with a validation error. The agent then retries with
the same broken format, wasting tokens and silently losing memories.

Add defensive coercion at two layers:

HTTP API (http.py):
- RecallRequest: field_validator on types and tags (mode="before") that
  parses JSON-string arrays back into lists
- MemoryItem: field_validator on metadata (mode="before") that parses
  JSON-string objects back into dicts (tags coercion was already present)

MCP tools (mcp_tools.py):
- build_content_dict: coerce metadata from JSON string to dict, matching
  the existing tags coercion in the same function
- both recall() variants: coerce types and tags from JSON strings before
  passing to the memory engine

All coercions are backward-compatible — correctly-typed inputs pass through
unchanged.

Fixes vectorize-io#849
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.

MCP: auto-coerce string-encoded arrays/objects in tool call arguments (tags, types, metadata)

1 participant