Skip to content

Commit ccd320b

Browse files
committed
Cospend: reject list_cospend_bills(search_term) without limit (raises ValueError)
1 parent 4b80b58 commit ccd320b

2 files changed

Lines changed: 15 additions & 12 deletions

File tree

src/nc_mcp_server/tools/cospend.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,11 @@ async def list_cospend_bills(
208208
include_bill_id: Force-include this bill id in the result even if
209209
it would be paginated out (used to keep a focused bill in view).
210210
search_term: Substring match (case-insensitive) on bill `what`,
211-
`comment`, or amount±1. NOTE: Cospend only applies the search
212-
when `limit` is also set — pass any limit (e.g. 1000) to make
213-
the filter take effect. Without a limit the search is silently
214-
ignored.
211+
`comment`, or amount±1. REQUIRES `limit` to be set — Cospend
212+
silently ignores the search and returns unfiltered results
213+
when no limit is provided, so this tool raises ValueError if
214+
`search_term` is given without `limit`. Pass any limit
215+
(e.g. 1000) to apply the filter.
215216
deleted: 0 = live bills (default), 1 = trashed bills only.
216217
217218
Returns:
@@ -221,6 +222,10 @@ async def list_cospend_bills(
221222
`search_term` even when search filters `bills`), `allBillIds`
222223
(full id list before pagination), `timestamp` (server response time).
223224
"""
225+
if search_term is not None and limit is None:
226+
raise ValueError(
227+
"limit is required when search_term is set (Cospend silently ignores the search otherwise)"
228+
)
224229
client = get_client()
225230
params = _body(
226231
offset=offset,

tests/integration/test_cospend.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -353,17 +353,15 @@ async def test_list_search_term_filters(self, nc_mcp: McpTestHelper) -> None:
353353
assert result["nb_bills"] == 2 # search doesn't affect the count
354354

355355
@pytest.mark.asyncio
356-
async def test_list_search_term_without_limit_is_ignored(self, nc_mcp: McpTestHelper) -> None:
357-
"""Documents the Cospend quirk: searchTerm has no effect without a limit."""
356+
async def test_list_search_term_without_limit_raises(self, nc_mcp: McpTestHelper) -> None:
357+
"""search_term without limit must fail fast — Cospend silently ignores it otherwise."""
358358
pid = "mcp-test-bill-searchnolim"
359359
await _make_project(nc_mcp, pid)
360360
alice = await _make_member(nc_mcp, pid, "Alice")
361-
bob = await _make_member(nc_mcp, pid, "Bob")
362-
await _make_bill(nc_mcp, pid, "Pizza", 20.0, alice["id"], [alice["id"], bob["id"]])
363-
await _make_bill(nc_mcp, pid, "Sushi", 30.0, alice["id"], [alice["id"], bob["id"]])
364-
result = json.loads(await nc_mcp.call("list_cospend_bills", project_id=pid, search_term="Pizz"))
365-
# Without limit, both bills come back — search is silently ignored.
366-
assert sorted(b["what"] for b in result["bills"]) == ["Pizza", "Sushi"]
361+
await _make_member(nc_mcp, pid, "Bob")
362+
await _make_bill(nc_mcp, pid, "Pizza", 20.0, alice["id"], [alice["id"]])
363+
with pytest.raises(ToolError, match="limit is required"):
364+
await nc_mcp.call("list_cospend_bills", project_id=pid, search_term="Pizz")
367365

368366
@pytest.mark.asyncio
369367
async def test_list_payer_id_filters(self, nc_mcp: McpTestHelper) -> None:

0 commit comments

Comments
 (0)