telegram-wallet-p2p-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 ADDED
@@ -0,0 +1,92 @@
1
+ # Telegram Wallet P2P MCP Server
2
+
3
+ > ⚠️ **Unofficial** — This project is not affiliated with, endorsed by, or related to Telegram or Wallet.
4
+
5
+ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server providing read-only tools for Telegram Wallet P2P market data analytics.
6
+
7
+ ## Tools
8
+
9
+ | Tool | Description |
10
+ |---|---|
11
+ | `get_p2p_ads` | Fetch active P2P ads filtered by crypto/fiat/side |
12
+ | `get_market_summary` | Aggregated analytics: price stats, payment methods, merchant distribution |
13
+ | `get_best_price` | Find the best available buy/sell price |
14
+
15
+ All tools return structured JSON suitable for AI consumption.
16
+
17
+ ## Setup
18
+
19
+ ### Prerequisites
20
+
21
+ - Node.js 18+
22
+ - A Wallet P2P API key ([how to get one](https://help.wallet.tg/article/934-p2p-api))
23
+
24
+ ### Installation
25
+
26
+ ```bash
27
+ npm install -g telegram-wallet-p2p-mcp
28
+ ```
29
+
30
+ Or build from source:
31
+
32
+ ```bash
33
+ cd packages/mcp
34
+ npm install
35
+ npm run build
36
+ ```
37
+
38
+ ### Configuration
39
+
40
+ Set your API key as an environment variable:
41
+
42
+ ```bash
43
+ export WALLET_P2P_API_KEY=your-api-key
44
+ ```
45
+
46
+ ### Usage with Claude Desktop
47
+
48
+ Add to your Claude Desktop configuration (`claude_desktop_config.json`):
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "telegram-wallet-p2p": {
54
+ "command": "npx",
55
+ "args": ["telegram-wallet-p2p-mcp"],
56
+ "env": {
57
+ "WALLET_P2P_API_KEY": "your-api-key"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### Usage with VS Code
65
+
66
+ Add to your VS Code settings (`.vscode/mcp.json`):
67
+
68
+ ```json
69
+ {
70
+ "servers": {
71
+ "telegram-wallet-p2p": {
72
+ "command": "npx",
73
+ "args": ["telegram-wallet-p2p-mcp"],
74
+ "env": {
75
+ "WALLET_P2P_API_KEY": "your-api-key"
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ## Example Prompts
83
+
84
+ Once connected, you can ask your AI assistant:
85
+
86
+ - *"What's the current best price for selling USDT in RUB?"*
87
+ - *"Show me a market summary for BTC/USD buy ads"*
88
+ - *"List all USDT/RUB sell ads that accept Tinkoff"*
89
+
90
+ ## License
91
+
92
+ MIT
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Telegram Wallet P2P MCP Server (Unofficial)
4
+ *
5
+ * An MCP server providing read-only tools for Telegram Wallet P2P market data.
6
+ * This is an UNOFFICIAL project — not affiliated with Telegram or Wallet.
7
+ *
8
+ * Tools:
9
+ * - get_p2p_ads: Fetch filtered P2P ads from the market
10
+ * - get_market_summary: Get aggregated market analytics
11
+ * - get_best_price: Find the best buy/sell price
12
+ *
13
+ * Configuration:
14
+ * - WALLET_P2P_API_KEY: Required environment variable for API authentication
15
+ */
16
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Telegram Wallet P2P MCP Server (Unofficial)
4
+ *
5
+ * An MCP server providing read-only tools for Telegram Wallet P2P market data.
6
+ * This is an UNOFFICIAL project — not affiliated with Telegram or Wallet.
7
+ *
8
+ * Tools:
9
+ * - get_p2p_ads: Fetch filtered P2P ads from the market
10
+ * - get_market_summary: Get aggregated market analytics
11
+ * - get_best_price: Find the best buy/sell price
12
+ *
13
+ * Configuration:
14
+ * - WALLET_P2P_API_KEY: Required environment variable for API authentication
15
+ */
16
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
+ import { z } from "zod";
19
+ const BASE_URL = "https://p2p.walletbot.me";
20
+ const ONLINE_ITEMS_PATH = "/p2p/integration-api/v1/item/online";
21
+ // --- API Client ---
22
+ async function fetchAds(apiKey, params) {
23
+ const url = `${BASE_URL}${ONLINE_ITEMS_PATH}`;
24
+ const response = await fetch(url, {
25
+ method: "POST",
26
+ headers: {
27
+ "X-API-Key": apiKey,
28
+ "Content-Type": "application/json",
29
+ Accept: "application/json",
30
+ },
31
+ body: JSON.stringify({
32
+ cryptoCurrency: params.cryptoCurrency,
33
+ fiatCurrency: params.fiatCurrency,
34
+ side: params.side,
35
+ page: params.page ?? 1,
36
+ pageSize: params.pageSize ?? 50,
37
+ }),
38
+ });
39
+ if (!response.ok) {
40
+ let errorMsg = `API error: ${response.status}`;
41
+ try {
42
+ const errorBody = (await response.json());
43
+ errorMsg = `API error ${response.status}: [${errorBody.errorCode}] ${errorBody.errorMessage}`;
44
+ }
45
+ catch {
46
+ // ignore parse errors
47
+ }
48
+ throw new Error(errorMsg);
49
+ }
50
+ const data = (await response.json());
51
+ return data.data;
52
+ }
53
+ // --- Analytics helpers ---
54
+ function calcPriceSpread(items) {
55
+ if (items.length === 0)
56
+ return null;
57
+ const prices = items.map((i) => parseFloat(i.price)).sort((a, b) => a - b);
58
+ const min = prices[0];
59
+ const max = prices[prices.length - 1];
60
+ const avg = prices.reduce((s, p) => s + p, 0) / prices.length;
61
+ const mid = Math.floor(prices.length / 2);
62
+ const median = prices.length % 2 !== 0
63
+ ? prices[mid]
64
+ : (prices[mid - 1] + prices[mid]) / 2;
65
+ return {
66
+ min: min.toFixed(4),
67
+ max: max.toFixed(4),
68
+ avg: avg.toFixed(4),
69
+ median: median.toFixed(4),
70
+ spread: (max - min).toFixed(4),
71
+ count: prices.length,
72
+ };
73
+ }
74
+ // --- Server setup ---
75
+ function getApiKey() {
76
+ const key = process.env.WALLET_P2P_API_KEY;
77
+ if (!key) {
78
+ console.error("Error: WALLET_P2P_API_KEY environment variable is required.");
79
+ process.exit(1);
80
+ }
81
+ return key;
82
+ }
83
+ const apiKey = getApiKey();
84
+ const server = new McpServer({
85
+ name: "telegram-wallet-p2p",
86
+ version: "0.1.0",
87
+ });
88
+ // --- Tool: get_p2p_ads ---
89
+ server.tool("get_p2p_ads", "Fetch active P2P market ads filtered by cryptocurrency, fiat currency, and trade side. Returns detailed ad data including price, quantity, payment methods, and trader info. This is an UNOFFICIAL tool — not affiliated with Telegram or Wallet.", {
90
+ cryptoCurrency: z
91
+ .string()
92
+ .describe('Cryptocurrency code, e.g. "USDT", "BTC", "TON"'),
93
+ fiatCurrency: z
94
+ .string()
95
+ .describe('Fiat currency code, e.g. "RUB", "USD", "EUR"'),
96
+ side: z.enum(["BUY", "SELL"]).describe("Trade side from the ad maker perspective"),
97
+ page: z.number().int().min(1).optional().describe("Page number (default: 1)"),
98
+ pageSize: z
99
+ .number()
100
+ .int()
101
+ .min(1)
102
+ .max(50)
103
+ .optional()
104
+ .describe("Items per page (default: 50, max: 50)"),
105
+ }, async ({ cryptoCurrency, fiatCurrency, side, page, pageSize }) => {
106
+ try {
107
+ const items = await fetchAds(apiKey, {
108
+ cryptoCurrency,
109
+ fiatCurrency,
110
+ side,
111
+ page,
112
+ pageSize,
113
+ });
114
+ return {
115
+ content: [
116
+ {
117
+ type: "text",
118
+ text: JSON.stringify({
119
+ totalResults: items.length,
120
+ cryptoCurrency,
121
+ fiatCurrency,
122
+ side,
123
+ ads: items.map((item) => ({
124
+ id: item.id,
125
+ nickname: item.nickname,
126
+ price: item.price,
127
+ availableQuantity: item.lastQuantity,
128
+ minAmount: item.minAmount,
129
+ maxAmount: item.maxAmount,
130
+ payments: item.payments,
131
+ completedOrders: item.orderNum,
132
+ completionRate: item.executeRate,
133
+ isOnline: item.isOnline,
134
+ merchantLevel: item.merchantLevel,
135
+ paymentPeriod: item.paymentPeriod,
136
+ autoAccept: item.isAutoAccept,
137
+ })),
138
+ }, null, 2),
139
+ },
140
+ ],
141
+ };
142
+ }
143
+ catch (error) {
144
+ return {
145
+ content: [
146
+ {
147
+ type: "text",
148
+ text: `Error fetching ads: ${error instanceof Error ? error.message : String(error)}`,
149
+ },
150
+ ],
151
+ isError: true,
152
+ };
153
+ }
154
+ });
155
+ // --- Tool: get_market_summary ---
156
+ server.tool("get_market_summary", "Get aggregated market analytics for a cryptocurrency/fiat pair including price statistics, payment method distribution, merchant levels, and trader metrics. This is an UNOFFICIAL tool.", {
157
+ cryptoCurrency: z
158
+ .string()
159
+ .describe('Cryptocurrency code, e.g. "USDT"'),
160
+ fiatCurrency: z
161
+ .string()
162
+ .describe('Fiat currency code, e.g. "RUB"'),
163
+ side: z.enum(["BUY", "SELL"]).describe("Trade side to analyze"),
164
+ }, async ({ cryptoCurrency, fiatCurrency, side }) => {
165
+ try {
166
+ const items = await fetchAds(apiKey, {
167
+ cryptoCurrency,
168
+ fiatCurrency,
169
+ side,
170
+ pageSize: 50,
171
+ });
172
+ const priceStats = calcPriceSpread(items);
173
+ const paymentMethods = {};
174
+ for (const item of items) {
175
+ for (const method of item.payments) {
176
+ paymentMethods[method] = (paymentMethods[method] ?? 0) + 1;
177
+ }
178
+ }
179
+ const merchantDist = {};
180
+ for (const item of items) {
181
+ merchantDist[item.merchantLevel] =
182
+ (merchantDist[item.merchantLevel] ?? 0) + 1;
183
+ }
184
+ const avgRate = items.length > 0
185
+ ? items.reduce((s, i) => s + parseFloat(i.executeRate), 0) /
186
+ items.length
187
+ : 0;
188
+ const summary = {
189
+ cryptoCurrency,
190
+ fiatCurrency,
191
+ side,
192
+ totalAds: items.length,
193
+ priceStats,
194
+ onlineTraders: items.filter((i) => i.isOnline).length,
195
+ autoAcceptAds: items.filter((i) => i.isAutoAccept).length,
196
+ paymentMethods,
197
+ merchantDistribution: merchantDist,
198
+ avgCompletionRate: avgRate.toFixed(4),
199
+ };
200
+ return {
201
+ content: [
202
+ {
203
+ type: "text",
204
+ text: JSON.stringify(summary, null, 2),
205
+ },
206
+ ],
207
+ };
208
+ }
209
+ catch (error) {
210
+ return {
211
+ content: [
212
+ {
213
+ type: "text",
214
+ text: `Error generating summary: ${error instanceof Error ? error.message : String(error)}`,
215
+ },
216
+ ],
217
+ isError: true,
218
+ };
219
+ }
220
+ });
221
+ // --- Tool: get_best_price ---
222
+ server.tool("get_best_price", "Find the best available price for buying or selling crypto on the P2P market. Returns the top ad with the most favorable price. This is an UNOFFICIAL tool.", {
223
+ cryptoCurrency: z
224
+ .string()
225
+ .describe('Cryptocurrency code, e.g. "USDT"'),
226
+ fiatCurrency: z
227
+ .string()
228
+ .describe('Fiat currency code, e.g. "RUB"'),
229
+ side: z
230
+ .enum(["BUY", "SELL"])
231
+ .describe("Trade side: BUY = find cheapest to buy, SELL = find highest to sell"),
232
+ }, async ({ cryptoCurrency, fiatCurrency, side }) => {
233
+ try {
234
+ const items = await fetchAds(apiKey, {
235
+ cryptoCurrency,
236
+ fiatCurrency,
237
+ side,
238
+ pageSize: 50,
239
+ });
240
+ if (items.length === 0) {
241
+ return {
242
+ content: [
243
+ {
244
+ type: "text",
245
+ text: JSON.stringify({
246
+ message: "No ads found for the specified criteria.",
247
+ cryptoCurrency,
248
+ fiatCurrency,
249
+ side,
250
+ }),
251
+ },
252
+ ],
253
+ };
254
+ }
255
+ const best = items.reduce((best, item) => {
256
+ const bestVal = parseFloat(best.price);
257
+ const itemVal = parseFloat(item.price);
258
+ if (side === "BUY") {
259
+ return itemVal < bestVal ? item : best;
260
+ }
261
+ else {
262
+ return itemVal > bestVal ? item : best;
263
+ }
264
+ });
265
+ return {
266
+ content: [
267
+ {
268
+ type: "text",
269
+ text: JSON.stringify({
270
+ cryptoCurrency,
271
+ fiatCurrency,
272
+ side,
273
+ bestPrice: best.price,
274
+ trader: {
275
+ nickname: best.nickname,
276
+ merchantLevel: best.merchantLevel,
277
+ completedOrders: best.orderNum,
278
+ completionRate: best.executeRate,
279
+ isOnline: best.isOnline,
280
+ },
281
+ availableQuantity: best.lastQuantity,
282
+ amountRange: {
283
+ min: best.minAmount,
284
+ max: best.maxAmount,
285
+ },
286
+ payments: best.payments,
287
+ autoAccept: best.isAutoAccept,
288
+ paymentPeriod: best.paymentPeriod,
289
+ totalAdsScanned: items.length,
290
+ }, null, 2),
291
+ },
292
+ ],
293
+ };
294
+ }
295
+ catch (error) {
296
+ return {
297
+ content: [
298
+ {
299
+ type: "text",
300
+ text: `Error finding best price: ${error instanceof Error ? error.message : String(error)}`,
301
+ },
302
+ ],
303
+ isError: true,
304
+ };
305
+ }
306
+ });
307
+ // --- Start server ---
308
+ async function main() {
309
+ const transport = new StdioServerTransport();
310
+ await server.connect(transport);
311
+ console.error("Telegram Wallet P2P MCP Server running on stdio");
312
+ }
313
+ main().catch((error) => {
314
+ console.error("Fatal error:", error);
315
+ process.exit(1);
316
+ });
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "telegram-wallet-p2p-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Unofficial MCP server for Telegram Wallet P2P market data analytics",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "telegram-wallet-p2p-mcp": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "npx -p typescript tsc",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "telegram",
21
+ "wallet",
22
+ "p2p",
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "crypto",
26
+ "analytics"
27
+ ],
28
+ "author": "Furkan Köykıran",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/furkankoykiran/telegram-wallet-p2p-sdk",
33
+ "directory": "packages/mcp"
34
+ },
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.27.0",
40
+ "zod": "^3.25.76"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.19.33",
44
+ "typescript": "^5.4.0"
45
+ }
46
+ }