|  | 
| 57 | 57 |   - [Advanced Usage](#advanced-usage) | 
| 58 | 58 |     - [Low-Level Server](#low-level-server) | 
| 59 | 59 |       - [Structured Output Support](#structured-output-support) | 
|  | 60 | +    - [Pagination (Advanced)](#pagination-advanced) | 
| 60 | 61 |     - [Writing MCP Clients](#writing-mcp-clients) | 
| 61 | 62 |     - [Client Display Utilities](#client-display-utilities) | 
| 62 | 63 |     - [OAuth Authentication for Clients](#oauth-authentication-for-clients) | 
| @@ -515,6 +516,41 @@ def debug_error(error: str) -> list[base.Message]: | 
| 515 | 516 | _Full example: [examples/snippets/servers/basic_prompt.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_prompt.py)_ | 
| 516 | 517 | <!-- /snippet-source --> | 
| 517 | 518 | 
 | 
|  | 519 | +### Icons | 
|  | 520 | + | 
|  | 521 | +MCP servers can provide icons for UI display. Icons can be added to the server implementation, tools, resources, and prompts: | 
|  | 522 | + | 
|  | 523 | +```python | 
|  | 524 | +from mcp.server.fastmcp import FastMCP, Icon | 
|  | 525 | + | 
|  | 526 | +# Create an icon from a file path or URL | 
|  | 527 | +icon = Icon( | 
|  | 528 | +    src="icon.png", | 
|  | 529 | +    mimeType="image/png", | 
|  | 530 | +    sizes="64x64" | 
|  | 531 | +) | 
|  | 532 | + | 
|  | 533 | +# Add icons to server | 
|  | 534 | +mcp = FastMCP( | 
|  | 535 | +    "My Server", | 
|  | 536 | +    website_url="https://example.com", | 
|  | 537 | +    icons=[icon] | 
|  | 538 | +) | 
|  | 539 | + | 
|  | 540 | +# Add icons to tools, resources, and prompts | 
|  | 541 | +@mcp.tool(icons=[icon]) | 
|  | 542 | +def my_tool(): | 
|  | 543 | +    """Tool with an icon.""" | 
|  | 544 | +    return "result" | 
|  | 545 | + | 
|  | 546 | +@mcp.resource("demo://resource", icons=[icon]) | 
|  | 547 | +def my_resource(): | 
|  | 548 | +    """Resource with an icon.""" | 
|  | 549 | +    return "content" | 
|  | 550 | +``` | 
|  | 551 | + | 
|  | 552 | +_Full example: [examples/fastmcp/icons_demo.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/fastmcp/icons_demo.py)_ | 
|  | 553 | + | 
| 518 | 554 | ### Images | 
| 519 | 555 | 
 | 
| 520 | 556 | FastMCP provides an `Image` class that automatically handles image data: | 
| @@ -746,6 +782,8 @@ async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerS | 
| 746 | 782 | _Full example: [examples/snippets/servers/elicitation.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/elicitation.py)_ | 
| 747 | 783 | <!-- /snippet-source --> | 
| 748 | 784 | 
 | 
|  | 785 | +Elicitation schemas support default values for all field types. Default values are automatically included in the JSON schema sent to clients, allowing them to pre-populate forms. | 
|  | 786 | + | 
| 749 | 787 | The `elicit()` method returns an `ElicitationResult` with: | 
| 750 | 788 | 
 | 
| 751 | 789 | - `action`: "accept", "decline", or "cancel" | 
| @@ -895,6 +933,8 @@ The FastMCP server instance accessible via `ctx.fastmcp` provides access to serv | 
| 895 | 933 | 
 | 
| 896 | 934 | - `ctx.fastmcp.name` - The server's name as defined during initialization | 
| 897 | 935 | - `ctx.fastmcp.instructions` - Server instructions/description provided to clients | 
|  | 936 | +- `ctx.fastmcp.website_url` - Optional website URL for the server | 
|  | 937 | +- `ctx.fastmcp.icons` - Optional list of icons for UI display | 
| 898 | 938 | - `ctx.fastmcp.settings` - Complete server configuration object containing: | 
| 899 | 939 |   - `debug` - Debug mode flag | 
| 900 | 940 |   - `log_level` - Current logging level | 
| @@ -1737,6 +1777,116 @@ Tools can return data in three ways: | 
| 1737 | 1777 | 
 | 
| 1738 | 1778 | When an `outputSchema` is defined, the server automatically validates the structured output against the schema. This ensures type safety and helps catch errors early. | 
| 1739 | 1779 | 
 | 
|  | 1780 | +### Pagination (Advanced) | 
|  | 1781 | + | 
|  | 1782 | +For servers that need to handle large datasets, the low-level server provides paginated versions of list operations. This is an optional optimization - most servers won't need pagination unless they're dealing with hundreds or thousands of items. | 
|  | 1783 | + | 
|  | 1784 | +#### Server-side Implementation | 
|  | 1785 | + | 
|  | 1786 | +<!-- snippet-source examples/snippets/servers/pagination_example.py --> | 
|  | 1787 | +```python | 
|  | 1788 | +""" | 
|  | 1789 | +Example of implementing pagination with MCP server decorators. | 
|  | 1790 | +""" | 
|  | 1791 | + | 
|  | 1792 | +from pydantic import AnyUrl | 
|  | 1793 | + | 
|  | 1794 | +import mcp.types as types | 
|  | 1795 | +from mcp.server.lowlevel import Server | 
|  | 1796 | + | 
|  | 1797 | +# Initialize the server | 
|  | 1798 | +server = Server("paginated-server") | 
|  | 1799 | + | 
|  | 1800 | +# Sample data to paginate | 
|  | 1801 | +ITEMS = [f"Item {i}" for i in range(1, 101)]  # 100 items | 
|  | 1802 | + | 
|  | 1803 | + | 
|  | 1804 | +@server.list_resources() | 
|  | 1805 | +async def list_resources_paginated(request: types.ListResourcesRequest) -> types.ListResourcesResult: | 
|  | 1806 | +    """List resources with pagination support.""" | 
|  | 1807 | +    page_size = 10 | 
|  | 1808 | + | 
|  | 1809 | +    # Extract cursor from request params | 
|  | 1810 | +    cursor = request.params.cursor if request.params is not None else None | 
|  | 1811 | + | 
|  | 1812 | +    # Parse cursor to get offset | 
|  | 1813 | +    start = 0 if cursor is None else int(cursor) | 
|  | 1814 | +    end = start + page_size | 
|  | 1815 | + | 
|  | 1816 | +    # Get page of resources | 
|  | 1817 | +    page_items = [ | 
|  | 1818 | +        types.Resource(uri=AnyUrl(f"resource://items/{item}"), name=item, description=f"Description for {item}") | 
|  | 1819 | +        for item in ITEMS[start:end] | 
|  | 1820 | +    ] | 
|  | 1821 | + | 
|  | 1822 | +    # Determine next cursor | 
|  | 1823 | +    next_cursor = str(end) if end < len(ITEMS) else None | 
|  | 1824 | + | 
|  | 1825 | +    return types.ListResourcesResult(resources=page_items, nextCursor=next_cursor) | 
|  | 1826 | +``` | 
|  | 1827 | + | 
|  | 1828 | +_Full example: [examples/snippets/servers/pagination_example.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/pagination_example.py)_ | 
|  | 1829 | +<!-- /snippet-source --> | 
|  | 1830 | + | 
|  | 1831 | +#### Client-side Consumption | 
|  | 1832 | + | 
|  | 1833 | +<!-- snippet-source examples/snippets/clients/pagination_client.py --> | 
|  | 1834 | +```python | 
|  | 1835 | +""" | 
|  | 1836 | +Example of consuming paginated MCP endpoints from a client. | 
|  | 1837 | +""" | 
|  | 1838 | + | 
|  | 1839 | +import asyncio | 
|  | 1840 | + | 
|  | 1841 | +from mcp.client.session import ClientSession | 
|  | 1842 | +from mcp.client.stdio import StdioServerParameters, stdio_client | 
|  | 1843 | +from mcp.types import Resource | 
|  | 1844 | + | 
|  | 1845 | + | 
|  | 1846 | +async def list_all_resources() -> None: | 
|  | 1847 | +    """Fetch all resources using pagination.""" | 
|  | 1848 | +    async with stdio_client(StdioServerParameters(command="uv", args=["run", "mcp-simple-pagination"])) as ( | 
|  | 1849 | +        read, | 
|  | 1850 | +        write, | 
|  | 1851 | +    ): | 
|  | 1852 | +        async with ClientSession(read, write) as session: | 
|  | 1853 | +            await session.initialize() | 
|  | 1854 | + | 
|  | 1855 | +            all_resources: list[Resource] = [] | 
|  | 1856 | +            cursor = None | 
|  | 1857 | + | 
|  | 1858 | +            while True: | 
|  | 1859 | +                # Fetch a page of resources | 
|  | 1860 | +                result = await session.list_resources(cursor=cursor) | 
|  | 1861 | +                all_resources.extend(result.resources) | 
|  | 1862 | + | 
|  | 1863 | +                print(f"Fetched {len(result.resources)} resources") | 
|  | 1864 | + | 
|  | 1865 | +                # Check if there are more pages | 
|  | 1866 | +                if result.nextCursor: | 
|  | 1867 | +                    cursor = result.nextCursor | 
|  | 1868 | +                else: | 
|  | 1869 | +                    break | 
|  | 1870 | + | 
|  | 1871 | +            print(f"Total resources: {len(all_resources)}") | 
|  | 1872 | + | 
|  | 1873 | + | 
|  | 1874 | +if __name__ == "__main__": | 
|  | 1875 | +    asyncio.run(list_all_resources()) | 
|  | 1876 | +``` | 
|  | 1877 | + | 
|  | 1878 | +_Full example: [examples/snippets/clients/pagination_client.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/pagination_client.py)_ | 
|  | 1879 | +<!-- /snippet-source --> | 
|  | 1880 | + | 
|  | 1881 | +#### Key Points | 
|  | 1882 | + | 
|  | 1883 | +- **Cursors are opaque strings** - the server defines the format (numeric offsets, timestamps, etc.) | 
|  | 1884 | +- **Return `nextCursor=None`** when there are no more pages | 
|  | 1885 | +- **Backward compatible** - clients that don't support pagination will still work (they'll just get the first page) | 
|  | 1886 | +- **Flexible page sizes** - Each endpoint can define its own page size based on data characteristics | 
|  | 1887 | + | 
|  | 1888 | +See the [simple-pagination example](examples/servers/simple-pagination) for a complete implementation. | 
|  | 1889 | + | 
| 1740 | 1890 | ### Writing MCP Clients | 
| 1741 | 1891 | 
 | 
| 1742 | 1892 | The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports): | 
|  | 
0 commit comments