You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
exceptExceptionase:
logger.error(f"Error upserting document in {keyspace}: {e}", exc_info=True)
returnFalse
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)
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.
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.
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 everyexception, log it, and return
False:Meanwhile
get_document_by_idin the same module re-raises. So the module is internallyinconsistent: reads raise with detail, writes return a bare boolean and discard the
reason.
For an agent-facing MCP tool this is a real problem:
insertreturningFalseisindistinguishable 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
the tool surface unpredictable.
auth-denied) are collapsed to one opaque
False.Suggested fix (choose one)
Raise on write failure like
get_document_by_iddoes, letting FastMCP surface atool error with the exception message. Simplest and makes the module consistent.
Return a structured result instead of a bare bool, e.g.
{"ok": False, "error": "<type>: <message>"}, so the agent gets an actionablereason. (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_idis the objective part; the swallowed reason is the impactful part.