Cursor Pagination
Large binaries can contain tens of thousands of functions, hundreds of thousands of cross-references, and thousands of strings. Returning all of that in a single tool response would overflow the MCP client’s context window and produce unusable output. MCGhidra uses cursor-based pagination to deliver results in controlled pages.
How it works
Section titled “How it works”When a paginated tool returns more items than the page size, the response includes a cursor_id. Pass that cursor ID to cursor_next to get the next page. Continue until has_more is false.
# First page: get 100 functions matching a patternresult = functions_list(page_size=100, grep="crypt|hash")# Returns:# {# result: [...],# pagination: {# cursor_id: "a1b2c3d4e5f67890",# total_count: 12847,# filtered_count: 847,# page_size: 100,# current_page: 1,# total_pages: 9,# has_more: true# }# }
# Next pageresult = cursor_next(cursor_id="a1b2c3d4e5f67890")# Returns page 2 of 9
# Continue until has_more is falseresult = cursor_next(cursor_id="a1b2c3d4e5f67890")# ...Each response also includes a _message field with a human-readable summary like “Showing 100 of 847 items (page 2/9). To get the next 100 items, call: cursor_next(cursor_id=‘a1b2c3d4e5f67890’)”. MCP clients use this to decide whether to continue fetching.
Server-side grep filtering
Section titled “Server-side grep filtering”The grep parameter filters results on the server before pagination. This is much more efficient than fetching everything and filtering client-side, because only matching items are stored in the cursor and counted toward page totals.
# Only functions with "auth" or "login" in their name/addressfunctions_list(grep="auth|login", page_size=50)
# Case-sensitive search (grep_ignorecase defaults to true)data_list_strings(grep="BEGIN CERTIFICATE", grep_ignorecase=false, page_size=50)The grep pattern is a regular expression. It matches against all string values in each result item — for a function, that means the name, address, and signature fields are all searched.
Pattern safety
Section titled “Pattern safety”Patterns are validated before execution to prevent runaway matches:
- Maximum 500 characters
- Maximum 15 repetition operators (
*,+,?,{n,m}) - Nested quantifiers like
(a+)+are rejected
If a pattern fails validation, the tool returns an error with code INVALID_GREP_PATTERN explaining what to fix.
The return_all option
Section titled “The return_all option”When you need all matching results without paging through cursors, pass return_all=True:
functions_list(grep="crypt", return_all=True)This bypasses pagination and returns every matching item in a single response. There is a token budget guard (default: 8,000 estimated tokens) that kicks in if the response would be too large. When the guard triggers, the response includes:
- A sample of the first 3 items
- The available field names
- Suggested narrower queries (grep patterns, field projections, or pagination)
Combine return_all with grep and fields to keep the response size down:
# Get all crypto-related function names and addresses (nothing else)functions_list(grep="crypt|aes|sha", fields=["name", "address"], return_all=True)Page size
Section titled “Page size”The page_size parameter controls how many items each page contains.
| Parameter | Default | Maximum |
|---|---|---|
page_size | 50 | 500 |
For most MCP client contexts, 50-100 items per page is a good balance between making progress and keeping individual responses readable. Going above 200 is rarely useful unless you are scripting.
Cursor lifecycle
Section titled “Cursor lifecycle”TTL and eviction
Section titled “TTL and eviction”Cursors expire after 5 minutes of inactivity (no cursor_next calls). The timer resets each time a cursor is accessed.
When more than 100 cursors exist for a session, the least-recently-used cursor is evicted to make room. In practice, you will rarely hit this limit unless you start many queries without finishing them.
Session isolation
Section titled “Session isolation”Each MCP client session gets its own set of cursors. You cannot access or interfere with another session’s cursors. Session IDs are derived from the MCP client context — they are not user-controllable.
Management tools
Section titled “Management tools”| Tool | What it does |
|---|---|
cursor_list() | Show all active cursors for the current session: IDs, page progress, TTL remaining, grep pattern |
cursor_delete(cursor_id="...") | Delete a specific cursor to free memory |
cursor_delete_all() | Delete all cursors for the current session |
These are useful for cleanup during long analysis sessions or when you want to re-run a query from scratch.
Example: scanning all strings for credentials
Section titled “Example: scanning all strings for credentials”# Start with a broad credential searchresult = data_list_strings(grep="password|secret|key|token|api_key|credential", page_size=100)
# Process first page of results# ... examine the strings ...
# Get more if there are additional pagesif result has cursor_id: result = cursor_next(cursor_id="...")Example: iterating through all functions matching a pattern
Section titled “Example: iterating through all functions matching a pattern”# First pageresult = functions_list(grep="handle_|process_|parse_", page_size=50)
# Loop through pageswhile result has cursor_id: # Decompile interesting functions from this page for func in result: if func looks relevant: functions_decompile(name=func["name"])
# Advance result = cursor_next(cursor_id="...")-
Prefer server-side
grepover fetching everything. A query likefunctions_list(grep="ssl")is far cheaper thanfunctions_list(return_all=True)followed by manual filtering. -
Use
fieldsto reduce response size. If you only need names and addresses,functions_list(fields=["name", "address"], page_size=100)cuts the per-item size significantly. -
Small page sizes (50-100) keep individual responses from consuming too much context. You can always fetch more pages.
-
If a cursor expires (5-minute TTL), just re-run the original query. The cursor IDs are not reusable — you get a new one each time.
-
For very large binaries (100K+ functions), start with grep-filtered queries rather than listing everything. Even paginated, iterating through 2,000 pages of 50 items each is slow and rarely what you actually need.