Source code for polymarket_mcp.servers.clob_public_server

"""FastMCP public CLOB server for Polymarket order books and pricing.

Purpose:
    Expose read-only order book, quote, and history tools for the Polymarket
    CLOB API.

Design:
    This module is for live market-state reads only. Tool docstrings help an
    LLM choose the correct tool for books, spot quotes, midpoint/spread, and
    historical price queries.
"""

from __future__ import annotations

import json

from fastmcp import FastMCP

from polymarket_mcp.models.clob import (
    OrderBookOutput,
    PriceHistoryArgs,
    PriceHistoryOutput,
    PriceQuoteOutput,
    PriceQuotesOutput,
    TokenArgs,
    TokensArgs,
)
from polymarket_mcp.services.clob_public import ClobPublicService
from polymarket_mcp.settings import load_settings

settings = load_settings()
service = ClobPublicService(settings)

mcp = FastMCP(
    name="polymarket-clob-public",
    instructions=(
        "Use this server for live public order book and pricing reads. "
        "Do not use it for market discovery or wallet analytics."
    ),
    mask_error_details=True,
    strict_input_validation=True,
)


[docs] @mcp.tool async def get_book(args: TokenArgs) -> OrderBookOutput: """Fetch the current order book for one token. Use this tool when the user wants live market microstructure such as bids, asks, depth, spread, or liquidity around the current price. Prefer this tool over ``get_price`` when depth matters, not just the current quote. Do not use this tool for market discovery; Gamma tools are better for finding the right market or token first. The input should be a single CLOB token ID, not a market slug or wallet address. A common next step is to summarize book depth or compare books across multiple tokens. Args: args: Single-token lookup arguments. Returns: OrderBookOutput: One normalized order book. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python result = await get_book(TokenArgs(token_id="123")) """ return OrderBookOutput(book=await service.get_book(args))
[docs] @mcp.tool async def get_books(args: TokensArgs) -> list[OrderBookOutput]: """Fetch current order books for multiple tokens. Use this tool when the user wants to compare liquidity or depth across several known tokens at once. Prefer this tool over repeated single-token lookups when you already know multiple token IDs. Do not use this tool if you still need to identify which market or token is relevant. The input should be a list of CLOB token IDs. A common next step is to rank the returned books by spread, depth, or visible liquidity. Args: args: Multi-token lookup arguments. Returns: list[OrderBookOutput]: Normalized order books in bulk. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python result = await get_books(TokensArgs(token_ids=["1", "2"])) """ return [OrderBookOutput(book=book) for book in await service.get_books(args)]
[docs] @mcp.tool async def get_price(args: TokenArgs) -> PriceQuoteOutput: """Fetch a current price quote for one token. Use this tool when the user wants the current quoted price or implied level for a known token but does not need the full order book. Prefer this tool over ``get_book`` when a lightweight spot quote is enough. Prefer ``get_midpoint`` or ``get_spread`` when the question is specifically about those metrics. The input should be a single CLOB token ID. A common next step is to compare the quote with historical prices or fetch the full book. Args: args: Single-token lookup arguments. Returns: PriceQuoteOutput: One normalized price quote. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python result = await get_price(TokenArgs(token_id="123")) """ return PriceQuoteOutput(quote=await service.get_price(args))
[docs] @mcp.tool async def get_prices(args: TokensArgs) -> PriceQuotesOutput: """Fetch current price quotes for multiple tokens. Use this tool when the user wants a quick multi-token quote snapshot without needing full order books for each token. Prefer this tool over repeated single-token quote calls when several token IDs are already known. Do not use this tool for discovery or wallet-level analysis. The input should be a list of CLOB token IDs. A common next step is to rank tokens by price or follow up with ``get_book`` on the most interesting ones. Args: args: Multi-token lookup arguments. Returns: PriceQuotesOutput: Multiple normalized price quotes. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python result = await get_prices(TokensArgs(token_ids=["1", "2"])) """ return PriceQuotesOutput(quotes=await service.get_prices(args))
[docs] @mcp.tool async def get_midpoint(args: TokenArgs) -> PriceQuoteOutput: """Fetch the midpoint for one token. Use this tool when the user specifically wants the midpoint rather than a full order book or generic quote. Prefer this tool when reasoning about fair value between best bid and best ask. If depth matters too, use ``get_book`` instead. The input should be a single CLOB token ID. Args: args: Single-token lookup arguments. Returns: PriceQuoteOutput: Quote object with midpoint populated when available. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python result = await get_midpoint(TokenArgs(token_id="123")) """ return PriceQuoteOutput(quote=await service.get_midpoint(args))
[docs] @mcp.tool async def get_spread(args: TokenArgs) -> PriceQuoteOutput: """Fetch the spread for one token. Use this tool when the user specifically wants transaction tightness, execution quality hints, or a quick liquidity proxy. Prefer this tool over ``get_book`` when only the spread is needed. If the user wants depth and book shape too, use ``get_book``. The input should be a single CLOB token ID. Args: args: Single-token lookup arguments. Returns: PriceQuoteOutput: Quote object with spread populated when available. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python result = await get_spread(TokenArgs(token_id="123")) """ return PriceQuoteOutput(quote=await service.get_spread(args))
[docs] @mcp.tool async def get_price_history(args: PriceHistoryArgs) -> PriceHistoryOutput: """Fetch historical price points for one token. Use this tool when the user wants trend, momentum, or time-series context for a known token. Prefer this tool over ``get_price`` when the question is about change over time rather than the current quote. Do not use this tool if the token ID is not yet known; use Gamma discovery first. The input should be a single CLOB token ID plus an interval and optional time bounds. A common next step is to summarize the trend or compare recent history with the current quote or spread. Args: args: Historical price query arguments. Returns: PriceHistoryOutput: Normalized historical price points. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python result = await get_price_history( PriceHistoryArgs(token_id="123", interval="1h"), ) """ points = await service.get_price_history(args) return PriceHistoryOutput( token_id=args.token_id, interval=args.interval, points=points, )
[docs] @mcp.resource("polymarket://clob/book/{token_id}") async def book_resource(token_id: str) -> str: """Read the canonical order book resource for one token. Args: token_id: Single CLOB token ID. Returns: str: JSON-encoded order book payload. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python text = await book_resource("123") """ payload = OrderBookOutput(book=await service.get_book(TokenArgs(token_id=token_id))) return json.dumps(payload.model_dump(mode="json"), indent=2, sort_keys=True)
[docs] @mcp.resource("polymarket://clob/price/{token_id}") async def price_resource(token_id: str) -> str: """Read the canonical price resource for one token. Args: token_id: Single CLOB token ID. Returns: str: JSON-encoded price quote payload. Raises: httpx.HTTPError: If the upstream CLOB request fails. Examples: .. code-block:: python text = await price_resource("123") """ payload = PriceQuoteOutput(quote=await service.get_price(TokenArgs(token_id=token_id))) return json.dumps(payload.model_dump(mode="json"), indent=2, sort_keys=True)