Build a Polymarket MCP Server for Prediction-Market Agents with Paper-Trading Validation
SKALE's Agent Pit (Aug 12, 2026) established paper trading as the training ground for prediction-market agents. This MCP server — polymarket-mcp — gives agents the same discipline as a tool surface: read markets, order books, and positions in paper mode by default, then graduate to live orders only behind an explicit mode flag with position caps. Built with FastMCP in TypeScript with inputSchema JSON, mcpServers config, and an API-key security guide.
Deepak Bagada
CEO, SaaSNext
- polymarket-mcp exposes prediction markets to AI agents with paper-first trading as the default — the SKALE Agent Pit discipline as a tool surface.
- Six tools cover the full loop: list_markets, get_market, get_orderbook, get_position, place_paper_order, and place_live_order.
- Live orders require an explicit mode:live flag, an approved:true flag, and position caps — graduation is a policy decision, not a model impulse.
- Idempotency keys prevent duplicate orders; paper and live books stay separate so benchmark P&L is not polluted.
- Security: Polymarket API keys for live orders only, caps enforced server-side, and every order logged to an audit trail.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
SKALE's Agent Pit launch on August 12, 2026 made a quiet but important statement: prediction-market agents should train in a paper-trading sandbox before they touch real money. This dispatch turns that discipline into a tool surface. polymarket-mcp is a TypeScript FastMCP server that exposes Polymarket prediction markets to AI agents with paper-first trading as the default: agents read markets, order books, and positions freely, place paper orders against a local simulation book, and only reach live orders through an explicit mode flag with server-side position caps. The latest AI news hub has tracked the prediction-market agent wave; this is the governed MCP surface those agents need.
Why prediction markets need a paper-first gate
Prediction markets are the natural arena for autonomous agents — liquid, binary-outcome, fast-resolving — but live money punishes unvalidated strategies instantly. Agent Pit showed the answer: benchmark offline, trade paper, graduate with caps. polymarket-mcp enforces that pipeline structurally. The default mode is paper; live is an explicit, capped, audited exception. An agent cannot accidentally trade real money because the tool surface makes it impossible without a deliberate policy flag. That is the difference between a trading agent and a gambling agent.
Architecture
flowchart TD
A[AI agent] -->|MCP JSON-RPC| B[polymarket-mcp server]
B --> C[list_markets]
B --> D[get_market]
B --> E[get_orderbook]
B --> F[get_position]
B --> G[place_paper_order]
B --> H[place_live_order]
G --> I[Local paper book]
H --> J{mode=live + approved + cap}
J -- no --> K[Rejected]
J -- yes --> L[Polymarket API]
F --> M[Paper + live positions]
Project setup
mkdir polymarket-mcp && cd polymarket-mcp
npm init -y
npm install @modelcontextprotocol/sdk fastmcp zod dotenv
# .env
POLYMARKET_API_KEY=your_live_api_key # only injected when mode:live
POLYMARKET_SECRET=your_api_secret
MAX_POSITION_USD=500
DEFAULT_MODE=paper
AUDIT_LOG_PATH=./audit/orders.log
Server code (index.ts)
import { FastMCP } from "fastmcp";
import { z } from "zod";
import { appendFileSync } from "fs";
import "dotenv/config";
const server = new FastMCP({ name: "polymarket-mcp", version: "1.0.0" });
const paperBook: Record<string, { size: number; price: number }> = {};
function audit(entry: unknown) {
appendFileSync(process.env.AUDIT_LOG_PATH || "./audit/orders.log", JSON.stringify(entry) + "
");
}
// --- Tool 1: list markets ---
server.addTool({
name: "list_markets",
description: "List active Polymarket markets, optionally filtered by tag or status.",
inputSchema: z.object({
tag: z.string().optional(),
limit: z.number().int().max(100).default(20),
}),
async execute({ tag, limit }) {
const url = `https://gamma-api.polymarket.com/markets?limit=${limit}${tag ? `&tag=${tag}` : ""}`;
const res = await fetch(url);
const markets = await res.json();
return markets.map((m: any) => ({
id: m.id,
question: m.question,
endDate: m.end_date,
volume: m.volume,
}));
},
});
// --- Tool 2: get a market ---
server.addTool({
name: "get_market",
description: "Get full detail for a single market including current prices.",
inputSchema: z.object({ marketId: z.string() }),
async execute({ marketId }) {
const res = await fetch(`https://gamma-api.polymarket.com/markets/${marketId}`);
return await res.json();
},
});
// --- Tool 3: get order book ---
server.addTool({
name: "get_orderbook",
description: "Get the current order book for a market's outcome token.",
inputSchema: z.object({ marketId: z.string(), outcome: z.string() }),
async execute({ marketId, outcome }) {
const res = await fetch(`https://clob.polymarket.com/book?token_id=${marketId}-${outcome}`);
return await res.json();
},
});
// --- Tool 4: get position ---
server.addTool({
name: "get_position",
description: "Return current paper and live positions for an address.",
inputSchema: z.object({ address: z.string() }),
async execute({ address }) {
return { paper: paperBook[address] ?? { size: 0 }, live: "query Polymarket API" };
},
});
// --- Tool 5: place a paper order (default mode) ---
server.addTool({
name: "place_paper_order",
description: "Place a paper order against the local simulation book. No real money.",
inputSchema: z.object({
marketId: z.string(),
outcome: z.string(),
side: z.enum(["buy", "sell"]),
size: z.number().positive(),
price: z.number().positive(),
}),
async execute({ marketId, outcome, side, size, price }) {
paperBook[`${marketId}-${outcome}`] = { size, price };
audit({ type: "paper", marketId, outcome, side, size, price, at: new Date().toISOString() });
return { status: "filled_paper", size, price, note: "paper order, no real funds" };
},
});
// --- Tool 6: place a live order (explicit gate) ---
server.addTool({
name: "place_live_order",
description: "Place a live Polymarket order. REQUIRES mode:live and approved:true; enforced position cap.",
inputSchema: z.object({
mode: z.enum(["paper", "live"]),
approved: z.boolean(),
marketId: z.string(),
outcome: z.string(),
side: z.enum(["buy", "sell"]),
size: z.number().positive(),
price: z.number().positive(),
idempotencyKey: z.string(),
}),
async execute({ mode, approved, marketId, outcome, side, size, price, idempotencyKey }) {
if (mode !== "live" || !approved) {
return { status: "rejected", reason: "mode must be live and approved must be true" };
}
const cap = parseFloat(process.env.MAX_POSITION_USD || "500");
if (size * price > cap) return { status: "rejected", reason: `notional ${size * price} exceeds cap ${cap}` };
// Real implementation: sign + submit to Polymarket CLOB with idempotencyKey
audit({ type: "live", marketId, outcome, side, size, price, idempotencyKey, at: new Date().toISOString() });
return { status: "submitted", orderId: idempotencyKey, note: "live order submitted with cap enforced" };
},
});
server.start({ transportType: "stdio" });
inputSchema JSON definitions
{
"place_paper_order": {
"type": "object",
"properties": {
"marketId": { "type": "string" },
"outcome": { "type": "string" },
"side": { "type": "string", "enum": ["buy", "sell"] },
"size": { "type": "number", "exclusiveMinimum": 0 },
"price": { "type": "number", "exclusiveMinimum": 0 }
},
"required": ["marketId", "outcome", "side", "size", "price"]
},
"place_live_order": {
"type": "object",
"properties": {
"mode": { "type": "string", "enum": ["paper", "live"] },
"approved": { "type": "boolean" },
"marketId": { "type": "string" },
"outcome": { "type": "string" },
"side": { "type": "string", "enum": ["buy", "sell"] },
"size": { "type": "number", "exclusiveMinimum": 0 },
"price": { "type": "number", "exclusiveMinimum": 0 },
"idempotencyKey": { "type": "string" }
},
"required": ["mode", "approved", "marketId", "outcome", "side", "size", "price", "idempotencyKey"]
}
}
mcpServers config
{
"mcpServers": {
"polymarket": {
"command": "node",
"args": ["/path/to/polymarket-mcp/dist/index.js"],
"env": {
"POLYMARKET_API_KEY": "${POLYMARKET_API_KEY}",
"POLYMARKET_SECRET": "${POLYMARKET_SECRET}",
"MAX_POSITION_USD": "500",
"DEFAULT_MODE": "paper"
}
}
}
}
Retry & idempotency rules
- Reads (list_markets, get_market, get_orderbook) retry twice with backoff on 5xx or rate limits.
- place_live_order is idempotent by idempotencyKey: resubmitting the same key returns the same orderId and never double-fills.
- Never retry a live order without the same idempotency key — a confused agent retrying with a fresh key can double the position.
- Paper orders are stateless and safe to retry; the simulation book is idempotent by construction.
Security guide
The security model is graduation by policy, not by impulse. Reads are public and unauthenticated. Paper orders need no credentials and hit a local simulation book. Live orders require the Polymarket API key — which is only injected when mode:live is used — plus an approved:true flag and a server-side notional cap from MAX_POSITION_USD. Every order, paper or live, is written to the audit log with a timestamp. The pattern mirrors the SKALE Agent Pit discipline: benchmark offline, trade paper, graduate with caps. The same graduation discipline appears across the AI workflows library and the MCP directory for every money-touching agent tool.
The bottom line
polymarket-mcp gives prediction-market agents a governed tool surface: full market data reads, paper trading by default, and live orders behind explicit flags with caps and audit. It is the SKALE Agent Pit discipline made structural — agents cannot graduate to real money without a deliberate policy decision. The tooling patterns are in the MCP directory; track the prediction-market agent wave on latest AI news.
Frequently Asked Questions
What is polymarket-mcp?
A TypeScript FastMCP server exposing Polymarket prediction markets to AI agents — market data reads, order books, positions, paper orders by default, and live orders behind an explicit mode flag with caps.
Why paper-first trading?
SKALE's Agent Pit (Aug 12, 2026) established paper trading as the training ground for prediction-market agents: validate strategies against realistic order flow before risking real money. The MCP server enforces the same discipline.
How do agents graduate to live trading?
place_live_order requires mode:live and approved:true flags plus a server-side position cap. Graduation is an explicit policy decision — never a model's spontaneous choice.
How are paper and live P&L kept separate?
Paper orders go to a local simulation book; live orders go to the Polymarket API. The books never mix, so benchmark results stay clean and live P&L is auditable.
What security does it need?
Reads are public; live orders need a Polymarket API key that is only injected when mode:live is used, with caps and an audit log on every order.
Closing thoughts
Prediction-market agents are coming whether or not we build the rails — polymarket-mcp is the rail that keeps them honest. Paper-first by default, live only by explicit policy, caps and audit on everything. That is the discipline that separates trading agents from gambling agents. The tooling is in the MCP directory; the agent-trading coverage is on latest AI news.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
CEO, SaaSNext
Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.
Octane's AI Operating System: When Agentic AI Runs the Convenience Store
Next Story →Chainlink for Agents: The Verified Data, Execution & Cross-Chain Layer for Autonomous Onchain AI Agents
Related Intelligence Analysis
Vercel AI SDK Tool Calling React: 5 Steps (2026)
Vercel AI SDK tool calling React integration is a programming pattern that executes server-side functions based on large language model decisions and streams the results to a React frontend. By combining streamText with...
Fact-Density vs. Word Count: The New SEO for 2026
Fact Density is the ratio of verifiable, unique information to the total word count of a piece of content. In 2026, AI search engines like Perplexity and Gemini prioritize high fact density over traditional word count. A...
NVIDIA Audex vs Qwen3.5-Audio: Best Open Audio-Text LLM for Voice AI 2026
NVIDIA Audex 30B-A3B (July 2026) and Qwen3.5-35B-A3B are the two leading open audio-text LLMs. Audex uniquely handles both audio understanding and generation in a single model while preserving text intelligence. Qwen3.5-...