Source code for polymarket_mcp.servers.data_server
"""FastMCP Data API server for Polymarket wallet and trade reads.
Purpose:
Expose Polymarket wallet, activity, and trade reads as MCP tools and
resources.
Design:
This module is read-only. Tool docstrings are written to help an LLM choose
the correct wallet or trade tool and understand the expected identifiers and
likely next steps.
"""
from __future__ import annotations
import json
from fastmcp import FastMCP
from polymarket_mcp.models.data import (
TradesArgs,
TradesOutput,
WalletActivityOutput,
WalletArgs,
WalletPositionsOutput,
)
from polymarket_mcp.services.data import DataApiService
from polymarket_mcp.settings import load_settings
settings = load_settings()
service = DataApiService(settings)
mcp = FastMCP(
name="polymarket-data",
instructions=(
"Use this server for wallet exposure, historical activity, and trade "
"reads. Do not use it for market discovery or live order book reads."
),
mask_error_details=True,
strict_input_validation=True,
)
[docs]
@mcp.tool
async def get_positions(args: WalletArgs) -> WalletPositionsOutput:
"""Fetch current positions for one wallet.
Use this tool when the user wants to know what a wallet currently holds,
which markets it is exposed to, or its current directional footprint.
Prefer this tool over ``get_activity`` when the question is about present
state rather than historical actions. Do not use this tool if you still
need to discover which market exists; that belongs to Gamma tools.
The input should be a wallet address, typically in ``0x...`` format. A
common next step is ``get_activity`` for recent changes or CLOB/Gamma tools
for deeper inspection of the markets referenced by the positions.
Args:
args: Wallet query arguments.
Returns:
WalletPositionsOutput: Current normalized positions for the wallet.
Raises:
httpx.HTTPError: If the upstream Data API request fails.
Examples:
.. code-block:: python
result = await get_positions(WalletArgs(user="0xabc"))
"""
positions = await service.get_positions(args)
return WalletPositionsOutput(user=args.user, positions=positions)
[docs]
@mcp.tool
async def get_closed_positions(args: WalletArgs) -> WalletPositionsOutput:
"""Fetch closed positions for one wallet.
Use this tool when the user wants completed or no-longer-open positions,
such as reviewing realized exposure or prior bets.
Prefer this tool over ``get_positions`` when historical closed exposure is
specifically requested. A common next step is ``get_activity`` or
``get_trades`` to explain how the wallet entered and exited those markets.
The input should be a wallet address, typically in ``0x...`` format.
Args:
args: Wallet query arguments.
Returns:
WalletPositionsOutput: Closed normalized positions for the wallet.
Raises:
httpx.HTTPError: If the upstream Data API request fails.
Examples:
.. code-block:: python
result = await get_closed_positions(WalletArgs(user="0xabc"))
"""
positions = await service.get_closed_positions(args)
return WalletPositionsOutput(user=args.user, positions=positions)
[docs]
@mcp.tool
async def get_activity(args: WalletArgs) -> WalletActivityOutput:
"""Fetch recent activity for one wallet.
Use this tool when the user wants to know what a wallet has been doing
recently, including buying, selling, or other account-level actions.
Prefer this tool over ``get_positions`` when the question is about recent
behavior rather than current holdings. Prefer ``get_trades`` when the user
specifically wants trade rows rather than broader account activity.
The input should be a wallet address, typically in ``0x...`` format. A
common next step is to inspect affected markets through Gamma or CLOB tools.
Args:
args: Wallet query arguments.
Returns:
WalletActivityOutput: Normalized activity rows for the wallet.
Raises:
httpx.HTTPError: If the upstream Data API request fails.
Examples:
.. code-block:: python
result = await get_activity(WalletArgs(user="0xabc"))
"""
activity = await service.get_activity(args)
return WalletActivityOutput(user=args.user, activity=activity)
[docs]
@mcp.tool
async def get_trades(args: TradesArgs) -> TradesOutput:
"""Fetch trade rows for a wallet or market filter.
Use this tool when the user wants trade-level records rather than current
holdings or general wallet activity. This is useful for detailed flow
analysis and execution history.
Prefer this tool over ``get_activity`` when exact trade rows matter. Prefer
Gamma tools when you still need to discover the right market first.
The input can include a wallet address, a market filter, or both, depending
on what the user already knows. A common next step is to summarize the
trade flow or compare it with current positions.
Args:
args: Trade query arguments.
Returns:
TradesOutput: Normalized trade rows.
Raises:
httpx.HTTPError: If the upstream Data API request fails.
Examples:
.. code-block:: python
result = await get_trades(TradesArgs(user="0xabc", limit=20))
"""
trades = await service.get_trades(args)
return TradesOutput(trades=trades)
[docs]
@mcp.resource("polymarket://data/user/{address}/positions")
async def positions_resource(address: str) -> str:
"""Read canonical current positions for a wallet.
Args:
address: Wallet address, usually in ``0x...`` format.
Returns:
str: JSON-encoded current positions payload.
Raises:
httpx.HTTPError: If the upstream Data API request fails.
Examples:
.. code-block:: python
text = await positions_resource("0xabc")
"""
payload = WalletPositionsOutput(
user=address,
positions=await service.get_positions(WalletArgs(user=address)),
)
return json.dumps(payload.model_dump(mode="json"), indent=2, sort_keys=True)
[docs]
@mcp.resource("polymarket://data/user/{address}/activity")
async def activity_resource(address: str) -> str:
"""Read canonical recent activity for a wallet.
Args:
address: Wallet address, usually in ``0x...`` format.
Returns:
str: JSON-encoded wallet activity payload.
Raises:
httpx.HTTPError: If the upstream Data API request fails.
Examples:
.. code-block:: python
text = await activity_resource("0xabc")
"""
payload = WalletActivityOutput(
user=address,
activity=await service.get_activity(WalletArgs(user=address)),
)
return json.dumps(payload.model_dump(mode="json"), indent=2, sort_keys=True)