Skip to content

bug: mempalace_search rejects empty-string wing/room with "must be a non-empty string" even though schema marks them optional #1084

@tramzel-milc

Description

@tramzel-milc

Summary

The `mempalace_search` tool marks `wing` and `room` as optional in its schema, but the handler rejects empty-string values with `{"error": "wing must be a non-empty string"}`. This is confusing and trips up LLM agents that default to filling every declared parameter.

Reproduction

# Call via MCP stdio (or from Claude Code / OpenCode / any MCP client)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "mempalace_search",
    "arguments": {
      "query": "some topic",
      "wing": "",
      "room": ""
    }
  }
}

Response:

{"error": "wing must be a non-empty string"}

Fixing wing and retrying with room: "" returns the equivalent error for room.

Schema context

From mempalace_search's tool definition (trimmed):

{
  "name": "mempalace_search",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query":  {"type": "string", ...},
      "wing":   {"type": "string", "description": "Filter by wing (optional)"},
      "room":   {"type": "string", "description": "Filter by room (optional)"},
      "limit":  {"type": "integer", ...},
      ...
    },
    "required": ["query"]
  }
}

The tool advertises wing and room as optional ("(optional)" in description, not in required array), so a caller reasonably expects either omission or empty value to mean "don't filter."

Why it matters (real-world agent trace)

In an OpenCode session, we give our agent an instructions file containing:

Before answering... call mempalace_search with just the query text (no wing/room filter).

The agent faithfully tried to comply but interpreted "no filter" as "empty string":

Call 1: {"query": "TrainTrac Express API Feed Management relationship", "wing": "", "room": ""}
        → error: wing must be a non-empty string

Call 2: {"query": "TrainTrac Feed domain relationship", "wing": "all", "room": ""}
        → error: room must be a non-empty string

Agent then gave up on mempalace_search and fell back to grep. Two otherwise-productive queries wasted.

This pattern will hit anyone whose agent instructions tell it to skip the filter — models often default to filling every declared field, especially when schemas mark optionality with description text rather than JSON Schema structure.

Proposed fix

Two options, listed in order of preference:

A) Treat empty string as "no filter" — easiest for callers, matches the "optional" intent:

if wing in (None, ""):
    wing = None  # -> no filter

B) Improve the error message — less breaking, still helpful:

{"error": "wing is empty — omit the 'wing' field from the request to skip filtering, or pass an actual wing name"}

Either works. (A) means agents never fail on this, and existing working callers are unaffected.

Workarounds I used

For now I've pushed a rule tweak in our devbox repo instructing the agent explicitly to omit wing / room rather than pass empty strings — visible to future readers at MILCGroup/milc-devbox#130. Happy to open a PR here too if option (A) looks good.

Environment

  • mempalace 3.3.1 (from pip install mempalace, installed ~2 days ago)
  • Called via MCP stdio from OpenCode 0.x
  • Python 3.10 on Ubuntu 22.04

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions