How the MCP Server Implements Pagination for Large OpenStack Resource Lists
The MCP server implements limit/offset pagination by validating parameters (limit capped at 200), slicing the full resource list from OpenStack SDK results, and returning rich metadata including has_next, next_offset, and total_count to enable efficient client-side iteration.
The call518/mcp-openstack-ops repository provides a Model Context Protocol (MCP) server that interfaces with OpenStack clouds. When retrieving potentially massive collections of servers, load balancers, or volumes, the server employs a consistent limit/offset pagination strategy across all service modules rather than streaming entire datasets in a single response.
Core Pagination Logic in the Compute Service
The primary pagination implementation resides in src/mcp_openstack_ops/services/compute.py within the get_instance_details function. This function retrieves server instances and applies consistent slicing logic regardless of the underlying OpenStack project size.
Parameter Validation and Safety Caps
Before processing any requests, the function sanitizes incoming pagination parameters to prevent abuse and ensure stable performance. According to the source code in call518/mcp-openstack-ops, the implementation enforces hard boundaries:
- Maximum limit: 200 items per request
- Minimum limit: 1 item per request
- Minimum offset: 0 (negative values reset to zero)
def get_instance_details(..., limit: int = 50, offset: int = 0, include_all: bool = False) -> Dict[str, Any]:
# Input sanitisation
if limit > 200: limit = 200
if limit < 1: limit = 1
if offset < 0: offset = 0
# ... additional implementation
Resource Slicing and Metadata Generation
After fetching the complete server list from the OpenStack SDK, the function applies Python list slicing to return only the requested window. The implementation calculates comprehensive pagination metadata to guide subsequent client requests:
# Fetch all servers (project-scoped)
all_servers = list(conn.compute.servers(details=True, all_projects=False))
total_count = len(all_servers)
# Apply pagination slice
if include_all:
paginated_servers = all_servers
else:
paginated_servers = all_servers[offset:offset + limit]
# Build metadata flags
has_next = (offset + limit) < total_count
has_prev = offset > 0
next_offset = offset + limit if has_next else None
prev_offset = max(0, offset - limit) if has_prev else None
result = {
'instances': instances,
'count': len(instances),
'total_count': total_count,
'limit': limit,
'offset': offset,
'has_next': has_next,
'has_prev': has_prev,
'next_offset': next_offset,
'prev_offset': prev_offset,
}
return result
The slice operation all_servers[offset:offset + limit] performs the actual pagination, while the metadata dictionary enables callers to navigate forward and backward through the full dataset.
Load Balancer Pagination Implementation
The load balancer service in src/mcp_openstack_ops/services/load_balancer/core.py follows an identical pattern through the get_load_balancer_list function. As implemented in call518/mcp-openstack-ops, this module validates the limit parameter and applies the same slicing technique to load balancer collections:
def get_load_balancer_list(limit: int = 50, offset: int = 0, include_all: bool = False) -> Dict[str, Any]:
# Limit validation
if not include_all:
limit = max(1, min(limit, 200))
# Collect project-scoped load balancers
all_lbs = [lb for lb in conn.load_balancer.load_balancers()
if getattr(lb, 'project_id', None) == current_project_id]
# Apply pagination slice
load_balancers = all_lbs if include_all else all_lbs[offset:offset + limit]
# Build summary with pagination metadata
result = {
'success': True,
'load_balancers': lb_details,
'summary': {
'total_returned': len(lb_details),
'limit': limit if not include_all else 'all',
'offset': offset if not include_all else 0,
'has_more': (offset + limit) < len(all_lbs),
'processing_time_seconds': round(processing_time, 2),
'project_id': current_project_id,
}
}
return result
Note that this service uses has_more rather than has_next, but provides equivalent functionality for determining if additional pages exist.
Tool Wrappers and API Exposure
The MCP tool layer exposes these pagination parameters directly to clients. In src/mcp_openstack_ops/tools/get_instance.py, the tool wrapper forwards arguments to the underlying service functions:
| Tool | Service Function | Exposed Parameters |
|---|---|---|
get_instance |
get_instance_details |
limit, offset, all_instances (maps to include_all) |
search_instances |
search_instances |
limit, offset |
get_load_balancer_list |
get_load_balancer_list |
limit, offset, include_all |
The get_instance tool specifically maps its all_instances boolean flag to the service's include_all parameter:
if all_instances:
result_data = _get_instance_details(
instance_names=None,
limit=limit,
offset=offset,
include_all=True
)
This architecture ensures that pagination controls remain consistent across all OpenStack resource types exposed through the MCP server.
Client-Side Pagination Workflow
To retrieve large datasets efficiently, clients should check the pagination metadata returned in each response and adjust the offset accordingly. Here is the standard iteration pattern for compute instances:
# Retrieve first page (default 50 items)
first_page = mcp.tools.get_instance(limit=50, offset=0)
# Check for additional pages
if first_page['has_next']:
second_page = mcp.tools.get_instance(
limit=50,
offset=first_page['next_offset']
)
For load balancers, the pattern is similar but uses the has_more field within the summary block:
# Request specific window
lbs = mcp.tools.get_load_balancer_list(limit=20, offset=40)
# Continue if more data exists
if lbs['summary']['has_more']:
next_page = mcp.tools.get_load_balancer_list(limit=20, offset=60)
Both patterns rely on the server-side metadata to safely traverse the entire resource inventory without missing items or exceeding API rate limits.
Summary
- Hard limits: The MCP server enforces a maximum
limitof 200 items and minimum of 1, withoffsetclamped to non-negative values across all services includingsrc/mcp_openstack_ops/services/compute.py. - Slicing strategy: Pagination occurs via Python list slicing (
items[offset:offset+limit]) after retrieving the full resource list from the OpenStack SDK, not through OpenStack's native API pagination. - Metadata richness: Every paginated response includes
total_count, boolean flags (has_next/has_more), and calculated offsets (next_offset,prev_offset) to support bidirectional navigation. - Bypass option: The
include_allparameter (exposed asall_instancesin some tools) allows internal processes to retrieve complete datasets without pagination overhead. - Consistent exposure: All resource-specific services (compute, load balancer, storage) follow identical pagination patterns, ensuring predictable behavior across the
call518/mcp-openstack-opscodebase.
Frequently Asked Questions
What is the maximum number of items the MCP server returns per page?
The server enforces a hard cap of 200 items per request. If a client specifies a limit exceeding 200, the value is automatically reduced to 200. Conversely, values below 1 are raised to 1 to ensure at least one item returns.
How does the include_all parameter affect pagination behavior?
When include_all is set to True, the server bypasses the slicing logic entirely and returns the complete resource list regardless of limit or offset values. This flag is primarily intended for internal administrative tools that require full dataset visibility without iterative API calls.
What metadata fields indicate whether more results are available?
For compute resources, check has_next (boolean) and next_offset (integer or None). For load balancers, inspect summary['has_more'] (boolean). Both patterns provide the next offset value required to fetch the subsequent page, or None/False when the current page represents the end of the dataset.
Does the MCP server use OpenStack's native pagination APIs?
No. The server retrieves the complete resource list from the OpenStack SDK (e.g., conn.compute.servers() or conn.load_balancer.load_balancers()), then applies client-side slicing to implement pagination. This approach ensures consistent pagination behavior across different OpenStack services regardless of whether the underlying API supports native limit/offset parameters.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →