Skip to content

Commit 033d707

Browse files
Merge pull request #37 from inventree/default-pagination
Increase default pagination limit
2 parents 7e2dfe6 + 2351446 commit 033d707

17 files changed

Lines changed: 145 additions & 56 deletions

inventree_mcp/test_mcp.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,3 +2308,34 @@ def test_none_filters_is_safe(self):
23082308
params = build_query_params({"a": 1}, None, limit=5, offset=0)
23092309

23102310
self.assertEqual(params, {"a": 1, "limit": 5, "offset": 0})
2311+
2312+
2313+
class ListToolDefaultLimitTest(InvenTreeTestCase):
2314+
"""Every list_* tool's own `limit` default must match DEFAULT_LIMIT, not just
2315+
clamp_limit()'s fallback for the limit<=0 edge case.
2316+
2317+
Each tools/*.py list_* function hardcodes its own `limit: int = ...`
2318+
default in its signature - an agent that omits `limit` entirely never
2319+
reaches clamp_limit()'s own DEFAULT_LIMIT fallback, it gets whatever
2320+
that per-tool default is. ClampLimitTest only covers the shared
2321+
fallback; this guards against one file's signature drifting back to the
2322+
old default (25) while _common.DEFAULT_LIMIT and the rest stay at 100 -
2323+
checked via each tool's registered input schema (what an MCP client
2324+
actually sees), not by re-reading the source.
2325+
"""
2326+
2327+
async def test_every_list_tool_defaults_limit_to_100(self):
2328+
tools = await mcp.list_tools()
2329+
list_tools = [tool for tool in tools if tool.name.startswith("list_")]
2330+
2331+
# Sanity check the filter above actually caught something, so this
2332+
# test can't silently pass by iterating over an empty list.
2333+
self.assertGreater(len(list_tools), 20)
2334+
2335+
wrong_defaults = {
2336+
tool.name: tool.input_schema["properties"]["limit"].get("default")
2337+
for tool in list_tools
2338+
if tool.input_schema["properties"]["limit"].get("default") != DEFAULT_LIMIT
2339+
}
2340+
2341+
self.assertEqual(wrong_defaults, {})

inventree_mcp/tools/_common.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from typing import Any
66

7-
DEFAULT_LIMIT = 25
7+
DEFAULT_LIMIT = 100
88
MAX_LIMIT = 100
99

1010

@@ -13,7 +13,11 @@ def clamp_limit(limit: int) -> int:
1313
1414
Prevents a single tool call from dumping an unbounded number of records
1515
(or being handed a negative/zero value which some list views treat as
16-
"no limit").
16+
"no limit"). DEFAULT_LIMIT == MAX_LIMIT deliberately - every list_* tool's
17+
own `limit` parameter already defaults to DEFAULT_LIMIT (so an agent
18+
that omits it gets a full page, minimizing tool calls for a large
19+
result set), and this is the fallback for the one case that bypasses
20+
that default: a caller passing limit=0 or a negative number explicitly.
1721
"""
1822
if limit <= 0:
1923
return DEFAULT_LIMIT

inventree_mcp/tools/attachments.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async def list_attachments(
2525
is_image: bool | None = None,
2626
ordering: str | None = None,
2727
filters: dict[str, Any] | None = None,
28-
limit: int = 25,
28+
limit: int = 100,
2929
offset: int = 0,
3030
) -> dict:
3131
"""List attachments (uploaded files or external links) linked to InvenTree records.
@@ -66,7 +66,9 @@ async def list_attachments(
6666
filters: additional filter parameters beyond the named arguments
6767
above - call describe_filters("attachment") to see what's
6868
available, e.g. filters={"upload_user": <id>}.
69-
limit: maximum number of results to return (capped at 100).
69+
limit: maximum number of results to return - defaults to 100 (the
70+
maximum) to minimize round trips for large result sets; pass a
71+
smaller value to page through results in smaller batches.
7072
offset: pagination offset.
7173
"""
7274
base: dict[str, Any] = {}

inventree_mcp/tools/bom.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async def list_bom_items(
3636
category: int | None = None,
3737
ordering: str | None = None,
3838
filters: dict[str, Any] | None = None,
39-
limit: int = 25,
39+
limit: int = 100,
4040
offset: int = 0,
4141
) -> dict:
4242
"""List BOM (Bill of Materials) items - the components required to build an assembly.
@@ -79,7 +79,9 @@ async def list_bom_items(
7979
above - call describe_filters("bom_item") to see what's
8080
available, e.g. filters={"validated": false} for lines whose
8181
checksum hasn't been confirmed, or filters={"available_stock": true}.
82-
limit: maximum number of results to return (capped at 100).
82+
limit: maximum number of results to return - defaults to 100 (the
83+
maximum) to minimize round trips for large result sets; pass a
84+
smaller value to page through results in smaller batches.
8385
offset: pagination offset.
8486
"""
8587
base: dict[str, Any] = {}
@@ -131,7 +133,7 @@ async def list_bom_substitutes(
131133
part: int | None = None,
132134
ordering: str | None = None,
133135
filters: dict[str, Any] | None = None,
134-
limit: int = 25,
136+
limit: int = 100,
135137
offset: int = 0,
136138
) -> dict:
137139
"""List BOM item substitutes - alternative parts permitted in place of a BOM line's sub_part.
@@ -157,7 +159,9 @@ async def list_bom_substitutes(
157159
filters: additional filter parameters beyond the named arguments
158160
above - call describe_filters("bom_substitute") to see what's
159161
available.
160-
limit: maximum number of results to return (capped at 100).
162+
limit: maximum number of results to return - defaults to 100 (the
163+
maximum) to minimize round trips for large result sets; pass a
164+
smaller value to page through results in smaller batches.
161165
offset: pagination offset.
162166
"""
163167
base: dict[str, Any] = {}

inventree_mcp/tools/build_orders.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ async def list_build_orders(
1717
outstanding: bool | None = None,
1818
ordering: str | None = None,
1919
filters: dict[str, Any] | None = None,
20-
limit: int = 25,
20+
limit: int = 100,
2121
offset: int = 0,
2222
) -> dict:
2323
"""List build orders (manufacturing orders).
@@ -46,7 +46,9 @@ async def list_build_orders(
4646
filters: additional filter parameters beyond the named arguments
4747
above - call describe_filters("build_order") to see what's
4848
available, e.g. filters={"overdue": true}.
49-
limit: maximum number of results to return (capped at 100).
49+
limit: maximum number of results to return - defaults to 100 (the
50+
maximum) to minimize round trips for large result sets; pass a
51+
smaller value to page through results in smaller batches.
5052
offset: pagination offset.
5153
"""
5254
base: dict[str, Any] = {}
@@ -104,7 +106,7 @@ async def list_build_lines(
104106
build: int | None = None,
105107
ordering: str | None = None,
106108
filters: dict[str, Any] | None = None,
107-
limit: int = 25,
109+
limit: int = 100,
108110
offset: int = 0,
109111
) -> dict:
110112
"""List build order line items - the components required to complete a build order.
@@ -132,7 +134,9 @@ async def list_build_lines(
132134
available, e.g. filters={"allocated": false} for lines still
133135
needing stock, or filters={"consumable": false} to exclude
134136
consumable (non-tracked) components.
135-
limit: maximum number of results to return (capped at 100).
137+
limit: maximum number of results to return - defaults to 100 (the
138+
maximum) to minimize round trips for large result sets; pass a
139+
smaller value to page through results in smaller batches.
136140
offset: pagination offset.
137141
"""
138142
base: dict[str, Any] = {}
@@ -183,7 +187,7 @@ async def list_build_items(
183187
part: int | None = None,
184188
ordering: str | None = None,
185189
filters: dict[str, Any] | None = None,
186-
limit: int = 25,
190+
limit: int = 100,
187191
offset: int = 0,
188192
) -> dict:
189193
"""List build order allocations - stock items reserved against build order lines.
@@ -211,7 +215,9 @@ async def list_build_items(
211215
above - call describe_filters("build_item") to see what's
212216
available, e.g. filters={"output": null} for allocations not
213217
yet installed into a specific build output.
214-
limit: maximum number of results to return (capped at 100).
218+
limit: maximum number of results to return - defaults to 100 (the
219+
maximum) to minimize round trips for large result sets; pass a
220+
smaller value to page through results in smaller batches.
215221
offset: pagination offset.
216222
"""
217223
base: dict[str, Any] = {}

inventree_mcp/tools/categories.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async def list_categories(
1616
parent: int | None = None,
1717
ordering: str | None = None,
1818
filters: dict[str, Any] | None = None,
19-
limit: int = 25,
19+
limit: int = 100,
2020
offset: int = 0,
2121
) -> dict:
2222
"""List part categories.
@@ -41,7 +41,9 @@ async def list_categories(
4141
than rejected.
4242
filters: additional filter parameters beyond the named arguments
4343
above - call describe_filters("category") to see what's available.
44-
limit: maximum number of results to return (capped at 100).
44+
limit: maximum number of results to return - defaults to 100 (the
45+
maximum) to minimize round trips for large result sets; pass a
46+
smaller value to page through results in smaller batches.
4547
offset: pagination offset.
4648
"""
4749
base: dict[str, Any] = {}

inventree_mcp/tools/companies.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async def list_companies(
1919
active: bool | None = None,
2020
ordering: str | None = None,
2121
filters: dict[str, Any] | None = None,
22-
limit: int = 25,
22+
limit: int = 100,
2323
offset: int = 0,
2424
) -> dict:
2525
"""List companies (suppliers, customers, and/or manufacturers).
@@ -47,7 +47,9 @@ async def list_companies(
4747
filters: additional filter parameters beyond the named arguments
4848
above - call describe_filters("company") to see what's
4949
available.
50-
limit: maximum number of results to return (capped at 100).
50+
limit: maximum number of results to return - defaults to 100 (the
51+
maximum) to minimize round trips for large result sets; pass a
52+
smaller value to page through results in smaller batches.
5153
offset: pagination offset.
5254
"""
5355
base: dict[str, Any] = {}
@@ -106,7 +108,7 @@ async def list_contacts(
106108
company: int | None = None,
107109
ordering: str | None = None,
108110
filters: dict[str, Any] | None = None,
109-
limit: int = 25,
111+
limit: int = 100,
110112
offset: int = 0,
111113
) -> dict:
112114
"""List contacts (people) at companies.
@@ -125,7 +127,9 @@ async def list_contacts(
125127
filters: additional filter parameters beyond the named arguments
126128
above - call describe_filters("contact") to see what's
127129
available.
128-
limit: maximum number of results to return (capped at 100).
130+
limit: maximum number of results to return - defaults to 100 (the
131+
maximum) to minimize round trips for large result sets; pass a
132+
smaller value to page through results in smaller batches.
129133
offset: pagination offset.
130134
"""
131135
base: dict[str, Any] = {}
@@ -175,7 +179,7 @@ async def list_addresses(
175179
company: int | None = None,
176180
ordering: str | None = None,
177181
filters: dict[str, Any] | None = None,
178-
limit: int = 25,
182+
limit: int = 100,
179183
offset: int = 0,
180184
) -> dict:
181185
"""List company addresses.
@@ -194,7 +198,9 @@ async def list_addresses(
194198
filters: additional filter parameters beyond the named arguments
195199
above - call describe_filters("address") to see what's
196200
available.
197-
limit: maximum number of results to return (capped at 100).
201+
limit: maximum number of results to return - defaults to 100 (the
202+
maximum) to minimize round trips for large result sets; pass a
203+
smaller value to page through results in smaller batches.
198204
offset: pagination offset.
199205
"""
200206
base: dict[str, Any] = {}

inventree_mcp/tools/locations.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async def list_locations(
1616
parent: int | None = None,
1717
ordering: str | None = None,
1818
filters: dict[str, Any] | None = None,
19-
limit: int = 25,
19+
limit: int = 100,
2020
offset: int = 0,
2121
) -> dict:
2222
"""List stock locations.
@@ -41,7 +41,9 @@ async def list_locations(
4141
than rejected.
4242
filters: additional filter parameters beyond the named arguments
4343
above - call describe_filters("location") to see what's available.
44-
limit: maximum number of results to return (capped at 100).
44+
limit: maximum number of results to return - defaults to 100 (the
45+
maximum) to minimize round trips for large result sets; pass a
46+
smaller value to page through results in smaller batches.
4547
offset: pagination offset.
4648
"""
4749
base: dict[str, Any] = {}

inventree_mcp/tools/parameters.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def list_parameters(
2828
template: int | None = None,
2929
ordering: str | None = None,
3030
filters: dict[str, Any] | None = None,
31-
limit: int = 25,
31+
limit: int = 100,
3232
offset: int = 0,
3333
) -> dict:
3434
"""List parameters (named attribute values) recorded against InvenTree records.
@@ -72,7 +72,9 @@ async def list_parameters(
7272
filters: additional filter parameters beyond the named arguments
7373
above - call describe_filters("parameter") to see what's
7474
available.
75-
limit: maximum number of results to return (capped at 100).
75+
limit: maximum number of results to return - defaults to 100 (the
76+
maximum) to minimize round trips for large result sets; pass a
77+
smaller value to page through results in smaller batches.
7678
offset: pagination offset.
7779
"""
7880
base: dict[str, Any] = {}
@@ -128,7 +130,7 @@ async def list_parameter_templates(
128130
search: str | None = None,
129131
ordering: str | None = None,
130132
filters: dict[str, Any] | None = None,
131-
limit: int = 25,
133+
limit: int = 100,
132134
offset: int = 0,
133135
) -> dict:
134136
"""List parameter templates - the named attribute definitions parameters reference.
@@ -154,7 +156,9 @@ async def list_parameter_templates(
154156
above - call describe_filters("parameter_template") to see
155157
what's available, e.g. filters={"units": "V"} or
156158
filters={"has_choices": true}.
157-
limit: maximum number of results to return (capped at 100).
159+
limit: maximum number of results to return - defaults to 100 (the
160+
maximum) to minimize round trips for large result sets; pass a
161+
smaller value to page through results in smaller batches.
158162
offset: pagination offset.
159163
"""
160164
base: dict[str, Any] = {}

inventree_mcp/tools/parts.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async def list_parts(
2222
active: bool | None = None,
2323
ordering: str | None = None,
2424
filters: dict[str, Any] | None = None,
25-
limit: int = 25,
25+
limit: int = 100,
2626
offset: int = 0,
2727
) -> dict:
2828
"""List parts in the InvenTree database.
@@ -46,7 +46,9 @@ async def list_parts(
4646
filters: additional filter parameters beyond the named arguments
4747
above - call describe_filters("part") to see what's available,
4848
e.g. filters={"is_variant": true}.
49-
limit: maximum number of results to return (capped at 100).
49+
limit: maximum number of results to return - defaults to 100 (the
50+
maximum) to minimize round trips for large result sets; pass a
51+
smaller value to page through results in smaller batches.
5052
offset: pagination offset.
5153
"""
5254
base: dict[str, Any] = {}

0 commit comments

Comments
 (0)