Skip to content

KV write tools return bool and swallow the failure reason (inconsistent with get, unhelpful to agents) BUG/ENHANCEMENT #192

Description

@celticht32

Summary

In src/cb_mcp/tools/kv.py, the four write tools (upsert_document_by_id,
insert_document_by_id, replace_document_by_id, delete_document_by_id) catch every
exception, log it, and return False:

except Exception as e:
    logger.error(f"Error upserting document in {keyspace}: {e}", exc_info=True)
    return False

Meanwhile get_document_by_id in the same module re-raises. So the module is internally
inconsistent: reads raise with detail, writes return a bare boolean and discard the
reason.

For an agent-facing MCP tool this is a real problem: insert returning False is
indistinguishable between "document already exists", "permission denied", "network
error", and "invalid content". The caller (an LLM) cannot tell what went wrong or how
to recover, and the docstrings only say "Returns True on success, False on failure."

Impact

  • Callers lose all diagnostic context on write failures; only the server logs have it.
  • Inconsistent error contract within one module (get raises; writes return bool) makes
    the tool surface unpredictable.
  • Distinct failure modes that warrant different responses (already-exists vs
    auth-denied) are collapsed to one opaque False.

Suggested fix (choose one)

  1. Raise on write failure like get_document_by_id does, letting FastMCP surface a
    tool error with the exception message. Simplest and makes the module consistent.

  2. Return a structured result instead of a bare bool, e.g.
    {"ok": False, "error": "<type>: <message>"}, so the agent gets an actionable
    reason. (This aligns with the structured-error direction discussed in Pre-PR discussion: adding ~150 admin tools — scope and architecture check #157.)

At minimum, differentiate the common, expected cases — DocumentExistsException for
insert, DocumentNotFoundException for replace — from unexpected errors, so the caller
can act on them.

Note

This may be an intentional design (bool returns are simple), which is why it is filed
as bug + enhancement rather than a pure defect. The internal inconsistency with
get_document_by_id is the objective part; the swallowed reason is the impactful part.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions