@catalyst-team/poly-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +317 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +86 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +173 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +155 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/guide.d.ts +12 -0
- package/dist/tools/guide.d.ts.map +1 -0
- package/dist/tools/guide.js +801 -0
- package/dist/tools/guide.js.map +1 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +27 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/market.d.ts +11 -0
- package/dist/tools/market.d.ts.map +1 -0
- package/dist/tools/market.js +314 -0
- package/dist/tools/market.js.map +1 -0
- package/dist/tools/order.d.ts +10 -0
- package/dist/tools/order.d.ts.map +1 -0
- package/dist/tools/order.js +258 -0
- package/dist/tools/order.js.map +1 -0
- package/dist/tools/trade.d.ts +38 -0
- package/dist/tools/trade.d.ts.map +1 -0
- package/dist/tools/trade.js +313 -0
- package/dist/tools/trade.js.map +1 -0
- package/dist/tools/trader.d.ts +11 -0
- package/dist/tools/trader.d.ts.map +1 -0
- package/dist/tools/trader.js +277 -0
- package/dist/tools/trader.js.map +1 -0
- package/dist/tools/wallet.d.ts +274 -0
- package/dist/tools/wallet.d.ts.map +1 -0
- package/dist/tools/wallet.js +579 -0
- package/dist/tools/wallet.js.map +1 -0
- package/dist/types.d.ts +413 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/docs/01-mcp.md +2075 -0
- package/package.json +55 -0
- package/src/errors.ts +124 -0
- package/src/index.ts +309 -0
- package/src/server.ts +183 -0
- package/src/tools/guide.ts +821 -0
- package/src/tools/index.ts +73 -0
- package/src/tools/market.ts +363 -0
- package/src/tools/order.ts +326 -0
- package/src/tools/trade.ts +417 -0
- package/src/tools/trader.ts +322 -0
- package/src/tools/wallet.ts +683 -0
- package/src/types.ts +472 -0
- package/tsconfig.json +20 -0
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@catalyst-team/poly-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "MCP (Model Context Protocol) server for Polymarket - enables AI agents to interact with prediction markets",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"poly-mcp": "./dist/server.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./server": {
|
|
18
|
+
"types": "./dist/server.d.ts",
|
|
19
|
+
"import": "./dist/server.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"polymarket",
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"ai",
|
|
27
|
+
"prediction-markets",
|
|
28
|
+
"claude",
|
|
29
|
+
"anthropic"
|
|
30
|
+
],
|
|
31
|
+
"author": "Catalyst Team",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
38
|
+
"ethers": "^5.7.2",
|
|
39
|
+
"@catalyst-team/poly-sdk": "0.1.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"tsx": "^4.7.0",
|
|
43
|
+
"typescript": "^5.3.3"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"ethers": "^5.7.2"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc",
|
|
50
|
+
"dev": "tsc --watch",
|
|
51
|
+
"start": "node dist/server.js",
|
|
52
|
+
"mcp": "node dist/server.js",
|
|
53
|
+
"mcp:dev": "tsx src/server.ts"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Error Handling for Polymarket Tools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export enum ErrorCode {
|
|
6
|
+
INVALID_ADDRESS = 'INVALID_ADDRESS',
|
|
7
|
+
MARKET_NOT_FOUND = 'MARKET_NOT_FOUND',
|
|
8
|
+
INSUFFICIENT_BALANCE = 'INSUFFICIENT_BALANCE',
|
|
9
|
+
ORDER_REJECTED = 'ORDER_REJECTED',
|
|
10
|
+
RATE_LIMITED = 'RATE_LIMITED',
|
|
11
|
+
AUTH_REQUIRED = 'AUTH_REQUIRED',
|
|
12
|
+
INVALID_INPUT = 'INVALID_INPUT',
|
|
13
|
+
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class McpToolError extends Error {
|
|
17
|
+
constructor(
|
|
18
|
+
public code: ErrorCode,
|
|
19
|
+
message: string,
|
|
20
|
+
public details?: Record<string, unknown>
|
|
21
|
+
) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'McpToolError';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
toResponse() {
|
|
27
|
+
return {
|
|
28
|
+
error: {
|
|
29
|
+
code: this.code,
|
|
30
|
+
message: this.message,
|
|
31
|
+
details: this.details,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function validateAddress(address: string): void {
|
|
38
|
+
if (!address || typeof address !== 'string') {
|
|
39
|
+
throw new McpToolError(
|
|
40
|
+
ErrorCode.INVALID_ADDRESS,
|
|
41
|
+
'Address is required'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (!address.startsWith('0x') || address.length !== 42) {
|
|
45
|
+
throw new McpToolError(
|
|
46
|
+
ErrorCode.INVALID_ADDRESS,
|
|
47
|
+
'Address must be a valid Ethereum address (0x...)',
|
|
48
|
+
{ address }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function validateConditionId(conditionId: string): void {
|
|
54
|
+
if (!conditionId || typeof conditionId !== 'string') {
|
|
55
|
+
throw new McpToolError(
|
|
56
|
+
ErrorCode.INVALID_INPUT,
|
|
57
|
+
'Condition ID is required'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (!conditionId.startsWith('0x')) {
|
|
61
|
+
throw new McpToolError(
|
|
62
|
+
ErrorCode.INVALID_INPUT,
|
|
63
|
+
'Condition ID must start with 0x',
|
|
64
|
+
{ conditionId }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function validateOutcome(outcome: string): void {
|
|
70
|
+
if (!['Yes', 'No'].includes(outcome)) {
|
|
71
|
+
throw new McpToolError(
|
|
72
|
+
ErrorCode.INVALID_INPUT,
|
|
73
|
+
'Outcome must be "Yes" or "No"',
|
|
74
|
+
{ outcome }
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function validateSide(side: string): void {
|
|
80
|
+
if (!['BUY', 'SELL'].includes(side)) {
|
|
81
|
+
throw new McpToolError(
|
|
82
|
+
ErrorCode.INVALID_INPUT,
|
|
83
|
+
'Side must be "BUY" or "SELL"',
|
|
84
|
+
{ side }
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function validatePrice(price: number): void {
|
|
90
|
+
if (typeof price !== 'number' || price < 0.001 || price > 0.999) {
|
|
91
|
+
throw new McpToolError(
|
|
92
|
+
ErrorCode.INVALID_INPUT,
|
|
93
|
+
'Price must be between 0.001 and 0.999',
|
|
94
|
+
{ price }
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function validatePositiveNumber(value: number, name: string): void {
|
|
100
|
+
if (typeof value !== 'number' || value <= 0) {
|
|
101
|
+
throw new McpToolError(
|
|
102
|
+
ErrorCode.INVALID_INPUT,
|
|
103
|
+
`${name} must be a positive number`,
|
|
104
|
+
{ [name]: value }
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function wrapError(err: unknown): McpToolError {
|
|
110
|
+
if (err instanceof McpToolError) {
|
|
111
|
+
return err;
|
|
112
|
+
}
|
|
113
|
+
if (err instanceof Error) {
|
|
114
|
+
// Check for known error types
|
|
115
|
+
if (err.message.includes('rate limit')) {
|
|
116
|
+
return new McpToolError(ErrorCode.RATE_LIMITED, 'Rate limit exceeded');
|
|
117
|
+
}
|
|
118
|
+
if (err.message.includes('not found')) {
|
|
119
|
+
return new McpToolError(ErrorCode.MARKET_NOT_FOUND, err.message);
|
|
120
|
+
}
|
|
121
|
+
return new McpToolError(ErrorCode.INTERNAL_ERROR, err.message);
|
|
122
|
+
}
|
|
123
|
+
return new McpToolError(ErrorCode.INTERNAL_ERROR, 'An unexpected error occurred');
|
|
124
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polymarket MCP Server
|
|
3
|
+
*
|
|
4
|
+
* This package provides MCP (Model Context Protocol) tools for interacting with Polymarket.
|
|
5
|
+
* It can be used to build an MCP server that exposes Polymarket functionality to AI agents.
|
|
6
|
+
*
|
|
7
|
+
* Tool Categories:
|
|
8
|
+
* - Trader Tools: Analyze traders/wallets (positions, trades, profiles, leaderboard)
|
|
9
|
+
* - Market Tools: Discover and analyze markets (get, search, trending, trades)
|
|
10
|
+
* - Order Tools: Analyze orderbooks (depth, best prices, execution estimates)
|
|
11
|
+
* - Trade Tools: Execute trades (limit orders, market orders) - requires API credentials
|
|
12
|
+
* - Wallet Tools: Deposits and authorization management
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { PolymarketSDK } from '@catalyst-team/poly-sdk';
|
|
17
|
+
* import { createMcpHandler, allToolDefinitions } from '@catalyst-team/poly-mcp';
|
|
18
|
+
*
|
|
19
|
+
* const sdk = new PolymarketSDK();
|
|
20
|
+
* const handler = createMcpHandler(sdk);
|
|
21
|
+
*
|
|
22
|
+
* // Use with MCP server
|
|
23
|
+
* const result = await handler('get_trader_positions', { address: '0x...' });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { PolymarketSDK } from '@catalyst-team/poly-sdk';
|
|
28
|
+
import { McpToolError, wrapError } from './errors.js';
|
|
29
|
+
import {
|
|
30
|
+
allToolDefinitions,
|
|
31
|
+
// Guide handlers
|
|
32
|
+
createGuideHandlers,
|
|
33
|
+
// Trader handlers
|
|
34
|
+
handleGetTraderPositions,
|
|
35
|
+
handleGetTraderTrades,
|
|
36
|
+
handleGetTraderProfile,
|
|
37
|
+
handleGetLeaderboard,
|
|
38
|
+
// Market handlers
|
|
39
|
+
handleGetMarket,
|
|
40
|
+
handleSearchMarkets,
|
|
41
|
+
handleGetTrendingMarkets,
|
|
42
|
+
handleGetMarketTrades,
|
|
43
|
+
// Order handlers
|
|
44
|
+
handleGetOrderbook,
|
|
45
|
+
handleGetBestPrices,
|
|
46
|
+
handleEstimateExecution,
|
|
47
|
+
// Trade handlers
|
|
48
|
+
handlePlaceLimitOrder,
|
|
49
|
+
handlePlaceMarketOrder,
|
|
50
|
+
handleCancelOrder,
|
|
51
|
+
handleGetMyOrders,
|
|
52
|
+
// Wallet handlers
|
|
53
|
+
handleGetSupportedAssets,
|
|
54
|
+
handleGetDepositAddresses,
|
|
55
|
+
handleDepositUsdc,
|
|
56
|
+
handleCheckAllowances,
|
|
57
|
+
handleApproveTrading,
|
|
58
|
+
handleSwap,
|
|
59
|
+
handleSwapAndDeposit,
|
|
60
|
+
handleGetTokenBalances,
|
|
61
|
+
handleGetWalletBalances,
|
|
62
|
+
handleGetSwapQuote,
|
|
63
|
+
handleGetAvailablePools,
|
|
64
|
+
} from './tools/index.js';
|
|
65
|
+
|
|
66
|
+
// Re-export types
|
|
67
|
+
export * from './types.js';
|
|
68
|
+
export * from './errors.js';
|
|
69
|
+
export { allToolDefinitions } from './tools/index.js';
|
|
70
|
+
|
|
71
|
+
// Tool handler type
|
|
72
|
+
export type ToolHandler = (
|
|
73
|
+
toolName: string,
|
|
74
|
+
args: Record<string, unknown>
|
|
75
|
+
) => Promise<unknown>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Create an MCP tool handler for the given SDK instance
|
|
79
|
+
*/
|
|
80
|
+
export function createMcpHandler(sdk: PolymarketSDK): ToolHandler {
|
|
81
|
+
// Create guide handlers (no SDK dependency)
|
|
82
|
+
const guideHandlers = createGuideHandlers();
|
|
83
|
+
|
|
84
|
+
return async (toolName: string, args: Record<string, unknown>): Promise<unknown> => {
|
|
85
|
+
try {
|
|
86
|
+
switch (toolName) {
|
|
87
|
+
// Guide Tool - should be called FIRST
|
|
88
|
+
case 'get_usage_guide':
|
|
89
|
+
return await guideHandlers.get_usage_guide(args);
|
|
90
|
+
|
|
91
|
+
// Trader Tools
|
|
92
|
+
case 'get_trader_positions':
|
|
93
|
+
return await handleGetTraderPositions(sdk, args as { address: string });
|
|
94
|
+
|
|
95
|
+
case 'get_trader_trades':
|
|
96
|
+
return await handleGetTraderTrades(sdk, args as {
|
|
97
|
+
address: string;
|
|
98
|
+
limit?: number;
|
|
99
|
+
side?: 'BUY' | 'SELL';
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
case 'get_trader_profile':
|
|
103
|
+
return await handleGetTraderProfile(sdk, args as { address: string });
|
|
104
|
+
|
|
105
|
+
case 'get_leaderboard':
|
|
106
|
+
return await handleGetLeaderboard(sdk, args as {
|
|
107
|
+
limit?: number;
|
|
108
|
+
offset?: number;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Market Tools
|
|
112
|
+
case 'get_market':
|
|
113
|
+
return await handleGetMarket(sdk, args as { identifier: string });
|
|
114
|
+
|
|
115
|
+
case 'search_markets':
|
|
116
|
+
return await handleSearchMarkets(sdk, args as {
|
|
117
|
+
query: string;
|
|
118
|
+
active?: boolean;
|
|
119
|
+
limit?: number;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
case 'get_trending_markets':
|
|
123
|
+
return await handleGetTrendingMarkets(sdk, args as {
|
|
124
|
+
limit?: number;
|
|
125
|
+
sortBy?: 'volume' | 'liquidity' | 'newest';
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
case 'get_market_trades':
|
|
129
|
+
return await handleGetMarketTrades(sdk, args as {
|
|
130
|
+
conditionId: string;
|
|
131
|
+
limit?: number;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Order Tools
|
|
135
|
+
case 'get_orderbook':
|
|
136
|
+
return await handleGetOrderbook(sdk, args as {
|
|
137
|
+
conditionId: string;
|
|
138
|
+
outcome: 'Yes' | 'No';
|
|
139
|
+
depth?: number;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
case 'get_best_prices':
|
|
143
|
+
return await handleGetBestPrices(sdk, args as {
|
|
144
|
+
conditionId: string;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
case 'estimate_execution':
|
|
148
|
+
return await handleEstimateExecution(sdk, args as {
|
|
149
|
+
conditionId: string;
|
|
150
|
+
outcome: 'Yes' | 'No';
|
|
151
|
+
side: 'BUY' | 'SELL';
|
|
152
|
+
amount: number;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Trade Tools
|
|
156
|
+
case 'place_limit_order':
|
|
157
|
+
return await handlePlaceLimitOrder(sdk, args as {
|
|
158
|
+
conditionId: string;
|
|
159
|
+
outcome: 'Yes' | 'No';
|
|
160
|
+
side: 'BUY' | 'SELL';
|
|
161
|
+
price: number;
|
|
162
|
+
size: number;
|
|
163
|
+
orderType?: 'GTC' | 'GTD';
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
case 'place_market_order':
|
|
167
|
+
return await handlePlaceMarketOrder(sdk, args as {
|
|
168
|
+
conditionId: string;
|
|
169
|
+
outcome: 'Yes' | 'No';
|
|
170
|
+
side: 'BUY' | 'SELL';
|
|
171
|
+
amount: number;
|
|
172
|
+
maxSlippage?: number;
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
case 'cancel_order':
|
|
176
|
+
return await handleCancelOrder(sdk, args as { orderId: string });
|
|
177
|
+
|
|
178
|
+
case 'get_my_orders':
|
|
179
|
+
return await handleGetMyOrders(sdk, args as {
|
|
180
|
+
status?: 'LIVE' | 'FILLED' | 'CANCELLED';
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Wallet Tools
|
|
184
|
+
case 'get_supported_deposit_assets':
|
|
185
|
+
return await handleGetSupportedAssets(sdk, args as {
|
|
186
|
+
chainId?: number;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
case 'get_deposit_addresses':
|
|
190
|
+
return await handleGetDepositAddresses(sdk);
|
|
191
|
+
|
|
192
|
+
case 'deposit_usdc':
|
|
193
|
+
return await handleDepositUsdc(sdk, args as {
|
|
194
|
+
amount: number;
|
|
195
|
+
token?: 'NATIVE_USDC' | 'USDC_E';
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
case 'check_allowances':
|
|
199
|
+
return await handleCheckAllowances(sdk);
|
|
200
|
+
|
|
201
|
+
case 'approve_trading':
|
|
202
|
+
return await handleApproveTrading(sdk);
|
|
203
|
+
|
|
204
|
+
case 'swap':
|
|
205
|
+
return await handleSwap(sdk, args as {
|
|
206
|
+
tokenIn: string;
|
|
207
|
+
tokenOut: string;
|
|
208
|
+
amount: string;
|
|
209
|
+
slippage?: number;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
case 'swap_and_deposit':
|
|
213
|
+
return await handleSwapAndDeposit(sdk, args as {
|
|
214
|
+
token: string;
|
|
215
|
+
amount: string;
|
|
216
|
+
slippage?: number;
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
case 'get_token_balances':
|
|
220
|
+
return await handleGetTokenBalances(sdk);
|
|
221
|
+
|
|
222
|
+
case 'get_wallet_balances':
|
|
223
|
+
return await handleGetWalletBalances(sdk, args as { address: string });
|
|
224
|
+
|
|
225
|
+
case 'get_swap_quote':
|
|
226
|
+
return await handleGetSwapQuote(sdk, args as {
|
|
227
|
+
tokenIn: string;
|
|
228
|
+
tokenOut: string;
|
|
229
|
+
amount: string;
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
case 'get_available_pools':
|
|
233
|
+
return await handleGetAvailablePools(sdk);
|
|
234
|
+
|
|
235
|
+
default:
|
|
236
|
+
throw new McpToolError(
|
|
237
|
+
'INVALID_INPUT' as unknown as import('./errors.js').ErrorCode,
|
|
238
|
+
`Unknown tool: ${toolName}`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
if (err instanceof McpToolError) {
|
|
243
|
+
return err.toResponse();
|
|
244
|
+
}
|
|
245
|
+
return wrapError(err).toResponse();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get all tool definitions for MCP registration
|
|
252
|
+
*/
|
|
253
|
+
export function getToolDefinitions() {
|
|
254
|
+
return allToolDefinitions;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* MCP Server factory (for use with @modelcontextprotocol/sdk)
|
|
259
|
+
*
|
|
260
|
+
* Example usage with MCP SDK:
|
|
261
|
+
* ```typescript
|
|
262
|
+
* import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
263
|
+
* import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
264
|
+
* import { PolymarketSDK } from '@catalyst-team/poly-sdk';
|
|
265
|
+
* import { registerMcpTools } from '@catalyst-team/poly-mcp';
|
|
266
|
+
*
|
|
267
|
+
* const server = new Server(
|
|
268
|
+
* { name: 'poly-mcp', version: '1.0.0' },
|
|
269
|
+
* { capabilities: { tools: {} } }
|
|
270
|
+
* );
|
|
271
|
+
*
|
|
272
|
+
* const sdk = new PolymarketSDK();
|
|
273
|
+
* registerMcpTools(server, sdk);
|
|
274
|
+
*
|
|
275
|
+
* const transport = new StdioServerTransport();
|
|
276
|
+
* await server.connect(transport);
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
export function registerMcpTools(
|
|
280
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
281
|
+
server: any,
|
|
282
|
+
sdk: PolymarketSDK
|
|
283
|
+
): void {
|
|
284
|
+
const handler = createMcpHandler(sdk);
|
|
285
|
+
|
|
286
|
+
// Register list tools handler
|
|
287
|
+
server.setRequestHandler(
|
|
288
|
+
{ method: 'tools/list' },
|
|
289
|
+
async () => ({ tools: allToolDefinitions })
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Register call tool handler
|
|
293
|
+
server.setRequestHandler(
|
|
294
|
+
{ method: 'tools/call' },
|
|
295
|
+
async (request: { params: { name: string; arguments?: Record<string, unknown> } }) => {
|
|
296
|
+
const { name, arguments: args = {} } = request.params;
|
|
297
|
+
const result = await handler(name, args);
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
content: [
|
|
301
|
+
{
|
|
302
|
+
type: 'text',
|
|
303
|
+
text: JSON.stringify(result, null, 2),
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
}
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Polymarket MCP Server
|
|
4
|
+
*
|
|
5
|
+
* A standalone MCP server that exposes Polymarket functionality to AI agents.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx @catalyst-team/poly-mcp
|
|
9
|
+
* # or
|
|
10
|
+
* node dist/server.js
|
|
11
|
+
*
|
|
12
|
+
* Configuration via environment variables:
|
|
13
|
+
* - POLY_PRIVATE_KEY: Wallet private key (required for trading, API credentials are derived automatically)
|
|
14
|
+
* - POLY_CHAIN_ID: Chain ID (137 for mainnet, 80002 for testnet, default: 137)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
18
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
|
+
import {
|
|
20
|
+
CallToolRequestSchema,
|
|
21
|
+
ListToolsRequestSchema,
|
|
22
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
23
|
+
import { Wallet } from 'ethers';
|
|
24
|
+
|
|
25
|
+
import { PolymarketSDK } from '@catalyst-team/poly-sdk';
|
|
26
|
+
import { allToolDefinitions } from './tools/index.js';
|
|
27
|
+
import { createMcpHandler } from './index.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a wallet signer from private key if available
|
|
31
|
+
*/
|
|
32
|
+
function createSigner(): Wallet | undefined {
|
|
33
|
+
const privateKey = process.env.POLY_PRIVATE_KEY;
|
|
34
|
+
if (!privateKey) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Ensure private key has 0x prefix
|
|
40
|
+
const formattedKey = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`;
|
|
41
|
+
return new Wallet(formattedKey);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('Failed to create wallet from POLY_PRIVATE_KEY:', error);
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Server metadata
|
|
49
|
+
const SERVER_NAME = 'polymarket-mcp';
|
|
50
|
+
const SERVER_VERSION = '0.1.0';
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Create and configure the MCP server
|
|
54
|
+
*/
|
|
55
|
+
function createServer(): Server {
|
|
56
|
+
const server = new Server(
|
|
57
|
+
{
|
|
58
|
+
name: SERVER_NAME,
|
|
59
|
+
version: SERVER_VERSION,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
capabilities: {
|
|
63
|
+
tools: {},
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Create signer from private key if available
|
|
69
|
+
const signer = createSigner();
|
|
70
|
+
|
|
71
|
+
// Initialize SDK with environment configuration
|
|
72
|
+
// API credentials are derived automatically from private key via createOrDeriveApiKey()
|
|
73
|
+
const sdk = new PolymarketSDK({
|
|
74
|
+
chainId: process.env.POLY_CHAIN_ID ? parseInt(process.env.POLY_CHAIN_ID) : 137,
|
|
75
|
+
signer,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Log trading status
|
|
79
|
+
if (signer) {
|
|
80
|
+
console.error(`Trading enabled for wallet: ${signer.address}`);
|
|
81
|
+
} else {
|
|
82
|
+
console.error('Trading disabled (set POLY_PRIVATE_KEY to enable)');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create the tool handler
|
|
86
|
+
const handler = createMcpHandler(sdk);
|
|
87
|
+
|
|
88
|
+
// Register list tools handler
|
|
89
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
90
|
+
return {
|
|
91
|
+
tools: allToolDefinitions.map((tool) => ({
|
|
92
|
+
name: tool.name,
|
|
93
|
+
description: tool.description,
|
|
94
|
+
inputSchema: tool.inputSchema,
|
|
95
|
+
})),
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Register call tool handler
|
|
100
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
101
|
+
const { name, arguments: args = {} } = request.params;
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const result = await handler(name, args as Record<string, unknown>);
|
|
105
|
+
|
|
106
|
+
// Check if result is an error response
|
|
107
|
+
if (result && typeof result === 'object' && 'error' in result) {
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: 'text',
|
|
112
|
+
text: JSON.stringify(result, null, 2),
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
isError: true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: 'text',
|
|
123
|
+
text: JSON.stringify(result, null, 2),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
129
|
+
return {
|
|
130
|
+
content: [
|
|
131
|
+
{
|
|
132
|
+
type: 'text',
|
|
133
|
+
text: JSON.stringify(
|
|
134
|
+
{
|
|
135
|
+
error: {
|
|
136
|
+
code: 'INTERNAL_ERROR',
|
|
137
|
+
message: errorMessage,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
null,
|
|
141
|
+
2
|
|
142
|
+
),
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
isError: true,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return server;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Main entry point
|
|
155
|
+
*/
|
|
156
|
+
async function main(): Promise<void> {
|
|
157
|
+
const server = createServer();
|
|
158
|
+
const transport = new StdioServerTransport();
|
|
159
|
+
|
|
160
|
+
// Handle shutdown gracefully
|
|
161
|
+
process.on('SIGINT', async () => {
|
|
162
|
+
await server.close();
|
|
163
|
+
process.exit(0);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
process.on('SIGTERM', async () => {
|
|
167
|
+
await server.close();
|
|
168
|
+
process.exit(0);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Connect and run
|
|
172
|
+
await server.connect(transport);
|
|
173
|
+
|
|
174
|
+
// Log to stderr (stdout is reserved for MCP protocol)
|
|
175
|
+
console.error(`${SERVER_NAME} v${SERVER_VERSION} started`);
|
|
176
|
+
console.error(`Available tools: ${allToolDefinitions.length}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Run the server
|
|
180
|
+
main().catch((error) => {
|
|
181
|
+
console.error('Failed to start MCP server:', error);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
});
|