Metadata-Version: 2.4
Name: mcplisten
Version: 0.1.0
Summary: Drain all pages from MCP SDK paginated list methods — get every tool, resource, and prompt without cursor management
Project-URL: Homepage, https://github.com/prasad-a-abhishek/mcplisten
Project-URL: Repository, https://github.com/prasad-a-abhishek/mcplisten
Project-URL: Issues, https://github.com/prasad-a-abhishek/mcplisten/issues
Author-email: Prasad Abhishek <prasad-a-abhishek@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Keywords: mcp,model-context-protocol,pagination,prompts,resources,tools
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# mcplisten

**Drain all pages from MCP SDK paginated list methods — get every tool, resource, and prompt without cursor management.**

[![PyPI version](https://img.shields.io/pypi/v/mcplisten.svg)](https://pypi.org/project/mcplisten/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)

> Python developers using the MCP Python SDK's `Client.list_tools()` only receive the **first page** of results when a server paginates its responses. mcplisten transparently drains every page so you get the complete inventory.

## Quick Start

```bash
pip install git+https://github.com/prasad-a-abhishek/mcplisten.git
```

```python
from mcplisten import list_all_tools

# Async usage (with MCP SDK Client)
from mcp import Client
async with Client(server_params) as client:
    tools = await list_all_tools(client)  # drains all pages
    print(f"Got {len(tools)} tools")

# Sync usage (with SyncMCPClient from mcpsync)
from mcpsync import SyncMCPClient
from mcplisten import list_all_tools_sync

with SyncMCPClient(params) as client:
    tools = list_all_tools_sync(client)
```

## ⚡ Performance & Benchmarks

mcplisten is a thin pagination drain helper — it adds zero computational overhead beyond the network I/O of fetching additional pages. The synchronous wrapper spawns a fresh event loop per call (thread-safe for single-shot use).

```bash
python3 benchmarks/run_benchmark.py
```

**Benchmark environment:** Python 3.11.15, Linux x86_64 (kernel 6.12.67), 4 vCPU.
*Note: mcplisten has no direct competitor — the MCP Python SDK itself does not provide `list_all_*` helpers (see [modelcontextprotocol/python-sdk#2556](https://github.com/modelcontextprotocol/python-sdk/issues/2556)).*

## Test Status

The repository ships with a **131-test** pytest suite covering every spec acceptance criterion (22/22), concurrent-call safety, unicode round-trip, dict-shaped and pydantic-shaped responses, CLI end-to-end against a fake paginating stdio server, and zero-dependency enforcement. Run `pytest tests/ -q` to reproduce (≈1 second).

**Spec:** see [spec.md](./spec.md) for the canonical 22-acceptance-criteria contract that drives the test suite.

## Why mcplisten?

The MCP Python SDK's `ClientSession.list_tools(cursor=...)` accepts a cursor parameter but requires callers to manually loop across pages. MCP gateways, Claude Code, Cursor, and the Inspector CLI all shipped with this single-page limitation — silently losing tools when servers register more tools than fit in one page.

**mcplisten is the fix:** a zero-dependency, zero-overhead wrapper that calls `list_tools(cursor=None)` then follows `nextCursor` until the server returns null. No SDK changes required. No configuration. Drop in.

| Feature | mcplisten | SDK default |
|---------|-----------|-------------|
| Multi-page drain | ✅ | ❌ single page only |
| Zero dependencies | ✅ | — |
| Sync + async | ✅ | async only |
| Generic `list_all()` | ✅ | ❌ |
| CLI included | ✅ | ❌ |

## Key Features

- **`list_all_tools(client)`** — drain all pages from `list_tools()`, returns `list[dict]`
- **`list_all_resources(client)`** — drain all pages from `list_resources()`
- **`list_all_prompts(client)`** — drain all pages from `list_prompts()`
- **`list_all_resource_templates(client)`** — drain all pages from `list_resource_templates()`
- **`list_all(client, method, items_key)`** — generic drain helper
- **Sync wrappers** — `list_all_*_sync()` variants for `SyncMCPClient`
- **CLI** — `mcplisten list-tools -- <server-cmd>`, `list-resources`, `list-prompts`
- **Zero dependencies** — pure Python stdlib, no third-party packages
- **Full type stubs** — Python 3.11+ with complete type annotations

## CLI Reference

```bash
# List all tools from a stdio server
mcplisten list-tools -- python -m my_mcp_server

# List all resources
mcplisten list-resources -- python -m my_mcp_server

# List all prompts
mcplisten list-prompts -- python -m my_mcp_server

# Verbose mode — includes per-server tool counts
mcplisten list-tools --verbose -- python -m my_mcp_server

# Help
mcplisten --help
mcplisten list-tools --help
```

## API Reference

### `list_all_tools(client)` — async

```python
from mcplisten import list_all_tools

tools: list[dict[str, Any]] = await list_all_tools(client)
```

Drains all pages from `client.list_tools()` (works with `Client`, `ClientSession`, and `ClientSessionGroup`).

### `list_all_tools_sync(client)` — sync

```python
from mcplisten import list_all_tools_sync

tools: list[dict[str, Any]] = list_all_tools_sync(client)
```

Synchronous wrapper for use with `SyncMCPClient` (from the `mcpsync` extra).

### `list_all(client, method, items_key)` — generic

```python
from mcplisten import list_all

result: list[dict[str, Any]] = await list_all(client, "list_tools", "tools")
```

Generic drain helper for any paginated list method. `items_key` is the dict key holding the items list in each page response.

### CLI

```
mcplisten [--help]
  list-tools [--verbose] [--] <server-cmd>...
  list-resources [--] <server-cmd>...
  list-prompts [--] <server-cmd>...
  list-resource-templates [--] <server-cmd>...
```

Exits 0 on success (print JSON to stdout), non-zero on error (print message to stderr).

## Limitations

- **Cursor timing**: if a server adds a tool between page 1 and page 2 of a drain, the caller may see duplicates. This is consistent with the MCP SDK's at-most-once semantics.
- **No streaming variant**: memory-constrained streaming (async generator) is a separate future feature.
- **SDK version sensitivity**: if the MCP SDK changes its pagination API (method signature or cursor field location), this library will break. Tests pin against a known SDK version.
- **No parallelization**: `ClientSessionGroup` drains each session sequentially, not in parallel.

## Non-Goals

- Changing the underlying MCP SDK's pagination mechanism
- Server-side pagination implementation
- Authentication (passes through to the underlying client)
- Tool/result caching

## License

MIT License — © 2026 Prasad Abhishek
