@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.
Files changed (59) hide show
  1. package/README.md +317 -0
  2. package/dist/errors.d.ts +33 -0
  3. package/dist/errors.d.ts.map +1 -0
  4. package/dist/errors.js +86 -0
  5. package/dist/errors.js.map +1 -0
  6. package/dist/index.d.ts +62 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +173 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/server.d.ts +17 -0
  11. package/dist/server.d.ts.map +1 -0
  12. package/dist/server.js +155 -0
  13. package/dist/server.js.map +1 -0
  14. package/dist/tools/guide.d.ts +12 -0
  15. package/dist/tools/guide.d.ts.map +1 -0
  16. package/dist/tools/guide.js +801 -0
  17. package/dist/tools/guide.js.map +1 -0
  18. package/dist/tools/index.d.ts +11 -0
  19. package/dist/tools/index.d.ts.map +1 -0
  20. package/dist/tools/index.js +27 -0
  21. package/dist/tools/index.js.map +1 -0
  22. package/dist/tools/market.d.ts +11 -0
  23. package/dist/tools/market.d.ts.map +1 -0
  24. package/dist/tools/market.js +314 -0
  25. package/dist/tools/market.js.map +1 -0
  26. package/dist/tools/order.d.ts +10 -0
  27. package/dist/tools/order.d.ts.map +1 -0
  28. package/dist/tools/order.js +258 -0
  29. package/dist/tools/order.js.map +1 -0
  30. package/dist/tools/trade.d.ts +38 -0
  31. package/dist/tools/trade.d.ts.map +1 -0
  32. package/dist/tools/trade.js +313 -0
  33. package/dist/tools/trade.js.map +1 -0
  34. package/dist/tools/trader.d.ts +11 -0
  35. package/dist/tools/trader.d.ts.map +1 -0
  36. package/dist/tools/trader.js +277 -0
  37. package/dist/tools/trader.js.map +1 -0
  38. package/dist/tools/wallet.d.ts +274 -0
  39. package/dist/tools/wallet.d.ts.map +1 -0
  40. package/dist/tools/wallet.js +579 -0
  41. package/dist/tools/wallet.js.map +1 -0
  42. package/dist/types.d.ts +413 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +5 -0
  45. package/dist/types.js.map +1 -0
  46. package/docs/01-mcp.md +2075 -0
  47. package/package.json +55 -0
  48. package/src/errors.ts +124 -0
  49. package/src/index.ts +309 -0
  50. package/src/server.ts +183 -0
  51. package/src/tools/guide.ts +821 -0
  52. package/src/tools/index.ts +73 -0
  53. package/src/tools/market.ts +363 -0
  54. package/src/tools/order.ts +326 -0
  55. package/src/tools/trade.ts +417 -0
  56. package/src/tools/trader.ts +322 -0
  57. package/src/tools/wallet.ts +683 -0
  58. package/src/types.ts +472 -0
  59. 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
+ });